зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound, a=merge
This commit is contained in:
Коммит
a0ecf84551
|
@ -709,7 +709,7 @@ function gKeywordURIFixup({ target: browser, data: fixupInfo }) {
|
|||
// making it the same as 7f000001, which is 127.0.0.1 aka localhost.
|
||||
// While 2130706433 would get normalized by network, 1097347366913
|
||||
// does not, and we have to deal with both cases here:
|
||||
if (isIPv4Address(asciiHost) || /^\d+$/.test(asciiHost))
|
||||
if (isIPv4Address(asciiHost) || /^(?:\d+|0x[a-f0-9]+)$/i.test(asciiHost))
|
||||
return;
|
||||
|
||||
let onLookupComplete = (request, record, status) => {
|
||||
|
|
|
@ -103,6 +103,29 @@ add_task(function* test_navigate_large_number() {
|
|||
});
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(function* test_navigate_small_hex_number() {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
||||
yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
yield* runURLBarSearchTest({
|
||||
valueToOpen: "0x1f00ffff",
|
||||
expectSearch: true,
|
||||
expectNotification: false
|
||||
});
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(function* test_navigate_large_hex_number() {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
|
||||
yield BrowserTestUtils.browserLoaded(tab.linkedBrowser);
|
||||
yield* runURLBarSearchTest({
|
||||
valueToOpen: "0x7f0000017f000001",
|
||||
expectSearch: true,
|
||||
expectNotification: false
|
||||
});
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
function get_test_function_for_localhost_with_hostname(hostName, isPrivate) {
|
||||
return function* test_navigate_single_host() {
|
||||
const pref = "browser.fixup.domainwhitelist.localhost";
|
||||
|
|
|
@ -11,6 +11,7 @@ function *task_openDownloadsSubPanel() {
|
|||
}
|
||||
|
||||
add_task(function* test_openDownloadsFolder() {
|
||||
yield SpecialPowers.pushPrefEnv({"set": [["browser.download.showPanelDropmarker", true]]});
|
||||
yield task_openPanel();
|
||||
|
||||
yield task_openDownloadsSubPanel();
|
||||
|
|
|
@ -87,7 +87,11 @@ const AutoMigrate = {
|
|||
// (Note that this ignores logins being removed as that doesn't
|
||||
// impair the 'undo' functionality of the import.)
|
||||
if (kPasswordManagerTopicTypes.has(data)) {
|
||||
this.removeUndoOption(this.UNDO_REMOVED_REASON_PASSWORD_CHANGE);
|
||||
// Ignore chrome:// things like sync credentials:
|
||||
let loginInfo = subject.QueryInterface(Ci.nsILoginInfo);
|
||||
if (!loginInfo.hostname || !loginInfo.hostname.startsWith("chrome://")) {
|
||||
this.removeUndoOption(this.UNDO_REMOVED_REASON_PASSWORD_CHANGE);
|
||||
}
|
||||
}
|
||||
} else if (topic == kSyncTopic) {
|
||||
this.removeUndoOption(this.UNDO_REMOVED_REASON_SYNC_SIGNIN);
|
||||
|
|
|
@ -7,42 +7,33 @@ import sys
|
|||
import threading
|
||||
import time
|
||||
|
||||
from win32 import procmem
|
||||
|
||||
def measure_vsize_threadfunc(proc, output_file):
|
||||
def periodically_print_status(proc):
|
||||
"""
|
||||
Measure the virtual memory usage of |proc| at regular intervals
|
||||
until it exits, then print the maximum value and write it to
|
||||
|output_file|. Also, print something to the console every
|
||||
half an hour to prevent the build job from getting killed when
|
||||
linking a large PGOed binary.
|
||||
Print something to the console every 20 minutes to prevent the build job
|
||||
from getting killed when linking a large binary.
|
||||
Check status of the linker every 0.5 seconds.
|
||||
"""
|
||||
maxvsize = 0
|
||||
idleTime = 0
|
||||
while proc.returncode is None:
|
||||
maxvsize, vsize = procmem.get_vmsize(proc._handle)
|
||||
time.sleep(0.5)
|
||||
idleTime += 0.5
|
||||
if idleTime > 30 * 60:
|
||||
print "Still linking, 30 minutes passed..."
|
||||
if idleTime > 20 * 60:
|
||||
print "Still linking, 20 minutes passed..."
|
||||
sys.stdout.flush()
|
||||
idleTime = 0
|
||||
print "TinderboxPrint: linker max vsize: %d" % maxvsize
|
||||
with open(output_file, "w") as f:
|
||||
f.write("%d\n" % maxvsize)
|
||||
|
||||
def measure_link_vsize(output_file, args):
|
||||
def wrap_linker(args):
|
||||
"""
|
||||
Execute |args|, and measure the maximum virtual memory usage of the process,
|
||||
printing it to stdout when finished.
|
||||
Execute |args| and pass resulting |proc| object to a second thread that
|
||||
will track the status of the started |proc|.
|
||||
"""
|
||||
|
||||
# This needs to be a list in order for the callback to set the
|
||||
# variable properly with python-2's scoping rules.
|
||||
t = [None]
|
||||
def callback(proc):
|
||||
t[0] = threading.Thread(target=measure_vsize_threadfunc,
|
||||
args=(proc, output_file))
|
||||
t[0] = threading.Thread(target=periodically_print_status,
|
||||
args=(proc,))
|
||||
t[0].start()
|
||||
exitcode = expandlibs_exec.main(args, proc_callback=callback)
|
||||
# Wait for the background thread to finish.
|
||||
|
@ -50,11 +41,7 @@ def measure_link_vsize(output_file, args):
|
|||
return exitcode
|
||||
|
||||
if __name__ == "__main__":
|
||||
if sys.platform != "win32":
|
||||
print >>sys.stderr, "link.py is only for use on Windows!"
|
||||
if len(sys.argv) < 2:
|
||||
print >>sys.stderr, "Usage: link.py <commandline>"
|
||||
sys.exit(1)
|
||||
if len(sys.argv) < 3:
|
||||
print >>sys.stderr, "Usage: link.py <output filename> <commandline>"
|
||||
sys.exit(1)
|
||||
output_file = sys.argv.pop(1)
|
||||
sys.exit(measure_link_vsize(output_file, sys.argv[1:]))
|
||||
sys.exit(wrap_linker(sys.argv[1:]))
|
||||
|
|
|
@ -144,7 +144,7 @@ var DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
|||
var promise = require("devtools/shared/deprecated-sync-thenables");
|
||||
var Editor = require("devtools/client/sourceeditor/editor");
|
||||
var DebuggerEditor = require("devtools/client/sourceeditor/debugger");
|
||||
var {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
|
||||
var Tooltip = require("devtools/client/shared/widgets/Tooltip");
|
||||
var FastListWidget = require("devtools/client/shared/widgets/FastListWidget");
|
||||
var {LocalizationHelper, ELLIPSIS} = require("devtools/shared/l10n");
|
||||
var {PrefsHelper} = require("devtools/client/shared/prefs");
|
||||
|
|
|
@ -201,15 +201,18 @@ function synthesizeKeyShortcut(key, target) {
|
|||
// parseElectronKey requires any window, just to access `KeyboardEvent`
|
||||
let window = Services.appShell.hiddenDOMWindow;
|
||||
let shortcut = KeyShortcuts.parseElectronKey(window, key);
|
||||
|
||||
info("Synthesizing key shortcut: " + key);
|
||||
EventUtils.synthesizeKey(shortcut.key || "", {
|
||||
keyCode: shortcut.keyCode,
|
||||
let keyEvent = {
|
||||
altKey: shortcut.alt,
|
||||
ctrlKey: shortcut.ctrl,
|
||||
metaKey: shortcut.meta,
|
||||
shiftKey: shortcut.shift
|
||||
}, target);
|
||||
};
|
||||
if (shortcut.keyCode) {
|
||||
keyEvent.keyCode = shortcut.keyCode;
|
||||
}
|
||||
|
||||
info("Synthesizing key shortcut: " + key);
|
||||
EventUtils.synthesizeKey(shortcut.key || "", keyEvent, target);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
// Avoid test timeouts on Linux debug builds where the test takes just a bit too long to
|
||||
// run (see bug 1258081).
|
||||
requestLongerTimeout(2);
|
||||
|
||||
// Tests that search filter escape keypress will clear the search field.
|
||||
|
||||
const TEST_URI = `
|
||||
|
|
|
@ -57,10 +57,8 @@ function HTMLEditor(htmlDocument) {
|
|||
this.editorInner.addEventListener("click", stopPropagation, false);
|
||||
this.editor = new Editor(config);
|
||||
|
||||
let iframe = this.editorInner.ownerDocument.createElement("iframe");
|
||||
this.editor.appendTo(this.editorInner, iframe).then(() => {
|
||||
this.hide(false);
|
||||
}).then(null, (err) => console.log(err.message));
|
||||
this.editor.appendToLocalElement(this.editorInner);
|
||||
this.hide(false);
|
||||
}
|
||||
|
||||
HTMLEditor.prototype = {
|
||||
|
|
|
@ -3198,6 +3198,7 @@ ElementEditor.prototype = {
|
|||
// Create the template editor, which will save some variables here.
|
||||
let data = {
|
||||
attrName: attribute.name,
|
||||
attrValue: attribute.value,
|
||||
tabindex: this.container.canFocus ? "0" : "-1",
|
||||
};
|
||||
this.template("attribute", data);
|
||||
|
|
|
@ -8,9 +8,14 @@
|
|||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/skin/markup.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css" type="text/css"/>
|
||||
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="chrome://devtools/content/shared/theme-switching.js"/>
|
||||
src="chrome://devtools/content/shared/theme-switching.js"></script>
|
||||
<script type="application/javascript;version=1.8"
|
||||
src="chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js"></script>
|
||||
|
||||
</head>
|
||||
<body class="theme-body devtools-monospace" role="application">
|
||||
|
|
|
@ -23,7 +23,9 @@ const TEST_DATA = [
|
|||
check: function* (inspector) {
|
||||
let {editor} = yield getContainerForSelector("#node1", inspector);
|
||||
ok([...editor.attrList.querySelectorAll(".attreditor")].some(attr => {
|
||||
return attr.textContent.trim() === "newattr=\"newattrval\"";
|
||||
return attr.textContent.trim() === "newattr=\"newattrval\""
|
||||
&& attr.dataset.value === "newattrval"
|
||||
&& attr.dataset.attr === "newattr";
|
||||
}), "newattr attribute found");
|
||||
}
|
||||
},
|
||||
|
@ -47,7 +49,9 @@ const TEST_DATA = [
|
|||
check: function* (inspector) {
|
||||
let {editor} = yield getContainerForSelector("#node1", inspector);
|
||||
ok([...editor.attrList.querySelectorAll(".attreditor")].some(attr => {
|
||||
return attr.textContent.trim() === "newattr=\"newattrval\"";
|
||||
return attr.textContent.trim() === "newattr=\"newattrval\""
|
||||
&& attr.dataset.value === "newattrval"
|
||||
&& attr.dataset.attr === "newattr";
|
||||
}), "newattr attribute found");
|
||||
}
|
||||
},
|
||||
|
@ -59,10 +63,42 @@ const TEST_DATA = [
|
|||
check: function* (inspector) {
|
||||
let {editor} = yield getContainerForSelector("#node1", inspector);
|
||||
ok([...editor.attrList.querySelectorAll(".attreditor")].some(attr => {
|
||||
return attr.textContent.trim() === "newattr=\"newattrchanged\"";
|
||||
return attr.textContent.trim() === "newattr=\"newattrchanged\""
|
||||
&& attr.dataset.value === "newattrchanged"
|
||||
&& attr.dataset.attr === "newattr";
|
||||
}), "newattr attribute found");
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Adding another attribute does not rerender unchanged attributes",
|
||||
test: function* (testActor, inspector) {
|
||||
let {editor} = yield getContainerForSelector("#node1", inspector);
|
||||
|
||||
// This test checks the impact on the markup-view nodes after setting attributes on
|
||||
// content nodes.
|
||||
info("Expect attribute-container for 'new-attr' from the previous test");
|
||||
let attributeContainer = editor.attrList.querySelector("[data-attr=newattr]");
|
||||
ok(attributeContainer, "attribute-container for 'newattr' found");
|
||||
|
||||
info("Set a flag on the attribute-container to check after the mutation");
|
||||
attributeContainer.beforeMutationFlag = true;
|
||||
|
||||
info("Add the attribute 'otherattr' on the content node to trigger the mutation");
|
||||
yield testActor.setAttribute("#node1", "otherattr", "othervalue");
|
||||
},
|
||||
check: function* (inspector) {
|
||||
let {editor} = yield getContainerForSelector("#node1", inspector);
|
||||
|
||||
info("Check the attribute-container for the new attribute mutation was created");
|
||||
let otherAttrContainer = editor.attrList.querySelector("[data-attr=otherattr]");
|
||||
ok(otherAttrContainer, "attribute-container for 'otherattr' found");
|
||||
|
||||
info("Check the attribute-container for 'new-attr' is the same node as earlier.");
|
||||
let newAttrContainer = editor.attrList.querySelector("[data-attr=newattr]");
|
||||
ok(newAttrContainer, "attribute-container for 'newattr' found");
|
||||
ok(newAttrContainer.beforeMutationFlag, "attribute-container same as earlier");
|
||||
}
|
||||
},
|
||||
{
|
||||
desc: "Adding ::after element",
|
||||
numMutations: 2,
|
||||
|
@ -292,7 +328,7 @@ add_task(function* () {
|
|||
def.resolve();
|
||||
}
|
||||
});
|
||||
yield test(testActor);
|
||||
yield test(testActor, inspector);
|
||||
yield def.promise;
|
||||
|
||||
info("Expanding all markup-view nodes to make sure new nodes are imported");
|
||||
|
|
|
@ -19,14 +19,10 @@ const {
|
|||
setImageTooltip,
|
||||
setBrokenImageTooltip,
|
||||
} = require("devtools/client/shared/widgets/tooltip/ImageTooltipHelper");
|
||||
const {
|
||||
CssDocsTooltip,
|
||||
} = require("devtools/client/shared/widgets/tooltip/CssDocsTooltip");
|
||||
const {
|
||||
SwatchColorPickerTooltip,
|
||||
SwatchCubicBezierTooltip,
|
||||
SwatchFilterTooltip
|
||||
} = require("devtools/client/shared/widgets/Tooltip");
|
||||
const CssDocsTooltip = require("devtools/client/shared/widgets/tooltip/CssDocsTooltip");
|
||||
const SwatchColorPickerTooltip = require("devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip");
|
||||
const SwatchCubicBezierTooltip = require("devtools/client/shared/widgets/tooltip/SwatchCubicBezierTooltip");
|
||||
const SwatchFilterTooltip = require("devtools/client/shared/widgets/tooltip/SwatchFilterTooltip");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const promise = require("promise");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
|
|
|
@ -108,12 +108,32 @@ autocompletion.commandkey=Space
|
|||
# These are key identifiers, not messages displayed to the user.
|
||||
showInformation2.commandkey=Shift-Ctrl-Space
|
||||
|
||||
# LOCALIZATION NOTE (find.commandkey): This is the key to use in
|
||||
# conjunction with accel (Command on Mac or Ctrl on other platforms) to find
|
||||
# the typed search
|
||||
find.commandkey=F
|
||||
# LOCALIZATION NOTE (find.key):
|
||||
# Key shortcut used to find the typed search
|
||||
# Do not localize "CmdOrCtrl", "F", or change the format of the string. These are
|
||||
# key identifiers, not messages displayed to the user.
|
||||
find.key=CmdOrCtrl+F
|
||||
|
||||
# LOCALIZATION NOTE (findAgain.commandkey): This is the key to use in
|
||||
# conjunction with accel (Command on Mac or Ctrl on other platforms) to find
|
||||
# again the typed search
|
||||
findAgain.commandkey=G
|
||||
# LOCALIZATION NOTE (replaceAll.key):
|
||||
# Key shortcut used to replace the content of the editor
|
||||
# Do not localize "Shift", "CmdOrCtrl", "F", or change the format of the string. These are
|
||||
# key identifiers, not messages displayed to the user.
|
||||
replaceAll.key=Shift+CmdOrCtrl+F
|
||||
|
||||
# LOCALIZATION NOTE (replaceAllMac.key):
|
||||
# Key shortcut used to replace the content of the editor on Mac
|
||||
# Do not localize "Alt", "CmdOrCtrl", "F", or change the format of the string. These are
|
||||
# key identifiers, not messages displayed to the user.
|
||||
replaceAllMac.key=Alt+CmdOrCtrl+F
|
||||
|
||||
# LOCALIZATION NOTE (findNext.key):
|
||||
# Key shortcut used to find again the typed search
|
||||
# Do not localize "CmdOrCtrl", "G", or change the format of the string. These are
|
||||
# key identifiers, not messages displayed to the user.
|
||||
findNext.key=CmdOrCtrl+G
|
||||
|
||||
# LOCALIZATION NOTE (findPrev.key):
|
||||
# Key shortcut used to find the previous typed search
|
||||
# Do not localize "Shift", "CmdOrCtrl", "G", or change the format of the string. These are
|
||||
# key identifiers, not messages displayed to the user.
|
||||
findPrev.key=Shift+CmdOrCtrl+G
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
; 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 is specifically to allow SeaMonkey and Thunderbird
|
||||
; to use the devtools client localizations from comm-*
|
||||
|
||||
[general]
|
||||
depth = ../../..
|
||||
|
||||
[compare]
|
||||
dirs = devtools/client
|
|
@ -315,7 +315,11 @@ pref("devtools.webconsole.timestampMessages", false);
|
|||
pref("devtools.webconsole.autoMultiline", true);
|
||||
|
||||
// Enable the experimental webconsole frontend (work in progress)
|
||||
#if defined(NIGHTLY_BUILD)
|
||||
pref("devtools.webconsole.new-frontend-enabled", true);
|
||||
#else
|
||||
pref("devtools.webconsole.new-frontend-enabled", false);
|
||||
#endif
|
||||
|
||||
// Enable the experimental support for source maps in console (work in progress)
|
||||
pref("devtools.sourcemap.locations.enabled", false);
|
||||
|
|
|
@ -13,8 +13,6 @@ const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
|
|||
|
||||
const Types = require("devtools/client/responsive.html/types");
|
||||
|
||||
const { addDevice, removeDevice } = require("devtools/client/shared/devices");
|
||||
|
||||
const testDevice = {
|
||||
"name": "Fake Phone RDM Test",
|
||||
"width": 320,
|
||||
|
@ -28,7 +26,7 @@ const testDevice = {
|
|||
};
|
||||
|
||||
// Add the new device to the list
|
||||
addDevice(testDevice);
|
||||
addDeviceForTest(testDevice);
|
||||
|
||||
addRDMTask(TEST_URL, function* ({ ui, manager }) {
|
||||
let { store } = ui.toolWindow;
|
||||
|
@ -65,9 +63,6 @@ addRDMTask(TEST_URL, function* ({ ui, manager }) {
|
|||
yield testUserAgent(ui, DEFAULT_UA);
|
||||
yield testDevicePixelRatio(ui, 1);
|
||||
yield testTouchEventsOverride(ui, false);
|
||||
|
||||
ok(removeDevice(testDevice),
|
||||
"Test Device properly removed.");
|
||||
});
|
||||
|
||||
function testViewportDimensions(ui, w, h) {
|
||||
|
|
|
@ -4,7 +4,7 @@ http://creativecommons.org/publicdomain/zero/1.0/ */
|
|||
"use strict";
|
||||
|
||||
// Test submitting display device changes on the device modal
|
||||
const { getDevices, addDevice } = require("devtools/client/shared/devices");
|
||||
const { getDevices } = require("devtools/client/shared/devices");
|
||||
|
||||
const addedDevice = {
|
||||
"name": "Fake Phone RDM Test",
|
||||
|
@ -107,7 +107,7 @@ addRDMTask(TEST_URL, function* ({ ui }) {
|
|||
checkedVal + " is unchecked in the device modal.");
|
||||
|
||||
// Let's add a dummy device to simulate featured flag changes for next test
|
||||
addDevice(addedDevice);
|
||||
addDeviceForTest(addedDevice);
|
||||
});
|
||||
|
||||
addRDMTask(TEST_URL, function* ({ ui }) {
|
||||
|
|
|
@ -37,6 +37,7 @@ const OPEN_DEVICE_MODAL_VALUE = "OPEN_DEVICE_MODAL";
|
|||
const { _loadPreferredDevices } = require("devtools/client/responsive.html/actions/devices");
|
||||
const { getOwnerWindow } = require("sdk/tabs/utils");
|
||||
const asyncStorage = require("devtools/shared/async-storage");
|
||||
const { addDevice, removeDevice } = require("devtools/client/shared/devices");
|
||||
|
||||
SimpleTest.requestCompleteLog();
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -229,9 +230,14 @@ function openDeviceModal(ui) {
|
|||
"The device modal is displayed.");
|
||||
}
|
||||
|
||||
function switchDevice({ toolWindow }, name) {
|
||||
function switchDevice({ toolWindow }, value) {
|
||||
return new Promise(resolve => {
|
||||
let select = toolWindow.document.querySelector(".viewport-device-selector");
|
||||
let selector = ".viewport-device-selector";
|
||||
let select = toolWindow.document.querySelector(selector);
|
||||
isnot(select, null, `selector "${selector}" should match an existing element.`);
|
||||
|
||||
let option = [...select.options].find(o => o.value === String(value));
|
||||
isnot(option, undefined, `value "${value}" should match an existing option.`);
|
||||
|
||||
let event = new toolWindow.UIEvent("change", {
|
||||
view: toolWindow,
|
||||
|
@ -239,13 +245,13 @@ function switchDevice({ toolWindow }, name) {
|
|||
cancelable: true
|
||||
});
|
||||
|
||||
select.addEventListener("change", function onChange() {
|
||||
is(select.value, name, "Device should be selected");
|
||||
select.removeEventListener("change", onChange);
|
||||
select.addEventListener("change", () => {
|
||||
is(select.value, value,
|
||||
`Select's option with value "${value}" should be selected.`);
|
||||
resolve();
|
||||
});
|
||||
}, { once: true });
|
||||
|
||||
select.value = name;
|
||||
select.value = value;
|
||||
select.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
|
@ -312,3 +318,13 @@ function forward(browser) {
|
|||
browser.goForward();
|
||||
return shown;
|
||||
}
|
||||
|
||||
function addDeviceForTest(device) {
|
||||
info(`Adding Test Device "${device.name}" to the list.`);
|
||||
addDevice(device);
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
// Note that assertions in cleanup functions are not displayed unless they failed.
|
||||
ok(removeDevice(device), `Removed Test Device "${device.name}" from the list.`);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ function runTests(aWindow, aScratchpad)
|
|||
is(editor.getCursor().line, 2, "line is correct");
|
||||
|
||||
desiredValue = 2;
|
||||
aWindow.goDoCommand("cmd_gotoLine");
|
||||
EventUtils.synthesizeKey("J", {accelKey: true}, aWindow);
|
||||
is(editor.getCursor().line, 1, "line is correct (again)");
|
||||
|
||||
editor.openDialog = oldPrompt;
|
||||
|
|
|
@ -11,7 +11,7 @@ const {SideMenuWidget} = require("resource://devtools/client/shared/widgets/Side
|
|||
const promise = require("promise");
|
||||
const Services = require("Services");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
|
||||
const Tooltip = require("devtools/client/shared/widgets/Tooltip");
|
||||
const Editor = require("devtools/client/sourceeditor/editor");
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
const {Heritage, WidgetMethods, setNamedTimeout} =
|
||||
|
|
|
@ -5,7 +5,11 @@
|
|||
/* eslint-env browser */
|
||||
"use strict";
|
||||
(function () {
|
||||
const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-dark-theme.css";
|
||||
const { utils: Cu } = Components;
|
||||
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const { gDevTools } = require("devtools/client/framework/devtools");
|
||||
const { watchCSS } = require("devtools/client/shared/css-reload");
|
||||
let documentElement = document.documentElement;
|
||||
|
||||
let os;
|
||||
|
@ -113,19 +117,22 @@
|
|||
loadEvents.push(loadPromise);
|
||||
}
|
||||
|
||||
// Floating scroll-bars like in OSX
|
||||
let hiddenDOMWindow = Cc["@mozilla.org/appshell/appShellService;1"]
|
||||
.getService(Ci.nsIAppShellService)
|
||||
.hiddenDOMWindow;
|
||||
try {
|
||||
const StylesheetUtils = require("sdk/stylesheet/utils");
|
||||
const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-dark-theme.css";
|
||||
|
||||
// TODO: extensions might want to customize scrollbar styles too.
|
||||
if (!hiddenDOMWindow.matchMedia("(-moz-overlay-scrollbars)").matches) {
|
||||
if (newTheme == "dark") {
|
||||
StylesheetUtils.loadSheet(window, SCROLLBARS_URL, "agent");
|
||||
} else if (oldTheme == "dark") {
|
||||
StylesheetUtils.removeSheet(window, SCROLLBARS_URL, "agent");
|
||||
// TODO: extensions might want to customize scrollbar styles too.
|
||||
if (!Services.appShell.hiddenDOMWindow
|
||||
.matchMedia("(-moz-overlay-scrollbars)").matches) {
|
||||
if (newTheme == "dark") {
|
||||
StylesheetUtils.loadSheet(window, SCROLLBARS_URL, "agent");
|
||||
} else if (oldTheme == "dark") {
|
||||
StylesheetUtils.removeSheet(window, SCROLLBARS_URL, "agent");
|
||||
}
|
||||
forceStyle();
|
||||
}
|
||||
forceStyle();
|
||||
} catch (e) {
|
||||
console.warn("customize scrollbar styles is only supported in firefox");
|
||||
}
|
||||
|
||||
Promise.all(loadEvents).then(() => {
|
||||
|
@ -163,13 +170,6 @@
|
|||
switchTheme(Services.prefs.getCharPref("devtools.theme"));
|
||||
}
|
||||
|
||||
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
const Services = require("Services");
|
||||
const { gDevTools } = require("devtools/client/framework/devtools");
|
||||
const StylesheetUtils = require("sdk/stylesheet/utils");
|
||||
const { watchCSS } = require("devtools/client/shared/css-reload");
|
||||
|
||||
if (documentElement.hasAttribute("force-theme")) {
|
||||
switchTheme(documentElement.getAttribute("force-theme"));
|
||||
} else {
|
||||
|
|
|
@ -5,18 +5,9 @@
|
|||
"use strict";
|
||||
|
||||
const defer = require("devtools/shared/defer");
|
||||
const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
|
||||
const {CubicBezierWidget} =
|
||||
require("devtools/client/shared/widgets/CubicBezierWidget");
|
||||
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
|
||||
const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
|
||||
const EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {colorUtils} = require("devtools/shared/css/color");
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
|
||||
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
const {KeyCodes} = require("devtools/client/shared/keycodes");
|
||||
const {TooltipToggle} = require("devtools/client/shared/widgets/tooltip/TooltipToggle");
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
const ESCAPE_KEYCODE = KeyCodes.DOM_VK_ESCAPE;
|
||||
|
@ -215,8 +206,6 @@ function Tooltip(doc, options) {
|
|||
}
|
||||
}
|
||||
|
||||
module.exports.Tooltip = Tooltip;
|
||||
|
||||
Tooltip.prototype = {
|
||||
defaultPosition: "before_start",
|
||||
// px
|
||||
|
@ -468,545 +457,4 @@ Tooltip.prototype = {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class for all (color, gradient, ...)-swatch based value editors inside
|
||||
* tooltips
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* The devtools toolbox, needed to get the devtools main window.
|
||||
*/
|
||||
function SwatchBasedEditorTooltip(toolbox, stylesheet) {
|
||||
EventEmitter.decorate(this);
|
||||
// Creating a tooltip instance
|
||||
// This one will consume outside clicks as it makes more sense to let the user
|
||||
// close the tooltip by clicking out
|
||||
// It will also close on <escape> and <enter>
|
||||
this.tooltip = new HTMLTooltip(toolbox, {
|
||||
type: "arrow",
|
||||
consumeOutsideClicks: true,
|
||||
useXulWrapper: true,
|
||||
stylesheet
|
||||
});
|
||||
|
||||
// By default, swatch-based editor tooltips revert value change on <esc> and
|
||||
// commit value change on <enter>
|
||||
this.shortcuts = new KeyShortcuts({
|
||||
window: this.tooltip.topWindow
|
||||
});
|
||||
this.shortcuts.on("Escape", (name, event) => {
|
||||
if (!this.tooltip.isVisible()) {
|
||||
return;
|
||||
}
|
||||
this.revert();
|
||||
this.hide();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
this.shortcuts.on("Return", (name, event) => {
|
||||
if (!this.tooltip.isVisible()) {
|
||||
return;
|
||||
}
|
||||
this.commit();
|
||||
this.hide();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// All target swatches are kept in a map, indexed by swatch DOM elements
|
||||
this.swatches = new Map();
|
||||
|
||||
// When a swatch is clicked, and for as long as the tooltip is shown, the
|
||||
// activeSwatch property will hold the reference to the swatch DOM element
|
||||
// that was clicked
|
||||
this.activeSwatch = null;
|
||||
|
||||
this._onSwatchClick = this._onSwatchClick.bind(this);
|
||||
}
|
||||
|
||||
SwatchBasedEditorTooltip.prototype = {
|
||||
/**
|
||||
* Show the editor tooltip for the currently active swatch.
|
||||
*
|
||||
* @return {Promise} a promise that resolves once the editor tooltip is displayed, or
|
||||
* immediately if there is no currently active swatch.
|
||||
*/
|
||||
show: function () {
|
||||
if (this.activeSwatch) {
|
||||
let onShown = this.tooltip.once("shown");
|
||||
this.tooltip.show(this.activeSwatch, "topcenter bottomleft");
|
||||
|
||||
// When the tooltip is closed by clicking outside the panel we want to
|
||||
// commit any changes.
|
||||
this.tooltip.once("hidden", () => {
|
||||
if (!this._reverted && !this.eyedropperOpen) {
|
||||
this.commit();
|
||||
}
|
||||
this._reverted = false;
|
||||
|
||||
// Once the tooltip is hidden we need to clean up any remaining objects.
|
||||
if (!this.eyedropperOpen) {
|
||||
this.activeSwatch = null;
|
||||
}
|
||||
});
|
||||
|
||||
return onShown;
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.tooltip.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new swatch DOM element to the list of swatch elements this editor
|
||||
* tooltip knows about. That means from now on, clicking on that swatch will
|
||||
* toggle the editor.
|
||||
*
|
||||
* @param {node} swatchEl
|
||||
* The element to add
|
||||
* @param {object} callbacks
|
||||
* Callbacks that will be executed when the editor wants to preview a
|
||||
* value change, or revert a change, or commit a change.
|
||||
* - onShow: will be called when one of the swatch tooltip is shown
|
||||
* - onPreview: will be called when one of the sub-classes calls
|
||||
* preview
|
||||
* - onRevert: will be called when the user ESCapes out of the tooltip
|
||||
* - onCommit: will be called when the user presses ENTER or clicks
|
||||
* outside the tooltip.
|
||||
*/
|
||||
addSwatch: function (swatchEl, callbacks = {}) {
|
||||
if (!callbacks.onShow) {
|
||||
callbacks.onShow = function () {};
|
||||
}
|
||||
if (!callbacks.onPreview) {
|
||||
callbacks.onPreview = function () {};
|
||||
}
|
||||
if (!callbacks.onRevert) {
|
||||
callbacks.onRevert = function () {};
|
||||
}
|
||||
if (!callbacks.onCommit) {
|
||||
callbacks.onCommit = function () {};
|
||||
}
|
||||
|
||||
this.swatches.set(swatchEl, {
|
||||
callbacks: callbacks
|
||||
});
|
||||
swatchEl.addEventListener("click", this._onSwatchClick, false);
|
||||
},
|
||||
|
||||
removeSwatch: function (swatchEl) {
|
||||
if (this.swatches.has(swatchEl)) {
|
||||
if (this.activeSwatch === swatchEl) {
|
||||
this.hide();
|
||||
this.activeSwatch = null;
|
||||
}
|
||||
swatchEl.removeEventListener("click", this._onSwatchClick, false);
|
||||
this.swatches.delete(swatchEl);
|
||||
}
|
||||
},
|
||||
|
||||
_onSwatchClick: function (event) {
|
||||
let swatch = this.swatches.get(event.target);
|
||||
|
||||
if (event.shiftKey) {
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (swatch) {
|
||||
this.activeSwatch = event.target;
|
||||
this.show();
|
||||
swatch.callbacks.onShow();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Not called by this parent class, needs to be taken care of by sub-classes
|
||||
*/
|
||||
preview: function (value) {
|
||||
if (this.activeSwatch) {
|
||||
let swatch = this.swatches.get(this.activeSwatch);
|
||||
swatch.callbacks.onPreview(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This parent class only calls this on <esc> keypress
|
||||
*/
|
||||
revert: function () {
|
||||
if (this.activeSwatch) {
|
||||
this._reverted = true;
|
||||
let swatch = this.swatches.get(this.activeSwatch);
|
||||
this.tooltip.once("hidden", () => {
|
||||
swatch.callbacks.onRevert();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This parent class only calls this on <enter> keypress
|
||||
*/
|
||||
commit: function () {
|
||||
if (this.activeSwatch) {
|
||||
let swatch = this.swatches.get(this.activeSwatch);
|
||||
swatch.callbacks.onCommit();
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.swatches.clear();
|
||||
this.activeSwatch = null;
|
||||
this.tooltip.off("keypress", this._onTooltipKeypress);
|
||||
this.tooltip.destroy();
|
||||
this.shortcuts.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The swatch color picker tooltip class is a specific class meant to be used
|
||||
* along with output-parser's generated color swatches.
|
||||
* It extends the parent SwatchBasedEditorTooltip class.
|
||||
* It just wraps a standard Tooltip and sets its content with an instance of a
|
||||
* color picker.
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* The devtools toolbox, needed to get the devtools main window.
|
||||
* @param {InspectorPanel} inspector
|
||||
* The inspector panel, needed for the eyedropper.
|
||||
*/
|
||||
function SwatchColorPickerTooltip(toolbox, inspector) {
|
||||
let stylesheet = "chrome://devtools/content/shared/widgets/spectrum.css";
|
||||
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
|
||||
|
||||
this.inspector = inspector;
|
||||
|
||||
// Creating a spectrum instance. this.spectrum will always be a promise that
|
||||
// resolves to the spectrum instance
|
||||
this.spectrum = this.setColorPickerContent([0, 0, 0, 1]);
|
||||
this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this);
|
||||
this._openEyeDropper = this._openEyeDropper.bind(this);
|
||||
}
|
||||
|
||||
module.exports.SwatchColorPickerTooltip = SwatchColorPickerTooltip;
|
||||
|
||||
SwatchColorPickerTooltip.prototype =
|
||||
Heritage.extend(SwatchBasedEditorTooltip.prototype, {
|
||||
/**
|
||||
* Fill the tooltip with a new instance of the spectrum color picker widget
|
||||
* initialized with the given color, and return the instance of spectrum
|
||||
*/
|
||||
setColorPickerContent: function (color) {
|
||||
let { doc } = this.tooltip;
|
||||
|
||||
let container = doc.createElementNS(XHTML_NS, "div");
|
||||
container.id = "spectrum-tooltip";
|
||||
let spectrumNode = doc.createElementNS(XHTML_NS, "div");
|
||||
spectrumNode.id = "spectrum";
|
||||
container.appendChild(spectrumNode);
|
||||
let eyedropper = doc.createElementNS(XHTML_NS, "button");
|
||||
eyedropper.id = "eyedropper-button";
|
||||
eyedropper.className = "devtools-button";
|
||||
container.appendChild(eyedropper);
|
||||
|
||||
this.tooltip.setContent(container, { width: 218, height: 224 });
|
||||
|
||||
let spectrum = new Spectrum(spectrumNode, color);
|
||||
|
||||
// Wait for the tooltip to be shown before calling spectrum.show
|
||||
// as it expect to be visible in order to compute DOM element sizes.
|
||||
this.tooltip.once("shown", () => {
|
||||
spectrum.show();
|
||||
});
|
||||
|
||||
return spectrum;
|
||||
},
|
||||
|
||||
/**
|
||||
* Overriding the SwatchBasedEditorTooltip.show function to set spectrum's
|
||||
* color.
|
||||
*/
|
||||
show: Task.async(function* () {
|
||||
// Call then parent class' show function
|
||||
yield SwatchBasedEditorTooltip.prototype.show.call(this);
|
||||
// Then set spectrum's color and listen to color changes to preview them
|
||||
if (this.activeSwatch) {
|
||||
this.currentSwatchColor = this.activeSwatch.nextSibling;
|
||||
this._originalColor = this.currentSwatchColor.textContent;
|
||||
let color = this.activeSwatch.style.backgroundColor;
|
||||
this.spectrum.off("changed", this._onSpectrumColorChange);
|
||||
this.spectrum.rgb = this._colorToRgba(color);
|
||||
this.spectrum.on("changed", this._onSpectrumColorChange);
|
||||
this.spectrum.updateUI();
|
||||
}
|
||||
|
||||
let {target} = this.inspector;
|
||||
target.actorHasMethod("inspector", "pickColorFromPage").then(value => {
|
||||
let tooltipDoc = this.tooltip.doc;
|
||||
let eyeButton = tooltipDoc.querySelector("#eyedropper-button");
|
||||
if (value && this.inspector.selection.nodeFront.isInHTMLDocument) {
|
||||
eyeButton.addEventListener("click", this._openEyeDropper);
|
||||
} else {
|
||||
eyeButton.style.display = "none";
|
||||
}
|
||||
this.emit("ready");
|
||||
}, e => console.error(e));
|
||||
}),
|
||||
|
||||
_onSpectrumColorChange: function (event, rgba, cssColor) {
|
||||
this._selectColor(cssColor);
|
||||
},
|
||||
|
||||
_selectColor: function (color) {
|
||||
if (this.activeSwatch) {
|
||||
this.activeSwatch.style.backgroundColor = color;
|
||||
this.activeSwatch.parentNode.dataset.color = color;
|
||||
|
||||
color = this._toDefaultType(color);
|
||||
this.currentSwatchColor.textContent = color;
|
||||
this.preview(color);
|
||||
|
||||
if (this.eyedropperOpen) {
|
||||
this.commit();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_openEyeDropper: function () {
|
||||
let {inspector, toolbox, telemetry} = this.inspector;
|
||||
telemetry.toolOpened("pickereyedropper");
|
||||
inspector.pickColorFromPage(toolbox, {copyOnSelect: false}).then(() => {
|
||||
this.eyedropperOpen = true;
|
||||
|
||||
// close the colorpicker tooltip so that only the eyedropper is open.
|
||||
this.hide();
|
||||
|
||||
this.tooltip.emit("eyedropper-opened");
|
||||
}, e => console.error(e));
|
||||
|
||||
inspector.once("color-picked", color => {
|
||||
toolbox.win.focus();
|
||||
this._selectColor(color);
|
||||
this._onEyeDropperDone();
|
||||
});
|
||||
|
||||
inspector.once("color-pick-canceled", () => {
|
||||
this._onEyeDropperDone();
|
||||
});
|
||||
},
|
||||
|
||||
_onEyeDropperDone: function () {
|
||||
this.eyedropperOpen = false;
|
||||
this.activeSwatch = null;
|
||||
},
|
||||
|
||||
_colorToRgba: function (color) {
|
||||
color = new colorUtils.CssColor(color);
|
||||
let rgba = color._getRGBATuple();
|
||||
return [rgba.r, rgba.g, rgba.b, rgba.a];
|
||||
},
|
||||
|
||||
_toDefaultType: function (color) {
|
||||
let colorObj = new colorUtils.CssColor(color);
|
||||
colorObj.setAuthoredUnitFromColor(this._originalColor);
|
||||
return colorObj.toString();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
SwatchBasedEditorTooltip.prototype.destroy.call(this);
|
||||
this.inspector = null;
|
||||
this.currentSwatchColor = null;
|
||||
this.spectrum.off("changed", this._onSpectrumColorChange);
|
||||
this.spectrum.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The swatch cubic-bezier tooltip class is a specific class meant to be used
|
||||
* along with rule-view's generated cubic-bezier swatches.
|
||||
* It extends the parent SwatchBasedEditorTooltip class.
|
||||
* It just wraps a standard Tooltip and sets its content with an instance of a
|
||||
* CubicBezierWidget.
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* The devtools toolbox, needed to get the devtools main window.
|
||||
*/
|
||||
function SwatchCubicBezierTooltip(toolbox) {
|
||||
let stylesheet = "chrome://devtools/content/shared/widgets/cubic-bezier.css";
|
||||
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
|
||||
|
||||
// Creating a cubic-bezier instance.
|
||||
// this.widget will always be a promise that resolves to the widget instance
|
||||
this.widget = this.setCubicBezierContent([0, 0, 1, 1]);
|
||||
this._onUpdate = this._onUpdate.bind(this);
|
||||
}
|
||||
|
||||
module.exports.SwatchCubicBezierTooltip = SwatchCubicBezierTooltip;
|
||||
|
||||
SwatchCubicBezierTooltip.prototype =
|
||||
Heritage.extend(SwatchBasedEditorTooltip.prototype, {
|
||||
/**
|
||||
* Fill the tooltip with a new instance of the cubic-bezier widget
|
||||
* initialized with the given value, and return a promise that resolves to
|
||||
* the instance of the widget
|
||||
*/
|
||||
setCubicBezierContent: function (bezier) {
|
||||
let { doc } = this.tooltip;
|
||||
|
||||
let container = doc.createElementNS(XHTML_NS, "div");
|
||||
container.className = "cubic-bezier-container";
|
||||
|
||||
this.tooltip.setContent(container, { width: 510, height: 370 });
|
||||
|
||||
let def = defer();
|
||||
|
||||
// Wait for the tooltip to be shown before calling instanciating the widget
|
||||
// as it expect its DOM elements to be visible.
|
||||
this.tooltip.once("shown", () => {
|
||||
let widget = new CubicBezierWidget(container, bezier);
|
||||
def.resolve(widget);
|
||||
});
|
||||
|
||||
return def.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Overriding the SwatchBasedEditorTooltip.show function to set the cubic
|
||||
* bezier curve in the widget
|
||||
*/
|
||||
show: Task.async(function* () {
|
||||
// Call the parent class' show function
|
||||
yield SwatchBasedEditorTooltip.prototype.show.call(this);
|
||||
// Then set the curve and listen to changes to preview them
|
||||
if (this.activeSwatch) {
|
||||
this.currentBezierValue = this.activeSwatch.nextSibling;
|
||||
this.widget.then(widget => {
|
||||
widget.off("updated", this._onUpdate);
|
||||
widget.cssCubicBezierValue = this.currentBezierValue.textContent;
|
||||
widget.on("updated", this._onUpdate);
|
||||
this.emit("ready");
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
_onUpdate: function (event, bezier) {
|
||||
if (!this.activeSwatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentBezierValue.textContent = bezier + "";
|
||||
this.preview(bezier + "");
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
SwatchBasedEditorTooltip.prototype.destroy.call(this);
|
||||
this.currentBezierValue = null;
|
||||
this.widget.then(widget => {
|
||||
widget.off("updated", this._onUpdate);
|
||||
widget.destroy();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* The swatch-based css filter tooltip class is a specific class meant to be
|
||||
* used along with rule-view's generated css filter swatches.
|
||||
* It extends the parent SwatchBasedEditorTooltip class.
|
||||
* It just wraps a standard Tooltip and sets its content with an instance of a
|
||||
* CSSFilterEditorWidget.
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* The devtools toolbox, needed to get the devtools main window.
|
||||
* @param {function} cssIsValid
|
||||
* A function to check that css declaration's name and values are valid together.
|
||||
* This can be obtained from "shared/fronts/css-properties.js".
|
||||
*/
|
||||
function SwatchFilterTooltip(toolbox, cssIsValid) {
|
||||
let stylesheet = "chrome://devtools/content/shared/widgets/filter-widget.css";
|
||||
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
|
||||
this._cssIsValid = cssIsValid;
|
||||
|
||||
// Creating a filter editor instance.
|
||||
this.widget = this.setFilterContent("none");
|
||||
this._onUpdate = this._onUpdate.bind(this);
|
||||
}
|
||||
|
||||
exports.SwatchFilterTooltip = SwatchFilterTooltip;
|
||||
|
||||
SwatchFilterTooltip.prototype =
|
||||
Heritage.extend(SwatchBasedEditorTooltip.prototype, {
|
||||
/**
|
||||
* Fill the tooltip with a new instance of the CSSFilterEditorWidget
|
||||
* widget initialized with the given filter value, and return a promise
|
||||
* that resolves to the instance of the widget when ready.
|
||||
*/
|
||||
setFilterContent: function (filter) {
|
||||
let { doc } = this.tooltip;
|
||||
|
||||
let container = doc.createElementNS(XHTML_NS, "div");
|
||||
container.id = "filter-container";
|
||||
|
||||
this.tooltip.setContent(container, { width: 510, height: 200 });
|
||||
|
||||
return new CSSFilterEditorWidget(container, filter, this._cssIsValid);
|
||||
},
|
||||
|
||||
show: Task.async(function* () {
|
||||
// Call the parent class' show function
|
||||
yield SwatchBasedEditorTooltip.prototype.show.call(this);
|
||||
// Then set the filter value and listen to changes to preview them
|
||||
if (this.activeSwatch) {
|
||||
this.currentFilterValue = this.activeSwatch.nextSibling;
|
||||
this.widget.off("updated", this._onUpdate);
|
||||
this.widget.on("updated", this._onUpdate);
|
||||
this.widget.setCssValue(this.currentFilterValue.textContent);
|
||||
this.widget.render();
|
||||
this.emit("ready");
|
||||
}
|
||||
}),
|
||||
|
||||
_onUpdate: function (event, filters) {
|
||||
if (!this.activeSwatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the old children and reparse the property value to
|
||||
// recompute them.
|
||||
while (this.currentFilterValue.firstChild) {
|
||||
this.currentFilterValue.firstChild.remove();
|
||||
}
|
||||
let node = this._parser.parseCssProperty("filter", filters, this._options);
|
||||
this.currentFilterValue.appendChild(node);
|
||||
|
||||
this.preview();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
SwatchBasedEditorTooltip.prototype.destroy.call(this);
|
||||
this.currentFilterValue = null;
|
||||
this.widget.off("updated", this._onUpdate);
|
||||
this.widget.destroy();
|
||||
},
|
||||
|
||||
/**
|
||||
* Like SwatchBasedEditorTooltip.addSwatch, but accepts a parser object
|
||||
* to use when previewing the updated property value.
|
||||
*
|
||||
* @param {node} swatchEl
|
||||
* @see SwatchBasedEditorTooltip.addSwatch
|
||||
* @param {object} callbacks
|
||||
* @see SwatchBasedEditorTooltip.addSwatch
|
||||
* @param {object} parser
|
||||
* A parser object; @see OutputParser object
|
||||
* @param {object} options
|
||||
* options to pass to the output parser, with
|
||||
* the option |filterSwatch| set.
|
||||
*/
|
||||
addSwatch: function (swatchEl, callbacks, parser, options) {
|
||||
SwatchBasedEditorTooltip.prototype.addSwatch.call(this, swatchEl,
|
||||
callbacks);
|
||||
this._parser = parser;
|
||||
this._options = options;
|
||||
}
|
||||
});
|
||||
module.exports = Tooltip;
|
||||
|
|
|
@ -37,8 +37,6 @@ function CssDocsTooltip(toolbox) {
|
|||
this.shortcuts.on("Escape", this._onShortcut);
|
||||
}
|
||||
|
||||
module.exports.CssDocsTooltip = CssDocsTooltip;
|
||||
|
||||
CssDocsTooltip.prototype = {
|
||||
/**
|
||||
* Load CSS docs for the given property,
|
||||
|
@ -91,3 +89,5 @@ CssDocsTooltip.prototype = {
|
|||
this.tooltip.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = CssDocsTooltip;
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
/* 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 EventEmitter = require("devtools/shared/event-emitter");
|
||||
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
|
||||
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
|
||||
|
||||
/**
|
||||
* Base class for all (color, gradient, ...)-swatch based value editors inside
|
||||
* tooltips
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* The devtools toolbox, needed to get the devtools main window.
|
||||
*/
|
||||
function SwatchBasedEditorTooltip(toolbox, stylesheet) {
|
||||
EventEmitter.decorate(this);
|
||||
// Creating a tooltip instance
|
||||
// This one will consume outside clicks as it makes more sense to let the user
|
||||
// close the tooltip by clicking out
|
||||
// It will also close on <escape> and <enter>
|
||||
this.tooltip = new HTMLTooltip(toolbox, {
|
||||
type: "arrow",
|
||||
consumeOutsideClicks: true,
|
||||
useXulWrapper: true,
|
||||
stylesheet
|
||||
});
|
||||
|
||||
// By default, swatch-based editor tooltips revert value change on <esc> and
|
||||
// commit value change on <enter>
|
||||
this.shortcuts = new KeyShortcuts({
|
||||
window: this.tooltip.topWindow
|
||||
});
|
||||
this.shortcuts.on("Escape", (name, event) => {
|
||||
if (!this.tooltip.isVisible()) {
|
||||
return;
|
||||
}
|
||||
this.revert();
|
||||
this.hide();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
this.shortcuts.on("Return", (name, event) => {
|
||||
if (!this.tooltip.isVisible()) {
|
||||
return;
|
||||
}
|
||||
this.commit();
|
||||
this.hide();
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// All target swatches are kept in a map, indexed by swatch DOM elements
|
||||
this.swatches = new Map();
|
||||
|
||||
// When a swatch is clicked, and for as long as the tooltip is shown, the
|
||||
// activeSwatch property will hold the reference to the swatch DOM element
|
||||
// that was clicked
|
||||
this.activeSwatch = null;
|
||||
|
||||
this._onSwatchClick = this._onSwatchClick.bind(this);
|
||||
}
|
||||
|
||||
SwatchBasedEditorTooltip.prototype = {
|
||||
/**
|
||||
* Show the editor tooltip for the currently active swatch.
|
||||
*
|
||||
* @return {Promise} a promise that resolves once the editor tooltip is displayed, or
|
||||
* immediately if there is no currently active swatch.
|
||||
*/
|
||||
show: function () {
|
||||
if (this.activeSwatch) {
|
||||
let onShown = this.tooltip.once("shown");
|
||||
this.tooltip.show(this.activeSwatch, "topcenter bottomleft");
|
||||
|
||||
// When the tooltip is closed by clicking outside the panel we want to
|
||||
// commit any changes.
|
||||
this.tooltip.once("hidden", () => {
|
||||
if (!this._reverted && !this.eyedropperOpen) {
|
||||
this.commit();
|
||||
}
|
||||
this._reverted = false;
|
||||
|
||||
// Once the tooltip is hidden we need to clean up any remaining objects.
|
||||
if (!this.eyedropperOpen) {
|
||||
this.activeSwatch = null;
|
||||
}
|
||||
});
|
||||
|
||||
return onShown;
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
hide: function () {
|
||||
this.tooltip.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new swatch DOM element to the list of swatch elements this editor
|
||||
* tooltip knows about. That means from now on, clicking on that swatch will
|
||||
* toggle the editor.
|
||||
*
|
||||
* @param {node} swatchEl
|
||||
* The element to add
|
||||
* @param {object} callbacks
|
||||
* Callbacks that will be executed when the editor wants to preview a
|
||||
* value change, or revert a change, or commit a change.
|
||||
* - onShow: will be called when one of the swatch tooltip is shown
|
||||
* - onPreview: will be called when one of the sub-classes calls
|
||||
* preview
|
||||
* - onRevert: will be called when the user ESCapes out of the tooltip
|
||||
* - onCommit: will be called when the user presses ENTER or clicks
|
||||
* outside the tooltip.
|
||||
*/
|
||||
addSwatch: function (swatchEl, callbacks = {}) {
|
||||
if (!callbacks.onShow) {
|
||||
callbacks.onShow = function () {};
|
||||
}
|
||||
if (!callbacks.onPreview) {
|
||||
callbacks.onPreview = function () {};
|
||||
}
|
||||
if (!callbacks.onRevert) {
|
||||
callbacks.onRevert = function () {};
|
||||
}
|
||||
if (!callbacks.onCommit) {
|
||||
callbacks.onCommit = function () {};
|
||||
}
|
||||
|
||||
this.swatches.set(swatchEl, {
|
||||
callbacks: callbacks
|
||||
});
|
||||
swatchEl.addEventListener("click", this._onSwatchClick, false);
|
||||
},
|
||||
|
||||
removeSwatch: function (swatchEl) {
|
||||
if (this.swatches.has(swatchEl)) {
|
||||
if (this.activeSwatch === swatchEl) {
|
||||
this.hide();
|
||||
this.activeSwatch = null;
|
||||
}
|
||||
swatchEl.removeEventListener("click", this._onSwatchClick, false);
|
||||
this.swatches.delete(swatchEl);
|
||||
}
|
||||
},
|
||||
|
||||
_onSwatchClick: function (event) {
|
||||
let swatch = this.swatches.get(event.target);
|
||||
|
||||
if (event.shiftKey) {
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
if (swatch) {
|
||||
this.activeSwatch = event.target;
|
||||
this.show();
|
||||
swatch.callbacks.onShow();
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Not called by this parent class, needs to be taken care of by sub-classes
|
||||
*/
|
||||
preview: function (value) {
|
||||
if (this.activeSwatch) {
|
||||
let swatch = this.swatches.get(this.activeSwatch);
|
||||
swatch.callbacks.onPreview(value);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This parent class only calls this on <esc> keypress
|
||||
*/
|
||||
revert: function () {
|
||||
if (this.activeSwatch) {
|
||||
this._reverted = true;
|
||||
let swatch = this.swatches.get(this.activeSwatch);
|
||||
this.tooltip.once("hidden", () => {
|
||||
swatch.callbacks.onRevert();
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This parent class only calls this on <enter> keypress
|
||||
*/
|
||||
commit: function () {
|
||||
if (this.activeSwatch) {
|
||||
let swatch = this.swatches.get(this.activeSwatch);
|
||||
swatch.callbacks.onCommit();
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
this.swatches.clear();
|
||||
this.activeSwatch = null;
|
||||
this.tooltip.off("keypress", this._onTooltipKeypress);
|
||||
this.tooltip.destroy();
|
||||
this.shortcuts.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = SwatchBasedEditorTooltip;
|
|
@ -0,0 +1,171 @@
|
|||
/* 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 {Task} = require("devtools/shared/task");
|
||||
const {colorUtils} = require("devtools/shared/css/color");
|
||||
const {Spectrum} = require("devtools/client/shared/widgets/Spectrum");
|
||||
const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");
|
||||
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
/**
|
||||
* The swatch color picker tooltip class is a specific class meant to be used
|
||||
* along with output-parser's generated color swatches.
|
||||
* It extends the parent SwatchBasedEditorTooltip class.
|
||||
* It just wraps a standard Tooltip and sets its content with an instance of a
|
||||
* color picker.
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* The devtools toolbox, needed to get the devtools main window.
|
||||
* @param {InspectorPanel} inspector
|
||||
* The inspector panel, needed for the eyedropper.
|
||||
*/
|
||||
function SwatchColorPickerTooltip(toolbox, inspector) {
|
||||
let stylesheet = "chrome://devtools/content/shared/widgets/spectrum.css";
|
||||
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
|
||||
|
||||
this.inspector = inspector;
|
||||
|
||||
// Creating a spectrum instance. this.spectrum will always be a promise that
|
||||
// resolves to the spectrum instance
|
||||
this.spectrum = this.setColorPickerContent([0, 0, 0, 1]);
|
||||
this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this);
|
||||
this._openEyeDropper = this._openEyeDropper.bind(this);
|
||||
}
|
||||
|
||||
SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.prototype, {
|
||||
/**
|
||||
* Fill the tooltip with a new instance of the spectrum color picker widget
|
||||
* initialized with the given color, and return the instance of spectrum
|
||||
*/
|
||||
setColorPickerContent: function (color) {
|
||||
let { doc } = this.tooltip;
|
||||
|
||||
let container = doc.createElementNS(XHTML_NS, "div");
|
||||
container.id = "spectrum-tooltip";
|
||||
let spectrumNode = doc.createElementNS(XHTML_NS, "div");
|
||||
spectrumNode.id = "spectrum";
|
||||
container.appendChild(spectrumNode);
|
||||
let eyedropper = doc.createElementNS(XHTML_NS, "button");
|
||||
eyedropper.id = "eyedropper-button";
|
||||
eyedropper.className = "devtools-button";
|
||||
container.appendChild(eyedropper);
|
||||
|
||||
this.tooltip.setContent(container, { width: 218, height: 224 });
|
||||
|
||||
let spectrum = new Spectrum(spectrumNode, color);
|
||||
|
||||
// Wait for the tooltip to be shown before calling spectrum.show
|
||||
// as it expect to be visible in order to compute DOM element sizes.
|
||||
this.tooltip.once("shown", () => {
|
||||
spectrum.show();
|
||||
});
|
||||
|
||||
return spectrum;
|
||||
},
|
||||
|
||||
/**
|
||||
* Overriding the SwatchBasedEditorTooltip.show function to set spectrum's
|
||||
* color.
|
||||
*/
|
||||
show: Task.async(function* () {
|
||||
// Call then parent class' show function
|
||||
yield SwatchBasedEditorTooltip.prototype.show.call(this);
|
||||
// Then set spectrum's color and listen to color changes to preview them
|
||||
if (this.activeSwatch) {
|
||||
this.currentSwatchColor = this.activeSwatch.nextSibling;
|
||||
this._originalColor = this.currentSwatchColor.textContent;
|
||||
let color = this.activeSwatch.style.backgroundColor;
|
||||
this.spectrum.off("changed", this._onSpectrumColorChange);
|
||||
this.spectrum.rgb = this._colorToRgba(color);
|
||||
this.spectrum.on("changed", this._onSpectrumColorChange);
|
||||
this.spectrum.updateUI();
|
||||
}
|
||||
|
||||
let {target} = this.inspector;
|
||||
target.actorHasMethod("inspector", "pickColorFromPage").then(value => {
|
||||
let tooltipDoc = this.tooltip.doc;
|
||||
let eyeButton = tooltipDoc.querySelector("#eyedropper-button");
|
||||
if (value && this.inspector.selection.nodeFront.isInHTMLDocument) {
|
||||
eyeButton.addEventListener("click", this._openEyeDropper);
|
||||
} else {
|
||||
eyeButton.style.display = "none";
|
||||
}
|
||||
this.emit("ready");
|
||||
}, e => console.error(e));
|
||||
}),
|
||||
|
||||
_onSpectrumColorChange: function (event, rgba, cssColor) {
|
||||
this._selectColor(cssColor);
|
||||
},
|
||||
|
||||
_selectColor: function (color) {
|
||||
if (this.activeSwatch) {
|
||||
this.activeSwatch.style.backgroundColor = color;
|
||||
this.activeSwatch.parentNode.dataset.color = color;
|
||||
|
||||
color = this._toDefaultType(color);
|
||||
this.currentSwatchColor.textContent = color;
|
||||
this.preview(color);
|
||||
|
||||
if (this.eyedropperOpen) {
|
||||
this.commit();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_openEyeDropper: function () {
|
||||
let {inspector, toolbox, telemetry} = this.inspector;
|
||||
telemetry.toolOpened("pickereyedropper");
|
||||
inspector.pickColorFromPage(toolbox, {copyOnSelect: false}).then(() => {
|
||||
this.eyedropperOpen = true;
|
||||
|
||||
// close the colorpicker tooltip so that only the eyedropper is open.
|
||||
this.hide();
|
||||
|
||||
this.tooltip.emit("eyedropper-opened");
|
||||
}, e => console.error(e));
|
||||
|
||||
inspector.once("color-picked", color => {
|
||||
toolbox.win.focus();
|
||||
this._selectColor(color);
|
||||
this._onEyeDropperDone();
|
||||
});
|
||||
|
||||
inspector.once("color-pick-canceled", () => {
|
||||
this._onEyeDropperDone();
|
||||
});
|
||||
},
|
||||
|
||||
_onEyeDropperDone: function () {
|
||||
this.eyedropperOpen = false;
|
||||
this.activeSwatch = null;
|
||||
},
|
||||
|
||||
_colorToRgba: function (color) {
|
||||
color = new colorUtils.CssColor(color);
|
||||
let rgba = color._getRGBATuple();
|
||||
return [rgba.r, rgba.g, rgba.b, rgba.a];
|
||||
},
|
||||
|
||||
_toDefaultType: function (color) {
|
||||
let colorObj = new colorUtils.CssColor(color);
|
||||
colorObj.setAuthoredUnitFromColor(this._originalColor);
|
||||
return colorObj.toString();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
SwatchBasedEditorTooltip.prototype.destroy.call(this);
|
||||
this.inspector = null;
|
||||
this.currentSwatchColor = null;
|
||||
this.spectrum.off("changed", this._onSpectrumColorChange);
|
||||
this.spectrum.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SwatchColorPickerTooltip;
|
|
@ -0,0 +1,100 @@
|
|||
/* 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 defer = require("devtools/shared/defer");
|
||||
const {Task} = require("devtools/shared/task");
|
||||
const {CubicBezierWidget} = require("devtools/client/shared/widgets/CubicBezierWidget");
|
||||
const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");
|
||||
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
/**
|
||||
* The swatch cubic-bezier tooltip class is a specific class meant to be used
|
||||
* along with rule-view's generated cubic-bezier swatches.
|
||||
* It extends the parent SwatchBasedEditorTooltip class.
|
||||
* It just wraps a standard Tooltip and sets its content with an instance of a
|
||||
* CubicBezierWidget.
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* The devtools toolbox, needed to get the devtools main window.
|
||||
*/
|
||||
function SwatchCubicBezierTooltip(toolbox) {
|
||||
let stylesheet = "chrome://devtools/content/shared/widgets/cubic-bezier.css";
|
||||
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
|
||||
|
||||
// Creating a cubic-bezier instance.
|
||||
// this.widget will always be a promise that resolves to the widget instance
|
||||
this.widget = this.setCubicBezierContent([0, 0, 1, 1]);
|
||||
this._onUpdate = this._onUpdate.bind(this);
|
||||
}
|
||||
|
||||
SwatchCubicBezierTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.prototype, {
|
||||
/**
|
||||
* Fill the tooltip with a new instance of the cubic-bezier widget
|
||||
* initialized with the given value, and return a promise that resolves to
|
||||
* the instance of the widget
|
||||
*/
|
||||
setCubicBezierContent: function (bezier) {
|
||||
let { doc } = this.tooltip;
|
||||
|
||||
let container = doc.createElementNS(XHTML_NS, "div");
|
||||
container.className = "cubic-bezier-container";
|
||||
|
||||
this.tooltip.setContent(container, { width: 510, height: 370 });
|
||||
|
||||
let def = defer();
|
||||
|
||||
// Wait for the tooltip to be shown before calling instanciating the widget
|
||||
// as it expect its DOM elements to be visible.
|
||||
this.tooltip.once("shown", () => {
|
||||
let widget = new CubicBezierWidget(container, bezier);
|
||||
def.resolve(widget);
|
||||
});
|
||||
|
||||
return def.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Overriding the SwatchBasedEditorTooltip.show function to set the cubic
|
||||
* bezier curve in the widget
|
||||
*/
|
||||
show: Task.async(function* () {
|
||||
// Call the parent class' show function
|
||||
yield SwatchBasedEditorTooltip.prototype.show.call(this);
|
||||
// Then set the curve and listen to changes to preview them
|
||||
if (this.activeSwatch) {
|
||||
this.currentBezierValue = this.activeSwatch.nextSibling;
|
||||
this.widget.then(widget => {
|
||||
widget.off("updated", this._onUpdate);
|
||||
widget.cssCubicBezierValue = this.currentBezierValue.textContent;
|
||||
widget.on("updated", this._onUpdate);
|
||||
this.emit("ready");
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
_onUpdate: function (event, bezier) {
|
||||
if (!this.activeSwatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentBezierValue.textContent = bezier + "";
|
||||
this.preview(bezier + "");
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
SwatchBasedEditorTooltip.prototype.destroy.call(this);
|
||||
this.currentBezierValue = null;
|
||||
this.widget.then(widget => {
|
||||
widget.off("updated", this._onUpdate);
|
||||
widget.destroy();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SwatchCubicBezierTooltip;
|
|
@ -0,0 +1,114 @@
|
|||
/* 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 {Task} = require("devtools/shared/task");
|
||||
const {CSSFilterEditorWidget} = require("devtools/client/shared/widgets/FilterWidget");
|
||||
const SwatchBasedEditorTooltip = require("devtools/client/shared/widgets/tooltip/SwatchBasedEditorTooltip");
|
||||
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
|
||||
const XHTML_NS = "http://www.w3.org/1999/xhtml";
|
||||
|
||||
/**
|
||||
* The swatch-based css filter tooltip class is a specific class meant to be
|
||||
* used along with rule-view's generated css filter swatches.
|
||||
* It extends the parent SwatchBasedEditorTooltip class.
|
||||
* It just wraps a standard Tooltip and sets its content with an instance of a
|
||||
* CSSFilterEditorWidget.
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* The devtools toolbox, needed to get the devtools main window.
|
||||
* @param {function} cssIsValid
|
||||
* A function to check that css declaration's name and values are valid together.
|
||||
* This can be obtained from "shared/fronts/css-properties.js".
|
||||
*/
|
||||
function SwatchFilterTooltip(toolbox, cssIsValid) {
|
||||
let stylesheet = "chrome://devtools/content/shared/widgets/filter-widget.css";
|
||||
SwatchBasedEditorTooltip.call(this, toolbox, stylesheet);
|
||||
this._cssIsValid = cssIsValid;
|
||||
|
||||
// Creating a filter editor instance.
|
||||
this.widget = this.setFilterContent("none");
|
||||
this._onUpdate = this._onUpdate.bind(this);
|
||||
}
|
||||
|
||||
SwatchFilterTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.prototype, {
|
||||
/**
|
||||
* Fill the tooltip with a new instance of the CSSFilterEditorWidget
|
||||
* widget initialized with the given filter value, and return a promise
|
||||
* that resolves to the instance of the widget when ready.
|
||||
*/
|
||||
setFilterContent: function (filter) {
|
||||
let { doc } = this.tooltip;
|
||||
|
||||
let container = doc.createElementNS(XHTML_NS, "div");
|
||||
container.id = "filter-container";
|
||||
|
||||
this.tooltip.setContent(container, { width: 510, height: 200 });
|
||||
|
||||
return new CSSFilterEditorWidget(container, filter, this._cssIsValid);
|
||||
},
|
||||
|
||||
show: Task.async(function* () {
|
||||
// Call the parent class' show function
|
||||
yield SwatchBasedEditorTooltip.prototype.show.call(this);
|
||||
// Then set the filter value and listen to changes to preview them
|
||||
if (this.activeSwatch) {
|
||||
this.currentFilterValue = this.activeSwatch.nextSibling;
|
||||
this.widget.off("updated", this._onUpdate);
|
||||
this.widget.on("updated", this._onUpdate);
|
||||
this.widget.setCssValue(this.currentFilterValue.textContent);
|
||||
this.widget.render();
|
||||
this.emit("ready");
|
||||
}
|
||||
}),
|
||||
|
||||
_onUpdate: function (event, filters) {
|
||||
if (!this.activeSwatch) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the old children and reparse the property value to
|
||||
// recompute them.
|
||||
while (this.currentFilterValue.firstChild) {
|
||||
this.currentFilterValue.firstChild.remove();
|
||||
}
|
||||
let node = this._parser.parseCssProperty("filter", filters, this._options);
|
||||
this.currentFilterValue.appendChild(node);
|
||||
|
||||
this.preview();
|
||||
},
|
||||
|
||||
destroy: function () {
|
||||
SwatchBasedEditorTooltip.prototype.destroy.call(this);
|
||||
this.currentFilterValue = null;
|
||||
this.widget.off("updated", this._onUpdate);
|
||||
this.widget.destroy();
|
||||
},
|
||||
|
||||
/**
|
||||
* Like SwatchBasedEditorTooltip.addSwatch, but accepts a parser object
|
||||
* to use when previewing the updated property value.
|
||||
*
|
||||
* @param {node} swatchEl
|
||||
* @see SwatchBasedEditorTooltip.addSwatch
|
||||
* @param {object} callbacks
|
||||
* @see SwatchBasedEditorTooltip.addSwatch
|
||||
* @param {object} parser
|
||||
* A parser object; @see OutputParser object
|
||||
* @param {object} options
|
||||
* options to pass to the output parser, with
|
||||
* the option |filterSwatch| set.
|
||||
*/
|
||||
addSwatch: function (swatchEl, callbacks, parser, options) {
|
||||
SwatchBasedEditorTooltip.prototype.addSwatch.call(this, swatchEl,
|
||||
callbacks);
|
||||
this._parser = parser;
|
||||
this._options = options;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = SwatchFilterTooltip;
|
|
@ -8,6 +8,10 @@ DevToolsModules(
|
|||
'CssDocsTooltip.js',
|
||||
'EventTooltipHelper.js',
|
||||
'ImageTooltipHelper.js',
|
||||
'SwatchBasedEditorTooltip.js',
|
||||
'SwatchColorPickerTooltip.js',
|
||||
'SwatchCubicBezierTooltip.js',
|
||||
'SwatchFilterTooltip.js',
|
||||
'TooltipToggle.js',
|
||||
'VariableContentHelper.js',
|
||||
)
|
||||
|
|
|
@ -34,6 +34,7 @@ const promise = require("promise");
|
|||
const events = require("devtools/shared/event-emitter");
|
||||
const { PrefObserver } = require("devtools/client/styleeditor/utils");
|
||||
const { getClientCssProperties } = require("devtools/shared/fronts/css-properties");
|
||||
const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
|
||||
|
||||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
const L10N = new LocalizationHelper("devtools/locale/sourceeditor.properties");
|
||||
|
@ -381,65 +382,6 @@ Editor.prototype = {
|
|||
popup.openPopupAtScreen(ev.screenX, ev.screenY, true);
|
||||
}, false);
|
||||
|
||||
// Intercept the find and find again keystroke on CodeMirror, to avoid
|
||||
// the browser's search
|
||||
|
||||
let findKey = L10N.getStr("find.commandkey");
|
||||
let findAgainKey = L10N.getStr("findAgain.commandkey");
|
||||
let [accel, modifier] = OS === "Darwin"
|
||||
? ["metaKey", "altKey"]
|
||||
: ["ctrlKey", "shiftKey"];
|
||||
|
||||
cm.getWrapperElement().addEventListener("keydown", ev => {
|
||||
let key = ev.key.toUpperCase();
|
||||
let node = ev.originalTarget;
|
||||
let isInput = node.tagName === "INPUT";
|
||||
let isSearchInput = isInput && node.type === "search";
|
||||
|
||||
// replace box is a different input instance than search, and it is
|
||||
// located in a code mirror dialog
|
||||
let isDialogInput = isInput &&
|
||||
node.parentNode &&
|
||||
node.parentNode.classList.contains("CodeMirror-dialog");
|
||||
|
||||
if (!ev[accel] || !(isSearchInput || isDialogInput)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (key === findKey) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (isSearchInput || ev[modifier]) {
|
||||
node.select();
|
||||
}
|
||||
} else if (key === findAgainKey) {
|
||||
ev.preventDefault();
|
||||
|
||||
if (!isSearchInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
let query = node.value;
|
||||
|
||||
// If there isn't a search state, or the text in the input does not
|
||||
// match with the current search state, we need to create a new one
|
||||
if (!cm.state.search || cm.state.search.query !== query) {
|
||||
cm.state.search = {
|
||||
posFrom: null,
|
||||
posTo: null,
|
||||
overlay: null,
|
||||
query
|
||||
};
|
||||
}
|
||||
|
||||
if (ev.shiftKey) {
|
||||
cm.execCommand("findPrev");
|
||||
} else {
|
||||
cm.execCommand("findNext");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cm.on("focus", () => this.emit("focus"));
|
||||
cm.on("scroll", () => this.emit("scroll"));
|
||||
cm.on("change", () => {
|
||||
|
@ -468,7 +410,7 @@ Editor.prototype = {
|
|||
return L10N.getStr(name);
|
||||
});
|
||||
|
||||
cm.getInputField().controllers.insertControllerAt(0, controller(this));
|
||||
this._initShortcuts(win);
|
||||
|
||||
editors.set(this, cm);
|
||||
|
||||
|
@ -996,8 +938,7 @@ Editor.prototype = {
|
|||
let doc = editors.get(this).getWrapperElement().ownerDocument;
|
||||
let div = doc.createElement("div");
|
||||
let inp = doc.createElement("input");
|
||||
let txt =
|
||||
doc.createTextNode(L10N.getStr("gotoLineCmd.promptTitle"));
|
||||
let txt = doc.createTextNode(L10N.getStr("gotoLineCmd.promptTitle"));
|
||||
|
||||
inp.type = "text";
|
||||
inp.style.width = "10em";
|
||||
|
@ -1091,6 +1032,65 @@ Editor.prototype = {
|
|||
{ line: end.line + 1, ch: end.ch });
|
||||
},
|
||||
|
||||
/**
|
||||
* Intercept CodeMirror's Find and replace key shortcut to select the search input
|
||||
*/
|
||||
findOrReplace: function (node, isReplaceAll) {
|
||||
let cm = editors.get(this);
|
||||
let isInput = node.tagName === "INPUT";
|
||||
let isSearchInput = isInput && node.type === "search";
|
||||
// replace box is a different input instance than search, and it is
|
||||
// located in a code mirror dialog
|
||||
let isDialogInput = isInput &&
|
||||
node.parentNode &&
|
||||
node.parentNode.classList.contains("CodeMirror-dialog");
|
||||
if (!(isSearchInput || isDialogInput)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isSearchInput || isReplaceAll) {
|
||||
// select the search input
|
||||
// it's the precise reason why we reimplement these key shortcuts
|
||||
node.select();
|
||||
}
|
||||
|
||||
// need to call it since we prevent the propagation of the event and
|
||||
// cancel codemirror's key handling
|
||||
cm.execCommand("find");
|
||||
},
|
||||
|
||||
/**
|
||||
* Intercept CodeMirror's findNext and findPrev key shortcut to allow
|
||||
* immediately search for next occurance after typing a word to search.
|
||||
*/
|
||||
findNextOrPrev: function (node, isFindPrev) {
|
||||
let cm = editors.get(this);
|
||||
let isInput = node.tagName === "INPUT";
|
||||
let isSearchInput = isInput && node.type === "search";
|
||||
if (!isSearchInput) {
|
||||
return;
|
||||
}
|
||||
let query = node.value;
|
||||
// cm.state.search allows to automatically start searching for the next occurance
|
||||
// it's the precise reason why we reimplement these key shortcuts
|
||||
if (!cm.state.search || cm.state.search.query !== query) {
|
||||
cm.state.search = {
|
||||
posFrom: null,
|
||||
posTo: null,
|
||||
overlay: null,
|
||||
query
|
||||
};
|
||||
}
|
||||
|
||||
// need to call it since we prevent the propagation of the event and
|
||||
// cancel codemirror's key handling
|
||||
if (isFindPrev) {
|
||||
cm.execCommand("findPrev");
|
||||
} else {
|
||||
cm.execCommand("findNext");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns current font size for the editor area, in pixels.
|
||||
*/
|
||||
|
@ -1259,6 +1259,77 @@ Editor.prototype = {
|
|||
|
||||
this.setOption("foldGutter", false);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Register all key shortcuts.
|
||||
*/
|
||||
_initShortcuts: function (win) {
|
||||
let shortcuts = new KeyShortcuts({
|
||||
window: win
|
||||
});
|
||||
this._onShortcut = this._onShortcut.bind(this);
|
||||
let keys = [
|
||||
"find.key",
|
||||
"findNext.key",
|
||||
"findPrev.key"
|
||||
];
|
||||
|
||||
if (OS === "Darwin") {
|
||||
keys.push("replaceAllMac.key");
|
||||
} else {
|
||||
keys.push("replaceAll.key");
|
||||
}
|
||||
// Process generic keys:
|
||||
keys.forEach(name => {
|
||||
let key = L10N.getStr(name);
|
||||
shortcuts.on(key, (_, event) => this._onShortcut(name, event));
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Key shortcut listener.
|
||||
*/
|
||||
_onShortcut: function (name, event) {
|
||||
if (!this._isInputOrTextarea(event.target)) {
|
||||
return;
|
||||
}
|
||||
let cm = editors.get(this);
|
||||
let node = event.originalTarget;
|
||||
|
||||
switch (name) {
|
||||
// replaceAll.key is Alt + find.key
|
||||
case "replaceAllMac.key":
|
||||
this.findOrReplace(node, true);
|
||||
break;
|
||||
// replaceAll.key is Shift + find.key
|
||||
case "replaceAll.key":
|
||||
this.findOrReplace(node, true);
|
||||
break;
|
||||
case "find.key":
|
||||
this.findOrReplace(node, false);
|
||||
break;
|
||||
// findPrev.key is Shift + findNext.key
|
||||
case "findPrev.key":
|
||||
this.findNextOrPrev(node, true);
|
||||
break;
|
||||
case "findNext.key":
|
||||
this.findNextOrPrev(node, false);
|
||||
break;
|
||||
default:
|
||||
console.error("Unexpected editor key shortcut", name);
|
||||
return;
|
||||
}
|
||||
// Prevent default for this action
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a node is an input or textarea
|
||||
*/
|
||||
_isInputOrTextarea: function (element) {
|
||||
let name = element.tagName.toLowerCase();
|
||||
return name === "input" || name === "textarea";
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1338,73 +1409,4 @@ function getCSSKeywords(cssProperties) {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a controller object that can be used for
|
||||
* editor-specific commands such as find, jump to line,
|
||||
* copy/paste, etc.
|
||||
*/
|
||||
function controller(ed) {
|
||||
return {
|
||||
supportsCommand: function (cmd) {
|
||||
switch (cmd) {
|
||||
case "cmd_find":
|
||||
case "cmd_findAgain":
|
||||
case "cmd_findPrevious":
|
||||
case "cmd_gotoLine":
|
||||
case "cmd_undo":
|
||||
case "cmd_redo":
|
||||
case "cmd_delete":
|
||||
case "cmd_selectAll":
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
isCommandEnabled: function (cmd) {
|
||||
let cm = editors.get(ed);
|
||||
|
||||
switch (cmd) {
|
||||
case "cmd_find":
|
||||
case "cmd_gotoLine":
|
||||
case "cmd_selectAll":
|
||||
return true;
|
||||
case "cmd_findAgain":
|
||||
return cm.state.search != null && cm.state.search.query != null;
|
||||
case "cmd_undo":
|
||||
return ed.canUndo();
|
||||
case "cmd_redo":
|
||||
return ed.canRedo();
|
||||
case "cmd_delete":
|
||||
return ed.somethingSelected();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
doCommand: function (cmd) {
|
||||
let cm = editors.get(ed);
|
||||
let map = {
|
||||
"cmd_selectAll": "selectAll",
|
||||
"cmd_find": "find",
|
||||
"cmd_undo": "undo",
|
||||
"cmd_redo": "redo",
|
||||
"cmd_delete": "delCharAfter",
|
||||
"cmd_findAgain": "findNext"
|
||||
};
|
||||
|
||||
if (map[cmd]) {
|
||||
cm.execCommand(map[cmd]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd == "cmd_gotoLine") {
|
||||
ed.jumpToLine();
|
||||
}
|
||||
},
|
||||
|
||||
onEvent: function () {}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = Editor;
|
||||
|
|
|
@ -23,6 +23,7 @@ support-files =
|
|||
helper_codemirror_runner.js
|
||||
cm_mode_ruby.js
|
||||
cm_script_injection_test.js
|
||||
!/devtools/client/framework/test/shared-head.js
|
||||
|
||||
[browser_editor_autocomplete_basic.js]
|
||||
[browser_editor_autocomplete_events.js]
|
||||
|
|
|
@ -6,8 +6,6 @@
|
|||
|
||||
const CSSCompleter = require("devtools/client/sourceeditor/css-autocompleter");
|
||||
const {InspectorFront} = require("devtools/shared/fronts/inspector");
|
||||
const {TargetFactory} = require("devtools/client/framework/target");
|
||||
const { Cc, Ci } = require("chrome");
|
||||
|
||||
const CSS_URI = "http://mochi.test:8888/browser/devtools/client/sourceeditor" +
|
||||
"/test/css_statemachine_testcases.css";
|
||||
|
|
|
@ -5,15 +5,12 @@
|
|||
"use strict";
|
||||
|
||||
const {InspectorFront} = require("devtools/shared/fronts/inspector");
|
||||
const {TargetFactory} = require("devtools/client/framework/target");
|
||||
const AUTOCOMPLETION_PREF = "devtools.editor.autocomplete";
|
||||
const TEST_URI = "data:text/html;charset=UTF-8,<html><body><bar></bar>" +
|
||||
"<div id='baz'></div><body></html>";
|
||||
|
||||
const wait = (delay) => new Promise(resolve => setTimeout(resolve, delay));
|
||||
|
||||
add_task(function* () {
|
||||
yield promiseTab(TEST_URI);
|
||||
yield addTab(TEST_URI);
|
||||
yield runTests();
|
||||
});
|
||||
|
||||
|
|
|
@ -8,12 +8,15 @@
|
|||
const {LocalizationHelper} = require("devtools/shared/l10n");
|
||||
const L10N = new LocalizationHelper("devtools/locale/sourceeditor.properties");
|
||||
|
||||
const FIND_KEY = L10N.getStr("find.commandkey");
|
||||
const FINDAGAIN_KEY = L10N.getStr("findAgain.commandkey");
|
||||
|
||||
const { OS } = Services.appinfo;
|
||||
|
||||
// On linux, getting immediately the selection's range here fails, returning
|
||||
const FIND_KEY = L10N.getStr("find.key");
|
||||
const FINDNEXT_KEY = L10N.getStr("findNext.key");
|
||||
const FINDPREV_KEY = L10N.getStr("findPrev.key");
|
||||
// the replace's key with the appropriate modifiers based on OS
|
||||
const REPLACE_KEY = OS == "Darwin" ? L10N.getStr("replaceAllMac.key") : L10N.getStr("replaceAll.key");
|
||||
|
||||
// values like it's not selected – even if the selection is visible.
|
||||
// For the record, setting the selection's range immediately doesn't have
|
||||
// any effect.
|
||||
|
@ -41,13 +44,12 @@ function openSearchBox(ed) {
|
|||
// The editor needs the focus to properly receive the `synthesizeKey`
|
||||
ed.focus();
|
||||
|
||||
EventUtils.synthesizeKey(FINDAGAIN_KEY, { accelKey: true }, edWin);
|
||||
|
||||
synthesizeKeyShortcut(FINDNEXT_KEY, edWin);
|
||||
input = edDoc.querySelector("input[type=search]");
|
||||
ok(input, "find again command key opens the search box");
|
||||
}
|
||||
|
||||
function testFindAgain(ed, inputLine, expectCursor, shiftKey = false) {
|
||||
function testFindAgain(ed, inputLine, expectCursor, isFindPrev = false) {
|
||||
let edDoc = ed.container.contentDocument;
|
||||
let edWin = edDoc.defaultView;
|
||||
|
||||
|
@ -58,7 +60,11 @@ function testFindAgain(ed, inputLine, expectCursor, shiftKey = false) {
|
|||
// it seems that during the tests can be lost
|
||||
input.focus();
|
||||
|
||||
EventUtils.synthesizeKey(FINDAGAIN_KEY, { accelKey: true, shiftKey }, edWin);
|
||||
if (isFindPrev) {
|
||||
synthesizeKeyShortcut(FINDPREV_KEY, edWin);
|
||||
} else {
|
||||
synthesizeKeyShortcut(FINDNEXT_KEY, edWin);
|
||||
}
|
||||
|
||||
ch(ed.getCursor(), expectCursor,
|
||||
"find: " + inputLine + " expects cursor: " + expectCursor.toSource());
|
||||
|
@ -82,7 +88,7 @@ const testSearchBoxTextIsSelected = Task.async(function* (ed) {
|
|||
ok(!input, "search box is closed");
|
||||
|
||||
// Re-open the search box
|
||||
EventUtils.synthesizeKey(FIND_KEY, { accelKey: true }, edWin);
|
||||
synthesizeKeyShortcut(FIND_KEY, edWin);
|
||||
|
||||
input = edDoc.querySelector("input[type=search]");
|
||||
ok(input, "find command key opens the search box");
|
||||
|
@ -97,7 +103,7 @@ const testSearchBoxTextIsSelected = Task.async(function* (ed) {
|
|||
// Removing selection
|
||||
input.setSelectionRange(0, 0);
|
||||
|
||||
EventUtils.synthesizeKey(FIND_KEY, { accelKey: true }, edWin);
|
||||
synthesizeKeyShortcut(FIND_KEY, edWin);
|
||||
|
||||
({ selectionStart, selectionEnd } = input);
|
||||
|
||||
|
@ -118,11 +124,7 @@ const testReplaceBoxTextIsSelected = Task.async(function* (ed) {
|
|||
// The editor needs the focus to properly receive the `synthesizeKey`
|
||||
ed.focus();
|
||||
|
||||
// Send the replace's key with the appropriate modifiers based on OS
|
||||
let [altKey, shiftKey] = OS === "Darwin" ? [true, false] : [false, true];
|
||||
|
||||
EventUtils.synthesizeKey(FIND_KEY,
|
||||
{ accelKey: true, altKey, shiftKey }, edWin);
|
||||
synthesizeKeyShortcut(REPLACE_KEY, edWin);
|
||||
|
||||
input = edDoc.querySelector(".CodeMirror-dialog > input");
|
||||
ok(input, "dialog box with replace is opened");
|
||||
|
@ -140,8 +142,7 @@ const testReplaceBoxTextIsSelected = Task.async(function* (ed) {
|
|||
ok(!(selectionStart === 0 && selectionEnd === value.length),
|
||||
"Text in dialog box is not selected");
|
||||
|
||||
EventUtils.synthesizeKey(FIND_KEY,
|
||||
{ accelKey: true, altKey, shiftKey }, edWin);
|
||||
synthesizeKeyShortcut(REPLACE_KEY, edWin);
|
||||
|
||||
({ selectionStart, selectionEnd } = input);
|
||||
|
||||
|
|
|
@ -1,14 +1,16 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* import-globals-from ../../framework/test/shared-head.js */
|
||||
"use strict";
|
||||
|
||||
const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
// shared-head.js handles imports, constants, and utility functions
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
|
||||
this);
|
||||
|
||||
const { NetUtil } = require("resource://gre/modules/NetUtil.jsm");
|
||||
const Editor = require("devtools/client/sourceeditor/editor");
|
||||
const promise = require("promise");
|
||||
const flags = require("devtools/shared/flags");
|
||||
const {getClientCssProperties} = require("devtools/shared/fronts/css-properties");
|
||||
|
||||
flags.testing = true;
|
||||
|
@ -16,29 +18,6 @@ SimpleTest.registerCleanupFunction(() => {
|
|||
flags.testing = false;
|
||||
});
|
||||
|
||||
/**
|
||||
* Open a new tab at a URL and call a callback on load
|
||||
*/
|
||||
function addTab(url, callback) {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab(url);
|
||||
let tab = gBrowser.selectedTab;
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
|
||||
return BrowserTestUtils.browserLoaded(browser).then(function () {
|
||||
if (typeof(callback) == "function") {
|
||||
callback(browser, tab, browser.contentDocument);
|
||||
}
|
||||
return tab;
|
||||
});
|
||||
}
|
||||
|
||||
function promiseTab(url) {
|
||||
return new Promise(resolve =>
|
||||
addTab(url, resolve));
|
||||
}
|
||||
|
||||
function promiseWaitForFocus() {
|
||||
return new Promise(resolve =>
|
||||
waitForFocus(resolve));
|
||||
|
|
|
@ -376,8 +376,7 @@ JSTerm.prototype = {
|
|||
}
|
||||
|
||||
if (this.hud.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
this.hud.newConsoleOutput.dispatchMessageAdd(response);
|
||||
callback && callback();
|
||||
this.hud.newConsoleOutput.dispatchMessageAdd(response, true).then(callback);
|
||||
return;
|
||||
}
|
||||
let msg = new Messages.JavaScriptEvalOutput(response,
|
||||
|
|
|
@ -21,19 +21,17 @@ const ConsoleOutput = createClass({
|
|||
displayName: "ConsoleOutput",
|
||||
|
||||
propTypes: {
|
||||
hudProxyClient: PropTypes.object.isRequired,
|
||||
messages: PropTypes.object.isRequired,
|
||||
messagesUi: PropTypes.object.isRequired,
|
||||
sourceMapService: PropTypes.object,
|
||||
onViewSourceInDebugger: PropTypes.func.isRequired,
|
||||
openNetworkPanel: PropTypes.func.isRequired,
|
||||
openLink: PropTypes.func.isRequired,
|
||||
emitNewMessage: PropTypes.func.isRequired,
|
||||
serviceContainer: PropTypes.shape({
|
||||
attachRefToHud: PropTypes.func.isRequired,
|
||||
}),
|
||||
autoscroll: PropTypes.bool.isRequired,
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
scrollToBottom(this.outputNode);
|
||||
this.props.serviceContainer.attachRefToHud("outputWrapper", this.outputNode);
|
||||
},
|
||||
|
||||
componentWillUpdate(nextProps, nextState) {
|
||||
|
@ -60,29 +58,19 @@ const ConsoleOutput = createClass({
|
|||
let {
|
||||
dispatch,
|
||||
autoscroll,
|
||||
hudProxyClient,
|
||||
messages,
|
||||
messagesUi,
|
||||
messagesTableData,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger,
|
||||
openNetworkPanel,
|
||||
openLink,
|
||||
emitNewMessage,
|
||||
serviceContainer,
|
||||
} = this.props;
|
||||
|
||||
let messageNodes = messages.map((message) => {
|
||||
return (
|
||||
MessageContainer({
|
||||
dispatch,
|
||||
hudProxyClient,
|
||||
message,
|
||||
key: message.id,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger,
|
||||
openNetworkPanel,
|
||||
openLink,
|
||||
emitNewMessage,
|
||||
serviceContainer,
|
||||
open: messagesUi.includes(message.id),
|
||||
tableData: messagesTableData.get(message.id),
|
||||
autoscroll,
|
||||
|
|
|
@ -24,18 +24,20 @@ const ConsoleTable = createClass({
|
|||
propTypes: {
|
||||
dispatch: PropTypes.func.isRequired,
|
||||
parameters: PropTypes.array.isRequired,
|
||||
hudProxyClient: PropTypes.object.isRequired,
|
||||
serviceContainer: PropTypes.shape({
|
||||
hudProxyClient: PropTypes.object.isRequired,
|
||||
}),
|
||||
id: PropTypes.string.isRequired,
|
||||
},
|
||||
|
||||
componentWillMount: function () {
|
||||
const {id, dispatch, hudProxyClient, parameters} = this.props;
|
||||
const {id, dispatch, serviceContainer, parameters} = this.props;
|
||||
|
||||
if (!Array.isArray(parameters) || parameters.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const client = new ObjectClient(hudProxyClient, parameters[0]);
|
||||
const client = new ObjectClient(serviceContainer.hudProxyClient, parameters[0]);
|
||||
let dataType = getParametersDataType(parameters);
|
||||
|
||||
// Get all the object properties.
|
||||
|
|
|
@ -26,9 +26,17 @@ const FilterBar = createClass({
|
|||
|
||||
propTypes: {
|
||||
filter: PropTypes.object.isRequired,
|
||||
serviceContainer: PropTypes.shape({
|
||||
attachRefToHud: PropTypes.func.isRequired,
|
||||
}).isRequired,
|
||||
ui: PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
this.props.serviceContainer.attachRefToHud("filterBox",
|
||||
this.wrapperNode.querySelector(".text-filter"));
|
||||
},
|
||||
|
||||
onClickMessagesClear: function () {
|
||||
this.props.dispatch(messagesClear());
|
||||
},
|
||||
|
@ -63,7 +71,7 @@ const FilterBar = createClass({
|
|||
onClick: this.onClickFilterBarToggle
|
||||
}),
|
||||
dom.input({
|
||||
className: "devtools-plaininput",
|
||||
className: "devtools-plaininput text-filter",
|
||||
type: "search",
|
||||
value: filter.text,
|
||||
placeholder: "Filter output",
|
||||
|
@ -141,8 +149,12 @@ const FilterBar = createClass({
|
|||
}
|
||||
|
||||
return (
|
||||
dom.div({className: "webconsole-filteringbar-wrapper"},
|
||||
...children
|
||||
dom.div({
|
||||
className: "webconsole-filteringbar-wrapper",
|
||||
ref: node => {
|
||||
this.wrapperNode = node;
|
||||
}
|
||||
}, ...children
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -32,12 +32,8 @@ const MessageContainer = createClass({
|
|||
|
||||
propTypes: {
|
||||
message: PropTypes.object.isRequired,
|
||||
sourceMapService: PropTypes.object,
|
||||
onViewSourceInDebugger: PropTypes.func.isRequired,
|
||||
openNetworkPanel: PropTypes.func.isRequired,
|
||||
openLink: PropTypes.func.isRequired,
|
||||
open: PropTypes.bool.isRequired,
|
||||
hudProxyClient: PropTypes.object.isRequired,
|
||||
serviceContainer: PropTypes.object.isRequired,
|
||||
autoscroll: PropTypes.bool.isRequired,
|
||||
},
|
||||
|
||||
|
|
|
@ -21,10 +21,8 @@ ConsoleApiCall.displayName = "ConsoleApiCall";
|
|||
|
||||
ConsoleApiCall.propTypes = {
|
||||
message: PropTypes.object.isRequired,
|
||||
sourceMapService: PropTypes.object,
|
||||
onViewSourceInDebugger: PropTypes.func.isRequired,
|
||||
open: PropTypes.bool,
|
||||
hudProxyClient: PropTypes.object.isRequired,
|
||||
serviceContainer: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
ConsoleApiCall.defaultProps = {
|
||||
|
@ -35,12 +33,9 @@ function ConsoleApiCall(props) {
|
|||
const {
|
||||
dispatch,
|
||||
message,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger,
|
||||
open,
|
||||
hudProxyClient,
|
||||
tableData,
|
||||
emitNewMessage,
|
||||
serviceContainer,
|
||||
} = props;
|
||||
const {
|
||||
id: messageId,
|
||||
|
@ -72,7 +67,7 @@ function ConsoleApiCall(props) {
|
|||
attachment = ConsoleTable({
|
||||
dispatch,
|
||||
id: message.id,
|
||||
hudProxyClient,
|
||||
serviceContainer,
|
||||
parameters: message.parameters,
|
||||
tableData
|
||||
});
|
||||
|
@ -92,9 +87,7 @@ function ConsoleApiCall(props) {
|
|||
frame,
|
||||
stacktrace,
|
||||
attachment,
|
||||
onViewSourceInDebugger,
|
||||
sourceMapService,
|
||||
emitNewMessage,
|
||||
serviceContainer,
|
||||
dispatch,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -31,6 +31,10 @@ function ConsoleCommand(props) {
|
|||
messageText: messageBody,
|
||||
} = props.message;
|
||||
|
||||
const {
|
||||
serviceContainer,
|
||||
} = props;
|
||||
|
||||
const childProps = {
|
||||
source,
|
||||
type,
|
||||
|
@ -38,6 +42,7 @@ function ConsoleCommand(props) {
|
|||
topLevelClasses: [],
|
||||
messageBody,
|
||||
scrollToMessage: props.autoscroll,
|
||||
serviceContainer,
|
||||
};
|
||||
return Message(childProps);
|
||||
}
|
||||
|
|
|
@ -21,12 +21,12 @@ EvaluationResult.propTypes = {
|
|||
};
|
||||
|
||||
function EvaluationResult(props) {
|
||||
const { message } = props;
|
||||
const { message, serviceContainer } = props;
|
||||
const {
|
||||
source,
|
||||
type,
|
||||
level,
|
||||
emitNewMessage,
|
||||
id: messageId,
|
||||
} = message;
|
||||
|
||||
let messageBody;
|
||||
|
@ -44,8 +44,9 @@ function EvaluationResult(props) {
|
|||
level,
|
||||
topLevelClasses,
|
||||
messageBody,
|
||||
messageId,
|
||||
scrollToMessage: props.autoscroll,
|
||||
emitNewMessage,
|
||||
serviceContainer,
|
||||
};
|
||||
return Message(childProps);
|
||||
}
|
||||
|
|
|
@ -19,17 +19,19 @@ NetworkEventMessage.displayName = "NetworkEventMessage";
|
|||
|
||||
NetworkEventMessage.propTypes = {
|
||||
message: PropTypes.object.isRequired,
|
||||
openNetworkPanel: PropTypes.func.isRequired,
|
||||
serviceContainer: PropTypes.shape({
|
||||
openNetworkPanel: PropTypes.func.isRequired,
|
||||
}),
|
||||
};
|
||||
|
||||
function NetworkEventMessage(props) {
|
||||
const { message, openNetworkPanel, emitNewMessage } = props;
|
||||
const { message, serviceContainer } = props;
|
||||
const { actor, source, type, level, request, isXHR } = message;
|
||||
|
||||
const topLevelClasses = [ "cm-s-mozilla" ];
|
||||
|
||||
function onUrlClick() {
|
||||
openNetworkPanel(actor);
|
||||
serviceContainer.openNetworkPanel(actor);
|
||||
}
|
||||
|
||||
const method = dom.span({className: "method" }, request.method);
|
||||
|
@ -47,7 +49,7 @@ function NetworkEventMessage(props) {
|
|||
level,
|
||||
topLevelClasses,
|
||||
messageBody,
|
||||
emitNewMessage,
|
||||
serviceContainer,
|
||||
};
|
||||
return Message(childProps);
|
||||
}
|
||||
|
|
|
@ -28,9 +28,7 @@ function PageError(props) {
|
|||
const {
|
||||
message,
|
||||
open,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger,
|
||||
emitNewMessage,
|
||||
serviceContainer,
|
||||
} = props;
|
||||
const {
|
||||
id: messageId,
|
||||
|
@ -54,9 +52,7 @@ function PageError(props) {
|
|||
repeat,
|
||||
frame,
|
||||
stacktrace,
|
||||
onViewSourceInDebugger,
|
||||
sourceMapService,
|
||||
emitNewMessage,
|
||||
serviceContainer,
|
||||
};
|
||||
return Message(childProps);
|
||||
}
|
||||
|
|
|
@ -36,16 +36,23 @@ const Message = createClass({
|
|||
stacktrace: PropTypes.any,
|
||||
messageId: PropTypes.string,
|
||||
scrollToMessage: PropTypes.bool,
|
||||
onViewSourceInDebugger: PropTypes.func,
|
||||
sourceMapService: PropTypes.any,
|
||||
serviceContainer: PropTypes.shape({
|
||||
emitNewMessage: PropTypes.func.isRequired,
|
||||
onViewSourceInDebugger: PropTypes.func.isRequired,
|
||||
sourceMapService: PropTypes.any,
|
||||
}),
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
if (this.messageNode && this.props.emitNewMessage) {
|
||||
this.props.emitNewMessage(this.messageNode);
|
||||
if (this.messageNode) {
|
||||
if (this.props.scrollToMessage) {
|
||||
this.messageNode.scrollIntoView();
|
||||
}
|
||||
// Event used in tests. Some message types don't pass it in because existing tests
|
||||
// did not emit for them.
|
||||
if (this.props.serviceContainer) {
|
||||
this.props.serviceContainer.emitNewMessage(this.messageNode, this.props.messageId);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -60,8 +67,7 @@ const Message = createClass({
|
|||
messageBody,
|
||||
frame,
|
||||
stacktrace,
|
||||
onViewSourceInDebugger,
|
||||
sourceMapService,
|
||||
serviceContainer,
|
||||
dispatch,
|
||||
} = this.props;
|
||||
|
||||
|
@ -79,7 +85,7 @@ const Message = createClass({
|
|||
} else if (stacktrace) {
|
||||
const child = open ? StackTrace({
|
||||
stacktrace: stacktrace,
|
||||
onViewSourceInDebugger: onViewSourceInDebugger
|
||||
onViewSourceInDebugger: serviceContainer.onViewSourceInDebugger
|
||||
}) : null;
|
||||
attachment = dom.div({ className: "stacktrace devtools-monospace" }, child);
|
||||
}
|
||||
|
@ -106,9 +112,9 @@ const Message = createClass({
|
|||
const location = dom.span({ className: "message-location devtools-monospace" },
|
||||
shouldRenderFrame ? FrameView({
|
||||
frame,
|
||||
onClick: onViewSourceInDebugger,
|
||||
onClick: serviceContainer.onViewSourceInDebugger,
|
||||
showEmptyPathAsHost: true,
|
||||
sourceMapService
|
||||
sourceMapService: serviceContainer.sourceMapService
|
||||
}) : null
|
||||
);
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ const NewConsoleOutputWrapper = BrowserLoader({
|
|||
baseURI: "resource://devtools/client/webconsole/new-console-output/",
|
||||
window}).require("./new-console-output-wrapper");
|
||||
|
||||
this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner, emitNewMessage) {
|
||||
this.NewConsoleOutput = function (parentNode, jsterm, toolbox, owner, serviceContainer) {
|
||||
console.log("Creating NewConsoleOutput", parentNode, NewConsoleOutputWrapper);
|
||||
return new NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner, emitNewMessage);
|
||||
return new NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner, serviceContainer);
|
||||
};
|
||||
|
|
|
@ -29,31 +29,38 @@ function NewConsoleOutputWrapper(parentNode, jsterm, toolbox, owner) {
|
|||
|
||||
NewConsoleOutputWrapper.prototype = {
|
||||
init: function () {
|
||||
const sourceMapService = this.toolbox ? this.toolbox._sourceMapService : null;
|
||||
const attachRefToHud = (id, node) => {
|
||||
this.jsterm.hud[id] = node;
|
||||
};
|
||||
|
||||
let childComponent = ConsoleOutput({
|
||||
hudProxyClient: this.jsterm.hud.proxy.client,
|
||||
sourceMapService,
|
||||
onViewSourceInDebugger: frame => this.toolbox.viewSourceInDebugger.call(
|
||||
this.toolbox,
|
||||
frame.url,
|
||||
frame.line
|
||||
),
|
||||
openNetworkPanel: (requestId) => {
|
||||
return this.toolbox.selectTool("netmonitor").then(panel => {
|
||||
return panel.panelWin.NetMonitorController.inspectRequest(requestId);
|
||||
});
|
||||
},
|
||||
openLink: (url) => {
|
||||
this.owner.openLink(url);
|
||||
},
|
||||
emitNewMessage: (node) => {
|
||||
this.jsterm.hud.emit("new-messages", new Set([{
|
||||
node
|
||||
}]));
|
||||
},
|
||||
serviceContainer: {
|
||||
attachRefToHud,
|
||||
emitNewMessage: (node, messageId) => {
|
||||
this.jsterm.hud.emit("new-messages", new Set([{
|
||||
node,
|
||||
messageId,
|
||||
}]));
|
||||
},
|
||||
hudProxyClient: this.jsterm.hud.proxy.client,
|
||||
onViewSourceInDebugger: frame => this.toolbox.viewSourceInDebugger.call(
|
||||
this.toolbox,
|
||||
frame.url,
|
||||
frame.line
|
||||
),
|
||||
openNetworkPanel: (requestId) => {
|
||||
return this.toolbox.selectTool("netmonitor").then(panel => {
|
||||
return panel.panelWin.NetMonitorController.inspectRequest(requestId);
|
||||
});
|
||||
},
|
||||
sourceMapService: this.toolbox ? this.toolbox._sourceMapService : null,
|
||||
}
|
||||
});
|
||||
let filterBar = FilterBar({
|
||||
serviceContainer: {
|
||||
attachRefToHud
|
||||
}
|
||||
});
|
||||
let filterBar = FilterBar({});
|
||||
let provider = React.createElement(
|
||||
Provider,
|
||||
{ store },
|
||||
|
@ -65,14 +72,34 @@ NewConsoleOutputWrapper.prototype = {
|
|||
|
||||
this.body = ReactDOM.render(provider, this.parentNode);
|
||||
},
|
||||
dispatchMessageAdd: (message) => {
|
||||
batchedMessageAdd(actions.messageAdd(message));
|
||||
dispatchMessageAdd: function(message, waitForResponse) {
|
||||
let action = actions.messageAdd(message);
|
||||
let messageId = action.message.get("id");
|
||||
batchedMessageAdd(action);
|
||||
|
||||
// Wait for the message to render to resolve with the DOM node.
|
||||
// This is just for backwards compatibility with old tests, and should
|
||||
// be removed once it's not needed anymore.
|
||||
if (waitForResponse) {
|
||||
return new Promise(resolve => {
|
||||
let jsterm = this.jsterm;
|
||||
jsterm.hud.on("new-messages", function onThisMessage(e, messages) {
|
||||
for (let m of messages) {
|
||||
if (m.messageId == messageId) {
|
||||
resolve(m.node);
|
||||
jsterm.hud.off("new-messages", onThisMessage);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
dispatchMessagesAdd: (messages) => {
|
||||
dispatchMessagesAdd: function(messages) {
|
||||
const batchedActions = messages.map(message => actions.messageAdd(message));
|
||||
store.dispatch(actions.batchActions(batchedActions));
|
||||
},
|
||||
dispatchMessagesClear: () => {
|
||||
dispatchMessagesClear: function() {
|
||||
store.dispatch(actions.messagesClear());
|
||||
},
|
||||
};
|
||||
|
|
|
@ -14,7 +14,7 @@ const ConsoleApiCall = createFactory(require("devtools/client/webconsole/new-con
|
|||
|
||||
// Test fakes.
|
||||
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
|
||||
const onViewSourceInDebugger = () => {};
|
||||
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
|
||||
|
||||
const tempfilePath = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js";
|
||||
|
||||
|
@ -22,7 +22,7 @@ describe("ConsoleAPICall component:", () => {
|
|||
describe("console.log", () => {
|
||||
it("renders string grips", () => {
|
||||
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
|
||||
const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger }));
|
||||
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body").text()).toBe("foobar test");
|
||||
expect(wrapper.find(".objectBox-string").length).toBe(2);
|
||||
|
@ -38,7 +38,7 @@ describe("ConsoleAPICall component:", () => {
|
|||
const message =
|
||||
stubPreparedMessages.get("console.log('foobar', 'test')")
|
||||
.set("repeat", 107);
|
||||
const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger }));
|
||||
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-repeats").text()).toBe("107");
|
||||
expect(wrapper.find(".message-repeats").prop("title")).toBe("107 repeats");
|
||||
|
@ -50,7 +50,7 @@ describe("ConsoleAPICall component:", () => {
|
|||
describe("console.count", () => {
|
||||
it("renders", () => {
|
||||
const message = stubPreparedMessages.get("console.count('bar')");
|
||||
const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger }));
|
||||
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body").text()).toBe("bar: 1");
|
||||
});
|
||||
|
@ -59,7 +59,7 @@ describe("ConsoleAPICall component:", () => {
|
|||
describe("console.assert", () => {
|
||||
it("renders", () => {
|
||||
const message = stubPreparedMessages.get("console.assert(false, {message: 'foobar'})");
|
||||
const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger }));
|
||||
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body").text()).toBe("Assertion failed: Object { message: \"foobar\" }");
|
||||
});
|
||||
|
@ -68,7 +68,7 @@ describe("ConsoleAPICall component:", () => {
|
|||
describe("console.time", () => {
|
||||
it("does not show anything", () => {
|
||||
const message = stubPreparedMessages.get("console.time('bar')");
|
||||
const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger }));
|
||||
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body").text()).toBe("");
|
||||
});
|
||||
|
@ -77,7 +77,7 @@ describe("ConsoleAPICall component:", () => {
|
|||
describe("console.timeEnd", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("console.timeEnd('bar')");
|
||||
const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger }));
|
||||
const wrapper = render(ConsoleApiCall({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body").text()).toBe(message.messageText);
|
||||
expect(wrapper.find(".message-body").text()).toMatch(/^bar: \d+(\.\d+)?ms$/);
|
||||
|
@ -87,7 +87,7 @@ describe("ConsoleAPICall component:", () => {
|
|||
describe("console.trace", () => {
|
||||
it("renders", () => {
|
||||
const message = stubPreparedMessages.get("console.trace()");
|
||||
const wrapper = render(ConsoleApiCall({ message, onViewSourceInDebugger, open: true }));
|
||||
const wrapper = render(ConsoleApiCall({ message, serviceContainer, open: true }));
|
||||
const filepath = `${tempfilePath}`;
|
||||
|
||||
expect(wrapper.find(".message-body").text()).toBe("console.trace()");
|
||||
|
|
|
@ -18,12 +18,13 @@ const {
|
|||
} = require("devtools/client/webconsole/new-console-output/constants");
|
||||
|
||||
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
|
||||
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
|
||||
|
||||
describe("FilterBar component:", () => {
|
||||
it("initial render", () => {
|
||||
const store = setupStore([]);
|
||||
|
||||
const wrapper = render(Provider({store}, FilterBar({})));
|
||||
const wrapper = render(Provider({store}, FilterBar({ serviceContainer })));
|
||||
const toolbar = wrapper.find(
|
||||
".devtools-toolbar.webconsole-filterbar-primary"
|
||||
);
|
||||
|
@ -39,7 +40,7 @@ describe("FilterBar component:", () => {
|
|||
expect(toolbar.children().eq(1).attr("title")).toBe("Toggle filter bar");
|
||||
|
||||
// Text filter
|
||||
expect(toolbar.children().eq(2).attr("class")).toBe("devtools-plaininput");
|
||||
expect(toolbar.children().eq(2).attr("class")).toBe("devtools-plaininput text-filter");
|
||||
expect(toolbar.children().eq(2).attr("placeholder")).toBe("Filter output");
|
||||
expect(toolbar.children().eq(2).attr("type")).toBe("search");
|
||||
expect(toolbar.children().eq(2).attr("value")).toBe("");
|
||||
|
@ -50,7 +51,7 @@ describe("FilterBar component:", () => {
|
|||
|
||||
expect(getAllUi(store.getState()).filterBarVisible).toBe(false);
|
||||
|
||||
const wrapper = mount(Provider({store}, FilterBar({})));
|
||||
const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
|
||||
wrapper.find(".devtools-filter-icon").simulate("click");
|
||||
|
||||
expect(getAllUi(store.getState()).filterBarVisible).toBe(true);
|
||||
|
@ -77,7 +78,7 @@ describe("FilterBar component:", () => {
|
|||
const store = setupStore([]);
|
||||
store.dispatch = sinon.spy();
|
||||
|
||||
const wrapper = mount(Provider({store}, FilterBar({})));
|
||||
const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
|
||||
wrapper.find(".devtools-clear-icon").simulate("click");
|
||||
const call = store.dispatch.getCall(0);
|
||||
expect(call.args[0]).toEqual({
|
||||
|
@ -88,7 +89,7 @@ describe("FilterBar component:", () => {
|
|||
it("sets filter text when text is typed", () => {
|
||||
const store = setupStore([]);
|
||||
|
||||
const wrapper = mount(Provider({store}, FilterBar({})));
|
||||
const wrapper = mount(Provider({store}, FilterBar({ serviceContainer })));
|
||||
wrapper.find(".devtools-plaininput").simulate("input", { target: { value: "a" } });
|
||||
expect(store.getState().filters.text).toBe("a");
|
||||
});
|
||||
|
|
|
@ -17,12 +17,12 @@ const PageError = require("devtools/client/webconsole/new-console-output/compone
|
|||
|
||||
// Test fakes.
|
||||
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
|
||||
const onViewSourceInDebugger = () => {};
|
||||
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
|
||||
|
||||
describe("MessageContainer component:", () => {
|
||||
it("pipes data to children as expected", () => {
|
||||
const message = stubPreparedMessages.get("console.log('foobar', 'test')");
|
||||
const rendered = renderComponent(MessageContainer, {message, onViewSourceInDebugger});
|
||||
const rendered = renderComponent(MessageContainer, {message, serviceContainer});
|
||||
|
||||
expect(rendered.textContent.includes("foobar")).toBe(true);
|
||||
});
|
||||
|
@ -46,7 +46,7 @@ describe("MessageContainer component:", () => {
|
|||
const { component, message } = info;
|
||||
const rendered = shallowRenderComponent(MessageContainer, {
|
||||
message,
|
||||
onViewSourceInDebugger,
|
||||
serviceContainer,
|
||||
});
|
||||
expect(rendered.type).toBe(component);
|
||||
});
|
||||
|
|
|
@ -14,16 +14,15 @@ const NetworkEventMessage = createFactory(require("devtools/client/webconsole/ne
|
|||
|
||||
// Test fakes.
|
||||
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
|
||||
const onViewSourceInDebugger = () => {};
|
||||
const openNetworkPanel = () => {};
|
||||
const openLink = () => {};
|
||||
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
|
||||
|
||||
const EXPECTED_URL = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/inexistent.html";
|
||||
|
||||
describe("NetworkEventMessage component:", () => {
|
||||
describe("GET request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("GET request");
|
||||
const wrapper = render(NetworkEventMessage({ message, onViewSourceInDebugger, openNetworkPanel, openLink }));
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body .method").text()).toBe("GET");
|
||||
expect(wrapper.find(".message-body .xhr").length).toBe(0);
|
||||
|
@ -36,7 +35,7 @@ describe("NetworkEventMessage component:", () => {
|
|||
describe("XHR GET request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("XHR GET request");
|
||||
const wrapper = render(NetworkEventMessage({ message, onViewSourceInDebugger, openNetworkPanel, openLink }));
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body .method").text()).toBe("GET");
|
||||
expect(wrapper.find(".message-body .xhr").length).toBe(1);
|
||||
|
@ -49,7 +48,7 @@ describe("NetworkEventMessage component:", () => {
|
|||
describe("XHR POST request", () => {
|
||||
it("renders as expected", () => {
|
||||
const message = stubPreparedMessages.get("XHR POST request");
|
||||
const wrapper = render(NetworkEventMessage({ message, onViewSourceInDebugger, openNetworkPanel, openLink }));
|
||||
const wrapper = render(NetworkEventMessage({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body .method").text()).toBe("POST");
|
||||
expect(wrapper.find(".message-body .xhr").length).toBe(1);
|
||||
|
|
|
@ -11,11 +11,12 @@ const PageError = require("devtools/client/webconsole/new-console-output/compone
|
|||
|
||||
// Test fakes.
|
||||
const { stubPreparedMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
|
||||
const serviceContainer = require("devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer");
|
||||
|
||||
describe("PageError component:", () => {
|
||||
it("renders", () => {
|
||||
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
|
||||
const wrapper = render(PageError({ message }));
|
||||
const wrapper = render(PageError({ message, serviceContainer }));
|
||||
|
||||
expect(wrapper.find(".message-body").text())
|
||||
.toBe("ReferenceError: asdf is not defined");
|
||||
|
@ -33,7 +34,7 @@ describe("PageError component:", () => {
|
|||
|
||||
it("has a stacktrace which can be openned", () => {
|
||||
const message = stubPreparedMessages.get("ReferenceError: asdf is not defined");
|
||||
const wrapper = render(PageError({ message, open: true }));
|
||||
const wrapper = render(PageError({ message, serviceContainer, open: true }));
|
||||
|
||||
// There should be three stacktrace items.
|
||||
const frameLinks = wrapper.find(`.stack-trace span.frame-link`);
|
||||
|
|
15
devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer.js
поставляемый
Normal file
15
devtools/client/webconsole/new-console-output/test/fixtures/serviceContainer.js
поставляемый
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
attachRefToHud: () => {},
|
||||
emitNewMessage: () => {},
|
||||
hudProxyClient: {},
|
||||
onViewSourceInDebugger: () => {},
|
||||
openNetworkPanel: () => {},
|
||||
sourceMapService: {
|
||||
subscribe: () => {},
|
||||
},
|
||||
};
|
|
@ -12,6 +12,7 @@ support-files =
|
|||
[browser_webconsole_filters.js]
|
||||
[browser_webconsole_init.js]
|
||||
[browser_webconsole_input_focus.js]
|
||||
[browser_webconsole_keyboard_accessibility.js]
|
||||
[browser_webconsole_observer_notifications.js]
|
||||
[browser_webconsole_vview_close_on_esc_key.js]
|
||||
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Check that basic keyboard shortcuts work in the web console.
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_URI =
|
||||
`data:text/html;charset=utf-8,<p>Test keyboard accessibility</p>
|
||||
<script>
|
||||
for (let i = 1; i <= 100; i++) {
|
||||
console.log("console message " + i);
|
||||
}
|
||||
</script>
|
||||
`;
|
||||
|
||||
add_task(function* () {
|
||||
let hud = yield openNewTabAndConsole(TEST_URI);
|
||||
info("Web Console opened");
|
||||
|
||||
const outputWrapper = hud.ui.outputWrapper;
|
||||
|
||||
yield waitFor(() => findMessages(hud, "").length == 100);
|
||||
|
||||
let currentPosition = outputWrapper.scrollTop;
|
||||
const bottom = currentPosition;
|
||||
|
||||
EventUtils.sendMouseEvent({type: "click"}, hud.jsterm.inputNode);
|
||||
|
||||
// Page up.
|
||||
EventUtils.synthesizeKey("VK_PAGE_UP", {});
|
||||
isnot(outputWrapper.scrollTop, currentPosition,
|
||||
"scroll position changed after page up");
|
||||
|
||||
// Page down.
|
||||
currentPosition = outputWrapper.scrollTop;
|
||||
EventUtils.synthesizeKey("VK_PAGE_DOWN", {});
|
||||
ok(outputWrapper.scrollTop > currentPosition,
|
||||
"scroll position now at bottom");
|
||||
|
||||
// Home
|
||||
EventUtils.synthesizeKey("VK_HOME", {});
|
||||
is(outputWrapper.scrollTop, 0, "scroll position now at top");
|
||||
|
||||
// End
|
||||
EventUtils.synthesizeKey("VK_END", {});
|
||||
let scrollTop = outputWrapper.scrollTop;
|
||||
ok(scrollTop > 0 && Math.abs(scrollTop - bottom) <= 5,
|
||||
"scroll position now at bottom");
|
||||
|
||||
// Clear output
|
||||
info("try ctrl-l to clear output");
|
||||
let clearShortcut;
|
||||
if (Services.appinfo.OS === "Darwin") {
|
||||
clearShortcut = WCUL10n.getStr("webconsole.clear.keyOSX");
|
||||
} else {
|
||||
clearShortcut = WCUL10n.getStr("webconsole.clear.key");
|
||||
}
|
||||
synthesizeKeyShortcut(clearShortcut);
|
||||
yield waitFor(() => findMessages(hud, "").length == 0);
|
||||
is(hud.jsterm.inputNode.getAttribute("focused"), "true", "jsterm input is focused");
|
||||
|
||||
// Focus filter
|
||||
info("try ctrl-f to focus filter");
|
||||
synthesizeKeyShortcut(WCUL10n.getStr("webconsole.find.key"));
|
||||
ok(!hud.jsterm.inputNode.getAttribute("focused"), "jsterm input is not focused");
|
||||
is(hud.ui.filterBox, outputWrapper.ownerDocument.activeElement,
|
||||
"filter input is focused");
|
||||
});
|
|
@ -12,6 +12,10 @@ Services.scriptloader.loadSubScript(
|
|||
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
|
||||
this);
|
||||
|
||||
var {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
|
||||
const WEBCONSOLE_STRINGS_URI = "devtools/locale/webconsole.properties";
|
||||
var WCUL10n = new WebConsoleUtils.L10n(WEBCONSOLE_STRINGS_URI);
|
||||
|
||||
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
|
||||
registerCleanupFunction(function* () {
|
||||
Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
|
||||
|
|
|
@ -581,7 +581,9 @@ WebConsoleFrame.prototype = {
|
|||
if (this.NEW_CONSOLE_OUTPUT_ENABLED) {
|
||||
// @TODO Remove this once JSTerm is handled with React/Redux.
|
||||
this.window.jsterm = this.jsterm;
|
||||
console.log("Entering experimental mode for console frontend");
|
||||
|
||||
// Remove context menu for now (see Bug 1307239).
|
||||
this.outputWrapper.removeAttribute("context");
|
||||
|
||||
// XXX: We should actually stop output from happening on old output
|
||||
// panel, but for now let's just hide it.
|
||||
|
|
|
@ -260,8 +260,6 @@ function Discovery() {
|
|||
this._onRemoteScan = this._onRemoteScan.bind(this);
|
||||
this._onRemoteUpdate = this._onRemoteUpdate.bind(this);
|
||||
this._purgeMissingDevices = this._purgeMissingDevices.bind(this);
|
||||
|
||||
Services.obs.addObserver(this, "network-active-changed", false);
|
||||
}
|
||||
|
||||
Discovery.prototype = {
|
||||
|
@ -380,24 +378,6 @@ Discovery.prototype = {
|
|||
this._transports.update = null;
|
||||
},
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
if (topic !== "network-active-changed") {
|
||||
return;
|
||||
}
|
||||
let activeNetworkInfo = subject;
|
||||
if (!activeNetworkInfo) {
|
||||
log("No active network info");
|
||||
return;
|
||||
}
|
||||
activeNetworkInfo = activeNetworkInfo.QueryInterface(Ci.nsINetworkInfo);
|
||||
log("Active network changed to: " + activeNetworkInfo.type);
|
||||
// UDP sockets go down when the device goes offline, so we'll restart them
|
||||
// when the active network goes back to WiFi.
|
||||
if (activeNetworkInfo.type === Ci.nsINetworkInfo.NETWORK_TYPE_WIFI) {
|
||||
this._restartListening();
|
||||
}
|
||||
},
|
||||
|
||||
_restartListening: function () {
|
||||
if (this._transports.scan) {
|
||||
this._stopListeningForScan();
|
||||
|
|
|
@ -212,6 +212,15 @@ public:
|
|||
|
||||
virtual bool HandleCDMProxyReady() { return false; }
|
||||
|
||||
virtual bool HandleAudioDecoded(MediaData* aAudio) { return false; }
|
||||
|
||||
virtual bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool HandleEndOfStream() { return false; }
|
||||
|
||||
protected:
|
||||
using Master = MediaDecoderStateMachine;
|
||||
explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
|
||||
|
@ -415,6 +424,26 @@ public:
|
|||
{
|
||||
return DECODER_STATE_DECODING_FIRSTFRAME;
|
||||
}
|
||||
|
||||
bool HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
mMaster->Push(aAudio, MediaData::AUDIO_DATA);
|
||||
mMaster->MaybeFinishDecodeFirstFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
mMaster->Push(aVideo, MediaData::VIDEO_DATA);
|
||||
mMaster->MaybeFinishDecodeFirstFrame();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleEndOfStream() override
|
||||
{
|
||||
mMaster->MaybeFinishDecodeFirstFrame();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MediaDecoderStateMachine::DecodingState
|
||||
|
@ -481,7 +510,61 @@ public:
|
|||
return DECODER_STATE_DECODING;
|
||||
}
|
||||
|
||||
bool HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
mMaster->Push(aAudio, MediaData::AUDIO_DATA);
|
||||
mMaster->MaybeStopPrerolling();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
mMaster->Push(aVideo, MediaData::VIDEO_DATA);
|
||||
mMaster->MaybeStopPrerolling();
|
||||
CheckSlowDecoding(aDecodeStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void CheckSlowDecoding(TimeStamp aDecodeStart)
|
||||
{
|
||||
// For non async readers, if the requested video sample was slow to
|
||||
// arrive, increase the amount of audio we buffer to ensure that we
|
||||
// don't run out of audio. This is unnecessary for async readers,
|
||||
// since they decode audio and video on different threads so they
|
||||
// are unlikely to run out of decoded audio.
|
||||
if (Reader()->IsAsync()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TimeDuration decodeTime = TimeStamp::Now() - aDecodeStart;
|
||||
int64_t adjustedTime = THRESHOLD_FACTOR * DurationToUsecs(decodeTime);
|
||||
if (adjustedTime > mMaster->mLowAudioThresholdUsecs &&
|
||||
!mMaster->HasLowBufferedData())
|
||||
{
|
||||
mMaster->mLowAudioThresholdUsecs =
|
||||
std::min(adjustedTime, mMaster->mAmpleAudioThresholdUsecs);
|
||||
|
||||
mMaster->mAmpleAudioThresholdUsecs =
|
||||
std::max(THRESHOLD_FACTOR * mMaster->mLowAudioThresholdUsecs,
|
||||
mMaster->mAmpleAudioThresholdUsecs);
|
||||
|
||||
SLOG("Slow video decode, set "
|
||||
"mLowAudioThresholdUsecs=%lld "
|
||||
"mAmpleAudioThresholdUsecs=%lld",
|
||||
mMaster->mLowAudioThresholdUsecs,
|
||||
mMaster->mAmpleAudioThresholdUsecs);
|
||||
}
|
||||
}
|
||||
|
||||
bool HandleEndOfStream() override
|
||||
{
|
||||
if (mMaster->CheckIfDecodeComplete()) {
|
||||
SetState(DECODER_STATE_COMPLETED);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Time at which we started decoding.
|
||||
TimeStamp mDecodeStartTime;
|
||||
};
|
||||
|
@ -516,6 +599,18 @@ public:
|
|||
SetState(DECODER_STATE_DORMANT);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
MOZ_ASSERT(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
MOZ_ASSERT(false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class MediaDecoderStateMachine::BufferingState
|
||||
|
@ -588,6 +683,35 @@ public:
|
|||
return DECODER_STATE_BUFFERING;
|
||||
}
|
||||
|
||||
bool HandleAudioDecoded(MediaData* aAudio) override
|
||||
{
|
||||
// This might be the sample we need to exit buffering.
|
||||
// Schedule Step() to check it.
|
||||
mMaster->Push(aAudio, MediaData::AUDIO_DATA);
|
||||
mMaster->ScheduleStateMachine();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStart) override
|
||||
{
|
||||
// This might be the sample we need to exit buffering.
|
||||
// Schedule Step() to check it.
|
||||
mMaster->Push(aVideo, MediaData::VIDEO_DATA);
|
||||
mMaster->ScheduleStateMachine();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HandleEndOfStream() override
|
||||
{
|
||||
if (mMaster->CheckIfDecodeComplete()) {
|
||||
SetState(DECODER_STATE_COMPLETED);
|
||||
} else {
|
||||
// Check if we can exit buffering.
|
||||
mMaster->ScheduleStateMachine();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
TimeStamp mBufferingStart;
|
||||
};
|
||||
|
@ -1015,45 +1139,17 @@ MediaDecoderStateMachine::NeedToDecodeAudio()
|
|||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::OnAudioDecoded(MediaData* aAudioSample)
|
||||
MediaDecoderStateMachine::OnAudioDecoded(MediaData* aAudio)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
|
||||
|
||||
RefPtr<MediaData> audio(aAudioSample);
|
||||
MOZ_ASSERT(audio);
|
||||
MOZ_ASSERT(aAudio);
|
||||
|
||||
// audio->GetEndTime() is not always mono-increasing in chained ogg.
|
||||
mDecodedAudioEndTime = std::max(audio->GetEndTime(), mDecodedAudioEndTime);
|
||||
mDecodedAudioEndTime = std::max(aAudio->GetEndTime(), mDecodedAudioEndTime);
|
||||
|
||||
SAMPLE_LOG("OnAudioDecoded [%lld,%lld]", audio->mTime, audio->GetEndTime());
|
||||
SAMPLE_LOG("OnAudioDecoded [%lld,%lld]", aAudio->mTime, aAudio->GetEndTime());
|
||||
|
||||
switch (mState) {
|
||||
case DECODER_STATE_BUFFERING: {
|
||||
// If we're buffering, this may be the sample we need to stop buffering.
|
||||
// Save it and schedule the state machine.
|
||||
Push(audio, MediaData::AUDIO_DATA);
|
||||
ScheduleStateMachine();
|
||||
return;
|
||||
}
|
||||
|
||||
case DECODER_STATE_DECODING_FIRSTFRAME: {
|
||||
Push(audio, MediaData::AUDIO_DATA);
|
||||
MaybeFinishDecodeFirstFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
case DECODER_STATE_DECODING: {
|
||||
Push(audio, MediaData::AUDIO_DATA);
|
||||
MaybeStopPrerolling();
|
||||
return;
|
||||
}
|
||||
|
||||
default: {
|
||||
// Ignore other cases.
|
||||
return;
|
||||
}
|
||||
}
|
||||
mStateObj->HandleAudioDecoded(aAudio);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1150,26 +1246,7 @@ MediaDecoderStateMachine::OnNotDecoded(MediaData::Type aType,
|
|||
|
||||
MaybeStopPrerolling();
|
||||
|
||||
switch (mState) {
|
||||
case DECODER_STATE_DECODING_FIRSTFRAME:
|
||||
MaybeFinishDecodeFirstFrame();
|
||||
return;
|
||||
case DECODER_STATE_BUFFERING:
|
||||
case DECODER_STATE_DECODING: {
|
||||
if (CheckIfDecodeComplete()) {
|
||||
SetState(DECODER_STATE_COMPLETED);
|
||||
return;
|
||||
}
|
||||
// Schedule next cycle to see if we can leave buffering state.
|
||||
if (mState == DECODER_STATE_BUFFERING) {
|
||||
ScheduleStateMachine();
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
return;
|
||||
}
|
||||
}
|
||||
mStateObj->HandleEndOfStream();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1193,65 +1270,18 @@ MediaDecoderStateMachine::MaybeFinishDecodeFirstFrame()
|
|||
}
|
||||
|
||||
void
|
||||
MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample,
|
||||
MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideo,
|
||||
TimeStamp aDecodeStartTime)
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
MOZ_ASSERT(mState != DECODER_STATE_SEEKING);
|
||||
|
||||
RefPtr<MediaData> video(aVideoSample);
|
||||
MOZ_ASSERT(video);
|
||||
MOZ_ASSERT(aVideo);
|
||||
|
||||
// Handle abnormal or negative timestamps.
|
||||
mDecodedVideoEndTime = std::max(mDecodedVideoEndTime, video->GetEndTime());
|
||||
mDecodedVideoEndTime = std::max(mDecodedVideoEndTime, aVideo->GetEndTime());
|
||||
|
||||
SAMPLE_LOG("OnVideoDecoded [%lld,%lld]", video->mTime, video->GetEndTime());
|
||||
SAMPLE_LOG("OnVideoDecoded [%lld,%lld]", aVideo->mTime, aVideo->GetEndTime());
|
||||
|
||||
switch (mState) {
|
||||
case DECODER_STATE_BUFFERING: {
|
||||
// If we're buffering, this may be the sample we need to stop buffering.
|
||||
// Save it and schedule the state machine.
|
||||
Push(video, MediaData::VIDEO_DATA);
|
||||
ScheduleStateMachine();
|
||||
return;
|
||||
}
|
||||
|
||||
case DECODER_STATE_DECODING_FIRSTFRAME: {
|
||||
Push(video, MediaData::VIDEO_DATA);
|
||||
MaybeFinishDecodeFirstFrame();
|
||||
return;
|
||||
}
|
||||
|
||||
case DECODER_STATE_DECODING: {
|
||||
Push(video, MediaData::VIDEO_DATA);
|
||||
MaybeStopPrerolling();
|
||||
|
||||
// For non async readers, if the requested video sample was slow to
|
||||
// arrive, increase the amount of audio we buffer to ensure that we
|
||||
// don't run out of audio. This is unnecessary for async readers,
|
||||
// since they decode audio and video on different threads so they
|
||||
// are unlikely to run out of decoded audio.
|
||||
if (mReader->IsAsync()) {
|
||||
return;
|
||||
}
|
||||
TimeDuration decodeTime = TimeStamp::Now() - aDecodeStartTime;
|
||||
if (THRESHOLD_FACTOR * DurationToUsecs(decodeTime) > mLowAudioThresholdUsecs &&
|
||||
!HasLowBufferedData())
|
||||
{
|
||||
mLowAudioThresholdUsecs =
|
||||
std::min(THRESHOLD_FACTOR * DurationToUsecs(decodeTime), mAmpleAudioThresholdUsecs);
|
||||
mAmpleAudioThresholdUsecs = std::max(THRESHOLD_FACTOR * mLowAudioThresholdUsecs,
|
||||
mAmpleAudioThresholdUsecs);
|
||||
DECODER_LOG("Slow video decode, set mLowAudioThresholdUsecs=%lld mAmpleAudioThresholdUsecs=%lld",
|
||||
mLowAudioThresholdUsecs, mAmpleAudioThresholdUsecs);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
// Ignore other cases.
|
||||
return;
|
||||
}
|
||||
}
|
||||
mStateObj->HandleVideoDecoded(aVideo, aDecodeStartTime);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -345,8 +345,8 @@ private:
|
|||
|
||||
// TODO: Those callback function may receive demuxed-only data.
|
||||
// Need to figure out a suitable API name for this case.
|
||||
void OnAudioDecoded(MediaData* aAudioSample);
|
||||
void OnVideoDecoded(MediaData* aVideoSample, TimeStamp aDecodeStartTime);
|
||||
void OnAudioDecoded(MediaData* aAudio);
|
||||
void OnVideoDecoded(MediaData* aVideo, TimeStamp aDecodeStartTime);
|
||||
void OnNotDecoded(MediaData::Type aType, const MediaResult& aError);
|
||||
|
||||
// Resets all state related to decoding and playback, emptying all buffers
|
||||
|
@ -724,8 +724,7 @@ private:
|
|||
bool DonePrerollingVideo()
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return !mIsVisible ||
|
||||
!IsVideoDecoding() ||
|
||||
return !IsVideoDecoding() ||
|
||||
static_cast<uint32_t>(VideoQueue().GetSize()) >=
|
||||
VideoPrerollFrames() * mPlaybackRate + 1;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,10 @@
|
|||
#include "MP4Decoder.h"
|
||||
#include "mozilla/dom/RemoteVideoDecoder.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "mozilla/WindowsVersion.h"
|
||||
#endif
|
||||
|
||||
#include "mp4_demuxer/H264.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -318,7 +322,14 @@ PDMFactory::CreatePDMs()
|
|||
}
|
||||
#endif
|
||||
#ifdef XP_WIN
|
||||
if (MediaPrefs::PDMWMFEnabled()) {
|
||||
if (MediaPrefs::PDMWMFEnabled() && IsVistaOrLater()) {
|
||||
// *Only* use WMF on Vista and later, as if Firefox is run in Windows 95
|
||||
// compatibility mode on Windows 7 (it does happen!) we may crash trying
|
||||
// to startup WMF. So we need to detect the OS version here, as in
|
||||
// compatibility mode IsVistaOrLater() and friends behave as if we're on
|
||||
// the emulated version of Windows. See bug 1279171.
|
||||
// Additionally, we don't want to start the RemoteDecoderModule if we
|
||||
// expect it's not going to work (i.e. on Windows older than Vista).
|
||||
m = new WMFDecoderModule();
|
||||
RefPtr<PlatformDecoderModule> remote = new dom::RemoteDecoderModule(m);
|
||||
mWMFFailedToLoad = !StartupPDM(remote);
|
||||
|
|
|
@ -202,6 +202,15 @@ LoadDLLs()
|
|||
HRESULT
|
||||
MFStartup()
|
||||
{
|
||||
if (!IsVistaOrLater()) {
|
||||
// *Only* use WMF on Vista and later, as if Firefox is run in Windows 95
|
||||
// compatibility mode on Windows 7 (it does happen!) we may crash trying
|
||||
// to startup WMF. So we need to detect the OS version here, as in
|
||||
// compatibility mode IsVistaOrLater() and friends behave as if we're on
|
||||
// the emulated version of Windows. See bug 1279171.
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
HRESULT hr = LoadDLLs();
|
||||
if (FAILED(hr)) {
|
||||
return hr;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
include protocol PContent;
|
||||
include protocol PBackground;
|
||||
|
||||
using mozilla::camera::CaptureEngine from "CamerasTypes.h";
|
||||
using mozilla::camera::CaptureEngine from "mozilla/media/CamerasTypes.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace camera {
|
||||
|
|
|
@ -8,7 +8,6 @@ if CONFIG['MOZ_WEBRTC']:
|
|||
EXPORTS += [
|
||||
'CamerasChild.h',
|
||||
'CamerasParent.h',
|
||||
'CamerasTypes.h',
|
||||
'LoadManager.h',
|
||||
'LoadManagerFactory.h',
|
||||
'LoadMonitor.h',
|
||||
|
@ -63,7 +62,8 @@ if CONFIG['_MSC_VER']:
|
|||
|
||||
EXPORTS.mozilla += ['ShmemPool.h',]
|
||||
|
||||
EXPORTS.mozilla.media += ['DeviceChangeCallback.h',
|
||||
EXPORTS.mozilla.media += ['CamerasTypes.h',
|
||||
'DeviceChangeCallback.h',
|
||||
'MediaChild.h',
|
||||
'MediaParent.h',
|
||||
'MediaSystemResourceClient.h',
|
||||
|
|
|
@ -136,7 +136,8 @@ AudioStreamAnalyser.prototype = {
|
|||
// else, we need more time
|
||||
requestAnimationFrame(analysisLoop);
|
||||
}
|
||||
analysisLoop();
|
||||
// We need to give the Analyser some time to start gathering data.
|
||||
wait(200).then(analysisLoop);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
|
@ -118,8 +118,6 @@ PresentationControlService.prototype = {
|
|||
DEBUG && log("PresentationControlService - service start on port: " + this._port); // jshint ignore:line
|
||||
|
||||
// Monitor network interface change to restart server socket.
|
||||
// Only B2G has nsINetworkManager
|
||||
Services.obs.addObserver(this, "network-active-changed", false);
|
||||
Services.obs.addObserver(this, "network:offline-status-changed", false);
|
||||
|
||||
this._notifyServerReady();
|
||||
|
@ -324,7 +322,6 @@ PresentationControlService.prototype = {
|
|||
this._serverSocket.close();
|
||||
this._serverSocket = null;
|
||||
|
||||
Services.obs.removeObserver(this, "network-active-changed");
|
||||
Services.obs.removeObserver(this, "network:offline-status-changed");
|
||||
|
||||
this._notifyServerStopped(Cr.NS_OK);
|
||||
|
@ -336,21 +333,6 @@ PresentationControlService.prototype = {
|
|||
observe: function(aSubject, aTopic, aData) {
|
||||
DEBUG && log("PresentationControlService - observe: " + aTopic); // jshint ignore:line
|
||||
switch (aTopic) {
|
||||
case "network-active-changed": {
|
||||
if (!aSubject) {
|
||||
DEBUG && log("No active network"); // jshint ignore:line
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restart service only when original status is online because other
|
||||
* cases will be handled by "network:offline-status-changed".
|
||||
*/
|
||||
if (!Services.io.offline) {
|
||||
this._restartServer();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "network:offline-status-changed": {
|
||||
if (aData == "offline") {
|
||||
DEBUG && log("network offline"); // jshint ignore:line
|
||||
|
|
|
@ -305,8 +305,7 @@ this.PushService = {
|
|||
case "quit-application":
|
||||
this.uninit();
|
||||
break;
|
||||
case "network-active-changed": /* On B2G. */
|
||||
case "network:offline-status-changed": /* On desktop. */
|
||||
case "network:offline-status-changed":
|
||||
this._stateChangeProcessEnqueue(_ =>
|
||||
this._changeStateOfflineEvent(aData === "offline", false)
|
||||
);
|
||||
|
@ -384,19 +383,6 @@ this.PushService = {
|
|||
});
|
||||
},
|
||||
|
||||
// utility function used to add/remove observers in startObservers() and
|
||||
// stopObservers()
|
||||
getNetworkStateChangeEventName: function() {
|
||||
try {
|
||||
let networkManager = Cc["@mozilla.org/network/manager;1"];
|
||||
if (networkManager) {
|
||||
networkManager.getService(Ci.nsINetworkManager);
|
||||
return "network-active-changed";
|
||||
}
|
||||
} catch (e) {}
|
||||
return "network:offline-status-changed";
|
||||
},
|
||||
|
||||
_findService: function(serverURL) {
|
||||
console.debug("findService()");
|
||||
|
||||
|
@ -533,26 +519,10 @@ this.PushService = {
|
|||
|
||||
Services.obs.addObserver(this, "clear-origin-data", false);
|
||||
|
||||
// On B2G the NetworkManager interface fires a network-active-changed
|
||||
// event.
|
||||
//
|
||||
// The "active network" is based on priority - i.e. Wi-Fi has higher
|
||||
// priority than data. The PushService should just use the preferred
|
||||
// network, and not care about all interface changes.
|
||||
// network-active-changed is not fired when the network goes offline, but
|
||||
// socket connections time out. The check for Services.io.offline in
|
||||
// PushServiceWebSocket._beginWSSetup() prevents unnecessary retries. When
|
||||
// the network comes back online, network-active-changed is fired.
|
||||
//
|
||||
// On non-B2G platforms, the offline-status-changed event is used to know
|
||||
// The offline-status-changed event is used to know
|
||||
// when to (dis)connect. It may not fire if the underlying OS changes
|
||||
// networks; in such a case we rely on timeout.
|
||||
//
|
||||
// On B2G both events fire, one after the other, when the network goes
|
||||
// online, so we explicitly check for the presence of NetworkManager and
|
||||
// don't add an observer for offline-status-changed on B2G.
|
||||
this._networkStateChangeEventName = this.getNetworkStateChangeEventName();
|
||||
Services.obs.addObserver(this, this._networkStateChangeEventName, false);
|
||||
Services.obs.addObserver(this, "network:offline-status-changed", false);
|
||||
|
||||
// Used to monitor if the user wishes to disable Push.
|
||||
prefs.observe("connection.enabled", this);
|
||||
|
@ -640,7 +610,7 @@ this.PushService = {
|
|||
|
||||
prefs.ignore("connection.enabled", this);
|
||||
|
||||
Services.obs.removeObserver(this, this._networkStateChangeEventName);
|
||||
Services.obs.removeObserver(this, "network:offline-status-changed");
|
||||
Services.obs.removeObserver(this, "clear-origin-data");
|
||||
Services.obs.removeObserver(this, "idle-daily");
|
||||
Services.obs.removeObserver(this, "perm-changed");
|
||||
|
|
|
@ -24,12 +24,6 @@ const {
|
|||
getCryptoParams,
|
||||
} = Cu.import("resource://gre/modules/PushCrypto.jsm");
|
||||
|
||||
if (AppConstants.MOZ_B2G) {
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gPowerManagerService",
|
||||
"@mozilla.org/power/powermanagerservice;1",
|
||||
"nsIPowerManagerService");
|
||||
}
|
||||
|
||||
const kPUSHWSDB_DB_NAME = "pushapi";
|
||||
const kPUSHWSDB_DB_VERSION = 5; // Change this if the IndexedDB format changes
|
||||
const kPUSHWSDB_STORE_NAME = "pushapi";
|
||||
|
@ -516,7 +510,6 @@ this.PushServiceWebSocket = {
|
|||
// Grab a wakelock before we open the socket to ensure we don't go to
|
||||
// sleep before connection the is opened.
|
||||
this._ws.asyncOpen(uri, uri.spec, 0, this._wsListener, null);
|
||||
this._acquireWakeLock();
|
||||
this._currentState = STATE_WAITING_FOR_WS_START;
|
||||
} catch(e) {
|
||||
console.error("beginWSSetup: Error opening websocket.",
|
||||
|
@ -537,48 +530,6 @@ this.PushServiceWebSocket = {
|
|||
return !!this._ws;
|
||||
},
|
||||
|
||||
_acquireWakeLock: function() {
|
||||
if (!AppConstants.MOZ_B2G) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the wake lock on non-B2G platforms to work around bug 1154492.
|
||||
if (!this._socketWakeLock) {
|
||||
console.debug("acquireWakeLock: Acquiring Socket Wakelock");
|
||||
this._socketWakeLock = gPowerManagerService.newWakeLock("cpu");
|
||||
}
|
||||
if (!this._socketWakeLockTimer) {
|
||||
console.debug("acquireWakeLock: Creating Socket WakeLock Timer");
|
||||
this._socketWakeLockTimer = Cc["@mozilla.org/timer;1"]
|
||||
.createInstance(Ci.nsITimer);
|
||||
}
|
||||
|
||||
console.debug("acquireWakeLock: Setting Socket WakeLock Timer");
|
||||
this._socketWakeLockTimer
|
||||
.initWithCallback(this._releaseWakeLock.bind(this),
|
||||
// Allow the same time for socket setup as we do for
|
||||
// requests after the setup. Fudge it a bit since
|
||||
// timers can be a little off and we don't want to go
|
||||
// to sleep just as the socket connected.
|
||||
this._requestTimeout + 1000,
|
||||
Ci.nsITimer.TYPE_ONE_SHOT);
|
||||
},
|
||||
|
||||
_releaseWakeLock: function() {
|
||||
if (!AppConstants.MOZ_B2G) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.debug("releaseWakeLock: Releasing Socket WakeLock");
|
||||
if (this._socketWakeLockTimer) {
|
||||
this._socketWakeLockTimer.cancel();
|
||||
}
|
||||
if (this._socketWakeLock) {
|
||||
this._socketWakeLock.unlock();
|
||||
this._socketWakeLock = null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Protocol handler invoked by server message.
|
||||
*/
|
||||
|
@ -1000,7 +951,6 @@ this.PushServiceWebSocket = {
|
|||
// begin Push protocol handshake
|
||||
_wsOnStart: function(context) {
|
||||
console.debug("wsOnStart()");
|
||||
this._releaseWakeLock();
|
||||
|
||||
if (this._currentState != STATE_WAITING_FOR_WS_START) {
|
||||
console.error("wsOnStart: NOT in STATE_WAITING_FOR_WS_START. Current",
|
||||
|
@ -1030,7 +980,6 @@ this.PushServiceWebSocket = {
|
|||
*/
|
||||
_wsOnStop: function(context, statusCode) {
|
||||
console.debug("wsOnStop()");
|
||||
this._releaseWakeLock();
|
||||
|
||||
if (statusCode != Cr.NS_OK && !this._skipReconnect) {
|
||||
console.debug("wsOnStop: Reconnecting after socket error", statusCode);
|
||||
|
|
|
@ -1144,19 +1144,11 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface,
|
|||
cairo_d2d_surface_t::TextRenderingState renderingState =
|
||||
scaled_font->rendering_mode;
|
||||
|
||||
if ((renderingState == cairo_d2d_surface_t::TEXT_RENDERING_NORMAL ||
|
||||
renderingState == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) &&
|
||||
!surface->base.permit_subpixel_antialiasing) {
|
||||
renderingState = cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE;
|
||||
}
|
||||
|
||||
IDWriteRenderingParams *params =
|
||||
DWriteFactory::RenderingParams(renderingState);
|
||||
|
||||
rv = gdiInterop->CreateBitmapRenderTarget(surface->dc,
|
||||
area.right - area.left,
|
||||
area.bottom - area.top,
|
||||
&rt);
|
||||
|
||||
if (FAILED(rv)) {
|
||||
if (rv == E_OUTOFMEMORY) {
|
||||
return (cairo_int_status_t)CAIRO_STATUS_NO_MEMORY;
|
||||
|
@ -1165,6 +1157,22 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface,
|
|||
}
|
||||
}
|
||||
|
||||
if ((renderingState == cairo_d2d_surface_t::TEXT_RENDERING_NORMAL ||
|
||||
renderingState == cairo_d2d_surface_t::TEXT_RENDERING_GDI_CLASSIC) &&
|
||||
!surface->base.permit_subpixel_antialiasing) {
|
||||
renderingState = cairo_d2d_surface_t::TEXT_RENDERING_NO_CLEARTYPE;
|
||||
IDWriteBitmapRenderTarget1* rt1;
|
||||
rv = rt->QueryInterface(&rt1);
|
||||
|
||||
if (SUCCEEDED(rv) && rt1) {
|
||||
rt1->SetTextAntialiasMode(DWRITE_TEXT_ANTIALIAS_MODE_GRAYSCALE);
|
||||
rt1->Release();
|
||||
}
|
||||
}
|
||||
|
||||
IDWriteRenderingParams *params =
|
||||
DWriteFactory::RenderingParams(renderingState);
|
||||
|
||||
/**
|
||||
* We set the number of pixels per DIP to 1.0. This is because we always want
|
||||
* to draw in device pixels, and not device independent pixels. On high DPI
|
||||
|
@ -1447,11 +1455,12 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface,
|
|||
color,
|
||||
dwritesf,
|
||||
fontArea);
|
||||
|
||||
#ifdef CAIRO_TRY_D2D_TO_GDI
|
||||
}
|
||||
#endif
|
||||
|
||||
return CAIRO_INT_STATUS_SUCCESS;
|
||||
return status;
|
||||
}
|
||||
|
||||
#define ENHANCED_CONTRAST_REGISTRY_KEY \
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
* Contributor(s):
|
||||
* Bas Schouten <bschouten@mozilla.com>
|
||||
*/
|
||||
#include <dwrite.h>
|
||||
#include <dwrite_1.h>
|
||||
#include <d2d1.h>
|
||||
|
||||
// DirectWrite is not available on all platforms.
|
||||
|
|
|
@ -50,8 +50,10 @@ check-vanilla-allocations-aggressive:
|
|||
$(PYTHON) $(topsrcdir)/config/check_vanilla_allocations.py --aggressive $(REAL_LIBRARY)
|
||||
|
||||
ifeq ($(OS_ARCH),Linux)
|
||||
ifeq (,$(filter -flto,$(CFLAGS) $(CXXFLAGS) $(LDFLAGS)))
|
||||
check:: check-vanilla-allocations
|
||||
endif
|
||||
endif
|
||||
|
||||
# Help ensure that the number of OOM errors in SpiderMonkey doesn't increase.
|
||||
# If the number of OOM errors changes, update the number below. We intend this
|
||||
|
|
|
@ -101,12 +101,11 @@ nsFirstLetterFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
|
|||
nsIFrame **outChildFrame)
|
||||
{
|
||||
nsIFrame *kid = mFrames.FirstChild();
|
||||
if (kid)
|
||||
{
|
||||
if (kid) {
|
||||
return kid->GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
return nsFrame::GetChildFrameContainingOffset(inContentOffset, inHint, outFrameContentOffset, outChildFrame);
|
||||
}
|
||||
}
|
||||
|
||||
// Needed for non-floating first-letter frames and for the continuations
|
||||
|
@ -240,8 +239,7 @@ nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
|
|||
ConsiderChildOverflow(aMetrics.mOverflowAreas, kid);
|
||||
|
||||
FinishAndStoreOverflow(&aMetrics);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Pretend we are a span and reflow the child frame
|
||||
nsLineLayout* ll = aReflowInput.mLineLayout;
|
||||
bool pushedFrame;
|
||||
|
@ -258,7 +256,13 @@ nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
|
|||
aMetrics.ISize(lineWM) = ll->EndSpan(this) + bp.IStartEnd(wm);
|
||||
ll->SetInFirstLetter(false);
|
||||
|
||||
nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm);
|
||||
if (mStyleContext->StyleTextReset()->mInitialLetterSize != 0.0f) {
|
||||
aMetrics.SetBlockStartAscent(kidMetrics.BlockStartAscent() +
|
||||
bp.BStart(wm));
|
||||
aMetrics.BSize(lineWM) = kidMetrics.BSize(lineWM) + bp.BStartEnd(wm);
|
||||
} else {
|
||||
nsLayoutUtils::SetBSizeFromFontMetrics(this, aMetrics, bp, lineWM, wm);
|
||||
}
|
||||
}
|
||||
|
||||
if (!NS_INLINE_IS_BREAK_BEFORE(aReflowStatus)) {
|
||||
|
@ -273,8 +277,7 @@ nsFirstLetterFrame::Reflow(nsPresContext* aPresContext,
|
|||
// Remove all of the childs next-in-flows
|
||||
kidNextInFlow->GetParent()->DeleteNextInFlowChild(kidNextInFlow, true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// Create a continuation for the child frame if it doesn't already
|
||||
// have one.
|
||||
if (!IsFloating()) {
|
||||
|
|
|
@ -8772,6 +8772,14 @@ nsTextFrame::IsFloatingFirstLetterChild() const
|
|||
frame->GetType() == nsGkAtoms::letterFrame;
|
||||
}
|
||||
|
||||
bool
|
||||
nsTextFrame::IsInitialLetterChild() const
|
||||
{
|
||||
nsIFrame* frame = GetParent();
|
||||
return frame && frame->StyleTextReset()->mInitialLetterSize != 0.0f &&
|
||||
frame->GetType() == nsGkAtoms::letterFrame;
|
||||
}
|
||||
|
||||
struct NewlineProperty {
|
||||
int32_t mStartOffset;
|
||||
// The offset of the first \n after mStartOffset, or -1 if there is none
|
||||
|
@ -9050,9 +9058,10 @@ nsTextFrame::ReflowText(nsLineLayout& aLineLayout, nscoord aAvailableWidth,
|
|||
|
||||
// The metrics for the text go in here
|
||||
gfxTextRun::Metrics textMetrics;
|
||||
gfxFont::BoundingBoxType boundingBoxType = IsFloatingFirstLetterChild() ?
|
||||
gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS :
|
||||
gfxFont::LOOSE_INK_EXTENTS;
|
||||
gfxFont::BoundingBoxType boundingBoxType =
|
||||
IsFloatingFirstLetterChild() || IsInitialLetterChild()
|
||||
? gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS
|
||||
: gfxFont::LOOSE_INK_EXTENTS;
|
||||
NS_ASSERTION(!(NS_REFLOW_CALC_BOUNDING_METRICS & aMetrics.mFlags),
|
||||
"We shouldn't be passed NS_REFLOW_CALC_BOUNDING_METRICS anymore");
|
||||
|
||||
|
|
|
@ -587,6 +587,8 @@ public:
|
|||
|
||||
bool IsFloatingFirstLetterChild() const;
|
||||
|
||||
bool IsInitialLetterChild() const;
|
||||
|
||||
virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override;
|
||||
|
||||
void AssignJustificationGaps(const mozilla::JustificationAssignment& aAssign);
|
||||
|
|
|
@ -553,7 +553,7 @@ skip-if(Android) == 363706-1.html 363706-1-ref.html
|
|||
!= 363706-1.html about:blank
|
||||
== 363728-1.html 363728-1-ref.html
|
||||
== 363728-2.html 363728-2-ref.html
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent,2,11) == 363858-1.html 363858-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) fuzzy-if(skiaContent||Android,4,11) == 363858-1.html 363858-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) == 363858-2.html 363858-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) == 363858-3.html 363858-3-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
skip-if(B2G||Mulet) == 363858-4.html 363858-4-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
== select-fieldset-1.html select-fieldset-ref.html
|
||||
fuzzy-if(Android,12,1) == select-fieldset-1.html select-fieldset-ref.html
|
||||
fails-if(Android||B2G||Mulet) fuzzy-if(skiaContent&&!Android,2,17) == select-fieldset-2.html select-fieldset-ref-disabled.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android||B2G||Mulet) fuzzy-if(skiaContent&&!Android,2,17) == select-fieldset-3.html select-fieldset-ref-disabled.html # Initial mulet triage: parity with B2G/B2G Desktop
|
||||
fails-if(Android) fuzzy-if(skiaContent&&!Android,2,13) == select-fieldset-4.html select-fieldset-ref.html
|
||||
|
|
|
@ -95,7 +95,7 @@ test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceE
|
|||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-select-listbox-contents-under-2.html threshold-select-listbox-contents-under-2.html
|
||||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-select-listbox-contents-at-1.html threshold-select-listbox-contents-at-1-ref.html
|
||||
test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-select-listbox-contents-at-2.html threshold-select-listbox-contents-at-2-ref.html
|
||||
fuzzy-if(gtkWidget,1,8) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-select-combobox-contents-under-1.html threshold-select-combobox-contents-under-1.html
|
||||
fuzzy-if(gtkWidget,1,8) fuzzy-if(Android,4,2) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-select-combobox-contents-under-1.html threshold-select-combobox-contents-under-1.html
|
||||
fuzzy-if(gtkWidget,1,8) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-select-combobox-contents-under-2.html threshold-select-combobox-contents-under-2.html
|
||||
fuzzy-if(gtkWidget,1,8) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-select-combobox-contents-at-1.html threshold-select-combobox-contents-at-1-ref.html
|
||||
fuzzy-if(gtkWidget,1,8) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,100) == threshold-select-combobox-contents-at-2.html threshold-select-combobox-contents-at-2-ref.html
|
||||
|
|
|
@ -36,3 +36,133 @@ skip css-values-3/vh_not_refreshing_on_chrome_iframe.html
|
|||
# because of support files (in iframe subdir) not being copied (bug 1256580)
|
||||
skip css-values-3/vh-support-transform-origin.html
|
||||
skip css-values-3/vh-support-transform-translate.html
|
||||
|
||||
# Fuzzy annotations for multicol tests are due to AA differences.
|
||||
# Fails annotations need to be triaged later. (Bug 1299006)
|
||||
fails css-multicol-1/multicol-block-clip-001.xht
|
||||
fails css-multicol-1/multicol-block-clip-002.xht
|
||||
fails css-multicol-1/multicol-br-inside-avoidcolumn-001.xht
|
||||
fails css-multicol-1/multicol-break-000.xht
|
||||
fails css-multicol-1/multicol-break-001.xht
|
||||
fuzzy(116,1008) css-multicol-1/multicol-clip-001.xht
|
||||
fuzzy(116,702) css-multicol-1/multicol-clip-002.xht
|
||||
fuzzy(116,467) css-multicol-1/multicol-collapsing-001.xht
|
||||
fuzzy(87,180) css-multicol-1/multicol-columns-001.xht
|
||||
fuzzy(87,180) css-multicol-1/multicol-columns-002.xht
|
||||
fuzzy(87,180) css-multicol-1/multicol-columns-003.xht
|
||||
fuzzy(87,180) css-multicol-1/multicol-columns-004.xht
|
||||
fuzzy(87,180) css-multicol-1/multicol-columns-005.xht
|
||||
fuzzy(87,180) css-multicol-1/multicol-columns-006.xht
|
||||
fuzzy(87,180) css-multicol-1/multicol-columns-007.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-columns-invalid-001.xht
|
||||
fails-if(OSX||winWidget) css-multicol-1/multicol-columns-invalid-002.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-columns-toolong-001.xht
|
||||
fuzzy(116,530) css-multicol-1/multicol-containing-001.xht
|
||||
fuzzy(215,241) css-multicol-1/multicol-containing-002.xht
|
||||
fuzzy(87,180) css-multicol-1/multicol-count-001.xht
|
||||
fails css-multicol-1/multicol-count-002.xht
|
||||
fails css-multicol-1/multicol-count-computed-001.xht
|
||||
fails css-multicol-1/multicol-count-computed-002.xht
|
||||
fails css-multicol-1/multicol-count-computed-003.xht
|
||||
fuzzy-if(winWidget||OSX||gtkWidget,112,861) fails-if(Android) css-multicol-1/multicol-count-computed-004.xht
|
||||
fails css-multicol-1/multicol-count-computed-005.xht
|
||||
fails css-multicol-1/multicol-count-large-001.xht
|
||||
fuzzy(255,132) css-multicol-1/multicol-count-large-002.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-count-negative-001.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-count-negative-002.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-count-non-integer-001.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-count-non-integer-002.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-count-non-integer-003.xht
|
||||
fuzzy(116,80) css-multicol-1/multicol-fill-auto-002.xht
|
||||
fuzzy(116,3270) css-multicol-1/multicol-fill-auto-003.xht
|
||||
fails css-multicol-1/multicol-fill-auto.xht
|
||||
fuzzy(116,80) css-multicol-1/multicol-fill-balance-001.xht
|
||||
fuzzy(116,821) css-multicol-1/multicol-gap-000.xht
|
||||
fuzzy(255,290) css-multicol-1/multicol-gap-001.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-gap-002.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-gap-003.xht
|
||||
fuzzy(107,1823) css-multicol-1/multicol-gap-fraction-001.xht
|
||||
fuzzy-if(winWidget||OSX||gtkWidget,204,1048) fails-if(Android) css-multicol-1/multicol-gap-large-001.xht
|
||||
fuzzy(225,920) css-multicol-1/multicol-gap-large-002.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-gap-negative-001.xht
|
||||
fails css-multicol-1/multicol-height-block-child-001.xht
|
||||
fuzzy(255,3762) css-multicol-1/multicol-inherit-001.xht
|
||||
fuzzy(116,1893) css-multicol-1/multicol-inherit-002.xht
|
||||
fails css-multicol-1/multicol-inherit-003.xht
|
||||
fails css-multicol-1/multicol-inherit-004.xht
|
||||
fuzzy(96,264) css-multicol-1/multicol-list-item-001.xht
|
||||
fuzzy(73,1200) css-multicol-1/multicol-margin-001.xht
|
||||
fuzzy(73,1200) css-multicol-1/multicol-margin-002.xht
|
||||
fuzzy(243,3322) css-multicol-1/multicol-margin-child-001.xht
|
||||
fuzzy(255,4008) css-multicol-1/multicol-nested-002.xht
|
||||
fuzzy(255,4109) css-multicol-1/multicol-nested-005.xht
|
||||
fuzzy(204,2463) css-multicol-1/multicol-nested-margin-001.xht
|
||||
fails-if(OSX||winWidget) css-multicol-1/multicol-nested-margin-002.xht
|
||||
fuzzy(204,2371) css-multicol-1/multicol-nested-margin-003.xht
|
||||
fuzzy(225,2529) css-multicol-1/multicol-nested-margin-004.xht
|
||||
fuzzy(225,2529) css-multicol-1/multicol-nested-margin-005.xht
|
||||
fuzzy(116,142) css-multicol-1/multicol-overflow-000.xht
|
||||
fuzzy(204,1844) css-multicol-1/multicol-overflowing-001.xht
|
||||
fuzzy-if(OSX,61,2) css-multicol-1/multicol-reduce-000.xht
|
||||
fuzzy-if(OSX,8,20) css-multicol-1/multicol-rule-000.xht
|
||||
fuzzy(116,1584) css-multicol-1/multicol-rule-001.xht
|
||||
fails css-multicol-1/multicol-rule-002.xht
|
||||
fails-if(OSX||winWidget) css-multicol-1/multicol-rule-003.xht
|
||||
fails-if(OSX||winWidget) css-multicol-1/multicol-rule-color-001.xht
|
||||
fuzzy(106,354) css-multicol-1/multicol-rule-dashed-000.xht
|
||||
fuzzy(106,354) css-multicol-1/multicol-rule-dotted-000.xht
|
||||
fuzzy(106,354) css-multicol-1/multicol-rule-double-000.xht
|
||||
fails-if(OSX||winWidget) css-multicol-1/multicol-rule-fraction-001.xht
|
||||
fails-if(OSX||winWidget) css-multicol-1/multicol-rule-fraction-002.xht
|
||||
fails css-multicol-1/multicol-rule-fraction-003.xht
|
||||
fuzzy(106,354) css-multicol-1/multicol-rule-groove-000.xht
|
||||
fuzzy(94,256) css-multicol-1/multicol-rule-hidden-000.xht
|
||||
fuzzy(106,354) css-multicol-1/multicol-rule-inset-000.xht
|
||||
fuzzy(106,354) css-multicol-1/multicol-rule-outset-000.xht
|
||||
fails css-multicol-1/multicol-rule-px-001.xht
|
||||
fuzzy(106,354) css-multicol-1/multicol-rule-ridge-000.xht
|
||||
fuzzy(106,354) css-multicol-1/multicol-rule-solid-000.xht
|
||||
fails css-multicol-1/multicol-rule-stacking-001.xht
|
||||
fails css-multicol-1/multicol-shorthand-001.xht
|
||||
fails css-multicol-1/multicol-span-000.xht
|
||||
fails css-multicol-1/multicol-span-all-001.xht
|
||||
fails css-multicol-1/multicol-span-all-002.xht
|
||||
fails css-multicol-1/multicol-span-all-003.xht
|
||||
fails css-multicol-1/multicol-span-all-child-001.xht
|
||||
fails-if(OSX||winWidget) css-multicol-1/multicol-span-all-child-002.xht
|
||||
fails css-multicol-1/multicol-span-all-margin-001.xht
|
||||
fails css-multicol-1/multicol-span-all-margin-002.xht
|
||||
fails css-multicol-1/multicol-span-all-margin-bottom-001.xht
|
||||
fails css-multicol-1/multicol-span-all-margin-nested-001.xht
|
||||
fails css-multicol-1/multicol-span-all-margin-nested-002.xht
|
||||
fails css-multicol-1/multicol-span-all-margin-nested-003.xht
|
||||
fails css-multicol-1/multicol-span-all-margin-nested-firstchild-001.xht
|
||||
fails css-multicol-1/multicol-span-float-001.xht
|
||||
fails css-multicol-1/multicol-span-none-001.xht
|
||||
fails css-multicol-1/multicol-table-cell-001.xht
|
||||
fails css-multicol-1/multicol-table-cell-height-001.xht
|
||||
fails css-multicol-1/multicol-table-cell-height-002.xht
|
||||
fails css-multicol-1/multicol-table-cell-vertical-align-001.xht
|
||||
fuzzy(204,930) css-multicol-1/multicol-width-002.xht
|
||||
fails css-multicol-1/multicol-width-count-002.xht
|
||||
fails css-multicol-1/multicol-width-ems-001.xht
|
||||
fails css-multicol-1/multicol-width-negative-001.xht
|
||||
fuzzy(225,1060) css-multicol-1/multicol-width-large-001.xht
|
||||
fails css-multicol-1/multicol-width-small-001.xht
|
||||
fuzzy(225,1060) css-multicol-1/multicol-width-invalid-001.xht
|
||||
fuzzy(225,1060) css-multicol-1/multicol-width-large-002.xht
|
||||
fails css-multicol-1/multicol-zero-height-001.xht
|
||||
fuzzy(225,13600) css-multicol-1/multicol-nested-column-rule-001.xht
|
||||
fuzzy(94,256) css-multicol-1/multicol-rule-none-000.xht
|
||||
|
||||
#Fails because of regression (Bug 1306769)
|
||||
fails css-multicol-1/multicol-rule-color-inherit-002.xht
|
||||
|
||||
#This test seems to pass only on Linux-opt build, fails on everything else
|
||||
#Therefore using fuzzy annotation as a catch all
|
||||
fuzzy(255,2808) css-multicol-1/multicol-rule-large-001.xht
|
||||
|
||||
#Fails because column-span property not implemented (Bug 616436)
|
||||
fails css-multicol-1/multicol-fill-auto-block-children-001.xht
|
||||
fails css-multicol-1/multicol-fill-auto-block-children-002.xht
|
||||
fails css-multicol-1/multicol-span-all-block-sibling-003.xht
|
||||
|
|
|
@ -34,6 +34,7 @@ gSubtrees = [
|
|||
os.path.join("css-namespaces-3"),
|
||||
os.path.join("css-conditional-3"),
|
||||
os.path.join("css-values-3"),
|
||||
os.path.join("css-multicol-1"),
|
||||
os.path.join("selectors-4"),
|
||||
]
|
||||
|
||||
|
@ -165,10 +166,12 @@ def map_file(fn, spec):
|
|||
|
||||
def load_flags_for(fn, spec):
|
||||
global gTestFlags
|
||||
document = get_document_for(fn)
|
||||
destname = os.path.join(spec, os.path.basename(fn))
|
||||
gTestFlags[destname] = []
|
||||
|
||||
if not (is_html(fn) or is_xml(fn)):
|
||||
return
|
||||
document = get_document_for(fn)
|
||||
for meta in document.getElementsByTagName("meta"):
|
||||
name = meta.getAttribute("name")
|
||||
if name == "flags":
|
||||
|
@ -177,6 +180,9 @@ def load_flags_for(fn, spec):
|
|||
def is_html(fn):
|
||||
return fn.endswith(".htm") or fn.endswith(".html")
|
||||
|
||||
def is_xml(fn):
|
||||
return fn.endswith(".xht") or fn.endswith(".xml") or fn.endswith(".xhtml") or fn.endswith(".svg")
|
||||
|
||||
def get_document_for(fn):
|
||||
document = None # an xml.dom.minidom document
|
||||
if is_html(fn):
|
||||
|
@ -191,6 +197,16 @@ def get_document_for(fn):
|
|||
return document
|
||||
|
||||
def add_test_items(fn, spec):
|
||||
if spec is None:
|
||||
for subtree in gSubtrees:
|
||||
if fn.startswith(subtree):
|
||||
spec = os.path.basename(subtree)
|
||||
break
|
||||
else:
|
||||
raise StandardError("Could not associate test " + fn + " with specification")
|
||||
if not (is_html(fn) or is_xml(fn)):
|
||||
map_file(fn, spec)
|
||||
return None
|
||||
document = get_document_for(fn)
|
||||
refs = []
|
||||
notrefs = []
|
||||
|
@ -202,16 +218,12 @@ def add_test_items(fn, spec):
|
|||
arr = notrefs
|
||||
else:
|
||||
continue
|
||||
arr.append(os.path.join(os.path.dirname(fn), str(link.getAttribute("href"))))
|
||||
if str(link.getAttribute("href")) != "":
|
||||
arr.append(os.path.join(os.path.dirname(fn), str(link.getAttribute("href"))))
|
||||
else:
|
||||
gLog.write("Warning: href attribute found empty in " + fn + "\n")
|
||||
if len(refs) > 1:
|
||||
raise StandardError("Need to add code to specify which reference we want to match.")
|
||||
if spec is None:
|
||||
for subtree in gSubtrees:
|
||||
if fn.startswith(subtree):
|
||||
spec = os.path.basename(subtree)
|
||||
break
|
||||
else:
|
||||
raise StandardError("Could not associate test " + fn + " with specification")
|
||||
for ref in refs:
|
||||
tests.append(["==", map_file(fn, spec), map_file(ref, spec)])
|
||||
for notref in notrefs:
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- Submitted from TestTWF Paris -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Test: Multi-column element via -moz-columns: [integer]</title>
|
||||
<link rel="author" title="Anselm Hannemann" href="mailto:info@anselm-hannemann.com"/>
|
||||
<link rel="author" title="Håkon Wium Lie" href="mailto:howcome@opera.com"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#columns"/>
|
||||
<link rel="match" href="reference/multicol-basic-ref.html"/>
|
||||
<meta name="flags" content="ahem"/>
|
||||
<link rel="reviewer" title="Elika J Etemad" href="http://fantasai.inkedblade.net/contact">
|
||||
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.multicol-wrapper>*{
|
||||
font-family: Ahem;
|
||||
}
|
||||
|
||||
div.multicol-wrapper{
|
||||
border: thin solid black;
|
||||
display: inline-block;
|
||||
margin: 1em auto;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.multicol-basic-ref{
|
||||
background: yellow;
|
||||
width: 360px;
|
||||
-moz-columns: 3;
|
||||
-moz-column-gap: 0;
|
||||
-moz-column-rule: none;
|
||||
}
|
||||
|
||||
.multicol-basic-ref-item{
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.item-1{
|
||||
background: purple;
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.item-2{
|
||||
background: orange;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.item-3{
|
||||
background: blue;
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test passes if there are three vertical stripes in the yellow box below: 1st purple, 2nd orange, 3rd blue.</p>
|
||||
<div class="multicol-wrapper">
|
||||
<div class="multicol-basic-ref">
|
||||
<span class="multicol-basic-ref-item item-1">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
<span class="multicol-basic-ref-item item-2">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
<span class="multicol-basic-ref-item item-3">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- Submitted from TestTWF Paris -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Test: Multi-column element via -moz-column-count: [integer]</title>
|
||||
<link rel="author" title="Anselm Hannemann" href="mailto:info@anselm-hannemann.com"/>
|
||||
<link rel="author" title="Håkon Wium Lie" href="mailto:howcome@opera.com"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#columns"/>
|
||||
<link rel="match" href="reference/multicol-basic-ref.html"/>
|
||||
<meta name="flags" content="ahem"/>
|
||||
<link rel="reviewer" title="Elika J Etemad" href="http://fantasai.inkedblade.net/contact">
|
||||
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.multicol-wrapper>*{
|
||||
font-family: Ahem;
|
||||
}
|
||||
|
||||
div.multicol-wrapper{
|
||||
border: thin solid black;
|
||||
display: inline-block;
|
||||
margin: 1em auto;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.multicol-basic-ref{
|
||||
background: yellow;
|
||||
width: 360px;
|
||||
-moz-column-count: 3;
|
||||
-moz-column-gap: 0;
|
||||
-moz-column-rule: none;
|
||||
}
|
||||
|
||||
.multicol-basic-ref-item{
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.item-1{
|
||||
background: purple;
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.item-2{
|
||||
background: orange;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.item-3{
|
||||
background: blue;
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test passes if there are three vertical stripes in the yellow box below: 1st purple, 2nd orange, 3rd blue.</p>
|
||||
<div class="multicol-wrapper">
|
||||
<div class="multicol-basic-ref">
|
||||
<span class="multicol-basic-ref-item item-1">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
<span class="multicol-basic-ref-item item-2">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
<span class="multicol-basic-ref-item item-3">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- Submitted from TestTWF Paris -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Test: Multi-column element via -moz-columns: [width]</title>
|
||||
<link rel="author" title="Anselm Hannemann" href="mailto:info@anselm-hannemann.com"/>
|
||||
<link rel="author" title="Håkon Wium Lie" href="mailto:howcome@opera.com"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#columns"/>
|
||||
<link rel="match" href="reference/multicol-basic-ref.html"/>
|
||||
<meta name="flags" content="ahem"/>
|
||||
<link rel="reviewer" title="Elika J Etemad" href="http://fantasai.inkedblade.net/contact">
|
||||
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.multicol-wrapper>*{
|
||||
font-family: Ahem;
|
||||
}
|
||||
|
||||
div.multicol-wrapper{
|
||||
border: thin solid black;
|
||||
display: inline-block;
|
||||
margin: 1em auto;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.multicol-basic-ref{
|
||||
background: yellow;
|
||||
width: 360px;
|
||||
-moz-columns: 120px;
|
||||
-moz-column-gap: 0;
|
||||
-moz-column-rule: none;
|
||||
}
|
||||
|
||||
.multicol-basic-ref-item{
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.item-1{
|
||||
background: purple;
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.item-2{
|
||||
background: orange;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.item-3{
|
||||
background: blue;
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test passes if there are three vertical stripes in the yellow box below: 1st purple, 2nd orange, 3rd blue.</p>
|
||||
<div class="multicol-wrapper">
|
||||
<div class="multicol-basic-ref">
|
||||
<span class="multicol-basic-ref-item item-1">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
<span class="multicol-basic-ref-item item-2">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
<span class="multicol-basic-ref-item item-3">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- Submitted from TestTWF Paris -->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CSS Test: Multi-column element via -moz-column-width: [width]</title>
|
||||
<link rel="author" title="Anselm Hannemann" href="mailto:info@anselm-hannemann.com"/>
|
||||
<link rel="author" title="Håkon Wium Lie" href="mailto:howcome@opera.com"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#columns"/>
|
||||
<link rel="match" href="reference/multicol-basic-ref.html"/>
|
||||
<meta name="flags" content="ahem"/>
|
||||
<link rel="reviewer" title="Elika J Etemad" href="http://fantasai.inkedblade.net/contact">
|
||||
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.multicol-wrapper>*{
|
||||
font-family: Ahem;
|
||||
}
|
||||
|
||||
div.multicol-wrapper{
|
||||
border: thin solid black;
|
||||
display: inline-block;
|
||||
margin: 1em auto;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.multicol-basic-ref{
|
||||
background: yellow;
|
||||
width: 360px;
|
||||
-moz-column-width: 120px;
|
||||
-moz-column-gap: 0;
|
||||
-moz-column-rule: none;
|
||||
}
|
||||
|
||||
.multicol-basic-ref-item{
|
||||
background: #000;
|
||||
}
|
||||
|
||||
.item-1{
|
||||
background: purple;
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.item-2{
|
||||
background: orange;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.item-3{
|
||||
background: blue;
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test passes if there are three vertical stripes in the yellow box below: 1st purple, 2nd orange, 3rd blue.</p>
|
||||
<div class="multicol-wrapper">
|
||||
<div class="multicol-basic-ref">
|
||||
<span class="multicol-basic-ref-item item-1">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
<span class="multicol-basic-ref-item item-2">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
<span class="multicol-basic-ref-item item-3">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</span>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,77 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!-- Submitted from TestTWF Paris -->
|
||||
<head>
|
||||
<title>CSS Test reference</title>
|
||||
<link rel="author" title="Anselm Hannemann" href="mailto:info@anselm-hannemann.com"/>
|
||||
<link rel="reviewer" title="Elika J Etemad" href="http://fantasai.inkedblade.net/contact">
|
||||
<meta name="flags" content="ahem"/>
|
||||
|
||||
<style type="text/css">
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
.multicol-wrapper>*{
|
||||
font-family: Ahem;
|
||||
}
|
||||
|
||||
div.multicol-wrapper{
|
||||
border: thin solid black;
|
||||
display: inline-block;
|
||||
margin: 1em auto;
|
||||
width: 360px;
|
||||
}
|
||||
|
||||
.multicol-basic-ref{
|
||||
background: yellow;
|
||||
width: 360px;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.multicol-basic-ref td{
|
||||
padding: 0;
|
||||
}
|
||||
.multicol-basic-ref-item{
|
||||
padding: 0;
|
||||
width: 120px;
|
||||
background: #000;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
display: inline;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.item-1{
|
||||
background: purple;
|
||||
color: purple;
|
||||
}
|
||||
|
||||
.item-2{
|
||||
background: orange;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.item-3{
|
||||
background: blue;
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p>Test passes if there are three vertical stripes in the yellow box below: 1st purple, 2nd orange, 3rd blue.</p>
|
||||
<div class="multicol-wrapper">
|
||||
<table class="multicol-basic-ref">
|
||||
<tr>
|
||||
<td><div class="multicol-basic-ref-item item-1">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</div></td>
|
||||
<td><div class="multicol-basic-ref-item item-2">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</div></td>
|
||||
<td><div class="multicol-basic-ref-item item-3">XXXX XXXX XXXX XXXX XXXX XXXX XXXX</div></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,33 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-24 -->
|
||||
<meta name="flags" content="image" />
|
||||
<style type="text/css"><![CDATA[
|
||||
table
|
||||
{
|
||||
border-spacing: 0px;
|
||||
border: gray solid 1em;
|
||||
font: 1.25em/1 serif;
|
||||
}
|
||||
|
||||
td
|
||||
{
|
||||
padding: 0;
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
td + td {width: 8em;}
|
||||
|
||||
img, td {vertical-align: top;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td><img src="support/swatch-blue.png" width="40" height="80" alt="Image download support must be enabled" /><img src="support/black20x20.png" width="50" height="20" alt="Image download support must be enabled" /></td><td><img src="support/swatch-orange.png" width="40" height="80" alt="Image download support must be enabled" /></td>
|
||||
</tr></table>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,55 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Multi-column Layout Test: Overflowed content inside multicol element</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-24 -->
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#overflow-inside-multicol-elements" title="8.1. Overflow inside multicol elements" />
|
||||
<link rel="match" href="multicol-block-clip-001-ref.xht"/>
|
||||
<meta name="flags" content="ahem" />
|
||||
<meta name="assert" content="This test checks that content in the normal flow that extends into column gaps is clipped in the middle of the column gap." />
|
||||
<style type="text/css"><![CDATA[
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
]]></style>
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
border: gray solid 1em;
|
||||
font: 1.25em/1 Ahem;
|
||||
orphans: 1;
|
||||
widows: 1;
|
||||
width: 11em;
|
||||
|
||||
-moz-column-count: 4;
|
||||
-moz-column-gap: 1em;
|
||||
}
|
||||
|
||||
h4
|
||||
{
|
||||
background: black;
|
||||
color: black;
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
width: 11em;
|
||||
}
|
||||
|
||||
#first-column {color: blue;}
|
||||
|
||||
#second-column {color: orange;}
|
||||
|
||||
#third-column, #fourth-column {color: white;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<span id="first-column"> ab cd ef gh </span>
|
||||
<h4> 1234567890123 </h4>
|
||||
<span id="second-column"> ij kl mn oq </span>
|
||||
<span id="third-column"> ab cd ef gh </span>
|
||||
<span id="fourth-column"> rs tu vw xy </span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,54 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-24 -->
|
||||
<meta name="flags" content="image" />
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
font: 1.25em/1 serif;
|
||||
border: 1em solid gray;
|
||||
width: 11em;
|
||||
height: 6em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
img {position: absolute;}
|
||||
|
||||
img#black {top: 4em;}
|
||||
|
||||
img#first-orange {top: 5em;}
|
||||
|
||||
img#second-orange {left: 4em;}
|
||||
|
||||
img#first-pink
|
||||
{
|
||||
left: 4em;
|
||||
top: 3em;
|
||||
}
|
||||
|
||||
img#second-pink {left: 8em;}
|
||||
|
||||
img#yellow
|
||||
{
|
||||
left: 8em;
|
||||
top: 1em;
|
||||
}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<img id="blue" src="support/swatch-blue.png" width="40" height="80" alt="Image download support must be enabled" />
|
||||
<img id="black" src="support/black20x20.png" width="70" height="20" alt="Image download support must be enabled" />
|
||||
<img id="first-orange" src="support/swatch-orange.png" width="40" height="20" alt="Image download support must be enabled" />
|
||||
<img id="second-orange" src="support/swatch-orange.png" width="40" height="60" alt="Image download support must be enabled" />
|
||||
<img id="first-pink" src="support/swatch-pink.png" width="40" height="60" alt="Image download support must be enabled" />
|
||||
<img id="second-pink" src="support/swatch-pink.png" width="40" height="20" alt="Image download support must be enabled" />
|
||||
<img id="yellow" src="support/swatch-yellow.png" width="40" height="80" alt="Image download support must be enabled" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,57 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Multi-column Layout Test: Overflowed content inside and outside multicol element</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-24 -->
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#overflow-inside-multicol-elements" title="8.1. Overflow inside multicol elements" />
|
||||
<link rel="match" href="multicol-block-clip-002-ref.xht"/>
|
||||
<meta name="flags" content="ahem" />
|
||||
<meta name="assert" content="This test checks that content in the normal flow that extends into column gaps is clipped in the middle of the column gap." />
|
||||
<style type="text/css"><![CDATA[
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
]]></style>
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
border: gray solid 1em;
|
||||
font: 1.25em/1 Ahem;
|
||||
orphans: 1;
|
||||
widows: 1;
|
||||
width: 11em;
|
||||
|
||||
-moz-column-count: 3;
|
||||
-moz-column-gap: 1em;
|
||||
}
|
||||
|
||||
h4
|
||||
{
|
||||
background: black;
|
||||
color: black;
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
width: 11em;
|
||||
}
|
||||
|
||||
#first-column {color: blue;}
|
||||
|
||||
#second-column {color: orange;}
|
||||
|
||||
#third-column {color: pink;}
|
||||
|
||||
#fourth-column {color: yellow;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<span id="first-column"> ab cd ef gh </span>
|
||||
<h4> 1234567890123 </h4>
|
||||
<span id="second-column"> ij kl mn oq </span>
|
||||
<span id="third-column"> ab cd ef gh </span>
|
||||
<span id="fourth-column"> rs tu vw xy </span>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>multicol | break-inside: avoid-column</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/"/>
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#column-breaks"/>
|
||||
<link rel="match" href="multicol-br-inside-avoidcolumn-ref.xht"/>
|
||||
<meta name="flags" content=""/>
|
||||
<style type="text/css"><![CDATA[
|
||||
html {
|
||||
width: 800px;
|
||||
background: white;
|
||||
}
|
||||
body {
|
||||
background: black;
|
||||
-moz-column-count: 3;
|
||||
-moz-column-gap: 0;
|
||||
-moz-column-fill: auto;
|
||||
height: 300px;
|
||||
}
|
||||
h1 {
|
||||
-moz-column-span: all;
|
||||
color: white;
|
||||
}
|
||||
div { background: red;
|
||||
height: 150px;
|
||||
break-inside: avoid-column;
|
||||
}
|
||||
span {
|
||||
float: left;
|
||||
}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>You should not see the word FAIL</h1>
|
||||
|
||||
<div>
|
||||
<span>FAIL</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span>FAIL</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span>FAIL</span>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>multicol | break-inside: avoid-column</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/"/>
|
||||
<style type="text/css"><![CDATA[
|
||||
html {
|
||||
width: 800px;
|
||||
background: white;
|
||||
}
|
||||
h1 {
|
||||
color: white;
|
||||
margin-top: 0;
|
||||
padding-top: 0.66em;
|
||||
}
|
||||
div {
|
||||
height: 450px;
|
||||
background: black;
|
||||
height: 300px;
|
||||
}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<h1>You should not see the word FAIL</h1>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Hakon Wium Lie" href="mailto:howcome@opera.com" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-08-25 -->
|
||||
<meta name="flags" content="ahem image" />
|
||||
<style type="text/css"><![CDATA[
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
]]></style>
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
background-color: yellow;
|
||||
margin-bottom: 0.5em;
|
||||
font: 1.25em/1 Ahem;
|
||||
height: 3em;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
img {vertical-align: top;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Test passes if the 2 horizontal bars are <strong>identical</strong>.</p>
|
||||
|
||||
<div><img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /></div>
|
||||
|
||||
<div><img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /></div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,60 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Multi-column Layout Test: break-after: column (basic)</title>
|
||||
<link rel="author" title="Hakon Wium Lie" href="mailto:howcome@opera.com" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-08-25 -->
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#break-before-break-after-break-inside" title="5.1. 'break-before', 'break-after', 'break-inside'" />
|
||||
<link rel="match" href="multicol-break-000-ref.xht" />
|
||||
<meta name="flags" content="ahem image" />
|
||||
<meta name="assert" content="This test checks that basic support of 'break-after: column'." />
|
||||
<style type="text/css"><![CDATA[
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
]]></style>
|
||||
<style type="text/css"><![CDATA[
|
||||
div#test, div#reference
|
||||
{
|
||||
background-color: yellow;
|
||||
margin-bottom: 0.5em;
|
||||
font: 1.25em/1 Ahem;
|
||||
height: 3em;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
div#test
|
||||
{
|
||||
-moz-column-fill: auto;
|
||||
-moz-column-gap: 0;
|
||||
-moz-column-width: 2em;
|
||||
|
||||
/*
|
||||
|
||||
N == 5;
|
||||
|
||||
W == 2em;
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
div#test > div {break-after: column;}
|
||||
|
||||
img {vertical-align: top;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Test passes if the 2 horizontal bars are <strong>identical</strong>.</p>
|
||||
|
||||
<div id="test">
|
||||
<div>A</div>
|
||||
<div>B</div>
|
||||
<div>C</div>
|
||||
</div>
|
||||
|
||||
<div id="reference"><img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /></div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-09-14 -->
|
||||
<meta name="flags" content="ahem image" />
|
||||
<style type="text/css"><![CDATA[
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
]]></style>
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
background-color: yellow;
|
||||
margin-bottom: 0.5em;
|
||||
font: 1.25em/1 Ahem;
|
||||
height: 3em;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
img {vertical-align: top;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Test passes if the 2 horizontal bars are <strong>identical</strong>.</p>
|
||||
|
||||
<div> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /></div>
|
||||
|
||||
<div> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /></div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,59 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Multi-column Layout Test: break-before: column (basic)</title>
|
||||
<link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-09-14 -->
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#break-before-break-after-break-inside" title="5.1. 'break-before', 'break-after', 'break-inside'" />
|
||||
<link rel="match" href="multicol-break-001-ref.xht" />
|
||||
<meta name="flags" content="ahem image" />
|
||||
<meta name="assert" content="This test checks that basic support of 'break-before: column'." />
|
||||
<style type="text/css"><![CDATA[
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
]]></style>
|
||||
<style type="text/css"><![CDATA[
|
||||
div#test, div#reference
|
||||
{
|
||||
background-color: yellow;
|
||||
margin-bottom: 0.5em;
|
||||
font: 1.25em/1 Ahem;
|
||||
height: 3em;
|
||||
width: 10em;
|
||||
}
|
||||
|
||||
div#test
|
||||
{
|
||||
-moz-column-fill: auto;
|
||||
-moz-column-gap: 0;
|
||||
-moz-column-width: 2em;
|
||||
|
||||
/*
|
||||
|
||||
N == 5;
|
||||
|
||||
W == 2em;
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
div#test > div {break-before: column;}
|
||||
|
||||
img {vertical-align: top;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p>Test passes if the 2 horizontal bars are <strong>identical</strong>.</p>
|
||||
|
||||
<div id="test">
|
||||
<div>A</div>
|
||||
<div>B</div>
|
||||
<div>C</div>
|
||||
</div>
|
||||
|
||||
<div id="reference"> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /> <img src="support/black20x20.png" alt="Image download support must be enabled" /></div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-24 -->
|
||||
<meta name="flags" content="image" />
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
border: gray solid 1em;
|
||||
font: 1.25em/1 serif;
|
||||
width: 11em;
|
||||
}
|
||||
|
||||
img
|
||||
{
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img + img {left: 3em;}
|
||||
|
||||
img + img + img {left: 6em;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<div><img src="support/black20x20.png" width="40" height="60" alt="Image download support must be enabled" /><img src="support/black20x20.png" width="40" height="60" alt="Image download support must be enabled" /><img src="support/swatch-blue.png" width="40" height="40" alt="Image download support must be enabled" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Multi-column Layout Test: overflowed content inside and outside multicol element</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-24 -->
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#overflow-inside-multicol-elements" title="8.1. Overflow inside multicol elements" />
|
||||
<link rel="match" href="multicol-clip-001-ref.xht" />
|
||||
<meta name="flags" content="ahem" />
|
||||
<meta name="assert" content="This test checks that content in the normal flow can extend into column gap before it reaches its middle. In this test, the 'l' and 'c' glyphs are painted into the left half of the column gap; the 'l' and 'e' glyphs extend outside the last column box at the edge of the multi-column and are therefore rendered thanks to the default 'overflow: visible' declaration." />
|
||||
<style type="text/css"><![CDATA[
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
]]></style>
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
background: white;
|
||||
border: gray solid 1em;
|
||||
color: blue;
|
||||
font: 1.25em/1 Ahem;
|
||||
orphans: 1;
|
||||
widows: 1;
|
||||
width: 11em;
|
||||
|
||||
-moz-column-count: 3;
|
||||
-moz-column-gap: 4em;
|
||||
}
|
||||
|
||||
span {color: black;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<span>
|
||||
bl ac
|
||||
bl ac
|
||||
</span>
|
||||
|
||||
<span>
|
||||
bl ac
|
||||
</span>
|
||||
bl ue
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-25 -->
|
||||
<meta name="flags" content="image" />
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
border: gray solid 1em;
|
||||
font: 1.25em/1 serif;
|
||||
width: 11em;
|
||||
}
|
||||
|
||||
img
|
||||
{
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img + img {left: 3em;}
|
||||
|
||||
img + img + img {left: 6em;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<div><img src="support/black20x20.png" width="40" height="60" alt="Image download support must be enabled" /><img src="support/black20x20.png" width="40" height="60" alt="Image download support must be enabled" /><img src="support/swatch-blue.png" width="20" height="40" alt="Image download support must be enabled" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,51 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Multi-column Layout Test: overflowed content inside and outside multicol element</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-25 -->
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-multicol/#overflow-inside-multicol-elements" title="8.1. Overflow inside multicol elements" />
|
||||
<link rel="match" href="multicol-clip-002-ref.xht" />
|
||||
<meta name="flags" content="ahem" />
|
||||
<meta name="assert" content="This test checks that content in the normal flow can extend into column gap before it reaches its middle. In this test, the 'l' and 'c' glyphs are painted into the left half of the column gap; the 'l' and 'e' glyphs extend outside the last column box at the edges of the multi-column and are therefore hidden thanks to the 'overflow: hidden' declaration." />
|
||||
<style type="text/css"><![CDATA[
|
||||
@font-face {
|
||||
font-family: Ahem;
|
||||
src: url("../../../fonts/Ahem.ttf");
|
||||
}
|
||||
]]></style>
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
background: white;
|
||||
border: gray solid 1em;
|
||||
color: blue;
|
||||
font: 1.25em/1 Ahem;
|
||||
orphans: 1;
|
||||
overflow: hidden;
|
||||
widows: 1;
|
||||
width: 11em;
|
||||
|
||||
-moz-column-count: 3;
|
||||
-moz-column-gap: 4em;
|
||||
}
|
||||
|
||||
span {color: black;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div>
|
||||
<span>
|
||||
bl ac
|
||||
bl ac
|
||||
</span>
|
||||
|
||||
<span>
|
||||
bl ac
|
||||
</span>
|
||||
bl ue
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,31 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>CSS Reftest Reference</title>
|
||||
<link rel="author" title="Opera Software ASA" href="http://www.opera.com/" />
|
||||
<link rel="reviewer" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/" /> <!-- 2013-07-25 -->
|
||||
<meta name="flags" content="image" />
|
||||
<style type="text/css"><![CDATA[
|
||||
div
|
||||
{
|
||||
background-color: black;
|
||||
border: black solid 1px;
|
||||
font: 1.25em/1 serif;
|
||||
padding: 1em;
|
||||
width: 8em;
|
||||
}
|
||||
|
||||
img
|
||||
{
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
img + img + img {left: 2em;}
|
||||
]]></style>
|
||||
</head>
|
||||
<body>
|
||||
<div><img src="support/swatch-yellow.png" width="40" height="20" alt="Image download support must be enabled" /><img src="support/swatch-yellow.png" width="20" height="60" alt="Image download support must be enabled" /><img src="support/swatch-yellow.png" width="20" height="60" alt="Image download support must be enabled" />
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче