merge mozilla-inbound to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2016-10-19 17:02:38 +02:00
Родитель 8f39c7b817 b91bc4c721
Коммит 036a7c4324
312 изменённых файлов: 18664 добавлений и 19578 удалений

Просмотреть файл

@ -66,7 +66,8 @@ enum MaiInterfaceType {
MAI_INTERFACE_TABLE,
MAI_INTERFACE_TEXT,
MAI_INTERFACE_DOCUMENT,
MAI_INTERFACE_IMAGE /* 10 */
MAI_INTERFACE_IMAGE, /* 10 */
MAI_INTERFACE_TABLE_CELL
};
static GType GetAtkTypeForMai(MaiInterfaceType type)
@ -94,12 +95,17 @@ static GType GetAtkTypeForMai(MaiInterfaceType type)
return ATK_TYPE_DOCUMENT;
case MAI_INTERFACE_IMAGE:
return ATK_TYPE_IMAGE;
case MAI_INTERFACE_TABLE_CELL:
MOZ_ASSERT(false);
}
return G_TYPE_INVALID;
}
#define NON_USER_EVENT ":system"
// The atk interfaces we can expose without checking what version of ATK we are
// dealing with. At the moment AtkTableCell is the only interface we can't
// always expose.
static const GInterfaceInfo atk_if_infos[] = {
{(GInterfaceInitFunc)componentInterfaceInitCB,
(GInterfaceFinalizeFunc) nullptr, nullptr},
@ -425,6 +431,15 @@ GetMaiAtkType(uint16_t interfacesBits)
}
}
// Special case AtkTableCell so we can check what version of Atk we are
// dealing with.
if (IsAtkVersionAtLeast(2, 12) && (interfacesBits & (1 << MAI_INTERFACE_TABLE_CELL))) {
const GInterfaceInfo cellInfo = {
(GInterfaceInitFunc)tableCellInterfaceInitCB,
(GInterfaceFinalizeFunc)nullptr, nullptr};
g_type_add_interface_static(type, gAtkTableCellGetTypeFunc(), &cellInfo);
}
return type;
}

Просмотреть файл

@ -27,6 +27,7 @@ void hypertextInterfaceInitCB(AtkHypertextIface* aIface);
void imageInterfaceInitCB(AtkImageIface* aIface);
void selectionInterfaceInitCB(AtkSelectionIface* aIface);
void tableInterfaceInitCB(AtkTableIface *aIface);
void tableCellInterfaceInitCB(AtkTableCellIface *aIface);
void textInterfaceInitCB(AtkTextIface* aIface);
void valueInterfaceInitCB(AtkValueIface *aIface);
}

Просмотреть файл

@ -28,6 +28,8 @@ using namespace mozilla::a11y;
int atkMajorVersion = 1, atkMinorVersion = 12;
GType (*gAtkTableCellGetTypeFunc)();
extern "C" {
typedef GType (* AtkGetTypeType) (void);
typedef void (*GnomeAccessibilityInit) (void);
@ -156,6 +158,9 @@ a11y::PlatformInit()
AtkSocketAccessible::g_atk_socket_embed;
}
gAtkTableCellGetTypeFunc = (GType (*)())
PR_FindFunctionSymbol(sATKLib, "atk_table_cell_get_type");
const char* (*atkGetVersion)() =
(const char* (*)()) PR_FindFunctionSymbol(sATKLib, "atk_get_version");
if (atkGetVersion) {

Просмотреть файл

@ -24,6 +24,7 @@ SOURCES += [
'nsMaiInterfaceImage.cpp',
'nsMaiInterfaceSelection.cpp',
'nsMaiInterfaceTable.cpp',
'nsMaiInterfaceTableCell.cpp',
'nsMaiInterfaceText.cpp',
'nsMaiInterfaceValue.cpp',
'Platform.cpp',

Просмотреть файл

@ -65,6 +65,10 @@ typedef struct _MaiAtkSocketClass
AtkSocketClass parent_class;
} MaiAtkSocketClass;
// This is a pointer to the atk_table_cell_get_type function if we are using
// a version of atk that defines that.
extern "C" GType (*gAtkTableCellGetTypeFunc)();
mozilla::a11y::AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj);
mozilla::a11y::ProxyAccessible* GetProxy(AtkObject* aAtkObj);
mozilla::a11y::AccessibleOrProxy GetInternalObj(AtkObject* aObj);

Просмотреть файл

@ -0,0 +1,216 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "InterfaceInitFuncs.h"
#include "Accessible-inl.h"
#include "AccessibleWrap.h"
#include "nsAccUtils.h"
#include "TableAccessible.h"
#include "TableCellAccessible.h"
#include "nsMai.h"
#include "ProxyAccessible.h"
#include "nsArrayUtils.h"
#include "mozilla/Likely.h"
using namespace mozilla::a11y;
extern "C" {
static gint
GetColumnSpanCB(AtkTableCell* aCell)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell));
if (accWrap) {
return accWrap->AsTableCell()->ColExtent();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
return proxy->ColExtent();
}
return 0;
}
static gboolean
GetRowSpanCB(AtkTableCell* aCell)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell));
if (accWrap) {
return accWrap->AsTableCell()->RowExtent();
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
return proxy->RowExtent();
}
return 0;
}
static gboolean
GetPositionCB(AtkTableCell* aCell, gint* aRow, gint* aCol)
{
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
TableCellAccessible* cell = accWrap->AsTableCell();
*aRow = cell->RowIdx();
*aCol = cell->ColIdx();
return true;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
uint32_t rowIdx = 0, colIdx = 0;
proxy->GetPosition(&rowIdx, &colIdx);
*aCol = colIdx;
*aRow = rowIdx;
return true;
}
return false;
}
static gboolean
GetColumnRowSpanCB(AtkTableCell* aCell, gint* aCol, gint* aRow,
gint* aColExtent, gint* aRowExtent) {
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
TableCellAccessible* cellAcc = accWrap->AsTableCell();
*aCol = cellAcc->ColIdx();
*aRow = cellAcc->RowIdx();
*aColExtent = cellAcc->ColExtent();
*aRowExtent = cellAcc->ColExtent();
return true;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
uint32_t colIdx = 0, rowIdx = 0, colExtent = 0, rowExtent = 0;
proxy->GetColRowExtents(&colIdx, &rowIdx, &colExtent, &rowExtent);
*aCol = colIdx;
*aRow = rowIdx;
*aColExtent = colExtent;
*aRowExtent = rowExtent;
return true;
}
return false;
}
static AtkObject*
GetTableCB(AtkTableCell* aTableCell)
{
AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aTableCell));
if (accWrap) {
TableAccessible* table = accWrap->AsTableCell()->Table();
if (!table) {
return nullptr;
}
Accessible* tableAcc = table->AsAccessible();
return tableAcc ? AccessibleWrap::GetAtkObject(tableAcc) : nullptr;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aTableCell))) {
ProxyAccessible* table = proxy->TableOfACell();
return table ? GetWrapperFor(table) : nullptr;
}
return nullptr;
}
static GPtrArray*
GetColumnHeaderCellsCB(AtkTableCell* aCell)
{
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
AutoTArray<Accessible*, 10> headers;
accWrap->AsTableCell()->ColHeaderCells(&headers);
if (headers.IsEmpty()) {
return nullptr;
}
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
for (Accessible* header: headers) {
AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header);
g_object_ref(atkHeader);
g_ptr_array_add(atkHeaders, atkHeader);
}
return atkHeaders;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
AutoTArray<ProxyAccessible*, 10> headers;
proxy->ColHeaderCells(&headers);
if (headers.IsEmpty()) {
return nullptr;
}
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
for (ProxyAccessible* header: headers) {
AtkObject* atkHeader = GetWrapperFor(header);
g_object_ref(atkHeader);
g_ptr_array_add(atkHeaders, atkHeader);
}
return atkHeaders;
}
return nullptr;
}
static GPtrArray*
GetRowHeaderCellsCB(AtkTableCell* aCell)
{
if (AccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aCell))) {
AutoTArray<Accessible*, 10> headers;
accWrap->AsTableCell()->RowHeaderCells(&headers);
if (headers.IsEmpty()) {
return nullptr;
}
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
for (Accessible* header: headers) {
AtkObject* atkHeader = AccessibleWrap::GetAtkObject(header);
g_object_ref(atkHeader);
g_ptr_array_add(atkHeaders, atkHeader);
}
return atkHeaders;
}
if (ProxyAccessible* proxy = GetProxy(ATK_OBJECT(aCell))) {
AutoTArray<ProxyAccessible*, 10> headers;
proxy->RowHeaderCells(&headers);
if (headers.IsEmpty()) {
return nullptr;
}
GPtrArray* atkHeaders = g_ptr_array_sized_new(headers.Length());
for (ProxyAccessible* header: headers) {
AtkObject* atkHeader = GetWrapperFor(header);
g_object_ref(atkHeader);
g_ptr_array_add(atkHeaders, atkHeader);
}
return atkHeaders;
}
return nullptr;
}
}
void
tableCellInterfaceInitCB(AtkTableCellIface* aIface)
{
NS_ASSERTION(aIface, "no interface!");
if (MOZ_UNLIKELY(!aIface))
return;
aIface->get_column_span = GetColumnSpanCB;
aIface->get_column_header_cells = GetColumnHeaderCellsCB;
aIface->get_position = GetPositionCB;
aIface->get_row_span = GetRowSpanCB;
aIface->get_row_header_cells = GetRowHeaderCellsCB;
aIface->get_row_column_span = GetColumnRowSpanCB;
aIface->get_table = GetTableCB;
}

Просмотреть файл

@ -175,10 +175,15 @@ uint32_t ColIdx();
uint32_t RowIdx();
void GetPosition(uint32_t* aColIdx, uint32_t* aRowIdx);
uint32_t ColExtent();
uint32_t RowExtent();
void GetColRowExtents(uint32_t* aColIdx, uint32_t* aRowIdx,
uint32_t* aColExtent, uint32_t* aRowExtent);
void ColHeaderCells(nsTArray<ProxyAccessible*>* aCells);
void RowHeaderCells(nsTArray<ProxyAccessible*>* aCells);

Просмотреть файл

@ -951,6 +951,41 @@ DocAccessibleChild::RecvRowIdx(const uint64_t& aID,
return true;
}
bool
DocAccessibleChild::RecvGetPosition(const uint64_t& aID,
uint32_t* aColIdx, uint32_t* aRowIdx)
{
*aColIdx = 0;
*aRowIdx = 0;
TableCellAccessible* acc = IdToTableCellAccessible(aID);
if (acc) {
*aColIdx = acc->ColIdx();
*aRowIdx = acc->RowIdx();
}
return true;
}
bool
DocAccessibleChild::RecvGetColRowExtents(const uint64_t& aID,
uint32_t* aColIdx, uint32_t* aRowIdx,
uint32_t* aColExtent, uint32_t* aRowExtent)
{
*aColIdx = 0;
*aRowIdx = 0;
*aColExtent = 0;
*aRowExtent = 0;
TableCellAccessible* acc = IdToTableCellAccessible(aID);
if (acc) {
*aColIdx = acc->ColIdx();
*aRowIdx = acc->RowIdx();
*aColExtent = acc->ColExtent();
*aRowExtent = acc->RowExtent();
}
return true;
}
bool
DocAccessibleChild::RecvColExtent(const uint64_t& aID,
uint32_t* aExtent)

Просмотреть файл

@ -259,6 +259,13 @@ public:
virtual bool RecvColExtent(const uint64_t& aID, uint32_t* aExtent) override;
virtual bool RecvGetPosition(const uint64_t& aID,
uint32_t* aColIdx, uint32_t* aRowIdx) override;
virtual bool RecvGetColRowExtents(const uint64_t& aID,
uint32_t* aColIdx, uint32_t* aRowIdx,
uint32_t* aColExtent, uint32_t* aRowExtent) override;
virtual bool RecvRowExtent(const uint64_t& aID, uint32_t* aExtent) override;
virtual bool RecvColHeaderCells(const uint64_t& aID,

Просмотреть файл

@ -173,8 +173,11 @@ child:
nested(inside_sync) sync TableOfACell(uint64_t aID) returns(uint64_t aTableID, bool aOk);
nested(inside_sync) sync ColIdx(uint64_t aID) returns(uint32_t aIndex);
nested(inside_sync) sync RowIdx(uint64_t aID) returns(uint32_t aIndex);
nested(inside_sync) sync GetPosition(uint64_t aID) returns(uint32_t aRow, uint32_t aCol);
nested(inside_sync) sync ColExtent(uint64_t aID) returns(uint32_t aExtent);
nested(inside_sync) sync RowExtent(uint64_t aID) returns(uint32_t aExtent);
nested(inside_sync) sync GetColRowExtents(uint64_t aID)
returns(uint32_t aCol, uint32_t aRow, uint32_t aColExtent, uint32_t aRowExtent);
nested(inside_sync) sync ColHeaderCells(uint64_t aID) returns(uint64_t[] aCells);
nested(inside_sync) sync RowHeaderCells(uint64_t aID) returns(uint64_t[] aCells);
nested(inside_sync) sync IsCellSelected(uint64_t aID) returns(bool aSelected);

Просмотреть файл

@ -517,6 +517,19 @@ ProxyAccessible::RowIdx()
return index;
}
void
ProxyAccessible::GetColRowExtents(uint32_t* aColIdx, uint32_t* aRowIdx,
uint32_t* aColExtent, uint32_t* aRowExtent)
{
Unused << mDoc->SendGetColRowExtents(mID, aColIdx, aRowIdx, aColExtent, aRowExtent);
}
void
ProxyAccessible::GetPosition(uint32_t* aColIdx, uint32_t* aRowIdx)
{
Unused << mDoc->SendGetPosition(mID, aColIdx, aRowIdx);
}
uint32_t
ProxyAccessible::ColExtent()
{

Просмотреть файл

@ -202,13 +202,26 @@ exports["test tab.readyState"] = (assert, done) => {
});
}
exports["test tab.readyState for existent tabs"] = (assert) => {
exports["test tab.readyState for existent tabs"] = function* (assert) {
assert.equal(tabs.length, 1, "tabs contains an existent tab");
function frameScript() {
sendAsyncMessage("test:contentDocument.readyState", content.document.readyState);
}
for (let tab of tabs) {
let browserForTab = getBrowserForTab(viewFor(tab));
assert.equal(browserForTab.contentDocument.readyState, tab.readyState,
"tab.readyState has the same value of the associated contentDocument.readyState CPOW");
let mm = browserForTab.messageManager;
yield new Promise((resolve) => {
mm.addMessageListener("test:contentDocument.readyState", function listener(evt) {
mm.removeMessageListener("test:contentDocument.readyState", listener);
assert.equal(evt.data, tab.readyState,
"tab.readyState has the same value of the associated contentDocument.readyState CPOW");
resolve();
});
mm.loadFrameScript(`data:,new ${frameScript};`, false);
});
}
}

Просмотреть файл

@ -9,7 +9,6 @@ support-files =
worker.js
[browser_aboutURLs.js]
skip-if = debug && os == "linux" # Assertion. See bug 1271182.
[browser_eme.js]
[browser_favicon.js]
[browser_forgetaboutsite.js]

Просмотреть файл

@ -114,20 +114,15 @@
}
.tab-sharing-icon-overlay[sharing="camera"] {
list-style-image: url("chrome://browser/skin/notification-icons.svg#camera");
list-style-image: url("chrome://browser/skin/notification-icons.svg#camera-sharing");
}
.tab-sharing-icon-overlay[sharing="microphone"] {
list-style-image: url("chrome://browser/skin/notification-icons.svg#microphone");
list-style-image: url("chrome://browser/skin/notification-icons.svg#microphone-sharing");
}
.tab-sharing-icon-overlay[sharing="screen"] {
list-style-image: url("chrome://browser/skin/notification-icons.svg#screen");
}
.tab-sharing-icon-overlay[sharing] {
filter: url("chrome://browser/skin/filters.svg#fill");
fill: rgb(224, 41, 29);
list-style-image: url("chrome://browser/skin/notification-icons.svg#screen-sharing");
}
.tab-icon-overlay {

Двоичные данные
config/external/icu/data/icudt56l.dat поставляемый

Двоичный файл не отображается.

Просмотреть файл

@ -900,9 +900,9 @@ endif
ifdef MOZ_RUST
ifdef RUST_LIBRARY_FILE
ifdef MOZ_DEBUG
cargo_build_flags =
else
# Permit users to pass flags to cargo from their mozconfigs (e.g. --color=always).
cargo_build_flags = $(CARGOFLAGS)
ifndef MOZ_DEBUG
cargo_build_flags = --release
endif
ifdef MOZ_CARGO_SUPPORTS_FROZEN

Просмотреть файл

@ -146,8 +146,8 @@ module.exports = {
"indent": [2, 2, {"SwitchCase": 1}],
// Enforces spacing between keys and values in object literal properties.
"key-spacing": [2, {"beforeColon": false, "afterColon": true}],
// Allow mixed 'LF' and 'CRLF' as linebreaks.
"linebreak-style": 0,
// Enforces unix style line breaks.
"linebreak-style": [2, "unix"],
// Don't enforce the maximum depth that blocks can be nested. The complexity
// rule is a better rule to check this.
"max-depth": 0,

Просмотреть файл

@ -1,86 +1,86 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const LAYOUT_ERRORS_L10N =
new LocalizationHelper("global/locale/layout_errors.properties");
// Test that when an animation is selected, its list of animated properties is
// displayed below it.
const EXPECTED_PROPERTIES = [
"background-color",
"background-position-x",
"background-position-y",
"background-size",
"border-bottom-left-radius",
"border-bottom-right-radius",
"border-top-left-radius",
"border-top-right-radius",
"filter",
"height",
"transform",
"width"
].sort();
add_task(function* () {
yield addTab(URL_ROOT + "doc_keyframes.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let propertiesList = timeline.rootWrapperEl
.querySelector(".animated-properties");
ok(!isNodeVisible(propertiesList),
"The list of properties panel is hidden by default");
info("Click to select the animation");
yield clickOnAnimation(panel, 0);
ok(isNodeVisible(propertiesList),
"The list of properties panel is shown");
ok(propertiesList.querySelectorAll(".property").length,
"The list of properties panel actually contains properties");
ok(hasExpectedProperties(propertiesList),
"The list of properties panel contains the right properties");
ok(hasExpectedWarnings(propertiesList),
"The list of properties panel contains the right warnings");
info("Click to unselect the animation");
yield clickOnAnimation(panel, 0, true);
ok(!isNodeVisible(propertiesList),
"The list of properties panel is hidden again");
});
function hasExpectedProperties(containerEl) {
let names = [...containerEl.querySelectorAll(".property .name")]
.map(n => n.textContent)
.sort();
if (names.length !== EXPECTED_PROPERTIES.length) {
return false;
}
for (let i = 0; i < names.length; i++) {
if (names[i] !== EXPECTED_PROPERTIES[i]) {
return false;
}
}
return true;
}
function hasExpectedWarnings(containerEl) {
let warnings = [...containerEl.querySelectorAll(".warning")];
for (let warning of warnings) {
let warningID =
"CompositorAnimationWarningTransformWithGeometricProperties";
if (warning.getAttribute("title") == LAYOUT_ERRORS_L10N.getStr(warningID)) {
return true;
}
}
return false;
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const LAYOUT_ERRORS_L10N =
new LocalizationHelper("global/locale/layout_errors.properties");
// Test that when an animation is selected, its list of animated properties is
// displayed below it.
const EXPECTED_PROPERTIES = [
"background-color",
"background-position-x",
"background-position-y",
"background-size",
"border-bottom-left-radius",
"border-bottom-right-radius",
"border-top-left-radius",
"border-top-right-radius",
"filter",
"height",
"transform",
"width"
].sort();
add_task(function* () {
yield addTab(URL_ROOT + "doc_keyframes.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let propertiesList = timeline.rootWrapperEl
.querySelector(".animated-properties");
ok(!isNodeVisible(propertiesList),
"The list of properties panel is hidden by default");
info("Click to select the animation");
yield clickOnAnimation(panel, 0);
ok(isNodeVisible(propertiesList),
"The list of properties panel is shown");
ok(propertiesList.querySelectorAll(".property").length,
"The list of properties panel actually contains properties");
ok(hasExpectedProperties(propertiesList),
"The list of properties panel contains the right properties");
ok(hasExpectedWarnings(propertiesList),
"The list of properties panel contains the right warnings");
info("Click to unselect the animation");
yield clickOnAnimation(panel, 0, true);
ok(!isNodeVisible(propertiesList),
"The list of properties panel is hidden again");
});
function hasExpectedProperties(containerEl) {
let names = [...containerEl.querySelectorAll(".property .name")]
.map(n => n.textContent)
.sort();
if (names.length !== EXPECTED_PROPERTIES.length) {
return false;
}
for (let i = 0; i < names.length; i++) {
if (names[i] !== EXPECTED_PROPERTIES[i]) {
return false;
}
}
return true;
}
function hasExpectedWarnings(containerEl) {
let warnings = [...containerEl.querySelectorAll(".warning")];
for (let warning of warnings) {
let warningID =
"CompositorAnimationWarningTransformWithGeometricProperties";
if (warning.getAttribute("title") == LAYOUT_ERRORS_L10N.getStr(warningID)) {
return true;
}
}
return false;
}

Просмотреть файл

@ -1,44 +1,44 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that animations displayed in the timeline can be selected by clicking
// them, and that this emits the right events and adds the right classes.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let selected = timeline.rootWrapperEl.querySelectorAll(".animation.selected");
ok(!selected.length, "There are no animations selected by default");
info("Click on the first animation, expect the right event and right class");
let animation0 = yield clickOnAnimation(panel, 0);
is(animation0, timeline.animations[0],
"The selected event was emitted with the right animation");
ok(isTimeBlockSelected(timeline, 0),
"The time block has the right selected class");
info("Click on the second animation, expect it to be selected too");
let animation1 = yield clickOnAnimation(panel, 1);
is(animation1, timeline.animations[1],
"The selected event was emitted with the right animation");
ok(isTimeBlockSelected(timeline, 1),
"The second time block has the right selected class");
info("Click again on the first animation and check if it unselects");
yield clickOnAnimation(panel, 0, true);
ok(!isTimeBlockSelected(timeline, 0),
"The first time block has been unselected");
});
function isTimeBlockSelected(timeline, index) {
let animation = timeline.rootWrapperEl.querySelectorAll(".animation")[index];
let animatedProperties = timeline.rootWrapperEl.querySelectorAll(
".animated-properties")[index];
return animation.classList.contains("selected") &&
animatedProperties.classList.contains("selected");
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that animations displayed in the timeline can be selected by clicking
// them, and that this emits the right events and adds the right classes.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let selected = timeline.rootWrapperEl.querySelectorAll(".animation.selected");
ok(!selected.length, "There are no animations selected by default");
info("Click on the first animation, expect the right event and right class");
let animation0 = yield clickOnAnimation(panel, 0);
is(animation0, timeline.animations[0],
"The selected event was emitted with the right animation");
ok(isTimeBlockSelected(timeline, 0),
"The time block has the right selected class");
info("Click on the second animation, expect it to be selected too");
let animation1 = yield clickOnAnimation(panel, 1);
is(animation1, timeline.animations[1],
"The selected event was emitted with the right animation");
ok(isTimeBlockSelected(timeline, 1),
"The second time block has the right selected class");
info("Click again on the first animation and check if it unselects");
yield clickOnAnimation(panel, 0, true);
ok(!isTimeBlockSelected(timeline, 0),
"The first time block has been unselected");
});
function isTimeBlockSelected(timeline, index) {
let animation = timeline.rootWrapperEl.querySelectorAll(".animation")[index];
let animatedProperties = timeline.rootWrapperEl.querySelectorAll(
".animated-properties")[index];
return animation.classList.contains("selected") &&
animatedProperties.classList.contains("selected");
}

Просмотреть файл

@ -1,43 +1,43 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the controller provides the document.timeline currentTime (at least
// the last known version since new animations were added).
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller} = yield openAnimationInspector();
ok(controller.documentCurrentTime, "The documentCurrentTime getter exists");
checkDocumentTimeIsCorrect(controller);
let time1 = controller.documentCurrentTime;
yield startNewAnimation(controller, panel);
checkDocumentTimeIsCorrect(controller);
let time2 = controller.documentCurrentTime;
ok(time2 > time1, "The new documentCurrentTime is higher than the old one");
});
function checkDocumentTimeIsCorrect(controller) {
let time = 0;
for (let {state} of controller.animationPlayers) {
time = Math.max(time, state.documentCurrentTime);
}
is(controller.documentCurrentTime, time,
"The documentCurrentTime is correct");
}
function* startNewAnimation(controller, panel) {
info("Add a new animation to the page and check the time again");
let onPlayerAdded = controller.once(controller.PLAYERS_UPDATED_EVENT);
yield executeInContent("devtools:test:setAttribute", {
selector: ".still",
attributeName: "class",
attributeValue: "ball still short"
});
yield onPlayerAdded;
yield waitForAllAnimationTargets(panel);
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the controller provides the document.timeline currentTime (at least
// the last known version since new animations were added).
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller} = yield openAnimationInspector();
ok(controller.documentCurrentTime, "The documentCurrentTime getter exists");
checkDocumentTimeIsCorrect(controller);
let time1 = controller.documentCurrentTime;
yield startNewAnimation(controller, panel);
checkDocumentTimeIsCorrect(controller);
let time2 = controller.documentCurrentTime;
ok(time2 > time1, "The new documentCurrentTime is higher than the old one");
});
function checkDocumentTimeIsCorrect(controller) {
let time = 0;
for (let {state} of controller.animationPlayers) {
time = Math.max(time, state.documentCurrentTime);
}
is(controller.documentCurrentTime, time,
"The documentCurrentTime is correct");
}
function* startNewAnimation(controller, panel) {
info("Add a new animation to the page and check the time again");
let onPlayerAdded = controller.once(controller.PLAYERS_UPDATED_EVENT);
yield executeInContent("devtools:test:setAttribute", {
selector: ".still",
attributeName: "class",
attributeValue: "ball still short"
});
yield onPlayerAdded;
yield waitForAllAnimationTargets(panel);
}

Просмотреть файл

@ -1,52 +1,52 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that animated properties' keyframes can be clicked, and that doing so
// sets the current time in the timeline.
add_task(function* () {
yield addTab(URL_ROOT + "doc_keyframes.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let {scrubberEl} = timeline;
// XXX: The scrollbar is placed in the timeline in such a way that it causes
// the animations to be slightly offset with the header when it appears.
// So for now, let's hide the scrollbar. Bug 1229340 should fix this.
timeline.animationsEl.style.overflow = "hidden";
info("Expand the animation");
yield clickOnAnimation(panel, 0);
info("Click on the first keyframe of the first animated property");
yield clickKeyframe(panel, 0, "background-color", 0);
info("Make sure the scrubber stopped moving and is at the right position");
yield assertScrubberMoving(panel, false);
checkScrubberPos(scrubberEl, 0);
info("Click on a keyframe in the middle");
yield clickKeyframe(panel, 0, "transform", 2);
info("Make sure the scrubber is at the right position");
checkScrubberPos(scrubberEl, 50);
});
function* clickKeyframe(panel, animIndex, property, index) {
let keyframeComponent = getKeyframeComponent(panel, animIndex, property);
let keyframeEl = getKeyframeEl(panel, animIndex, property, index);
let onSelect = keyframeComponent.once("frame-selected");
EventUtils.sendMouseEvent({type: "click"}, keyframeEl,
keyframeEl.ownerDocument.defaultView);
yield onSelect;
}
function checkScrubberPos(scrubberEl, pos) {
let newPos = Math.round(parseFloat(scrubberEl.style.left));
let expectedPos = Math.round(pos);
is(newPos, expectedPos, `The scrubber is at ${pos}%`);
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that animated properties' keyframes can be clicked, and that doing so
// sets the current time in the timeline.
add_task(function* () {
yield addTab(URL_ROOT + "doc_keyframes.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let {scrubberEl} = timeline;
// XXX: The scrollbar is placed in the timeline in such a way that it causes
// the animations to be slightly offset with the header when it appears.
// So for now, let's hide the scrollbar. Bug 1229340 should fix this.
timeline.animationsEl.style.overflow = "hidden";
info("Expand the animation");
yield clickOnAnimation(panel, 0);
info("Click on the first keyframe of the first animated property");
yield clickKeyframe(panel, 0, "background-color", 0);
info("Make sure the scrubber stopped moving and is at the right position");
yield assertScrubberMoving(panel, false);
checkScrubberPos(scrubberEl, 0);
info("Click on a keyframe in the middle");
yield clickKeyframe(panel, 0, "transform", 2);
info("Make sure the scrubber is at the right position");
checkScrubberPos(scrubberEl, 50);
});
function* clickKeyframe(panel, animIndex, property, index) {
let keyframeComponent = getKeyframeComponent(panel, animIndex, property);
let keyframeEl = getKeyframeEl(panel, animIndex, property, index);
let onSelect = keyframeComponent.once("frame-selected");
EventUtils.sendMouseEvent({type: "click"}, keyframeEl,
keyframeEl.ownerDocument.defaultView);
yield onSelect;
}
function checkScrubberPos(scrubberEl, pos) {
let newPos = Math.round(parseFloat(scrubberEl.style.left));
let expectedPos = Math.round(pos);
is(newPos, expectedPos, `The scrubber is at ${pos}%`);
}

Просмотреть файл

@ -1,74 +1,74 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that when an animation is selected and its list of properties is shown,
// there are keyframes markers next to each property being animated.
const EXPECTED_PROPERTIES = [
"backgroundColor",
"backgroundPosition",
"backgroundSize",
"borderBottomLeftRadius",
"borderBottomRightRadius",
"borderTopLeftRadius",
"borderTopRightRadius",
"filter",
"height",
"transform",
"width"
];
add_task(function* () {
yield addTab(URL_ROOT + "doc_keyframes.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
info("Expand the animation");
yield clickOnAnimation(panel, 0);
ok(timeline.rootWrapperEl.querySelectorAll(".frames .keyframes").length,
"There are container elements for displaying keyframes");
let data = yield getExpectedKeyframesData(timeline.animations[0]);
for (let propertyName in data) {
info("Check the keyframe markers for " + propertyName);
let widthMarkerSelector = ".frame[data-property=" + propertyName + "]";
let markers = timeline.rootWrapperEl.querySelectorAll(widthMarkerSelector);
is(markers.length, data[propertyName].length,
"The right number of keyframes was found for " + propertyName);
let offsets = [...markers].map(m => parseFloat(m.dataset.offset));
let values = [...markers].map(m => m.dataset.value);
for (let i = 0; i < markers.length; i++) {
is(markers[i].dataset.offset, offsets[i],
"Marker " + i + " for " + propertyName + " has the right offset");
is(markers[i].dataset.value, values[i],
"Marker " + i + " for " + propertyName + " has the right value");
}
}
});
function* getExpectedKeyframesData(animation) {
// We're testing the UI state here, so it's fine to get the list of expected
// properties from the animation actor.
let properties = yield animation.getProperties();
let data = {};
for (let expectedProperty of EXPECTED_PROPERTIES) {
data[expectedProperty] = [];
for (let {name, values} of properties) {
if (name !== expectedProperty) {
continue;
}
for (let {offset, value} of values) {
data[expectedProperty].push({offset, value});
}
}
}
return data;
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that when an animation is selected and its list of properties is shown,
// there are keyframes markers next to each property being animated.
const EXPECTED_PROPERTIES = [
"backgroundColor",
"backgroundPosition",
"backgroundSize",
"borderBottomLeftRadius",
"borderBottomRightRadius",
"borderTopLeftRadius",
"borderTopRightRadius",
"filter",
"height",
"transform",
"width"
];
add_task(function* () {
yield addTab(URL_ROOT + "doc_keyframes.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
info("Expand the animation");
yield clickOnAnimation(panel, 0);
ok(timeline.rootWrapperEl.querySelectorAll(".frames .keyframes").length,
"There are container elements for displaying keyframes");
let data = yield getExpectedKeyframesData(timeline.animations[0]);
for (let propertyName in data) {
info("Check the keyframe markers for " + propertyName);
let widthMarkerSelector = ".frame[data-property=" + propertyName + "]";
let markers = timeline.rootWrapperEl.querySelectorAll(widthMarkerSelector);
is(markers.length, data[propertyName].length,
"The right number of keyframes was found for " + propertyName);
let offsets = [...markers].map(m => parseFloat(m.dataset.offset));
let values = [...markers].map(m => m.dataset.value);
for (let i = 0; i < markers.length; i++) {
is(markers[i].dataset.offset, offsets[i],
"Marker " + i + " for " + propertyName + " has the right offset");
is(markers[i].dataset.value, values[i],
"Marker " + i + " for " + propertyName + " has the right value");
}
}
});
function* getExpectedKeyframesData(animation) {
// We're testing the UI state here, so it's fine to get the list of expected
// properties from the animation actor.
let properties = yield animation.getProperties();
let data = {};
for (let expectedProperty of EXPECTED_PROPERTIES) {
data[expectedProperty] = [];
for (let {name, values} of properties) {
if (name !== expectedProperty) {
continue;
}
for (let {offset, value} of values) {
data[expectedProperty].push({offset, value});
}
}
}
return data;
}

Просмотреть файл

@ -1,31 +1,31 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that when animations are added later (through animation mutations) and
// if these animations have the same names, then all of them are still being
// displayed (which should be true as long as these animations apply to
// different nodes).
add_task(function* () {
yield addTab(URL_ROOT + "doc_negative_animation.html");
let {controller, panel} = yield openAnimationInspector();
info("Wait until all animations have been added " +
"(they're added with setTimeout)");
while (controller.animationPlayers.length < 3) {
yield controller.once(controller.PLAYERS_UPDATED_EVENT);
}
yield waitForAllAnimationTargets(panel);
is(panel.animationsTimelineComponent.animations.length, 3,
"The timeline shows 3 animations too");
// Reduce the known nodeFronts to a set to make them unique.
let nodeFronts = new Set(panel.animationsTimelineComponent
.targetNodes.map(n => n.previewer.nodeFront));
is(nodeFronts.size, 3,
"The animations are applied to 3 different node fronts");
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that when animations are added later (through animation mutations) and
// if these animations have the same names, then all of them are still being
// displayed (which should be true as long as these animations apply to
// different nodes).
add_task(function* () {
yield addTab(URL_ROOT + "doc_negative_animation.html");
let {controller, panel} = yield openAnimationInspector();
info("Wait until all animations have been added " +
"(they're added with setTimeout)");
while (controller.animationPlayers.length < 3) {
yield controller.once(controller.PLAYERS_UPDATED_EVENT);
}
yield waitForAllAnimationTargets(panel);
is(panel.animationsTimelineComponent.animations.length, 3,
"The timeline shows 3 animations too");
// Reduce the known nodeFronts to a set to make them unique.
let nodeFronts = new Set(panel.animationsTimelineComponent
.targetNodes.map(n => n.previewer.nodeFront));
is(nodeFronts.size, 3,
"The animations are applied to 3 different node fronts");
});

Просмотреть файл

@ -1,49 +1,49 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that animated pseudo-elements do show in the timeline.
add_task(function* () {
yield addTab(URL_ROOT + "doc_pseudo_elements.html");
let {inspector, panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
info("With <body> selected by default check the content of the timeline");
is(timeline.timeBlocks.length, 3, "There are 3 animations in the timeline");
let getTargetNodeText = index => {
let el = timeline.targetNodes[index].previewer.previewEl;
return [...el.childNodes]
.map(n => n.style.display === "none" ? "" : n.textContent)
.join("");
};
is(getTargetNodeText(0), "body", "The first animated node is <body>");
is(getTargetNodeText(1), "::before", "The second animated node is ::before");
is(getTargetNodeText(2), "::after", "The third animated node is ::after");
info("Getting the before and after nodeFronts");
let bodyContainer = yield getContainerForSelector("body", inspector);
let getBodyChildNodeFront = index => {
return bodyContainer.elt.children[1].childNodes[index].container.node;
};
let beforeNode = getBodyChildNodeFront(0);
let afterNode = getBodyChildNodeFront(1);
info("Select the ::before pseudo-element in the inspector");
yield selectNode(beforeNode, inspector);
is(timeline.timeBlocks.length, 1, "There is 1 animation in the timeline");
is(timeline.targetNodes[0].previewer.nodeFront,
inspector.selection.nodeFront,
"The right node front is displayed in the timeline");
info("Select the ::after pseudo-element in the inspector");
yield selectNode(afterNode, inspector);
is(timeline.timeBlocks.length, 1, "There is 1 animation in the timeline");
is(timeline.targetNodes[0].previewer.nodeFront,
inspector.selection.nodeFront,
"The right node front is displayed in the timeline");
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that animated pseudo-elements do show in the timeline.
add_task(function* () {
yield addTab(URL_ROOT + "doc_pseudo_elements.html");
let {inspector, panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
info("With <body> selected by default check the content of the timeline");
is(timeline.timeBlocks.length, 3, "There are 3 animations in the timeline");
let getTargetNodeText = index => {
let el = timeline.targetNodes[index].previewer.previewEl;
return [...el.childNodes]
.map(n => n.style.display === "none" ? "" : n.textContent)
.join("");
};
is(getTargetNodeText(0), "body", "The first animated node is <body>");
is(getTargetNodeText(1), "::before", "The second animated node is ::before");
is(getTargetNodeText(2), "::after", "The third animated node is ::after");
info("Getting the before and after nodeFronts");
let bodyContainer = yield getContainerForSelector("body", inspector);
let getBodyChildNodeFront = index => {
return bodyContainer.elt.children[1].childNodes[index].container.node;
};
let beforeNode = getBodyChildNodeFront(0);
let afterNode = getBodyChildNodeFront(1);
info("Select the ::before pseudo-element in the inspector");
yield selectNode(beforeNode, inspector);
is(timeline.timeBlocks.length, 1, "There is 1 animation in the timeline");
is(timeline.targetNodes[0].previewer.nodeFront,
inspector.selection.nodeFront,
"The right node front is displayed in the timeline");
info("Select the ::after pseudo-element in the inspector");
yield selectNode(afterNode, inspector);
is(timeline.timeBlocks.length, 1, "There is 1 animation in the timeline");
is(timeline.targetNodes[0].previewer.nodeFront,
inspector.selection.nodeFront,
"The right node front is displayed in the timeline");
});

Просмотреть файл

@ -1,57 +1,57 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Test that when animations displayed in the timeline are running on the
// compositor, they get a special icon and information in the tooltip.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {inspector, panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
info("Select a test node we know has an animation running on the compositor");
yield selectNodeAndWaitForAnimations(".animated", inspector);
let animationEl = timeline.animationsEl.querySelector(".animation");
ok(animationEl.classList.contains("fast-track"),
"The animation element has the fast-track css class");
ok(hasTooltip(animationEl,
ANIMATION_L10N.getStr("player.allPropertiesOnCompositorTooltip")),
"The animation element has the right tooltip content");
info("Select a node we know doesn't have an animation on the compositor");
yield selectNodeAndWaitForAnimations(".no-compositor", inspector);
animationEl = timeline.animationsEl.querySelector(".animation");
ok(!animationEl.classList.contains("fast-track"),
"The animation element does not have the fast-track css class");
ok(!hasTooltip(animationEl,
ANIMATION_L10N.getStr("player.allPropertiesOnCompositorTooltip")),
"The animation element does not have oncompositor tooltip content");
ok(!hasTooltip(animationEl,
ANIMATION_L10N.getStr("player.somePropertiesOnCompositorTooltip")),
"The animation element does not have oncompositor tooltip content");
info("Select a node we know has animation on the compositor and not on the" +
" compositor");
yield selectNodeAndWaitForAnimations(".compositor-notall", inspector);
animationEl = timeline.animationsEl.querySelector(".animation");
ok(animationEl.classList.contains("fast-track"),
"The animation element has the fast-track css class");
ok(hasTooltip(animationEl,
ANIMATION_L10N.getStr("player.somePropertiesOnCompositorTooltip")),
"The animation element has the right tooltip content");
});
function hasTooltip(animationEl, expected) {
let el = animationEl.querySelector(".name");
let tooltip = el.getAttribute("title");
return tooltip.indexOf(expected) !== -1;
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Test that when animations displayed in the timeline are running on the
// compositor, they get a special icon and information in the tooltip.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {inspector, panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
info("Select a test node we know has an animation running on the compositor");
yield selectNodeAndWaitForAnimations(".animated", inspector);
let animationEl = timeline.animationsEl.querySelector(".animation");
ok(animationEl.classList.contains("fast-track"),
"The animation element has the fast-track css class");
ok(hasTooltip(animationEl,
ANIMATION_L10N.getStr("player.allPropertiesOnCompositorTooltip")),
"The animation element has the right tooltip content");
info("Select a node we know doesn't have an animation on the compositor");
yield selectNodeAndWaitForAnimations(".no-compositor", inspector);
animationEl = timeline.animationsEl.querySelector(".animation");
ok(!animationEl.classList.contains("fast-track"),
"The animation element does not have the fast-track css class");
ok(!hasTooltip(animationEl,
ANIMATION_L10N.getStr("player.allPropertiesOnCompositorTooltip")),
"The animation element does not have oncompositor tooltip content");
ok(!hasTooltip(animationEl,
ANIMATION_L10N.getStr("player.somePropertiesOnCompositorTooltip")),
"The animation element does not have oncompositor tooltip content");
info("Select a node we know has animation on the compositor and not on the" +
" compositor");
yield selectNodeAndWaitForAnimations(".compositor-notall", inspector);
animationEl = timeline.animationsEl.querySelector(".animation");
ok(animationEl.classList.contains("fast-track"),
"The animation element has the fast-track css class");
ok(hasTooltip(animationEl,
ANIMATION_L10N.getStr("player.somePropertiesOnCompositorTooltip")),
"The animation element has the right tooltip content");
});
function hasTooltip(animationEl, expected) {
let el = animationEl.querySelector(".name");
let tooltip = el.getAttribute("title");
return tooltip.indexOf(expected) !== -1;
}

Просмотреть файл

@ -1,54 +1,54 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Test that the DOM element targets displayed in animation player widgets can
// be used to highlight elements in the DOM and select them in the inspector.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let targets = panel.animationsTimelineComponent.targetNodes;
info("Click on the highlighter icon for the first animated node");
let domNodePreview1 = targets[0].previewer;
yield lockHighlighterOn(domNodePreview1);
ok(domNodePreview1.highlightNodeEl.classList.contains("selected"),
"The highlighter icon is selected");
info("Click on the highlighter icon for the second animated node");
let domNodePreview2 = targets[1].previewer;
yield lockHighlighterOn(domNodePreview2);
ok(domNodePreview2.highlightNodeEl.classList.contains("selected"),
"The highlighter icon is selected");
ok(!domNodePreview1.highlightNodeEl.classList.contains("selected"),
"The highlighter icon for the first node is unselected");
info("Click again to unhighlight");
yield unlockHighlighterOn(domNodePreview2);
ok(!domNodePreview2.highlightNodeEl.classList.contains("selected"),
"The highlighter icon for the second node is unselected");
});
function* lockHighlighterOn(domNodePreview) {
let onLocked = domNodePreview.once("target-highlighter-locked");
clickOnHighlighterIcon(domNodePreview);
yield onLocked;
}
function* unlockHighlighterOn(domNodePreview) {
let onUnlocked = domNodePreview.once("target-highlighter-unlocked");
clickOnHighlighterIcon(domNodePreview);
yield onUnlocked;
}
function clickOnHighlighterIcon(domNodePreview) {
let lockEl = domNodePreview.highlightNodeEl;
EventUtils.sendMouseEvent({type: "click"}, lockEl,
lockEl.ownerDocument.defaultView);
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Test that the DOM element targets displayed in animation player widgets can
// be used to highlight elements in the DOM and select them in the inspector.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let targets = panel.animationsTimelineComponent.targetNodes;
info("Click on the highlighter icon for the first animated node");
let domNodePreview1 = targets[0].previewer;
yield lockHighlighterOn(domNodePreview1);
ok(domNodePreview1.highlightNodeEl.classList.contains("selected"),
"The highlighter icon is selected");
info("Click on the highlighter icon for the second animated node");
let domNodePreview2 = targets[1].previewer;
yield lockHighlighterOn(domNodePreview2);
ok(domNodePreview2.highlightNodeEl.classList.contains("selected"),
"The highlighter icon is selected");
ok(!domNodePreview1.highlightNodeEl.classList.contains("selected"),
"The highlighter icon for the first node is unselected");
info("Click again to unhighlight");
yield unlockHighlighterOn(domNodePreview2);
ok(!domNodePreview2.highlightNodeEl.classList.contains("selected"),
"The highlighter icon for the second node is unselected");
});
function* lockHighlighterOn(domNodePreview) {
let onLocked = domNodePreview.once("target-highlighter-locked");
clickOnHighlighterIcon(domNodePreview);
yield onLocked;
}
function* unlockHighlighterOn(domNodePreview) {
let onUnlocked = domNodePreview.once("target-highlighter-unlocked");
clickOnHighlighterIcon(domNodePreview);
yield onUnlocked;
}
function clickOnHighlighterIcon(domNodePreview) {
let lockEl = domNodePreview.highlightNodeEl;
EventUtils.sendMouseEvent({type: "click"}, lockEl,
lockEl.ownerDocument.defaultView);
}

Просмотреть файл

@ -1,48 +1,48 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline toolbar displays the current time, and that it
// changes when animations are playing, gets back to 0 when animations are
// rewound, and stops when animations are paused.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let label = panel.timelineCurrentTimeEl;
ok(label, "The current time label exists");
// On page load animations are playing so the time shoud change, although we
// don't want to test the exact value of the time displayed, just that it
// actually changes.
info("Make sure the time displayed actually changes");
yield isCurrentTimeLabelChanging(panel, true);
info("Pause the animations and check that the time stops changing");
yield clickTimelinePlayPauseButton(panel);
yield isCurrentTimeLabelChanging(panel, false);
info("Rewind the animations and check that the time stops changing");
yield clickTimelineRewindButton(panel);
yield isCurrentTimeLabelChanging(panel, false);
is(label.textContent, "00:00.000");
});
function* isCurrentTimeLabelChanging(panel, isChanging) {
let label = panel.timelineCurrentTimeEl;
let time1 = label.textContent;
yield new Promise(r => setTimeout(r, 200));
let time2 = label.textContent;
if (isChanging) {
ok(time1 !== time2, "The text displayed in the label changes with time");
} else {
is(time1, time2, "The text displayed in the label doesn't change");
}
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline toolbar displays the current time, and that it
// changes when animations are playing, gets back to 0 when animations are
// rewound, and stops when animations are paused.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let label = panel.timelineCurrentTimeEl;
ok(label, "The current time label exists");
// On page load animations are playing so the time shoud change, although we
// don't want to test the exact value of the time displayed, just that it
// actually changes.
info("Make sure the time displayed actually changes");
yield isCurrentTimeLabelChanging(panel, true);
info("Pause the animations and check that the time stops changing");
yield clickTimelinePlayPauseButton(panel);
yield isCurrentTimeLabelChanging(panel, false);
info("Rewind the animations and check that the time stops changing");
yield clickTimelineRewindButton(panel);
yield isCurrentTimeLabelChanging(panel, false);
is(label.textContent, "00:00.000");
});
function* isCurrentTimeLabelChanging(panel, isChanging) {
let label = panel.timelineCurrentTimeEl;
let time1 = label.textContent;
yield new Promise(r => setTimeout(r, 200));
let time2 = label.textContent;
if (isChanging) {
ok(time1 !== time2, "The text displayed in the label changes with time");
} else {
is(time1, time2, "The text displayed in the label doesn't change");
}
}

Просмотреть файл

@ -1,84 +1,84 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the iteration start is displayed correctly in time blocks.
add_task(function* () {
yield addTab(URL_ROOT + "doc_script_animation.html");
let {panel} = yield openAnimationInspector();
let timelineComponent = panel.animationsTimelineComponent;
let timeBlockComponents = timelineComponent.timeBlocks;
let detailsComponents = timelineComponent.details;
for (let i = 0; i < timeBlockComponents.length; i++) {
info(`Expand time block ${i} so its keyframes are visible`);
yield clickOnAnimation(panel, i);
info(`Check the state of time block ${i}`);
let {containerEl, animation: {state}} = timeBlockComponents[i];
checkAnimationTooltip(containerEl, state);
checkIterationBackground(containerEl, state);
// Get the first set of keyframes (there's only one animated property
// anyway), and the first frame element from there, we're only interested in
// its offset.
let keyframeComponent = detailsComponents[i].keyframeComponents[0];
let frameEl = keyframeComponent.keyframesEl.querySelector(".frame");
checkKeyframeOffset(containerEl, frameEl, state);
}
});
function checkAnimationTooltip(el, {iterationStart, duration}) {
info("Check an animation's iterationStart data in its tooltip");
let title = el.querySelector(".name").getAttribute("title");
let iterationStartTime = iterationStart * duration / 1000;
let iterationStartTimeString = iterationStartTime.toLocaleString(undefined, {
maximumFractionDigits: 2,
minimumFractionDigits: 2
}).replace(".", "\\.");
let iterationStartString = iterationStart.toString().replace(".", "\\.");
let regex = new RegExp("Iteration start: " + iterationStartString +
" \\(" + iterationStartTimeString + "s\\)");
ok(title.match(regex), "The tooltip shows the expected iteration start");
}
function checkIterationBackground(el, {iterationCount, iterationStart}) {
info("Check the background-image used to display iterations is offset " +
"correctly to represent the iterationStart");
let iterationsEl = el.querySelector(".iterations");
let start = getIterationStartFromBackground(iterationsEl, iterationCount);
is(start, iterationStart % 1,
"The right background-position for iteration start");
}
function getIterationStartFromBackground(el, iterationCount) {
if (iterationCount == 1) {
let size = parseFloat(/([.\d]+)%/.exec(el.style.backgroundSize)[1]);
return 1 - size / 100;
}
let size = parseFloat(/([.\d]+)%/.exec(el.style.backgroundSize)[1]);
let position = parseFloat(/([-\d]+)%/.exec(el.style.backgroundPosition)[1]);
let iterationStartW = -position / size * (100 - size);
let rounded = Math.round(iterationStartW * 100);
return rounded / 10000;
}
function checkKeyframeOffset(timeBlockEl, frameEl, {iterationStart}) {
info("Check that the first keyframe is offset correctly");
let start = getIterationStartFromLeft(frameEl);
is(start, iterationStart % 1, "The frame offset for iteration start");
}
function getIterationStartFromLeft(el) {
let left = 100 - parseFloat(/(\d+)%/.exec(el.style.left)[1]);
return left / 100;
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Check that the iteration start is displayed correctly in time blocks.
add_task(function* () {
yield addTab(URL_ROOT + "doc_script_animation.html");
let {panel} = yield openAnimationInspector();
let timelineComponent = panel.animationsTimelineComponent;
let timeBlockComponents = timelineComponent.timeBlocks;
let detailsComponents = timelineComponent.details;
for (let i = 0; i < timeBlockComponents.length; i++) {
info(`Expand time block ${i} so its keyframes are visible`);
yield clickOnAnimation(panel, i);
info(`Check the state of time block ${i}`);
let {containerEl, animation: {state}} = timeBlockComponents[i];
checkAnimationTooltip(containerEl, state);
checkIterationBackground(containerEl, state);
// Get the first set of keyframes (there's only one animated property
// anyway), and the first frame element from there, we're only interested in
// its offset.
let keyframeComponent = detailsComponents[i].keyframeComponents[0];
let frameEl = keyframeComponent.keyframesEl.querySelector(".frame");
checkKeyframeOffset(containerEl, frameEl, state);
}
});
function checkAnimationTooltip(el, {iterationStart, duration}) {
info("Check an animation's iterationStart data in its tooltip");
let title = el.querySelector(".name").getAttribute("title");
let iterationStartTime = iterationStart * duration / 1000;
let iterationStartTimeString = iterationStartTime.toLocaleString(undefined, {
maximumFractionDigits: 2,
minimumFractionDigits: 2
}).replace(".", "\\.");
let iterationStartString = iterationStart.toString().replace(".", "\\.");
let regex = new RegExp("Iteration start: " + iterationStartString +
" \\(" + iterationStartTimeString + "s\\)");
ok(title.match(regex), "The tooltip shows the expected iteration start");
}
function checkIterationBackground(el, {iterationCount, iterationStart}) {
info("Check the background-image used to display iterations is offset " +
"correctly to represent the iterationStart");
let iterationsEl = el.querySelector(".iterations");
let start = getIterationStartFromBackground(iterationsEl, iterationCount);
is(start, iterationStart % 1,
"The right background-position for iteration start");
}
function getIterationStartFromBackground(el, iterationCount) {
if (iterationCount == 1) {
let size = parseFloat(/([.\d]+)%/.exec(el.style.backgroundSize)[1]);
return 1 - size / 100;
}
let size = parseFloat(/([.\d]+)%/.exec(el.style.backgroundSize)[1]);
let position = parseFloat(/([-\d]+)%/.exec(el.style.backgroundPosition)[1]);
let iterationStartW = -position / size * (100 - size);
let rounded = Math.round(iterationStartW * 100);
return rounded / 10000;
}
function checkKeyframeOffset(timeBlockEl, frameEl, {iterationStart}) {
info("Check that the first keyframe is offset correctly");
let start = getIterationStartFromLeft(frameEl);
is(start, iterationStart % 1, "The frame offset for iteration start");
}
function getIterationStartFromLeft(el) {
let left = 100 - parseFloat(/(\d+)%/.exec(el.style.left)[1]);
return left / 100;
}

Просмотреть файл

@ -1,110 +1,110 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline toolbar contains a pause button and that this pause
// button can be clicked. Check that when it is, the current animations
// displayed in the timeline get their playstates changed accordingly, and check
// that the scrubber resumes/stops moving.
// Also checks that the button goes to the right state when the scrubber has
// reached the end of the timeline: continues to be in playing mode for infinite
// animations, goes to paused mode otherwise.
// And test that clicking the button once the scrubber has reached the end of
// the timeline does the right thing.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, inspector} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let btn = panel.playTimelineButtonEl;
ok(btn, "The play/pause button exists");
ok(!btn.classList.contains("paused"),
"The play/pause button is in its playing state");
info("Click on the button to pause all timeline animations");
yield clickTimelinePlayPauseButton(panel);
ok(btn.classList.contains("paused"),
"The play/pause button is in its paused state");
yield assertScrubberMoving(panel, false);
info("Click again on the button to play all timeline animations");
yield clickTimelinePlayPauseButton(panel);
ok(!btn.classList.contains("paused"),
"The play/pause button is in its playing state again");
yield assertScrubberMoving(panel, true);
// Some animations on the test page are infinite, so the scrubber won't stop
// at the end of the timeline, and the button should remain in play mode.
info("Select an infinite animation, reload the page and wait for the " +
"animation to complete");
yield selectNodeAndWaitForAnimations(".multi", inspector);
yield reloadTab(inspector);
yield waitForOutOfBoundScrubber(timeline);
ok(!btn.classList.contains("paused"),
"The button is in its playing state still, animations are infinite.");
yield assertScrubberMoving(panel, true);
info("Click on the button after the scrubber has moved out of bounds");
yield clickTimelinePlayPauseButton(panel);
ok(btn.classList.contains("paused"),
"The button can be paused after the scrubber has moved out of bounds");
yield assertScrubberMoving(panel, false);
// For a finite animation though, once the scrubber reaches the end of the
// timeline, it should go back to paused mode.
info("Select a finite animation, reload the page and wait for the " +
"animation to complete");
yield selectNodeAndWaitForAnimations(".negative-delay", inspector);
let onScrubberStopped = waitForScrubberStopped(timeline);
yield reloadTab(inspector);
yield onScrubberStopped;
ok(btn.classList.contains("paused"),
"The button is in paused state once finite animations are done");
yield assertScrubberMoving(panel, false);
info("Click again on the button to play the animation from the start again");
yield clickTimelinePlayPauseButton(panel);
ok(!btn.classList.contains("paused"),
"Clicking the button once finite animations are done should restart them");
yield assertScrubberMoving(panel, true);
});
function waitForOutOfBoundScrubber({win, scrubberEl}) {
return new Promise(resolve => {
function check() {
let pos = scrubberEl.getBoxQuads()[0].bounds.right;
let width = win.document.documentElement.offsetWidth;
if (pos >= width) {
setTimeout(resolve, 50);
} else {
setTimeout(check, 50);
}
}
check();
});
}
function waitForScrubberStopped(timeline) {
return new Promise(resolve => {
timeline.on("timeline-data-changed",
function onTimelineData(e, {isMoving}) {
if (!isMoving) {
timeline.off("timeline-data-changed", onTimelineData);
resolve();
}
});
});
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline toolbar contains a pause button and that this pause
// button can be clicked. Check that when it is, the current animations
// displayed in the timeline get their playstates changed accordingly, and check
// that the scrubber resumes/stops moving.
// Also checks that the button goes to the right state when the scrubber has
// reached the end of the timeline: continues to be in playing mode for infinite
// animations, goes to paused mode otherwise.
// And test that clicking the button once the scrubber has reached the end of
// the timeline does the right thing.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, inspector} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let btn = panel.playTimelineButtonEl;
ok(btn, "The play/pause button exists");
ok(!btn.classList.contains("paused"),
"The play/pause button is in its playing state");
info("Click on the button to pause all timeline animations");
yield clickTimelinePlayPauseButton(panel);
ok(btn.classList.contains("paused"),
"The play/pause button is in its paused state");
yield assertScrubberMoving(panel, false);
info("Click again on the button to play all timeline animations");
yield clickTimelinePlayPauseButton(panel);
ok(!btn.classList.contains("paused"),
"The play/pause button is in its playing state again");
yield assertScrubberMoving(panel, true);
// Some animations on the test page are infinite, so the scrubber won't stop
// at the end of the timeline, and the button should remain in play mode.
info("Select an infinite animation, reload the page and wait for the " +
"animation to complete");
yield selectNodeAndWaitForAnimations(".multi", inspector);
yield reloadTab(inspector);
yield waitForOutOfBoundScrubber(timeline);
ok(!btn.classList.contains("paused"),
"The button is in its playing state still, animations are infinite.");
yield assertScrubberMoving(panel, true);
info("Click on the button after the scrubber has moved out of bounds");
yield clickTimelinePlayPauseButton(panel);
ok(btn.classList.contains("paused"),
"The button can be paused after the scrubber has moved out of bounds");
yield assertScrubberMoving(panel, false);
// For a finite animation though, once the scrubber reaches the end of the
// timeline, it should go back to paused mode.
info("Select a finite animation, reload the page and wait for the " +
"animation to complete");
yield selectNodeAndWaitForAnimations(".negative-delay", inspector);
let onScrubberStopped = waitForScrubberStopped(timeline);
yield reloadTab(inspector);
yield onScrubberStopped;
ok(btn.classList.contains("paused"),
"The button is in paused state once finite animations are done");
yield assertScrubberMoving(panel, false);
info("Click again on the button to play the animation from the start again");
yield clickTimelinePlayPauseButton(panel);
ok(!btn.classList.contains("paused"),
"Clicking the button once finite animations are done should restart them");
yield assertScrubberMoving(panel, true);
});
function waitForOutOfBoundScrubber({win, scrubberEl}) {
return new Promise(resolve => {
function check() {
let pos = scrubberEl.getBoxQuads()[0].bounds.right;
let width = win.document.documentElement.offsetWidth;
if (pos >= width) {
setTimeout(resolve, 50);
} else {
setTimeout(check, 50);
}
}
check();
});
}
function waitForScrubberStopped(timeline) {
return new Promise(resolve => {
timeline.on("timeline-data-changed",
function onTimelineData(e, {isMoving}) {
if (!isMoving) {
timeline.off("timeline-data-changed", onTimelineData);
resolve();
}
});
});
}

Просмотреть файл

@ -1,56 +1,56 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline toolbar contains a playback rate selector UI and that
// it can be used to change the playback rate of animations in the timeline.
// Also check that it displays the rate of the current animations in case they
// all have the same rate, or that it displays the empty value in case they
// have mixed rates.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller, inspector, toolbox} = yield openAnimationInspector();
// In this test, we disable the highlighter on purpose because of the way
// events are simulated to select an option in the playbackRate <select>.
// Indeed, this may cause mousemove events to be triggered on the nodes that
// are underneath the <select>, and these are AnimationTargetNode instances.
// Simulating mouse events on them will cause the highlighter to emit requests
// and this might cause the test to fail if they happen after it has ended.
disableHighlighter(toolbox);
let select = panel.rateSelectorEl.firstChild;
ok(select, "The rate selector exists");
info("Change all of the current animations' rates to 0.5");
yield changeTimelinePlaybackRate(panel, .5);
checkAllAnimationsRatesChanged(controller, select, .5);
info("Select just one animated node and change its rate only");
yield selectNodeAndWaitForAnimations(".animated", inspector);
yield changeTimelinePlaybackRate(panel, 2);
checkAllAnimationsRatesChanged(controller, select, 2);
info("Select the <body> again, it should now have mixed-rates animations");
yield selectNodeAndWaitForAnimations("body", inspector);
is(select.value, "", "The selected rate is empty");
info("Change the rate for these mixed-rate animations");
yield changeTimelinePlaybackRate(panel, 1);
checkAllAnimationsRatesChanged(controller, select, 1);
});
function checkAllAnimationsRatesChanged({animationPlayers}, select, rate) {
ok(animationPlayers.every(({state}) => state.playbackRate === rate),
"All animations' rates have been set to " + rate);
is(select.value, rate, "The right value is displayed in the select");
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline toolbar contains a playback rate selector UI and that
// it can be used to change the playback rate of animations in the timeline.
// Also check that it displays the rate of the current animations in case they
// all have the same rate, or that it displays the empty value in case they
// have mixed rates.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller, inspector, toolbox} = yield openAnimationInspector();
// In this test, we disable the highlighter on purpose because of the way
// events are simulated to select an option in the playbackRate <select>.
// Indeed, this may cause mousemove events to be triggered on the nodes that
// are underneath the <select>, and these are AnimationTargetNode instances.
// Simulating mouse events on them will cause the highlighter to emit requests
// and this might cause the test to fail if they happen after it has ended.
disableHighlighter(toolbox);
let select = panel.rateSelectorEl.firstChild;
ok(select, "The rate selector exists");
info("Change all of the current animations' rates to 0.5");
yield changeTimelinePlaybackRate(panel, .5);
checkAllAnimationsRatesChanged(controller, select, .5);
info("Select just one animated node and change its rate only");
yield selectNodeAndWaitForAnimations(".animated", inspector);
yield changeTimelinePlaybackRate(panel, 2);
checkAllAnimationsRatesChanged(controller, select, 2);
info("Select the <body> again, it should now have mixed-rates animations");
yield selectNodeAndWaitForAnimations("body", inspector);
is(select.value, "", "The selected rate is empty");
info("Change the rate for these mixed-rate animations");
yield changeTimelinePlaybackRate(panel, 1);
checkAllAnimationsRatesChanged(controller, select, 1);
});
function checkAllAnimationsRatesChanged({animationPlayers}, select, rate) {
ok(animationPlayers.every(({state}) => state.playbackRate === rate),
"All animations' rates have been set to " + rate);
is(select.value, rate, "The right value is displayed in the select");
}

Просмотреть файл

@ -1,51 +1,51 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline toolbar contains a rewind button and that it can be
// clicked. Check that when it is, the current animations displayed in the
// timeline get their playstates changed to paused, and their currentTimes
// reset to 0, and that the scrubber stops moving and is positioned to the
// start.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller} = yield openAnimationInspector();
let players = controller.animationPlayers;
let btn = panel.rewindTimelineButtonEl;
ok(btn, "The rewind button exists");
info("Click on the button to rewind all timeline animations");
yield clickTimelineRewindButton(panel);
info("Check that the scrubber has stopped moving");
yield assertScrubberMoving(panel, false);
ok(players.every(({state}) => state.currentTime === 0),
"All animations' currentTimes have been set to 0");
ok(players.every(({state}) => state.playState === "paused"),
"All animations have been paused");
info("Play the animations again");
yield clickTimelinePlayPauseButton(panel);
info("And pause them after a short while");
yield new Promise(r => setTimeout(r, 200));
info("Check that rewinding when animations are paused works too");
yield clickTimelineRewindButton(panel);
info("Check that the scrubber has stopped moving");
yield assertScrubberMoving(panel, false);
ok(players.every(({state}) => state.currentTime === 0),
"All animations' currentTimes have been set to 0");
ok(players.every(({state}) => state.playState === "paused"),
"All animations have been paused");
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline toolbar contains a rewind button and that it can be
// clicked. Check that when it is, the current animations displayed in the
// timeline get their playstates changed to paused, and their currentTimes
// reset to 0, and that the scrubber stops moving and is positioned to the
// start.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller} = yield openAnimationInspector();
let players = controller.animationPlayers;
let btn = panel.rewindTimelineButtonEl;
ok(btn, "The rewind button exists");
info("Click on the button to rewind all timeline animations");
yield clickTimelineRewindButton(panel);
info("Check that the scrubber has stopped moving");
yield assertScrubberMoving(panel, false);
ok(players.every(({state}) => state.currentTime === 0),
"All animations' currentTimes have been set to 0");
ok(players.every(({state}) => state.playState === "paused"),
"All animations have been paused");
info("Play the animations again");
yield clickTimelinePlayPauseButton(panel);
info("And pause them after a short while");
yield new Promise(r => setTimeout(r, 200));
info("Check that rewinding when animations are paused works too");
yield clickTimelineRewindButton(panel);
info("Check that the scrubber has stopped moving");
yield assertScrubberMoving(panel, false);
ok(players.every(({state}) => state.currentTime === 0),
"All animations' currentTimes have been set to 0");
ok(players.every(({state}) => state.playState === "paused"),
"All animations have been paused");
});

Просмотреть файл

@ -1,28 +1,28 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the scrubber in the timeline moves when animations are playing.
// The animations in the test page last for a very long time, so the test just
// measures the position of the scrubber once, then waits for some time to pass
// and measures its position again.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let scrubberEl = timeline.scrubberEl;
let startPos = scrubberEl.getBoundingClientRect().left;
info("Wait for some time to check that the scrubber moves");
yield new Promise(r => setTimeout(r, 2000));
let endPos = scrubberEl.getBoundingClientRect().left;
ok(endPos > startPos, "The scrubber has moved");
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the scrubber in the timeline moves when animations are playing.
// The animations in the test page last for a very long time, so the test just
// measures the position of the scrubber once, then waits for some time to pass
// and measures its position again.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel} = yield openAnimationInspector();
let timeline = panel.animationsTimelineComponent;
let scrubberEl = timeline.scrubberEl;
let startPos = scrubberEl.getBoundingClientRect().left;
info("Wait for some time to check that the scrubber moves");
yield new Promise(r => setTimeout(r, 2000));
let endPos = scrubberEl.getBoundingClientRect().left;
ok(endPos > startPos, "The scrubber has moved");
});

Просмотреть файл

@ -1,41 +1,41 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline displays animations' duration, delay iteration
// counts and iteration start in tooltips.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller} = yield openAnimationInspector();
info("Getting the animation element from the panel");
let timelineEl = panel.animationsTimelineComponent.rootWrapperEl;
let timeBlockNameEls = timelineEl.querySelectorAll(".time-block .name");
// Verify that each time-block's name element has a tooltip that looks sort of
// ok. We don't need to test the actual content.
[...timeBlockNameEls].forEach((el, i) => {
ok(el.hasAttribute("title"), "The tooltip is defined for animation " + i);
let title = el.getAttribute("title");
if (controller.animationPlayers[i].state.delay) {
ok(title.match(/Delay: [\d.-]+s/), "The tooltip shows the delay");
}
ok(title.match(/Duration: [\d.]+s/), "The tooltip shows the duration");
if (controller.animationPlayers[i].state.endDelay) {
ok(title.match(/End delay: [\d.-]+s/), "The tooltip shows the endDelay");
}
if (controller.animationPlayers[i].state.iterationCount !== 1) {
ok(title.match(/Repeats: /), "The tooltip shows the iterations");
} else {
ok(!title.match(/Repeats: /), "The tooltip doesn't show the iterations");
}
ok(!title.match(/Iteration start:/),
"The tooltip doesn't show the iteration start");
});
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Check that the timeline displays animations' duration, delay iteration
// counts and iteration start in tooltips.
add_task(function* () {
yield addTab(URL_ROOT + "doc_simple_animation.html");
let {panel, controller} = yield openAnimationInspector();
info("Getting the animation element from the panel");
let timelineEl = panel.animationsTimelineComponent.rootWrapperEl;
let timeBlockNameEls = timelineEl.querySelectorAll(".time-block .name");
// Verify that each time-block's name element has a tooltip that looks sort of
// ok. We don't need to test the actual content.
[...timeBlockNameEls].forEach((el, i) => {
ok(el.hasAttribute("title"), "The tooltip is defined for animation " + i);
let title = el.getAttribute("title");
if (controller.animationPlayers[i].state.delay) {
ok(title.match(/Delay: [\d.-]+s/), "The tooltip shows the delay");
}
ok(title.match(/Duration: [\d.]+s/), "The tooltip shows the duration");
if (controller.animationPlayers[i].state.endDelay) {
ok(title.match(/End delay: [\d.-]+s/), "The tooltip shows the endDelay");
}
if (controller.animationPlayers[i].state.iterationCount !== 1) {
ok(title.match(/Repeats: /), "The tooltip shows the iterations");
} else {
ok(!title.match(/Repeats: /), "The tooltip doesn't show the iterations");
}
ok(!title.match(/Iteration start:/),
"The tooltip doesn't show the iteration start");
});
});

Просмотреть файл

@ -1,66 +1,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
html, body {
margin: 0;
height: 100%;
overflow: hidden;
}
div {
position: absolute;
top: 0;
left: -500px;
height: 20px;
width: 500px;
color: red;
background: linear-gradient(to left, currentColor, currentColor 2px, transparent);
}
.zero {
color: blue;
top: 20px;
}
.positive {
color: green;
top: 40px;
}
.negative.move { animation: 5s -1s move linear forwards; }
.zero.move { animation: 5s 0s move linear forwards; }
.positive.move { animation: 5s 1s move linear forwards; }
@keyframes move {
to {
transform: translateX(500px);
}
}
</style>
</head>
<body>
<div class="negative"></div>
<div class="zero"></div>
<div class="positive"></div>
<script>
"use strict";
var negative = document.querySelector(".negative");
var zero = document.querySelector(".zero");
var positive = document.querySelector(".positive");
// The non-delayed animation starts now.
zero.classList.add("move");
// The negative-delayed animation starts in 1 second.
setTimeout(function () {
negative.classList.add("move");
}, 1000);
// The positive-delayed animation starts in 200 ms.
setTimeout(function () {
positive.classList.add("move");
}, 200);
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<style>
html, body {
margin: 0;
height: 100%;
overflow: hidden;
}
div {
position: absolute;
top: 0;
left: -500px;
height: 20px;
width: 500px;
color: red;
background: linear-gradient(to left, currentColor, currentColor 2px, transparent);
}
.zero {
color: blue;
top: 20px;
}
.positive {
color: green;
top: 40px;
}
.negative.move { animation: 5s -1s move linear forwards; }
.zero.move { animation: 5s 0s move linear forwards; }
.positive.move { animation: 5s 1s move linear forwards; }
@keyframes move {
to {
transform: translateX(500px);
}
}
</style>
</head>
<body>
<div class="negative"></div>
<div class="zero"></div>
<div class="positive"></div>
<script>
"use strict";
var negative = document.querySelector(".negative");
var zero = document.querySelector(".zero");
var positive = document.querySelector(".positive");
// The non-delayed animation starts now.
zero.classList.add("move");
// The negative-delayed animation starts in 1 second.
setTimeout(function () {
negative.classList.add("move");
}, 1000);
// The positive-delayed animation starts in 200 ms.
setTimeout(function () {
positive.classList.add("move");
}, 200);
</script>
</body>
</html>

Просмотреть файл

@ -1,62 +1,62 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {formatStopwatchTime} = require("devtools/client/animationinspector/utils");
const TEST_DATA = [{
desc: "Formatting 0",
time: 0,
expected: "00:00.000"
}, {
desc: "Formatting null",
time: null,
expected: "00:00.000"
}, {
desc: "Formatting undefined",
time: undefined,
expected: "00:00.000"
}, {
desc: "Formatting a small number of ms",
time: 13,
expected: "00:00.013"
}, {
desc: "Formatting a slightly larger number of ms",
time: 500,
expected: "00:00.500"
}, {
desc: "Formatting 1 second",
time: 1000,
expected: "00:01.000"
}, {
desc: "Formatting a number of seconds",
time: 1532,
expected: "00:01.532"
}, {
desc: "Formatting a big number of seconds",
time: 58450,
expected: "00:58.450"
}, {
desc: "Formatting 1 minute",
time: 60000,
expected: "01:00.000"
}, {
desc: "Formatting a number of minutes",
time: 263567,
expected: "04:23.567"
}, {
desc: "Formatting a large number of minutes",
time: 1000 * 60 * 60 * 3,
expected: "180:00.000"
}];
function run_test() {
for (let {desc, time, expected} of TEST_DATA) {
equal(formatStopwatchTime(time), expected, desc);
}
}
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {formatStopwatchTime} = require("devtools/client/animationinspector/utils");
const TEST_DATA = [{
desc: "Formatting 0",
time: 0,
expected: "00:00.000"
}, {
desc: "Formatting null",
time: null,
expected: "00:00.000"
}, {
desc: "Formatting undefined",
time: undefined,
expected: "00:00.000"
}, {
desc: "Formatting a small number of ms",
time: 13,
expected: "00:00.013"
}, {
desc: "Formatting a slightly larger number of ms",
time: 500,
expected: "00:00.500"
}, {
desc: "Formatting 1 second",
time: 1000,
expected: "00:01.000"
}, {
desc: "Formatting a number of seconds",
time: 1532,
expected: "00:01.532"
}, {
desc: "Formatting a big number of seconds",
time: 58450,
expected: "00:58.450"
}, {
desc: "Formatting 1 minute",
time: 60000,
expected: "01:00.000"
}, {
desc: "Formatting a number of minutes",
time: 263567,
expected: "04:23.567"
}, {
desc: "Formatting a large number of minutes",
time: 1000 * 60 * 60 * 3,
expected: "180:00.000"
}];
function run_test() {
for (let {desc, time, expected} of TEST_DATA) {
equal(formatStopwatchTime(time), expected, desc);
}
}

Просмотреть файл

@ -1,27 +1,27 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {getCssPropertyName} = require("devtools/client/animationinspector/components/animation-details");
const TEST_DATA = [{
jsName: "alllowercase",
cssName: "alllowercase"
}, {
jsName: "borderWidth",
cssName: "border-width"
}, {
jsName: "borderTopRightRadius",
cssName: "border-top-right-radius"
}];
function run_test() {
for (let {jsName, cssName} of TEST_DATA) {
equal(getCssPropertyName(jsName), cssName);
}
}
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
var Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {getCssPropertyName} = require("devtools/client/animationinspector/components/animation-details");
const TEST_DATA = [{
jsName: "alllowercase",
cssName: "alllowercase"
}, {
jsName: "borderWidth",
cssName: "border-width"
}, {
jsName: "borderTopRightRadius",
cssName: "border-top-right-radius"
}];
function run_test() {
for (let {jsName, cssName} of TEST_DATA) {
equal(getCssPropertyName(jsName), cssName);
}
}

Просмотреть файл

@ -1,54 +1,54 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {TimeScale} = require("devtools/client/animationinspector/utils");
const TEST_ENDDELAY_X = [{
desc: "Testing positive-endDelay animations",
animations: [{
previousStartTime: 0,
duration: 500,
playbackRate: 1,
iterationCount: 3,
delay: 500,
endDelay: 500
}],
expectedEndDelayX: 80
}, {
desc: "Testing negative-endDelay animations",
animations: [{
previousStartTime: 0,
duration: 500,
playbackRate: 1,
iterationCount: 9,
delay: 500,
endDelay: -500
}],
expectedEndDelayX: 90
}];
function run_test() {
do_print("Test calculating endDelayX");
// Be independent of possible prior tests
TimeScale.reset();
for (let {desc, animations, expectedEndDelayX} of TEST_ENDDELAY_X) {
do_print(`Adding animations: ${desc}`);
for (let state of animations) {
TimeScale.addAnimation(state);
let {endDelayX} = TimeScale.getAnimationDimensions({state});
equal(endDelayX, expectedEndDelayX);
TimeScale.reset();
}
}
}
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const Cu = Components.utils;
const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
const {TimeScale} = require("devtools/client/animationinspector/utils");
const TEST_ENDDELAY_X = [{
desc: "Testing positive-endDelay animations",
animations: [{
previousStartTime: 0,
duration: 500,
playbackRate: 1,
iterationCount: 3,
delay: 500,
endDelay: 500
}],
expectedEndDelayX: 80
}, {
desc: "Testing negative-endDelay animations",
animations: [{
previousStartTime: 0,
duration: 500,
playbackRate: 1,
iterationCount: 9,
delay: 500,
endDelay: -500
}],
expectedEndDelayX: 90
}];
function run_test() {
do_print("Test calculating endDelayX");
// Be independent of possible prior tests
TimeScale.reset();
for (let {desc, animations, expectedEndDelayX} of TEST_ENDDELAY_X) {
do_print(`Adding animations: ${desc}`);
for (let state of animations) {
TimeScale.addAnimation(state);
let {endDelayX} = TimeScale.getAnimationDimensions({state});
equal(endDelayX, expectedEndDelayX);
TimeScale.reset();
}
}
}

Просмотреть файл

@ -1,49 +1,49 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that nodes don't start dragging before the mouse has moved by at least
// the minimum vertical distance defined in markup-view.js by
// DRAG_DROP_MIN_INITIAL_DISTANCE.
const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
const TEST_NODE = "#test";
// Keep this in sync with DRAG_DROP_MIN_INITIAL_DISTANCE in markup-view.js
const MIN_DISTANCE = 10;
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Drag the test node by half of the minimum distance");
yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE / 2);
yield checkIsDragging(inspector, TEST_NODE, false);
info("Drag the test node by exactly the minimum distance");
yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE);
yield checkIsDragging(inspector, TEST_NODE, true);
inspector.markup.cancelDragging();
info("Drag the test node by more than the minimum distance");
yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE * 2);
yield checkIsDragging(inspector, TEST_NODE, true);
inspector.markup.cancelDragging();
info("Drag the test node by minus the minimum distance");
yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE * -1);
yield checkIsDragging(inspector, TEST_NODE, true);
inspector.markup.cancelDragging();
});
function* checkIsDragging(inspector, selector, isDragging) {
let container = yield getContainerForSelector(selector, inspector);
if (isDragging) {
ok(container.isDragging, "The container is being dragged");
ok(inspector.markup.isDragging, "And the markup-view knows it");
} else {
ok(!container.isDragging, "The container hasn't been marked as dragging");
ok(!inspector.markup.isDragging, "And the markup-view either");
}
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that nodes don't start dragging before the mouse has moved by at least
// the minimum vertical distance defined in markup-view.js by
// DRAG_DROP_MIN_INITIAL_DISTANCE.
const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
const TEST_NODE = "#test";
// Keep this in sync with DRAG_DROP_MIN_INITIAL_DISTANCE in markup-view.js
const MIN_DISTANCE = 10;
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Drag the test node by half of the minimum distance");
yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE / 2);
yield checkIsDragging(inspector, TEST_NODE, false);
info("Drag the test node by exactly the minimum distance");
yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE);
yield checkIsDragging(inspector, TEST_NODE, true);
inspector.markup.cancelDragging();
info("Drag the test node by more than the minimum distance");
yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE * 2);
yield checkIsDragging(inspector, TEST_NODE, true);
inspector.markup.cancelDragging();
info("Drag the test node by minus the minimum distance");
yield simulateNodeDrag(inspector, TEST_NODE, 0, MIN_DISTANCE * -1);
yield checkIsDragging(inspector, TEST_NODE, true);
inspector.markup.cancelDragging();
});
function* checkIsDragging(inspector, selector, isDragging) {
let container = yield getContainerForSelector(selector, inspector);
if (isDragging) {
ok(container.isDragging, "The container is being dragged");
ok(inspector.markup.isDragging, "And the markup-view knows it");
} else {
ok(!container.isDragging, "The container hasn't been marked as dragging");
ok(!inspector.markup.isDragging, "And the markup-view either");
}
}

Просмотреть файл

@ -1,63 +1,63 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test which nodes are consider draggable by the markup-view.
const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
// Test cases should be objects with the following properties:
// - node {String|Function} A CSS selector that uniquely identifies the node to
// be tested. Or a generator function called in a Task that should return the
// corresponding MarkupContainer object to be tested.
// - draggable {Boolean} Whether or not the node should be draggable.
const TEST_DATA = [
{ node: "head", draggable: false },
{ node: "body", draggable: false },
{ node: "html", draggable: false },
{ node: "style", draggable: true },
{ node: "a", draggable: true },
{ node: "p", draggable: true },
{ node: "input", draggable: true },
{ node: "div", draggable: true },
{
node: function* (inspector) {
let parentFront = yield getNodeFront("#before", inspector);
let {nodes} = yield inspector.walker.children(parentFront);
// Getting the comment node.
return getContainerForNodeFront(nodes[1], inspector);
},
draggable: true
},
{
node: function* (inspector) {
let parentFront = yield getNodeFront("#test", inspector);
let {nodes} = yield inspector.walker.children(parentFront);
// Getting the ::before pseudo element.
return getContainerForNodeFront(nodes[0], inspector);
},
draggable: false
}
];
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
yield inspector.markup.expandAll();
for (let {node, draggable} of TEST_DATA) {
let container;
let name;
if (typeof node === "string") {
container = yield getContainerForSelector(node, inspector);
name = node;
} else {
container = yield node(inspector);
name = container.toString();
}
let status = draggable ? "draggable" : "not draggable";
info(`Testing ${name}, expecting it to be ${status}`);
is(container.isDraggable(), draggable, `The node is ${status}`);
}
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test which nodes are consider draggable by the markup-view.
const TEST_URL = URL_ROOT + "doc_markup_dragdrop.html";
// Test cases should be objects with the following properties:
// - node {String|Function} A CSS selector that uniquely identifies the node to
// be tested. Or a generator function called in a Task that should return the
// corresponding MarkupContainer object to be tested.
// - draggable {Boolean} Whether or not the node should be draggable.
const TEST_DATA = [
{ node: "head", draggable: false },
{ node: "body", draggable: false },
{ node: "html", draggable: false },
{ node: "style", draggable: true },
{ node: "a", draggable: true },
{ node: "p", draggable: true },
{ node: "input", draggable: true },
{ node: "div", draggable: true },
{
node: function* (inspector) {
let parentFront = yield getNodeFront("#before", inspector);
let {nodes} = yield inspector.walker.children(parentFront);
// Getting the comment node.
return getContainerForNodeFront(nodes[1], inspector);
},
draggable: true
},
{
node: function* (inspector) {
let parentFront = yield getNodeFront("#test", inspector);
let {nodes} = yield inspector.walker.children(parentFront);
// Getting the ::before pseudo element.
return getContainerForNodeFront(nodes[0], inspector);
},
draggable: false
}
];
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
yield inspector.markup.expandAll();
for (let {node, draggable} of TEST_DATA) {
let container;
let name;
if (typeof node === "string") {
container = yield getContainerForSelector(node, inspector);
name = node;
} else {
container = yield node(inspector);
name = container.toString();
}
let status = draggable ? "draggable" : "not draggable";
info(`Testing ${name}, expecting it to be ${status}`);
is(container.isDraggable(), draggable, `The node is ${status}`);
}
});

Просмотреть файл

@ -1,63 +1,63 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that attributes can be deleted from the markup-view with the delete key
// when they are focused.
const HTML = '<div id="id" class="class" data-id="id"></div>';
const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
// List of all the test cases. Each item is an object with the following props:
// - selector: the css selector of the node that should be selected
// - attribute: the name of the attribute that should be focused. Do not
// specify an attribute that would make it impossible to find the node using
// selector.
// Note that after each test case, undo is called.
const TEST_DATA = [{
selector: "#id",
attribute: "class"
}, {
selector: "#id",
attribute: "data-id"
}];
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
let {walker} = inspector;
for (let {selector, attribute} of TEST_DATA) {
info("Get the container for node " + selector);
let {editor} = yield getContainerForSelector(selector, inspector);
info("Focus attribute " + attribute);
let attr = editor.attrElements.get(attribute).querySelector(".editable");
attr.focus();
info("Delete the attribute by pressing delete");
let mutated = inspector.once("markupmutation");
EventUtils.sendKey("delete", inspector.panelWin);
yield mutated;
info("Check that the node is still here");
let node = yield walker.querySelector(walker.rootNode, selector);
ok(node, "The node hasn't been deleted");
info("Check that the attribute has been deleted");
node = yield walker.querySelector(walker.rootNode,
selector + "[" + attribute + "]");
ok(!node, "The attribute does not exist anymore in the DOM");
ok(!editor.attrElements.get(attribute),
"The attribute has been removed from the container");
info("Undo the change");
yield undoChange(inspector);
node = yield walker.querySelector(walker.rootNode,
selector + "[" + attribute + "]");
ok(node, "The attribute is back in the DOM");
ok(editor.attrElements.get(attribute),
"The attribute is back on the container");
}
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that attributes can be deleted from the markup-view with the delete key
// when they are focused.
const HTML = '<div id="id" class="class" data-id="id"></div>';
const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
// List of all the test cases. Each item is an object with the following props:
// - selector: the css selector of the node that should be selected
// - attribute: the name of the attribute that should be focused. Do not
// specify an attribute that would make it impossible to find the node using
// selector.
// Note that after each test case, undo is called.
const TEST_DATA = [{
selector: "#id",
attribute: "class"
}, {
selector: "#id",
attribute: "data-id"
}];
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
let {walker} = inspector;
for (let {selector, attribute} of TEST_DATA) {
info("Get the container for node " + selector);
let {editor} = yield getContainerForSelector(selector, inspector);
info("Focus attribute " + attribute);
let attr = editor.attrElements.get(attribute).querySelector(".editable");
attr.focus();
info("Delete the attribute by pressing delete");
let mutated = inspector.once("markupmutation");
EventUtils.sendKey("delete", inspector.panelWin);
yield mutated;
info("Check that the node is still here");
let node = yield walker.querySelector(walker.rootNode, selector);
ok(node, "The node hasn't been deleted");
info("Check that the attribute has been deleted");
node = yield walker.querySelector(walker.rootNode,
selector + "[" + attribute + "]");
ok(!node, "The attribute does not exist anymore in the DOM");
ok(!editor.attrElements.get(attribute),
"The attribute has been removed from the container");
info("Undo the change");
yield undoChange(inspector);
node = yield walker.querySelector(walker.rootNode,
selector + "[" + attribute + "]");
ok(node, "The attribute is back in the DOM");
ok(editor.attrElements.get(attribute),
"The attribute is back on the container");
}
});

Просмотреть файл

@ -1,87 +1,87 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the keyboard shortcut "S" used to scroll to the selected node.
const HTML =
`<div style="width: 300px; height: 3000px; position:relative;">
<div id="scroll-top"
style="height: 50px; top: 0; position:absolute;">
TOP</div>
<div id="scroll-bottom"
style="height: 50px; bottom: 0; position:absolute;">
BOTTOM</div>
</div>`;
const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
add_task(function* () {
let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
info("Make sure the markup frame has the focus");
inspector.markup._frame.focus();
info("Before test starts, #scroll-top is visible, #scroll-bottom is hidden");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
info("Select the #scroll-bottom node");
yield selectNode("#scroll-bottom", inspector);
info("Press S to scroll to the bottom node");
let waitForScroll = testActor.waitForEventOnNode("scroll");
yield EventUtils.synthesizeKey("S", {}, inspector.panelWin);
yield waitForScroll;
ok(true, "Scroll event received");
info("#scroll-top should be scrolled out, #scroll-bottom should be visible");
yield checkElementIsInViewport("#scroll-top", false, testActor);
yield checkElementIsInViewport("#scroll-bottom", true, testActor);
info("Select the #scroll-top node");
yield selectNode("#scroll-top", inspector);
info("Press S to scroll to the top node");
waitForScroll = testActor.waitForEventOnNode("scroll");
yield EventUtils.synthesizeKey("S", {}, inspector.panelWin);
yield waitForScroll;
ok(true, "Scroll event received");
info("#scroll-top should be visible, #scroll-bottom should be scrolled out");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
info("Select #scroll-bottom node");
yield selectNode("#scroll-bottom", inspector);
info("Press shift + S, nothing should happen due to the modifier");
yield EventUtils.synthesizeKey("S", {shiftKey: true}, inspector.panelWin);
info("Same state, #scroll-top is visible, #scroll-bottom is scrolled out");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
});
/**
* Verify that the element matching the provided selector is either in or out
* of the viewport, depending on the provided "expected" argument.
* Returns a promise that will resolve when the test has been performed.
*
* @param {String} selector
* css selector for the element to test
* @param {Boolean} expected
* true if the element is expected to be in the viewport, false otherwise
* @param {TestActor} testActor
* current test actor
* @return {Promise} promise
*/
function* checkElementIsInViewport(selector, expected, testActor) {
let isInViewport = yield testActor.eval(`
let node = content.document.querySelector("${selector}");
let rect = node.getBoundingClientRect();
rect.bottom >= 0 && rect.right >= 0 &&
rect.top <= content.innerHeight && rect.left <= content.innerWidth;
`);
is(isInViewport, expected,
selector + " in the viewport: expected to be " + expected);
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the keyboard shortcut "S" used to scroll to the selected node.
const HTML =
`<div style="width: 300px; height: 3000px; position:relative;">
<div id="scroll-top"
style="height: 50px; top: 0; position:absolute;">
TOP</div>
<div id="scroll-bottom"
style="height: 50px; bottom: 0; position:absolute;">
BOTTOM</div>
</div>`;
const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
add_task(function* () {
let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
info("Make sure the markup frame has the focus");
inspector.markup._frame.focus();
info("Before test starts, #scroll-top is visible, #scroll-bottom is hidden");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
info("Select the #scroll-bottom node");
yield selectNode("#scroll-bottom", inspector);
info("Press S to scroll to the bottom node");
let waitForScroll = testActor.waitForEventOnNode("scroll");
yield EventUtils.synthesizeKey("S", {}, inspector.panelWin);
yield waitForScroll;
ok(true, "Scroll event received");
info("#scroll-top should be scrolled out, #scroll-bottom should be visible");
yield checkElementIsInViewport("#scroll-top", false, testActor);
yield checkElementIsInViewport("#scroll-bottom", true, testActor);
info("Select the #scroll-top node");
yield selectNode("#scroll-top", inspector);
info("Press S to scroll to the top node");
waitForScroll = testActor.waitForEventOnNode("scroll");
yield EventUtils.synthesizeKey("S", {}, inspector.panelWin);
yield waitForScroll;
ok(true, "Scroll event received");
info("#scroll-top should be visible, #scroll-bottom should be scrolled out");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
info("Select #scroll-bottom node");
yield selectNode("#scroll-bottom", inspector);
info("Press shift + S, nothing should happen due to the modifier");
yield EventUtils.synthesizeKey("S", {shiftKey: true}, inspector.panelWin);
info("Same state, #scroll-top is visible, #scroll-bottom is scrolled out");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
});
/**
* Verify that the element matching the provided selector is either in or out
* of the viewport, depending on the provided "expected" argument.
* Returns a promise that will resolve when the test has been performed.
*
* @param {String} selector
* css selector for the element to test
* @param {Boolean} expected
* true if the element is expected to be in the viewport, false otherwise
* @param {TestActor} testActor
* current test actor
* @return {Promise} promise
*/
function* checkElementIsInViewport(selector, expected, testActor) {
let isInViewport = yield testActor.eval(`
let node = content.document.querySelector("${selector}");
let rect = node.getBoundingClientRect();
rect.bottom >= 0 && rect.right >= 0 &&
rect.top <= content.innerHeight && rect.left <= content.innerWidth;
`);
is(isInViewport, expected,
selector + " in the viewport: expected to be " + expected);
}

Просмотреть файл

@ -1,66 +1,66 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that whitespace text nodes do show up in the markup-view when needed.
const TEST_URL = URL_ROOT + "doc_markup_whitespace.html";
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
let {markup} = inspector;
yield markup.expandAll();
info("Verify the number of child nodes and child elements in body");
// Body has 5 element children, but there are 6 text nodes in there too, they come from
// the HTML file formatting (spaces and carriage returns).
let {numNodes, numChildren} = yield testActor.getNodeInfo("body");
is(numNodes, 11, "The body node has 11 child nodes (includes text nodes)");
is(numChildren, 5, "The body node has 5 child elements (only element nodes)");
// In body, there are only block-level elements, so whitespace text nodes do not have
// layout, so they should be skipped in the markup-view.
info("Check that the body's whitespace text node children aren't shown");
let bodyContainer = markup.getContainer(inspector.selection.nodeFront);
let childContainers = bodyContainer.getChildContainers();
is(childContainers.length, 5,
"Only the element nodes are shown in the markup view");
// div#inline has 3 element children, but there are 4 text nodes in there too, like in
// body, they come from spaces and carriage returns in the HTML file.
info("Verify the number of child nodes and child elements in div#inline");
({numNodes, numChildren} = yield testActor.getNodeInfo("#inline"));
is(numNodes, 7, "The div#inline node has 7 child nodes (includes text nodes)");
is(numChildren, 3, "The div#inline node has 3 child elements (only element nodes)");
// Within the inline formatting context in div#inline, the whitespace text nodes between
// the images have layout, so they should appear in the markup-view.
info("Check that the div#inline's whitespace text node children are shown");
yield selectNode("#inline", inspector);
let divContainer = markup.getContainer(inspector.selection.nodeFront);
childContainers = divContainer.getChildContainers();
is(childContainers.length, 5,
"Both the element nodes and some text nodes are shown in the markup view");
// div#pre has 2 element children, but there are 3 text nodes in there too, like in
// div#inline, they come from spaces and carriage returns in the HTML file.
info("Verify the number of child nodes and child elements in div#pre");
({numNodes, numChildren} = yield testActor.getNodeInfo("#pre"));
is(numNodes, 5, "The div#pre node has 5 child nodes (includes text nodes)");
is(numChildren, 2, "The div#pre node has 2 child elements (only element nodes)");
// Within the inline formatting context in div#pre, the whitespace text nodes between
// the images have layout, so they should appear in the markup-view, but since
// white-space is set to pre, then the whitespace text nodes before and after the first
// and last image should also appear.
info("Check that the div#pre's whitespace text node children are shown");
yield selectNode("#pre", inspector);
divContainer = markup.getContainer(inspector.selection.nodeFront);
childContainers = divContainer.getChildContainers();
is(childContainers.length, 5,
"Both the element nodes and all text nodes are shown in the markup view");
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that whitespace text nodes do show up in the markup-view when needed.
const TEST_URL = URL_ROOT + "doc_markup_whitespace.html";
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(TEST_URL);
let {markup} = inspector;
yield markup.expandAll();
info("Verify the number of child nodes and child elements in body");
// Body has 5 element children, but there are 6 text nodes in there too, they come from
// the HTML file formatting (spaces and carriage returns).
let {numNodes, numChildren} = yield testActor.getNodeInfo("body");
is(numNodes, 11, "The body node has 11 child nodes (includes text nodes)");
is(numChildren, 5, "The body node has 5 child elements (only element nodes)");
// In body, there are only block-level elements, so whitespace text nodes do not have
// layout, so they should be skipped in the markup-view.
info("Check that the body's whitespace text node children aren't shown");
let bodyContainer = markup.getContainer(inspector.selection.nodeFront);
let childContainers = bodyContainer.getChildContainers();
is(childContainers.length, 5,
"Only the element nodes are shown in the markup view");
// div#inline has 3 element children, but there are 4 text nodes in there too, like in
// body, they come from spaces and carriage returns in the HTML file.
info("Verify the number of child nodes and child elements in div#inline");
({numNodes, numChildren} = yield testActor.getNodeInfo("#inline"));
is(numNodes, 7, "The div#inline node has 7 child nodes (includes text nodes)");
is(numChildren, 3, "The div#inline node has 3 child elements (only element nodes)");
// Within the inline formatting context in div#inline, the whitespace text nodes between
// the images have layout, so they should appear in the markup-view.
info("Check that the div#inline's whitespace text node children are shown");
yield selectNode("#inline", inspector);
let divContainer = markup.getContainer(inspector.selection.nodeFront);
childContainers = divContainer.getChildContainers();
is(childContainers.length, 5,
"Both the element nodes and some text nodes are shown in the markup view");
// div#pre has 2 element children, but there are 3 text nodes in there too, like in
// div#inline, they come from spaces and carriage returns in the HTML file.
info("Verify the number of child nodes and child elements in div#pre");
({numNodes, numChildren} = yield testActor.getNodeInfo("#pre"));
is(numNodes, 5, "The div#pre node has 5 child nodes (includes text nodes)");
is(numChildren, 2, "The div#pre node has 2 child elements (only element nodes)");
// Within the inline formatting context in div#pre, the whitespace text nodes between
// the images have layout, so they should appear in the markup-view, but since
// white-space is set to pre, then the whitespace text nodes before and after the first
// and last image should also appear.
info("Check that the div#pre's whitespace text node children are shown");
yield selectNode("#pre", inspector);
divContainer = markup.getContainer(inspector.selection.nodeFront);
childContainers = divContainer.getChildContainers();
is(childContainers.length, 5,
"Both the element nodes and all text nodes are shown in the markup view");
});

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -1,25 +1,25 @@
<!DOCTYPE html>
<html class="html">
<head class="head">
<meta charset=utf-8 />
<title>Image and Canvas markup-view test</title>
</head>
<body>
<img class="local" src="chrome://branding/content/about-logo.png" />
<img class="data" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAADI5JREFUeNrsWwuQFNUVPf1m5z87szv7HWSWj8CigBFMEFZKiQsB1PgJwUAZg1HBpIQsKmokEhNjWUnFVPnDWBT+KolJYbRMoqUVq0yCClpqiX8sCchPWFwVlt2db7+X93pez7zu6Vn2NxsVWh8987p7pu+9555z7+tZjTGGY3kjOMa34w447oBjfKsY7i/UNM3Y8eFSAkD50Plgw03K5P9gvGv7U5ieeR3PszeREiPNX3/0DL4hjslzhm8THh+OITfXk3dhiv4GDtGPVzCaeJmPLYzuu5qJuWfuw2QTlcN1X9pwQU7LhdZ/ZAseD45cOh9hHvDkc/yAF/DNhdb5Mrr3PvBMaAYW8fMSIi2G497IMEK/YutGtAYr6+ej+nxu/NN8Ks3N7AR6HgcLz0Eg1Ljg1UcxZzi5qewIkMYLRweTr2Kzp+nmyXAd5pS3XQDd+N/4h4zgu9FI7brlXf90nMEnuwQxlvv+hosE3TuexmWeysmT4W+WxkMaLzf9Y8ATgjcUn7T9H1gqrpFq8eV1gMn6t16NhngjfoX6q4DUP032Rd4LJgpSLwJ1yzFqBG69eRkah0MVyo0Acfe+yy9AG4nMiYCkeM53KKFXncBLAXqEm+wCqZwaueq7WCmuLTcKSJmj737ol2hurA9eq9VdyiO8yWa3NNyog+SB5CZodSsQq/dfu34tJpYbBaTMzvVddDZu16q5smXf4G8zEvqm4cyaAmJPuTJk3oJWdS4WzcVtfMZbThSQckb/pYfRGgo3zNOqZnEHbJPGK4abaDCQIIsT8V/qTaBqHkLh6LzXH8XZQhbLhYKyyCC/WeHYcNdmvOgfe8skzbWL270/T3wf7tSx/lGCbTu8xlzzmCSWLc5iwmgikcCHi3Mga0Ry913vBFvQwg90l6M4ImWKfsWOp7DSWxmfpPlCFuPFfsNfKrCnPYpQKIRgqBK7D0SxYaNHwkEiJMtl0ReDp3Lc5D3PGoTo/sKngCl7a5chFqvBatKwjBd7WwqIlzB/78NcoUcp5VSgGxm+7b8eqQRGnHMO634epO4S1EZww09/iFg5UmGoESDuznP1xVhTUX1WWHPzjpd25wyH0hRxI3LGM75nxmuNEEUVpAN0XgxmPoKralakbQnWlIMQyVBD/w+3orkq4lvualjKyWwzt4MaxqspQHVhPOWG64bxYuhZXSFGWhipbSDVragOu5Y9eAsmDDUKyBA703vemVhHoueD6e9wAzJK1WfmN0Umk5GGM4kEMZcuIECqgjm0nldAqmbjwtm4VxZH5AvlADP6mx9Eqy9Q0+KqW8Ch+47FaMMYmnNGfY1iPMshoC6qFxme4wQ+0p+ARE6H3+9veWEDWgUhDhUKyFARn4jM5BNxT0XsMg7bfymGK1ov3wtjDfhL4w0HVGUVBEjDaaE+QNdrcNWch1PG4W6xrjBUXECGivg++Cva3JUT4iQUz3V2RsSVaKLwOuDT89A3HdBQoxhNC+fnVm74ual2EG893P6G+PuP4SfiO4cCBWQooL9qCWKNXPbcI37Aa/lnlZxXRt4RFONGwSDCPAHqOuqjWct1QiEMw5mChM5X4K47FyNqcd3aK9AwFH0CGYLoe1ctxk2eWi57rg5JfGp9rzC6ggCdFlAgHBDw5Yxlcg6G8SyHCjMlsgmDD9zhSeHlF+JnAgWDTQUy2NxfdwOao1UVV3pi3+bE97YSbWpLAbn6zefHNQkp1PMpIBwwvslKgIYTKM2nEpNzrGcH3FXTEal0L38kJ4uDQgEZbO4vnI173LXf5NHZaiUxtaCxyZuo/rK6LpUg54yg3zTWRAArvDcRIPZ6BqzrQ1REpmL+DNw32OKIDCb3X1qPVn8wNNMT4w2bvs+q4bAZrqBh2skaL3yyhhIIZ4i6oHkUK0RckcB8GigEyRIH4A6Mgc8fatl0/+BkkQxC9gIT4ljna1rIZW9rEdNbjJcNjsnoYj7LHWCUwpITzEgzRQKZ3XAFHbTzA3hrz8TEUUZxFBhoKpABQt/97p+w0hMZG68I8R6FtlsJT3FELndZntjM+VMnylKYq8GJI3UZaRMpquGSGFVOEfv0YZBMNzz+uvjbfzS6xQERIhlI9FcvQWNdFVb7x1zCb+QNK8vb9NsiifmI5hBgVoOCBC1sb0ab5RomqENxLO3eA1/0NDRU47q2RQNbRCUDIb7lF2CNL3ZGxEV4n08TVvZWYG4pZyV0zUdS45tyCBByOHWiyvZmxFXDCyRo1ge5+Sy0TA+8lWMiP/6O0S32exGV9Jf4fr8azdUR3zL/CZz4MtvzdX5uOYs6NDOmpkuj5Huh+7qUQSYl0ThHzw0YQzcGo6bhzEqoYq5rN3yRiYiG3Vfe2Ybm/qKA9NNZ3nNm4F7/yDkg9AN+U1mHiBcXP8zuDN76jj8hg1QyiWQigalj02BJPhK8I0zxijAjhp5zhlpLUDvS+BCy2HMAvvB4XDgL9/SXC0g/ou/5+6/xLX8w0uJrOIkXfPvyhY0F6gr7M8H0KWFYikcqAXakB+xwD9CdREBLoau7Gz3cAdSIdLFxFtJTCqRChSjnutvhDcREtzjz2Tswtz+yeNRFUeXZXtWux7C1fuoVcbd3J//ipDX3uZZDLGrwweS+UBLL5TDliVBnF8P7H+XI8aRRGsIBJg/Zlslt1+W+D1JWoSyi+kD9jfhs78t7mhZhSl+fLfY1Bdyv3I8V/qpY3B1McgN7ZFT5/vNO0I5DPLLdPBIJA8qc4h2I0QplYfDpJwHT+aj0246r5S8rToG8OjCle8wk4OLvvYGa+Ovr84uo2qBSwJS9G5egoZFLTfiEqWDtbwGfHgKOdPHcS+ai7XDzMPW/FJRLGGcxnBbK4YJC2K+h+T6Bdu5CqHqCWERd3bawb7JI+iJ735+LNaHaprBLLHBm08U3XxShEsdt+f3eTh3v7aC95Dct4RCWL5OZWh/oXBZThxAIxyOXLzBk8aiEWJID8rK3CpPOmeHaGpvCS+7EHv5FujVHUSJPLXvIFeHcNc+9xrB2gws9KZdxuLFax/WLM5gzzSm/lTXF/OdAcapyvjxPqxqHjr2v4ckX2bS2dRBrc5lSdpKjEJ9/9tdwX2WMd53ZQ2IVo3RES+UwVSpCPvYepNx4gmTGDUKIMQ4eduPnD7mx9xOn/KZKOlFbStjONxHTtR+BYAPmnoZ1Zp8wkBRwP/EL3u0F/C2hGl7vpz7vW37T3vP7if8wroKuoh8ribknX9BK5rcF+mo1qKaKyRPJTgTDjbzY8szcuLb3bpH00u35T47j7prRpwDJTxzyG0dHgxPp5bPG8VdkpfPbUg3SgoOo2mwVukb98D5EqpswZTTulCggTk4gpYhv0++wIhCJxr0+Hq1sondis0SE2oxQe3qWXwWyO4DSQg9gJ8Iiw1VFcGqXxet0N9xE4ygIxv/9W6wo9WyROEX/R+eiobYSq2vHTOR631Eiv2lRfh9dvxkumkXh92Qsx8XrAJ+7YGbWuhxOi/U+31NQmzyqNYG8N/3wfo6CRtRHcN01FzkvojohwLu0VVvDa56IS/xcj2b7nN+O+m0jqpE1wMPXZxAN9iCVThtDvH7gmiRGRpU8Lspv1Uhq4wIVdQoyuGSLNYPKUCS8+CzNURbzMmjK3i8u0U793lmuV0ef9nWQ5MGC/DiUqEUSaCtXna9RJEspZS1lrXINK/pcq+SpT50t98QKMq1FRmDfx3vxty102k0PM4ssEnvuz5+G26Ij4yDpz6z9fV8bkyIkqBFkhej0Ib+ZQ34XJK9AfozaiimqIoX3Jp3tiISrcfYpuN2+iFph/02P36PNC9fVcCnp6H9jYouKyfaWufz5Tp9tVxcUniw7IohZv4dZz81/ns67z3AYPrc2n0+Ix2q8k0PWjgBy88XaibnfK9A+5LdDY2Ivhy36fbT8Zv3Lb1U1qLqUxorXEEXIs0mjjrtxoTZWtdvigNs2sgPiujTv6DIZLld6b/V5742JZV3fUsUVFy5gdsNtKWFzUCEVbNepD1MkSMVbsb6SZm7jI3/zODtQKgUMsOw8wDZ63t5xcV1TnaEAxoc6wrqY+Fj+N4DsqOnhOIdicrQSm1MPYCPlIqHn5bbHg8/bj2D3QfZnCX3mpAICDZV8jH5kpbZqTD0W+DxaA74CWzLN2nd14OlL72J38Lf7+TjC7dadZFDoZJQPrtaIKL/G0L6ktptPZVJ8fMqHYPZOKYPMyQGadIJfDvdXwAFiZOTvDBPydf5vk4rWA+RfdhBlaF/yDDBRoMu9pfnSjv/p7DG+HXfAcQcc49v/BBgAcFAO4DmB2GQAAAAASUVORK5CYII=" />
<img class="remote" src="http://example.com/browser/devtools/client/inspector/markup/test/doc_markup_tooltip.png" />
<canvas class="canvas" width="600" height="600"></canvas>
<script type="text/javascript">
"use strict";
let context = document.querySelector(".canvas").getContext("2d");
context.beginPath();
context.moveTo(300, 0);
context.lineTo(600, 600);
context.lineTo(0, 600);
context.closePath();
context.fillStyle = "#ffc821";
context.fill();
</script>
</body>
</html>
<!DOCTYPE html>
<html class="html">
<head class="head">
<meta charset=utf-8 />
<title>Image and Canvas markup-view test</title>
</head>
<body>
<img class="local" src="chrome://branding/content/about-logo.png" />
<img class="data" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAADI5JREFUeNrsWwuQFNUVPf1m5z87szv7HWSWj8CigBFMEFZKiQsB1PgJwUAZg1HBpIQsKmokEhNjWUnFVPnDWBT+KolJYbRMoqUVq0yCClpqiX8sCchPWFwVlt2db7+X93pez7zu6Vn2NxsVWh8987p7pu+9555z7+tZjTGGY3kjOMa34w447oBjfKsY7i/UNM3Y8eFSAkD50Plgw03K5P9gvGv7U5ieeR3PszeREiPNX3/0DL4hjslzhm8THh+OITfXk3dhiv4GDtGPVzCaeJmPLYzuu5qJuWfuw2QTlcN1X9pwQU7LhdZ/ZAseD45cOh9hHvDkc/yAF/DNhdb5Mrr3PvBMaAYW8fMSIi2G497IMEK/YutGtAYr6+ej+nxu/NN8Ks3N7AR6HgcLz0Eg1Ljg1UcxZzi5qewIkMYLRweTr2Kzp+nmyXAd5pS3XQDd+N/4h4zgu9FI7brlXf90nMEnuwQxlvv+hosE3TuexmWeysmT4W+WxkMaLzf9Y8ATgjcUn7T9H1gqrpFq8eV1gMn6t16NhngjfoX6q4DUP032Rd4LJgpSLwJ1yzFqBG69eRkah0MVyo0Acfe+yy9AG4nMiYCkeM53KKFXncBLAXqEm+wCqZwaueq7WCmuLTcKSJmj737ol2hurA9eq9VdyiO8yWa3NNyog+SB5CZodSsQq/dfu34tJpYbBaTMzvVddDZu16q5smXf4G8zEvqm4cyaAmJPuTJk3oJWdS4WzcVtfMZbThSQckb/pYfRGgo3zNOqZnEHbJPGK4abaDCQIIsT8V/qTaBqHkLh6LzXH8XZQhbLhYKyyCC/WeHYcNdmvOgfe8skzbWL270/T3wf7tSx/lGCbTu8xlzzmCSWLc5iwmgikcCHi3Mga0Ry913vBFvQwg90l6M4ImWKfsWOp7DSWxmfpPlCFuPFfsNfKrCnPYpQKIRgqBK7D0SxYaNHwkEiJMtl0ReDp3Lc5D3PGoTo/sKngCl7a5chFqvBatKwjBd7WwqIlzB/78NcoUcp5VSgGxm+7b8eqQRGnHMO634epO4S1EZww09/iFg5UmGoESDuznP1xVhTUX1WWHPzjpd25wyH0hRxI3LGM75nxmuNEEUVpAN0XgxmPoKralakbQnWlIMQyVBD/w+3orkq4lvualjKyWwzt4MaxqspQHVhPOWG64bxYuhZXSFGWhipbSDVragOu5Y9eAsmDDUKyBA703vemVhHoueD6e9wAzJK1WfmN0Umk5GGM4kEMZcuIECqgjm0nldAqmbjwtm4VxZH5AvlADP6mx9Eqy9Q0+KqW8Ch+47FaMMYmnNGfY1iPMshoC6qFxme4wQ+0p+ARE6H3+9veWEDWgUhDhUKyFARn4jM5BNxT0XsMg7bfymGK1ov3wtjDfhL4w0HVGUVBEjDaaE+QNdrcNWch1PG4W6xrjBUXECGivg++Cva3JUT4iQUz3V2RsSVaKLwOuDT89A3HdBQoxhNC+fnVm74ual2EG893P6G+PuP4SfiO4cCBWQooL9qCWKNXPbcI37Aa/lnlZxXRt4RFONGwSDCPAHqOuqjWct1QiEMw5mChM5X4K47FyNqcd3aK9AwFH0CGYLoe1ctxk2eWi57rg5JfGp9rzC6ggCdFlAgHBDw5Yxlcg6G8SyHCjMlsgmDD9zhSeHlF+JnAgWDTQUy2NxfdwOao1UVV3pi3+bE97YSbWpLAbn6zefHNQkp1PMpIBwwvslKgIYTKM2nEpNzrGcH3FXTEal0L38kJ4uDQgEZbO4vnI173LXf5NHZaiUxtaCxyZuo/rK6LpUg54yg3zTWRAArvDcRIPZ6BqzrQ1REpmL+DNw32OKIDCb3X1qPVn8wNNMT4w2bvs+q4bAZrqBh2skaL3yyhhIIZ4i6oHkUK0RckcB8GigEyRIH4A6Mgc8fatl0/+BkkQxC9gIT4ljna1rIZW9rEdNbjJcNjsnoYj7LHWCUwpITzEgzRQKZ3XAFHbTzA3hrz8TEUUZxFBhoKpABQt/97p+w0hMZG68I8R6FtlsJT3FELndZntjM+VMnylKYq8GJI3UZaRMpquGSGFVOEfv0YZBMNzz+uvjbfzS6xQERIhlI9FcvQWNdFVb7x1zCb+QNK8vb9NsiifmI5hBgVoOCBC1sb0ab5RomqENxLO3eA1/0NDRU47q2RQNbRCUDIb7lF2CNL3ZGxEV4n08TVvZWYG4pZyV0zUdS45tyCBByOHWiyvZmxFXDCyRo1ge5+Sy0TA+8lWMiP/6O0S32exGV9Jf4fr8azdUR3zL/CZz4MtvzdX5uOYs6NDOmpkuj5Huh+7qUQSYl0ThHzw0YQzcGo6bhzEqoYq5rN3yRiYiG3Vfe2Ybm/qKA9NNZ3nNm4F7/yDkg9AN+U1mHiBcXP8zuDN76jj8hg1QyiWQigalj02BJPhK8I0zxijAjhp5zhlpLUDvS+BCy2HMAvvB4XDgL9/SXC0g/ou/5+6/xLX8w0uJrOIkXfPvyhY0F6gr7M8H0KWFYikcqAXakB+xwD9CdREBLoau7Gz3cAdSIdLFxFtJTCqRChSjnutvhDcREtzjz2Tswtz+yeNRFUeXZXtWux7C1fuoVcbd3J//ipDX3uZZDLGrwweS+UBLL5TDliVBnF8P7H+XI8aRRGsIBJg/Zlslt1+W+D1JWoSyi+kD9jfhs78t7mhZhSl+fLfY1Bdyv3I8V/qpY3B1McgN7ZFT5/vNO0I5DPLLdPBIJA8qc4h2I0QplYfDpJwHT+aj0246r5S8rToG8OjCle8wk4OLvvYGa+Ovr84uo2qBSwJS9G5egoZFLTfiEqWDtbwGfHgKOdPHcS+ai7XDzMPW/FJRLGGcxnBbK4YJC2K+h+T6Bdu5CqHqCWERd3bawb7JI+iJ735+LNaHaprBLLHBm08U3XxShEsdt+f3eTh3v7aC95Dct4RCWL5OZWh/oXBZThxAIxyOXLzBk8aiEWJID8rK3CpPOmeHaGpvCS+7EHv5FujVHUSJPLXvIFeHcNc+9xrB2gws9KZdxuLFax/WLM5gzzSm/lTXF/OdAcapyvjxPqxqHjr2v4ckX2bS2dRBrc5lSdpKjEJ9/9tdwX2WMd53ZQ2IVo3RES+UwVSpCPvYepNx4gmTGDUKIMQ4eduPnD7mx9xOn/KZKOlFbStjONxHTtR+BYAPmnoZ1Zp8wkBRwP/EL3u0F/C2hGl7vpz7vW37T3vP7if8wroKuoh8ribknX9BK5rcF+mo1qKaKyRPJTgTDjbzY8szcuLb3bpH00u35T47j7prRpwDJTxzyG0dHgxPp5bPG8VdkpfPbUg3SgoOo2mwVukb98D5EqpswZTTulCggTk4gpYhv0++wIhCJxr0+Hq1sondis0SE2oxQe3qWXwWyO4DSQg9gJ8Iiw1VFcGqXxet0N9xE4ygIxv/9W6wo9WyROEX/R+eiobYSq2vHTOR631Eiv2lRfh9dvxkumkXh92Qsx8XrAJ+7YGbWuhxOi/U+31NQmzyqNYG8N/3wfo6CRtRHcN01FzkvojohwLu0VVvDa56IS/xcj2b7nN+O+m0jqpE1wMPXZxAN9iCVThtDvH7gmiRGRpU8Lspv1Uhq4wIVdQoyuGSLNYPKUCS8+CzNURbzMmjK3i8u0U793lmuV0ef9nWQ5MGC/DiUqEUSaCtXna9RJEspZS1lrXINK/pcq+SpT50t98QKMq1FRmDfx3vxty102k0PM4ssEnvuz5+G26Ij4yDpz6z9fV8bkyIkqBFkhej0Ib+ZQ34XJK9AfozaiimqIoX3Jp3tiISrcfYpuN2+iFph/02P36PNC9fVcCnp6H9jYouKyfaWufz5Tp9tVxcUniw7IohZv4dZz81/ns67z3AYPrc2n0+Ix2q8k0PWjgBy88XaibnfK9A+5LdDY2Ivhy36fbT8Zv3Lb1U1qLqUxorXEEXIs0mjjrtxoTZWtdvigNs2sgPiujTv6DIZLld6b/V5742JZV3fUsUVFy5gdsNtKWFzUCEVbNepD1MkSMVbsb6SZm7jI3/zODtQKgUMsOw8wDZ63t5xcV1TnaEAxoc6wrqY+Fj+N4DsqOnhOIdicrQSm1MPYCPlIqHn5bbHg8/bj2D3QfZnCX3mpAICDZV8jH5kpbZqTD0W+DxaA74CWzLN2nd14OlL72J38Lf7+TjC7dadZFDoZJQPrtaIKL/G0L6ktptPZVJ8fMqHYPZOKYPMyQGadIJfDvdXwAFiZOTvDBPydf5vk4rWA+RfdhBlaF/yDDBRoMu9pfnSjv/p7DG+HXfAcQcc49v/BBgAcFAO4DmB2GQAAAAASUVORK5CYII=" />
<img class="remote" src="http://example.com/browser/devtools/client/inspector/markup/test/doc_markup_tooltip.png" />
<canvas class="canvas" width="600" height="600"></canvas>
<script type="text/javascript">
"use strict";
let context = document.querySelector(".canvas").getContext("2d");
context.beginPath();
context.moveTo(300, 0);
context.lineTo(600, 600);
context.lineTo(0, 600);
context.closePath();
context.fillStyle = "#ffc821";
context.fill();
</script>
</body>
</html>

Просмотреть файл

@ -1,30 +1,30 @@
/* 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/ */
"use strict";
// Tests adding a new rule and a new property in this rule.
add_task(function* () {
yield addTab("data:text/html;charset=utf-8,<div id='testid'>Styled Node</div>");
let {inspector, view} = yield openRuleView();
info("Selecting the test node");
yield selectNode("#testid", inspector);
info("Adding a new rule for this node and blurring the new selector field");
yield addNewRuleAndDismissEditor(inspector, view, "#testid", 1);
info("Adding a new property for this rule");
let ruleEditor = getRuleViewRuleEditor(view, 1);
let onRuleViewChanged = view.once("ruleview-changed");
ruleEditor.addProperty("font-weight", "bold", "", true);
yield onRuleViewChanged;
let textProps = ruleEditor.rule.textProps;
let prop = textProps[textProps.length - 1];
is(prop.name, "font-weight", "The last property name is font-weight");
is(prop.value, "bold", "The last property value is bold");
});
/* 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/ */
"use strict";
// Tests adding a new rule and a new property in this rule.
add_task(function* () {
yield addTab("data:text/html;charset=utf-8,<div id='testid'>Styled Node</div>");
let {inspector, view} = yield openRuleView();
info("Selecting the test node");
yield selectNode("#testid", inspector);
info("Adding a new rule for this node and blurring the new selector field");
yield addNewRuleAndDismissEditor(inspector, view, "#testid", 1);
info("Adding a new property for this rule");
let ruleEditor = getRuleViewRuleEditor(view, 1);
let onRuleViewChanged = view.once("ruleview-changed");
ruleEditor.addProperty("font-weight", "bold", "", true);
yield onRuleViewChanged;
let textProps = ruleEditor.rule.textProps;
let prop = textProps[textProps.length - 1];
is(prop.name, "font-weight", "The last property name is font-weight");
is(prop.value, "bold", "The last property value is bold");
});

Просмотреть файл

@ -1,42 +1,42 @@
/* 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/ */
"use strict";
// Tests the a new CSS rule can be added using the context menu.
const TEST_URI = '<div id="testid">Test Node</div>';
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#testid", inspector);
yield addNewRuleFromContextMenu(inspector, view);
yield testNewRule(view);
});
function* addNewRuleFromContextMenu(inspector, view) {
info("Waiting for context menu to be shown");
let allMenuItems = openStyleContextMenuAndGetAllItems(view, view.element);
let menuitemAddRule = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.addNewRule"));
ok(menuitemAddRule.visible, "Add rule is visible");
info("Adding the new rule and expecting a ruleview-changed event");
let onRuleViewChanged = view.once("ruleview-changed");
menuitemAddRule.click();
yield onRuleViewChanged;
}
function* testNewRule(view) {
let ruleEditor = getRuleViewRuleEditor(view, 1);
let editor = ruleEditor.selectorText.ownerDocument.activeElement;
is(editor.value, "#testid", "Selector editor value is as expected");
info("Escaping from the selector field the change");
EventUtils.synthesizeKey("VK_ESCAPE", {});
}
/* 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/ */
"use strict";
// Tests the a new CSS rule can be added using the context menu.
const TEST_URI = '<div id="testid">Test Node</div>';
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#testid", inspector);
yield addNewRuleFromContextMenu(inspector, view);
yield testNewRule(view);
});
function* addNewRuleFromContextMenu(inspector, view) {
info("Waiting for context menu to be shown");
let allMenuItems = openStyleContextMenuAndGetAllItems(view, view.element);
let menuitemAddRule = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.addNewRule"));
ok(menuitemAddRule.visible, "Add rule is visible");
info("Adding the new rule and expecting a ruleview-changed event");
let onRuleViewChanged = view.once("ruleview-changed");
menuitemAddRule.click();
yield onRuleViewChanged;
}
function* testNewRule(view) {
let ruleEditor = getRuleViewRuleEditor(view, 1);
let editor = ruleEditor.selectorText.ownerDocument.activeElement;
is(editor.value, "#testid", "Selector editor value is as expected");
info("Escaping from the selector field the change");
EventUtils.synthesizeKey("VK_ESCAPE", {});
}

Просмотреть файл

@ -1,277 +1,277 @@
/* 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/ */
"use strict";
// Test that increasing/decreasing values in rule view using
// arrow keys works correctly.
const TEST_URI = `
<style>
#test {
margin-top: 0px;
padding-top: 0px;
color: #000000;
background-color: #000000;
background: none;
transition: initial;
z-index: 0;
}
</style>
<div id="test"></div>
`;
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#test", inspector);
yield testMarginIncrements(view);
yield testVariousUnitIncrements(view);
yield testHexIncrements(view);
yield testAlphaHexIncrements(view);
yield testRgbIncrements(view);
yield testShorthandIncrements(view);
yield testOddCases(view);
yield testZeroValueIncrements(view);
});
function* testMarginIncrements(view) {
info("Testing keyboard increments on the margin property");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let marginPropEditor = idRuleEditor.rule.textProps[0].editor;
yield runIncrementTest(marginPropEditor, view, {
1: {alt: true, start: "0px", end: "0.1px", selectAll: true},
2: {start: "0px", end: "1px", selectAll: true},
3: {shift: true, start: "0px", end: "10px", selectAll: true},
4: {down: true, alt: true, start: "0.1px", end: "0px", selectAll: true},
5: {down: true, start: "0px", end: "-1px", selectAll: true},
6: {down: true, shift: true, start: "0px", end: "-10px", selectAll: true},
7: {pageUp: true, shift: true, start: "0px", end: "100px", selectAll: true},
8: {pageDown: true, shift: true, start: "0px", end: "-100px",
selectAll: true},
9: {start: "0", end: "1px", selectAll: true},
10: {down: true, start: "0", end: "-1px", selectAll: true},
});
}
function* testVariousUnitIncrements(view) {
info("Testing keyboard increments on values with various units");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let paddingPropEditor = idRuleEditor.rule.textProps[1].editor;
yield runIncrementTest(paddingPropEditor, view, {
1: {start: "0px", end: "1px", selectAll: true},
2: {start: "0pt", end: "1pt", selectAll: true},
3: {start: "0pc", end: "1pc", selectAll: true},
4: {start: "0em", end: "1em", selectAll: true},
5: {start: "0%", end: "1%", selectAll: true},
6: {start: "0in", end: "1in", selectAll: true},
7: {start: "0cm", end: "1cm", selectAll: true},
8: {start: "0mm", end: "1mm", selectAll: true},
9: {start: "0ex", end: "1ex", selectAll: true},
10: {start: "0", end: "1px", selectAll: true},
11: {down: true, start: "0", end: "-1px", selectAll: true},
});
}
function* testHexIncrements(view) {
info("Testing keyboard increments with hex colors");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor;
yield runIncrementTest(hexColorPropEditor, view, {
1: {start: "#CCCCCC", end: "#CDCDCD", selectAll: true},
2: {shift: true, start: "#CCCCCC", end: "#DCDCDC", selectAll: true},
3: {start: "#CCCCCC", end: "#CDCCCC", selection: [1, 3]},
4: {shift: true, start: "#CCCCCC", end: "#DCCCCC", selection: [1, 3]},
5: {start: "#FFFFFF", end: "#FFFFFF", selectAll: true},
6: {down: true, shift: true, start: "#000000", end: "#000000",
selectAll: true}
});
}
function* testAlphaHexIncrements(view) {
info("Testing keyboard increments with alpha hex colors");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor;
yield runIncrementTest(hexColorPropEditor, view, {
1: {start: "#CCCCCCAA", end: "#CDCDCDAB", selectAll: true},
2: {shift: true, start: "#CCCCCCAA", end: "#DCDCDCBA", selectAll: true},
3: {start: "#CCCCCCAA", end: "#CDCCCCAA", selection: [1, 3]},
4: {shift: true, start: "#CCCCCCAA", end: "#DCCCCCAA", selection: [1, 3]},
5: {start: "#FFFFFFFF", end: "#FFFFFFFF", selectAll: true},
6: {down: true, shift: true, start: "#00000000", end: "#00000000",
selectAll: true}
});
}
function* testRgbIncrements(view) {
info("Testing keyboard increments with rgb colors");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let rgbColorPropEditor = idRuleEditor.rule.textProps[3].editor;
yield runIncrementTest(rgbColorPropEditor, view, {
1: {start: "rgb(0,0,0)", end: "rgb(0,1,0)", selection: [6, 7]},
2: {shift: true, start: "rgb(0,0,0)", end: "rgb(0,10,0)",
selection: [6, 7]},
3: {start: "rgb(0,255,0)", end: "rgb(0,255,0)", selection: [6, 9]},
4: {shift: true, start: "rgb(0,250,0)", end: "rgb(0,255,0)",
selection: [6, 9]},
5: {down: true, start: "rgb(0,0,0)", end: "rgb(0,0,0)", selection: [6, 7]},
6: {down: true, shift: true, start: "rgb(0,5,0)", end: "rgb(0,0,0)",
selection: [6, 7]}
});
}
function* testShorthandIncrements(view) {
info("Testing keyboard increments within shorthand values");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let paddingPropEditor = idRuleEditor.rule.textProps[1].editor;
yield runIncrementTest(paddingPropEditor, view, {
1: {start: "0px 0px 0px 0px", end: "0px 1px 0px 0px", selection: [4, 7]},
2: {shift: true, start: "0px 0px 0px 0px", end: "0px 10px 0px 0px",
selection: [4, 7]},
3: {start: "0px 0px 0px 0px", end: "1px 0px 0px 0px", selectAll: true},
4: {shift: true, start: "0px 0px 0px 0px", end: "10px 0px 0px 0px",
selectAll: true},
5: {down: true, start: "0px 0px 0px 0px", end: "0px 0px -1px 0px",
selection: [8, 11]},
6: {down: true, shift: true, start: "0px 0px 0px 0px",
end: "-10px 0px 0px 0px", selectAll: true},
7: {up: true, start: "0.1em .1em 0em 0em", end: "0.1em 1.1em 0em 0em",
selection: [6, 9]},
8: {up: true, alt: true, start: "0.1em .9em 0em 0em",
end: "0.1em 1em 0em 0em", selection: [6, 9]},
9: {up: true, shift: true, start: "0.2em .2em 0em 0em",
end: "0.2em 10.2em 0em 0em", selection: [6, 9]}
});
}
function* testOddCases(view) {
info("Testing some more odd cases");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let marginPropEditor = idRuleEditor.rule.textProps[0].editor;
yield runIncrementTest(marginPropEditor, view, {
1: {start: "98.7%", end: "99.7%", selection: [3, 3]},
2: {alt: true, start: "98.7%", end: "98.8%", selection: [3, 3]},
3: {start: "0", end: "1px"},
4: {down: true, start: "0", end: "-1px"},
5: {start: "'a=-1'", end: "'a=0'", selection: [4, 4]},
6: {start: "0 -1px", end: "0 0px", selection: [2, 2]},
7: {start: "url(-1)", end: "url(-1)", selection: [4, 4]},
8: {start: "url('test1.1.png')", end: "url('test1.2.png')",
selection: [11, 11]},
9: {start: "url('test1.png')", end: "url('test2.png')", selection: [9, 9]},
10: {shift: true, start: "url('test1.1.png')", end: "url('test11.1.png')",
selection: [9, 9]},
11: {down: true, start: "url('test-1.png')", end: "url('test-2.png')",
selection: [9, 11]},
12: {start: "url('test1.1.png')", end: "url('test1.2.png')",
selection: [11, 12]},
13: {down: true, alt: true, start: "url('test-0.png')",
end: "url('test--0.1.png')", selection: [10, 11]},
14: {alt: true, start: "url('test--0.1.png')", end: "url('test-0.png')",
selection: [10, 14]}
});
}
function* testZeroValueIncrements(view) {
info("Testing a valid unit is added when incrementing from 0");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let backgroundPropEditor = idRuleEditor.rule.textProps[4].editor;
yield runIncrementTest(backgroundPropEditor, view, {
1: { start: "url(test-0.png) no-repeat 0 0",
end: "url(test-0.png) no-repeat 1px 0", selection: [26, 26] },
2: { start: "url(test-0.png) no-repeat 0 0",
end: "url(test-0.png) no-repeat 0 1px", selection: [28, 28] },
3: { start: "url(test-0.png) no-repeat center/0",
end: "url(test-0.png) no-repeat center/1px", selection: [34, 34] },
4: { start: "url(test-0.png) no-repeat 0 0",
end: "url(test-1.png) no-repeat 0 0", selection: [10, 10] },
5: { start: "linear-gradient(0, red 0, blue 0)",
end: "linear-gradient(1deg, red 0, blue 0)", selection: [17, 17] },
6: { start: "linear-gradient(1deg, red 0, blue 0)",
end: "linear-gradient(1deg, red 1px, blue 0)", selection: [27, 27] },
7: { start: "linear-gradient(1deg, red 0, blue 0)",
end: "linear-gradient(1deg, red 0, blue 1px)", selection: [35, 35] },
});
let transitionPropEditor = idRuleEditor.rule.textProps[5].editor;
yield runIncrementTest(transitionPropEditor, view, {
1: { start: "all 0 ease-out", end: "all 1s ease-out", selection: [5, 5] },
2: { start: "margin 4s, color 0",
end: "margin 4s, color 1s", selection: [18, 18] },
});
let zIndexPropEditor = idRuleEditor.rule.textProps[6].editor;
yield runIncrementTest(zIndexPropEditor, view, {
1: {start: "0", end: "1", selection: [1, 1]},
});
}
function* runIncrementTest(propertyEditor, view, tests) {
let editor = yield focusEditableField(view, propertyEditor.valueSpan);
for (let test in tests) {
yield testIncrement(editor, tests[test], view, propertyEditor);
}
// Blur the field to put back the UI in its initial state (and avoid pending
// requests when the test ends).
let onRuleViewChanged = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
view.throttle.flush();
yield onRuleViewChanged;
}
function* testIncrement(editor, options, view) {
editor.input.value = options.start;
let input = editor.input;
if (options.selectAll) {
input.select();
} else if (options.selection) {
input.setSelectionRange(options.selection[0], options.selection[1]);
}
is(input.value, options.start, "Value initialized at " + options.start);
let onRuleViewChanged = view.once("ruleview-changed");
let onKeyUp = once(input, "keyup");
let key;
key = options.down ? "VK_DOWN" : "VK_UP";
if (options.pageDown) {
key = "VK_PAGE_DOWN";
} else if (options.pageUp) {
key = "VK_PAGE_UP";
}
EventUtils.synthesizeKey(key, {altKey: options.alt, shiftKey: options.shift},
view.styleWindow);
yield onKeyUp;
// Only expect a change if the value actually changed!
/* 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/ */
"use strict";
// Test that increasing/decreasing values in rule view using
// arrow keys works correctly.
const TEST_URI = `
<style>
#test {
margin-top: 0px;
padding-top: 0px;
color: #000000;
background-color: #000000;
background: none;
transition: initial;
z-index: 0;
}
</style>
<div id="test"></div>
`;
add_task(function* () {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#test", inspector);
yield testMarginIncrements(view);
yield testVariousUnitIncrements(view);
yield testHexIncrements(view);
yield testAlphaHexIncrements(view);
yield testRgbIncrements(view);
yield testShorthandIncrements(view);
yield testOddCases(view);
yield testZeroValueIncrements(view);
});
function* testMarginIncrements(view) {
info("Testing keyboard increments on the margin property");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let marginPropEditor = idRuleEditor.rule.textProps[0].editor;
yield runIncrementTest(marginPropEditor, view, {
1: {alt: true, start: "0px", end: "0.1px", selectAll: true},
2: {start: "0px", end: "1px", selectAll: true},
3: {shift: true, start: "0px", end: "10px", selectAll: true},
4: {down: true, alt: true, start: "0.1px", end: "0px", selectAll: true},
5: {down: true, start: "0px", end: "-1px", selectAll: true},
6: {down: true, shift: true, start: "0px", end: "-10px", selectAll: true},
7: {pageUp: true, shift: true, start: "0px", end: "100px", selectAll: true},
8: {pageDown: true, shift: true, start: "0px", end: "-100px",
selectAll: true},
9: {start: "0", end: "1px", selectAll: true},
10: {down: true, start: "0", end: "-1px", selectAll: true},
});
}
function* testVariousUnitIncrements(view) {
info("Testing keyboard increments on values with various units");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let paddingPropEditor = idRuleEditor.rule.textProps[1].editor;
yield runIncrementTest(paddingPropEditor, view, {
1: {start: "0px", end: "1px", selectAll: true},
2: {start: "0pt", end: "1pt", selectAll: true},
3: {start: "0pc", end: "1pc", selectAll: true},
4: {start: "0em", end: "1em", selectAll: true},
5: {start: "0%", end: "1%", selectAll: true},
6: {start: "0in", end: "1in", selectAll: true},
7: {start: "0cm", end: "1cm", selectAll: true},
8: {start: "0mm", end: "1mm", selectAll: true},
9: {start: "0ex", end: "1ex", selectAll: true},
10: {start: "0", end: "1px", selectAll: true},
11: {down: true, start: "0", end: "-1px", selectAll: true},
});
}
function* testHexIncrements(view) {
info("Testing keyboard increments with hex colors");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor;
yield runIncrementTest(hexColorPropEditor, view, {
1: {start: "#CCCCCC", end: "#CDCDCD", selectAll: true},
2: {shift: true, start: "#CCCCCC", end: "#DCDCDC", selectAll: true},
3: {start: "#CCCCCC", end: "#CDCCCC", selection: [1, 3]},
4: {shift: true, start: "#CCCCCC", end: "#DCCCCC", selection: [1, 3]},
5: {start: "#FFFFFF", end: "#FFFFFF", selectAll: true},
6: {down: true, shift: true, start: "#000000", end: "#000000",
selectAll: true}
});
}
function* testAlphaHexIncrements(view) {
info("Testing keyboard increments with alpha hex colors");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor;
yield runIncrementTest(hexColorPropEditor, view, {
1: {start: "#CCCCCCAA", end: "#CDCDCDAB", selectAll: true},
2: {shift: true, start: "#CCCCCCAA", end: "#DCDCDCBA", selectAll: true},
3: {start: "#CCCCCCAA", end: "#CDCCCCAA", selection: [1, 3]},
4: {shift: true, start: "#CCCCCCAA", end: "#DCCCCCAA", selection: [1, 3]},
5: {start: "#FFFFFFFF", end: "#FFFFFFFF", selectAll: true},
6: {down: true, shift: true, start: "#00000000", end: "#00000000",
selectAll: true}
});
}
function* testRgbIncrements(view) {
info("Testing keyboard increments with rgb colors");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let rgbColorPropEditor = idRuleEditor.rule.textProps[3].editor;
yield runIncrementTest(rgbColorPropEditor, view, {
1: {start: "rgb(0,0,0)", end: "rgb(0,1,0)", selection: [6, 7]},
2: {shift: true, start: "rgb(0,0,0)", end: "rgb(0,10,0)",
selection: [6, 7]},
3: {start: "rgb(0,255,0)", end: "rgb(0,255,0)", selection: [6, 9]},
4: {shift: true, start: "rgb(0,250,0)", end: "rgb(0,255,0)",
selection: [6, 9]},
5: {down: true, start: "rgb(0,0,0)", end: "rgb(0,0,0)", selection: [6, 7]},
6: {down: true, shift: true, start: "rgb(0,5,0)", end: "rgb(0,0,0)",
selection: [6, 7]}
});
}
function* testShorthandIncrements(view) {
info("Testing keyboard increments within shorthand values");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let paddingPropEditor = idRuleEditor.rule.textProps[1].editor;
yield runIncrementTest(paddingPropEditor, view, {
1: {start: "0px 0px 0px 0px", end: "0px 1px 0px 0px", selection: [4, 7]},
2: {shift: true, start: "0px 0px 0px 0px", end: "0px 10px 0px 0px",
selection: [4, 7]},
3: {start: "0px 0px 0px 0px", end: "1px 0px 0px 0px", selectAll: true},
4: {shift: true, start: "0px 0px 0px 0px", end: "10px 0px 0px 0px",
selectAll: true},
5: {down: true, start: "0px 0px 0px 0px", end: "0px 0px -1px 0px",
selection: [8, 11]},
6: {down: true, shift: true, start: "0px 0px 0px 0px",
end: "-10px 0px 0px 0px", selectAll: true},
7: {up: true, start: "0.1em .1em 0em 0em", end: "0.1em 1.1em 0em 0em",
selection: [6, 9]},
8: {up: true, alt: true, start: "0.1em .9em 0em 0em",
end: "0.1em 1em 0em 0em", selection: [6, 9]},
9: {up: true, shift: true, start: "0.2em .2em 0em 0em",
end: "0.2em 10.2em 0em 0em", selection: [6, 9]}
});
}
function* testOddCases(view) {
info("Testing some more odd cases");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let marginPropEditor = idRuleEditor.rule.textProps[0].editor;
yield runIncrementTest(marginPropEditor, view, {
1: {start: "98.7%", end: "99.7%", selection: [3, 3]},
2: {alt: true, start: "98.7%", end: "98.8%", selection: [3, 3]},
3: {start: "0", end: "1px"},
4: {down: true, start: "0", end: "-1px"},
5: {start: "'a=-1'", end: "'a=0'", selection: [4, 4]},
6: {start: "0 -1px", end: "0 0px", selection: [2, 2]},
7: {start: "url(-1)", end: "url(-1)", selection: [4, 4]},
8: {start: "url('test1.1.png')", end: "url('test1.2.png')",
selection: [11, 11]},
9: {start: "url('test1.png')", end: "url('test2.png')", selection: [9, 9]},
10: {shift: true, start: "url('test1.1.png')", end: "url('test11.1.png')",
selection: [9, 9]},
11: {down: true, start: "url('test-1.png')", end: "url('test-2.png')",
selection: [9, 11]},
12: {start: "url('test1.1.png')", end: "url('test1.2.png')",
selection: [11, 12]},
13: {down: true, alt: true, start: "url('test-0.png')",
end: "url('test--0.1.png')", selection: [10, 11]},
14: {alt: true, start: "url('test--0.1.png')", end: "url('test-0.png')",
selection: [10, 14]}
});
}
function* testZeroValueIncrements(view) {
info("Testing a valid unit is added when incrementing from 0");
let idRuleEditor = getRuleViewRuleEditor(view, 1);
let backgroundPropEditor = idRuleEditor.rule.textProps[4].editor;
yield runIncrementTest(backgroundPropEditor, view, {
1: { start: "url(test-0.png) no-repeat 0 0",
end: "url(test-0.png) no-repeat 1px 0", selection: [26, 26] },
2: { start: "url(test-0.png) no-repeat 0 0",
end: "url(test-0.png) no-repeat 0 1px", selection: [28, 28] },
3: { start: "url(test-0.png) no-repeat center/0",
end: "url(test-0.png) no-repeat center/1px", selection: [34, 34] },
4: { start: "url(test-0.png) no-repeat 0 0",
end: "url(test-1.png) no-repeat 0 0", selection: [10, 10] },
5: { start: "linear-gradient(0, red 0, blue 0)",
end: "linear-gradient(1deg, red 0, blue 0)", selection: [17, 17] },
6: { start: "linear-gradient(1deg, red 0, blue 0)",
end: "linear-gradient(1deg, red 1px, blue 0)", selection: [27, 27] },
7: { start: "linear-gradient(1deg, red 0, blue 0)",
end: "linear-gradient(1deg, red 0, blue 1px)", selection: [35, 35] },
});
let transitionPropEditor = idRuleEditor.rule.textProps[5].editor;
yield runIncrementTest(transitionPropEditor, view, {
1: { start: "all 0 ease-out", end: "all 1s ease-out", selection: [5, 5] },
2: { start: "margin 4s, color 0",
end: "margin 4s, color 1s", selection: [18, 18] },
});
let zIndexPropEditor = idRuleEditor.rule.textProps[6].editor;
yield runIncrementTest(zIndexPropEditor, view, {
1: {start: "0", end: "1", selection: [1, 1]},
});
}
function* runIncrementTest(propertyEditor, view, tests) {
let editor = yield focusEditableField(view, propertyEditor.valueSpan);
for (let test in tests) {
yield testIncrement(editor, tests[test], view, propertyEditor);
}
// Blur the field to put back the UI in its initial state (and avoid pending
// requests when the test ends).
let onRuleViewChanged = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
view.throttle.flush();
yield onRuleViewChanged;
}
function* testIncrement(editor, options, view) {
editor.input.value = options.start;
let input = editor.input;
if (options.selectAll) {
input.select();
} else if (options.selection) {
input.setSelectionRange(options.selection[0], options.selection[1]);
}
is(input.value, options.start, "Value initialized at " + options.start);
let onRuleViewChanged = view.once("ruleview-changed");
let onKeyUp = once(input, "keyup");
let key;
key = options.down ? "VK_DOWN" : "VK_UP";
if (options.pageDown) {
key = "VK_PAGE_DOWN";
} else if (options.pageUp) {
key = "VK_PAGE_UP";
}
EventUtils.synthesizeKey(key, {altKey: options.alt, shiftKey: options.shift},
view.styleWindow);
yield onKeyUp;
// Only expect a change if the value actually changed!
if (options.start !== options.end) {
view.throttle.flush();
yield onRuleViewChanged;
}
is(input.value, options.end, "Value changed to " + options.end);
}
view.throttle.flush();
yield onRuleViewChanged;
}
is(input.value, options.end, "Value changed to " + options.end);
}

Просмотреть файл

@ -1,352 +1,352 @@
/* 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 EventEmitter = require("devtools/shared/event-emitter");
const {createNode} = require("devtools/client/animationinspector/utils");
const { LocalizationHelper } = require("devtools/shared/l10n");
const STRINGS_URI = "devtools/locale/inspector.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
/**
* UI component responsible for displaying a preview of a dom node.
* @param {InspectorPanel} inspector Requires a reference to the inspector-panel
* to highlight and select the node, as well as refresh it when there are
* mutations.
* @param {Object} options Supported properties are:
* - compact {Boolean} Defaults to false.
* By default, nodes are previewed like <tag id="id" class="class">
* If true, nodes will be previewed like tag#id.class instead.
*/
function DomNodePreview(inspector, options = {}) {
this.inspector = inspector;
this.options = options;
this.onPreviewMouseOver = this.onPreviewMouseOver.bind(this);
this.onPreviewMouseOut = this.onPreviewMouseOut.bind(this);
this.onSelectElClick = this.onSelectElClick.bind(this);
this.onMarkupMutations = this.onMarkupMutations.bind(this);
this.onHighlightElClick = this.onHighlightElClick.bind(this);
this.onHighlighterLocked = this.onHighlighterLocked.bind(this);
EventEmitter.decorate(this);
}
exports.DomNodePreview = DomNodePreview;
DomNodePreview.prototype = {
init: function (containerEl) {
let document = containerEl.ownerDocument;
// Init the markup for displaying the target node.
this.el = createNode({
parent: containerEl,
attributes: {
"class": "animation-target"
}
});
// Icon to select the node in the inspector.
this.highlightNodeEl = createNode({
parent: this.el,
nodeType: "span",
attributes: {
"class": "node-highlighter",
"title": L10N.getStr("inspector.nodePreview.highlightNodeLabel")
}
});
// Wrapper used for mouseover/out event handling.
this.previewEl = createNode({
parent: this.el,
nodeType: "span",
attributes: {
"title": L10N.getStr("inspector.nodePreview.selectNodeLabel")
}
});
if (!this.options.compact) {
this.previewEl.appendChild(document.createTextNode("<"));
}
// Only used for ::before and ::after pseudo-elements.
this.pseudoEl = createNode({
parent: this.previewEl,
nodeType: "span",
attributes: {
"class": "pseudo-element theme-fg-color5"
}
});
// Tag name.
this.tagNameEl = createNode({
parent: this.previewEl,
nodeType: "span",
attributes: {
"class": "tag-name theme-fg-color3"
}
});
// Id attribute container.
this.idEl = createNode({
parent: this.previewEl,
nodeType: "span"
});
if (!this.options.compact) {
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "attribute-name theme-fg-color2"
},
textContent: "id"
});
this.idEl.appendChild(document.createTextNode("=\""));
} else {
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "theme-fg-color6"
},
textContent: "#"
});
}
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "attribute-value theme-fg-color6"
}
});
if (!this.options.compact) {
this.idEl.appendChild(document.createTextNode("\""));
}
// Class attribute container.
this.classEl = createNode({
parent: this.previewEl,
nodeType: "span"
});
if (!this.options.compact) {
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "attribute-name theme-fg-color2"
},
textContent: "class"
});
this.classEl.appendChild(document.createTextNode("=\""));
} else {
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "theme-fg-color6"
},
textContent: "."
});
}
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "attribute-value theme-fg-color6"
}
});
if (!this.options.compact) {
this.classEl.appendChild(document.createTextNode("\""));
this.previewEl.appendChild(document.createTextNode(">"));
}
this.startListeners();
},
startListeners: function () {
// Init events for highlighting and selecting the node.
this.previewEl.addEventListener("mouseover", this.onPreviewMouseOver);
this.previewEl.addEventListener("mouseout", this.onPreviewMouseOut);
this.previewEl.addEventListener("click", this.onSelectElClick);
this.highlightNodeEl.addEventListener("click", this.onHighlightElClick);
// Start to listen for markupmutation events.
this.inspector.on("markupmutation", this.onMarkupMutations);
// Listen to the target node highlighter.
HighlighterLock.on("highlighted", this.onHighlighterLocked);
},
stopListeners: function () {
HighlighterLock.off("highlighted", this.onHighlighterLocked);
this.inspector.off("markupmutation", this.onMarkupMutations);
this.previewEl.removeEventListener("mouseover", this.onPreviewMouseOver);
this.previewEl.removeEventListener("mouseout", this.onPreviewMouseOut);
this.previewEl.removeEventListener("click", this.onSelectElClick);
this.highlightNodeEl.removeEventListener("click", this.onHighlightElClick);
},
destroy: function () {
HighlighterLock.unhighlight().catch(e => console.error(e));
this.stopListeners();
this.el.remove();
this.el = this.tagNameEl = this.idEl = this.classEl = this.pseudoEl = null;
this.highlightNodeEl = this.previewEl = null;
this.nodeFront = this.inspector = null;
},
get highlighterUtils() {
if (this.inspector && this.inspector.toolbox) {
return this.inspector.toolbox.highlighterUtils;
}
return null;
},
onPreviewMouseOver: function () {
if (!this.nodeFront || !this.highlighterUtils) {
return;
}
this.highlighterUtils.highlightNodeFront(this.nodeFront)
.catch(e => console.error(e));
},
onPreviewMouseOut: function () {
if (!this.nodeFront || !this.highlighterUtils) {
return;
}
this.highlighterUtils.unhighlight()
.catch(e => console.error(e));
},
onSelectElClick: function () {
if (!this.nodeFront) {
return;
}
this.inspector.selection.setNodeFront(this.nodeFront, "dom-node-preview");
},
onHighlightElClick: function (e) {
e.stopPropagation();
let classList = this.highlightNodeEl.classList;
let isHighlighted = classList.contains("selected");
if (isHighlighted) {
classList.remove("selected");
HighlighterLock.unhighlight().then(() => {
this.emit("target-highlighter-unlocked");
}, error => console.error(error));
} else {
classList.add("selected");
HighlighterLock.highlight(this).then(() => {
this.emit("target-highlighter-locked");
}, error => console.error(error));
}
},
onHighlighterLocked: function (e, domNodePreview) {
if (domNodePreview !== this) {
this.highlightNodeEl.classList.remove("selected");
}
},
onMarkupMutations: function (e, mutations) {
if (!this.nodeFront) {
return;
}
for (let {target} of mutations) {
if (target === this.nodeFront) {
// Re-render with the same nodeFront to update the output.
this.render(this.nodeFront);
break;
}
}
},
render: function (nodeFront) {
this.nodeFront = nodeFront;
let {displayName, attributes} = nodeFront;
if (nodeFront.isPseudoElement) {
this.pseudoEl.textContent = nodeFront.isBeforePseudoElement
? "::before"
: "::after";
this.pseudoEl.style.display = "inline";
this.tagNameEl.style.display = "none";
} else {
this.tagNameEl.textContent = displayName;
this.pseudoEl.style.display = "none";
this.tagNameEl.style.display = "inline";
}
let idIndex = attributes.findIndex(({name}) => name === "id");
if (idIndex > -1 && attributes[idIndex].value) {
this.idEl.querySelector(".attribute-value").textContent =
attributes[idIndex].value;
this.idEl.style.display = "inline";
} else {
this.idEl.style.display = "none";
}
let classIndex = attributes.findIndex(({name}) => name === "class");
if (classIndex > -1 && attributes[classIndex].value) {
let value = attributes[classIndex].value;
if (this.options.compact) {
value = value.split(" ").join(".");
}
this.classEl.querySelector(".attribute-value").textContent = value;
this.classEl.style.display = "inline";
} else {
this.classEl.style.display = "none";
}
}
};
/**
* HighlighterLock is a helper used to lock the highlighter on DOM nodes in the
* page.
* It instantiates a new highlighter that is then shared amongst all instances
* of DomNodePreview. This is useful because that means showing the highlighter
* on one node will unhighlight the previously highlighted one, but will not
* interfere with the default inspector highlighter.
*/
var HighlighterLock = {
highlighter: null,
isShown: false,
highlight: Task.async(function* (animationTargetNode) {
if (!this.highlighter) {
let util = animationTargetNode.inspector.toolbox.highlighterUtils;
this.highlighter = yield util.getHighlighterByType("BoxModelHighlighter");
}
yield this.highlighter.show(animationTargetNode.nodeFront);
this.isShown = true;
this.emit("highlighted", animationTargetNode);
}),
unhighlight: Task.async(function* () {
if (!this.highlighter || !this.isShown) {
return;
}
yield this.highlighter.hide();
this.isShown = false;
this.emit("unhighlighted");
})
};
EventEmitter.decorate(HighlighterLock);
/* 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 EventEmitter = require("devtools/shared/event-emitter");
const {createNode} = require("devtools/client/animationinspector/utils");
const { LocalizationHelper } = require("devtools/shared/l10n");
const STRINGS_URI = "devtools/locale/inspector.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
/**
* UI component responsible for displaying a preview of a dom node.
* @param {InspectorPanel} inspector Requires a reference to the inspector-panel
* to highlight and select the node, as well as refresh it when there are
* mutations.
* @param {Object} options Supported properties are:
* - compact {Boolean} Defaults to false.
* By default, nodes are previewed like <tag id="id" class="class">
* If true, nodes will be previewed like tag#id.class instead.
*/
function DomNodePreview(inspector, options = {}) {
this.inspector = inspector;
this.options = options;
this.onPreviewMouseOver = this.onPreviewMouseOver.bind(this);
this.onPreviewMouseOut = this.onPreviewMouseOut.bind(this);
this.onSelectElClick = this.onSelectElClick.bind(this);
this.onMarkupMutations = this.onMarkupMutations.bind(this);
this.onHighlightElClick = this.onHighlightElClick.bind(this);
this.onHighlighterLocked = this.onHighlighterLocked.bind(this);
EventEmitter.decorate(this);
}
exports.DomNodePreview = DomNodePreview;
DomNodePreview.prototype = {
init: function (containerEl) {
let document = containerEl.ownerDocument;
// Init the markup for displaying the target node.
this.el = createNode({
parent: containerEl,
attributes: {
"class": "animation-target"
}
});
// Icon to select the node in the inspector.
this.highlightNodeEl = createNode({
parent: this.el,
nodeType: "span",
attributes: {
"class": "node-highlighter",
"title": L10N.getStr("inspector.nodePreview.highlightNodeLabel")
}
});
// Wrapper used for mouseover/out event handling.
this.previewEl = createNode({
parent: this.el,
nodeType: "span",
attributes: {
"title": L10N.getStr("inspector.nodePreview.selectNodeLabel")
}
});
if (!this.options.compact) {
this.previewEl.appendChild(document.createTextNode("<"));
}
// Only used for ::before and ::after pseudo-elements.
this.pseudoEl = createNode({
parent: this.previewEl,
nodeType: "span",
attributes: {
"class": "pseudo-element theme-fg-color5"
}
});
// Tag name.
this.tagNameEl = createNode({
parent: this.previewEl,
nodeType: "span",
attributes: {
"class": "tag-name theme-fg-color3"
}
});
// Id attribute container.
this.idEl = createNode({
parent: this.previewEl,
nodeType: "span"
});
if (!this.options.compact) {
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "attribute-name theme-fg-color2"
},
textContent: "id"
});
this.idEl.appendChild(document.createTextNode("=\""));
} else {
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "theme-fg-color6"
},
textContent: "#"
});
}
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "attribute-value theme-fg-color6"
}
});
if (!this.options.compact) {
this.idEl.appendChild(document.createTextNode("\""));
}
// Class attribute container.
this.classEl = createNode({
parent: this.previewEl,
nodeType: "span"
});
if (!this.options.compact) {
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "attribute-name theme-fg-color2"
},
textContent: "class"
});
this.classEl.appendChild(document.createTextNode("=\""));
} else {
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "theme-fg-color6"
},
textContent: "."
});
}
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "attribute-value theme-fg-color6"
}
});
if (!this.options.compact) {
this.classEl.appendChild(document.createTextNode("\""));
this.previewEl.appendChild(document.createTextNode(">"));
}
this.startListeners();
},
startListeners: function () {
// Init events for highlighting and selecting the node.
this.previewEl.addEventListener("mouseover", this.onPreviewMouseOver);
this.previewEl.addEventListener("mouseout", this.onPreviewMouseOut);
this.previewEl.addEventListener("click", this.onSelectElClick);
this.highlightNodeEl.addEventListener("click", this.onHighlightElClick);
// Start to listen for markupmutation events.
this.inspector.on("markupmutation", this.onMarkupMutations);
// Listen to the target node highlighter.
HighlighterLock.on("highlighted", this.onHighlighterLocked);
},
stopListeners: function () {
HighlighterLock.off("highlighted", this.onHighlighterLocked);
this.inspector.off("markupmutation", this.onMarkupMutations);
this.previewEl.removeEventListener("mouseover", this.onPreviewMouseOver);
this.previewEl.removeEventListener("mouseout", this.onPreviewMouseOut);
this.previewEl.removeEventListener("click", this.onSelectElClick);
this.highlightNodeEl.removeEventListener("click", this.onHighlightElClick);
},
destroy: function () {
HighlighterLock.unhighlight().catch(e => console.error(e));
this.stopListeners();
this.el.remove();
this.el = this.tagNameEl = this.idEl = this.classEl = this.pseudoEl = null;
this.highlightNodeEl = this.previewEl = null;
this.nodeFront = this.inspector = null;
},
get highlighterUtils() {
if (this.inspector && this.inspector.toolbox) {
return this.inspector.toolbox.highlighterUtils;
}
return null;
},
onPreviewMouseOver: function () {
if (!this.nodeFront || !this.highlighterUtils) {
return;
}
this.highlighterUtils.highlightNodeFront(this.nodeFront)
.catch(e => console.error(e));
},
onPreviewMouseOut: function () {
if (!this.nodeFront || !this.highlighterUtils) {
return;
}
this.highlighterUtils.unhighlight()
.catch(e => console.error(e));
},
onSelectElClick: function () {
if (!this.nodeFront) {
return;
}
this.inspector.selection.setNodeFront(this.nodeFront, "dom-node-preview");
},
onHighlightElClick: function (e) {
e.stopPropagation();
let classList = this.highlightNodeEl.classList;
let isHighlighted = classList.contains("selected");
if (isHighlighted) {
classList.remove("selected");
HighlighterLock.unhighlight().then(() => {
this.emit("target-highlighter-unlocked");
}, error => console.error(error));
} else {
classList.add("selected");
HighlighterLock.highlight(this).then(() => {
this.emit("target-highlighter-locked");
}, error => console.error(error));
}
},
onHighlighterLocked: function (e, domNodePreview) {
if (domNodePreview !== this) {
this.highlightNodeEl.classList.remove("selected");
}
},
onMarkupMutations: function (e, mutations) {
if (!this.nodeFront) {
return;
}
for (let {target} of mutations) {
if (target === this.nodeFront) {
// Re-render with the same nodeFront to update the output.
this.render(this.nodeFront);
break;
}
}
},
render: function (nodeFront) {
this.nodeFront = nodeFront;
let {displayName, attributes} = nodeFront;
if (nodeFront.isPseudoElement) {
this.pseudoEl.textContent = nodeFront.isBeforePseudoElement
? "::before"
: "::after";
this.pseudoEl.style.display = "inline";
this.tagNameEl.style.display = "none";
} else {
this.tagNameEl.textContent = displayName;
this.pseudoEl.style.display = "none";
this.tagNameEl.style.display = "inline";
}
let idIndex = attributes.findIndex(({name}) => name === "id");
if (idIndex > -1 && attributes[idIndex].value) {
this.idEl.querySelector(".attribute-value").textContent =
attributes[idIndex].value;
this.idEl.style.display = "inline";
} else {
this.idEl.style.display = "none";
}
let classIndex = attributes.findIndex(({name}) => name === "class");
if (classIndex > -1 && attributes[classIndex].value) {
let value = attributes[classIndex].value;
if (this.options.compact) {
value = value.split(" ").join(".");
}
this.classEl.querySelector(".attribute-value").textContent = value;
this.classEl.style.display = "inline";
} else {
this.classEl.style.display = "none";
}
}
};
/**
* HighlighterLock is a helper used to lock the highlighter on DOM nodes in the
* page.
* It instantiates a new highlighter that is then shared amongst all instances
* of DomNodePreview. This is useful because that means showing the highlighter
* on one node will unhighlight the previously highlighted one, but will not
* interfere with the default inspector highlighter.
*/
var HighlighterLock = {
highlighter: null,
isShown: false,
highlight: Task.async(function* (animationTargetNode) {
if (!this.highlighter) {
let util = animationTargetNode.inspector.toolbox.highlighterUtils;
this.highlighter = yield util.getHighlighterByType("BoxModelHighlighter");
}
yield this.highlighter.show(animationTargetNode.nodeFront);
this.isShown = true;
this.emit("highlighted", animationTargetNode);
}),
unhighlight: Task.async(function* () {
if (!this.highlighter || !this.isShown) {
return;
}
yield this.highlighter.hide();
this.isShown = false;
this.emit("unhighlighted");
})
};
EventEmitter.decorate(HighlighterLock);

Просмотреть файл

@ -1,109 +1,109 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* Tests both Copy URL and Copy Data URL context menu items */
const TEST_DATA_URI = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=";
// Invalid URL still needs to be reachable otherwise getImageDataUrl will
// timeout. DevTools chrome:// URLs aren't content accessible, so use some
// random resource:// URL here.
const INVALID_IMAGE_URI = "resource://devtools/client/definitions.js";
const ERROR_MESSAGE = STYLE_INSPECTOR_L10N.getStr("styleinspector.copyImageDataUrlError");
add_task(function* () {
const TEST_URI = `<style type="text/css">
.valid-background {
background-image: url(${TEST_DATA_URI});
}
.invalid-background {
background-image: url(${INVALID_IMAGE_URI});
}
</style>
<div class="valid-background">Valid background image</div>
<div class="invalid-background">Invalid background image</div>`;
yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_URI));
yield startTest();
});
function* startTest() {
info("Opening rule view");
let {inspector, view} = yield openRuleView();
info("Test valid background image URL in rule view");
yield testCopyUrlToClipboard({view, inspector}, "data-uri",
".valid-background", TEST_DATA_URI);
yield testCopyUrlToClipboard({view, inspector}, "url",
".valid-background", TEST_DATA_URI);
info("Test invalid background image URL in rue view");
yield testCopyUrlToClipboard({view, inspector}, "data-uri",
".invalid-background", ERROR_MESSAGE);
yield testCopyUrlToClipboard({view, inspector}, "url",
".invalid-background", INVALID_IMAGE_URI);
info("Opening computed view");
view = selectComputedView(inspector);
info("Test valid background image URL in computed view");
yield testCopyUrlToClipboard({view, inspector}, "data-uri",
".valid-background", TEST_DATA_URI);
yield testCopyUrlToClipboard({view, inspector}, "url",
".valid-background", TEST_DATA_URI);
info("Test invalid background image URL in computed view");
yield testCopyUrlToClipboard({view, inspector}, "data-uri",
".invalid-background", ERROR_MESSAGE);
yield testCopyUrlToClipboard({view, inspector}, "url",
".invalid-background", INVALID_IMAGE_URI);
}
function* testCopyUrlToClipboard({view, inspector}, type, selector, expected) {
info("Select node in inspector panel");
yield selectNode(selector, inspector);
info("Retrieve background-image link for selected node in current " +
"styleinspector view");
let property = getBackgroundImageProperty(view, selector);
let imageLink = property.valueSpan.querySelector(".theme-link");
ok(imageLink, "Background-image link element found");
info("Simulate right click on the background-image URL");
let allMenuItems = openStyleContextMenuAndGetAllItems(view, imageLink);
let menuitemCopyUrl = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyUrl"));
let menuitemCopyImageDataUrl = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyImageDataUrl"));
info("Context menu is displayed");
ok(menuitemCopyUrl.visible,
"\"Copy URL\" menu entry is displayed");
ok(menuitemCopyImageDataUrl.visible,
"\"Copy Image Data-URL\" menu entry is displayed");
if (type == "data-uri") {
info("Click Copy Data URI and wait for clipboard");
yield waitForClipboardPromise(() => {
return menuitemCopyImageDataUrl.click();
}, expected);
} else {
info("Click Copy URL and wait for clipboard");
yield waitForClipboardPromise(() => {
return menuitemCopyUrl.click();
}, expected);
}
info("Hide context menu");
}
function getBackgroundImageProperty(view, selector) {
let isRuleView = view instanceof CssRuleView;
if (isRuleView) {
return getRuleViewProperty(view, selector, "background-image");
}
return getComputedViewProperty(view, "background-image");
}
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* Tests both Copy URL and Copy Data URL context menu items */
const TEST_DATA_URI = "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=";
// Invalid URL still needs to be reachable otherwise getImageDataUrl will
// timeout. DevTools chrome:// URLs aren't content accessible, so use some
// random resource:// URL here.
const INVALID_IMAGE_URI = "resource://devtools/client/definitions.js";
const ERROR_MESSAGE = STYLE_INSPECTOR_L10N.getStr("styleinspector.copyImageDataUrlError");
add_task(function* () {
const TEST_URI = `<style type="text/css">
.valid-background {
background-image: url(${TEST_DATA_URI});
}
.invalid-background {
background-image: url(${INVALID_IMAGE_URI});
}
</style>
<div class="valid-background">Valid background image</div>
<div class="invalid-background">Invalid background image</div>`;
yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_URI));
yield startTest();
});
function* startTest() {
info("Opening rule view");
let {inspector, view} = yield openRuleView();
info("Test valid background image URL in rule view");
yield testCopyUrlToClipboard({view, inspector}, "data-uri",
".valid-background", TEST_DATA_URI);
yield testCopyUrlToClipboard({view, inspector}, "url",
".valid-background", TEST_DATA_URI);
info("Test invalid background image URL in rue view");
yield testCopyUrlToClipboard({view, inspector}, "data-uri",
".invalid-background", ERROR_MESSAGE);
yield testCopyUrlToClipboard({view, inspector}, "url",
".invalid-background", INVALID_IMAGE_URI);
info("Opening computed view");
view = selectComputedView(inspector);
info("Test valid background image URL in computed view");
yield testCopyUrlToClipboard({view, inspector}, "data-uri",
".valid-background", TEST_DATA_URI);
yield testCopyUrlToClipboard({view, inspector}, "url",
".valid-background", TEST_DATA_URI);
info("Test invalid background image URL in computed view");
yield testCopyUrlToClipboard({view, inspector}, "data-uri",
".invalid-background", ERROR_MESSAGE);
yield testCopyUrlToClipboard({view, inspector}, "url",
".invalid-background", INVALID_IMAGE_URI);
}
function* testCopyUrlToClipboard({view, inspector}, type, selector, expected) {
info("Select node in inspector panel");
yield selectNode(selector, inspector);
info("Retrieve background-image link for selected node in current " +
"styleinspector view");
let property = getBackgroundImageProperty(view, selector);
let imageLink = property.valueSpan.querySelector(".theme-link");
ok(imageLink, "Background-image link element found");
info("Simulate right click on the background-image URL");
let allMenuItems = openStyleContextMenuAndGetAllItems(view, imageLink);
let menuitemCopyUrl = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyUrl"));
let menuitemCopyImageDataUrl = allMenuItems.find(item => item.label ===
STYLE_INSPECTOR_L10N.getStr("styleinspector.contextmenu.copyImageDataUrl"));
info("Context menu is displayed");
ok(menuitemCopyUrl.visible,
"\"Copy URL\" menu entry is displayed");
ok(menuitemCopyImageDataUrl.visible,
"\"Copy Image Data-URL\" menu entry is displayed");
if (type == "data-uri") {
info("Click Copy Data URI and wait for clipboard");
yield waitForClipboardPromise(() => {
return menuitemCopyImageDataUrl.click();
}, expected);
} else {
info("Click Copy URL and wait for clipboard");
yield waitForClipboardPromise(() => {
return menuitemCopyUrl.click();
}, expected);
}
info("Hide context menu");
}
function getBackgroundImageProperty(view, selector) {
let isRuleView = view instanceof CssRuleView;
if (isRuleView) {
return getRuleViewProperty(view, selector, "background-image");
}
return getComputedViewProperty(view, "background-image");
}

Просмотреть файл

@ -1,22 +1,22 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the add node button and context menu items are present in the UI.
const TEST_URL = "data:text/html;charset=utf-8,<h1>Add node</h1>";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
let {panelDoc} = inspector;
let allMenuItems = openContextMenuAndGetAllItems(inspector);
let menuItem = allMenuItems.find(item => item.id === "node-menu-add");
ok(menuItem, "The item is in the menu");
let toolbarButton =
panelDoc.querySelector("#inspector-toolbar #inspector-element-add-button");
ok(toolbarButton, "The add button is in the toolbar");
});
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the add node button and context menu items are present in the UI.
const TEST_URL = "data:text/html;charset=utf-8,<h1>Add node</h1>";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
let {panelDoc} = inspector;
let allMenuItems = openContextMenuAndGetAllItems(inspector);
let menuItem = allMenuItems.find(item => item.id === "node-menu-add");
ok(menuItem, "The item is in the menu");
let toolbarButton =
panelDoc.querySelector("#inspector-toolbar #inspector-element-add-button");
ok(toolbarButton, "The add button is in the toolbar");
});

Просмотреть файл

@ -1,63 +1,63 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the add node button and context menu items have the right state
// depending on the current selection.
const TEST_URL = URL_ROOT + "doc_inspector_add_node.html";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Select the DOCTYPE element");
let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
yield selectNode(nodes[0], inspector);
assertState(false, inspector,
"The button and item are disabled on DOCTYPE");
info("Select the ::before pseudo-element");
let body = yield getNodeFront("body", inspector);
({nodes} = yield inspector.walker.children(body));
yield selectNode(nodes[0], inspector);
assertState(false, inspector,
"The button and item are disabled on a pseudo-element");
info("Select the svg element");
yield selectNode("svg", inspector);
assertState(false, inspector,
"The button and item are disabled on a SVG element");
info("Select the div#foo element");
yield selectNode("#foo", inspector);
assertState(true, inspector,
"The button and item are enabled on a DIV element");
info("Select the documentElement element (html)");
yield selectNode("html", inspector);
assertState(false, inspector,
"The button and item are disabled on the documentElement");
info("Select the iframe element");
yield selectNode("iframe", inspector);
assertState(false, inspector,
"The button and item are disabled on an IFRAME element");
});
function assertState(isEnabled, inspector, desc) {
let doc = inspector.panelDoc;
let btn = doc.querySelector("#inspector-element-add-button");
// Force an update of the context menu to make sure menu items are updated
// according to the current selection. This normally happens when the menu is
// opened, but for the sake of this test's simplicity, we directly call the
// private update function instead.
let allMenuItems = openContextMenuAndGetAllItems(inspector);
let menuItem = allMenuItems.find(item => item.id === "node-menu-add");
ok(menuItem, "The item is in the menu");
is(!menuItem.disabled, isEnabled, desc);
is(!btn.hasAttribute("disabled"), isEnabled, desc);
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the add node button and context menu items have the right state
// depending on the current selection.
const TEST_URL = URL_ROOT + "doc_inspector_add_node.html";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Select the DOCTYPE element");
let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
yield selectNode(nodes[0], inspector);
assertState(false, inspector,
"The button and item are disabled on DOCTYPE");
info("Select the ::before pseudo-element");
let body = yield getNodeFront("body", inspector);
({nodes} = yield inspector.walker.children(body));
yield selectNode(nodes[0], inspector);
assertState(false, inspector,
"The button and item are disabled on a pseudo-element");
info("Select the svg element");
yield selectNode("svg", inspector);
assertState(false, inspector,
"The button and item are disabled on a SVG element");
info("Select the div#foo element");
yield selectNode("#foo", inspector);
assertState(true, inspector,
"The button and item are enabled on a DIV element");
info("Select the documentElement element (html)");
yield selectNode("html", inspector);
assertState(false, inspector,
"The button and item are disabled on the documentElement");
info("Select the iframe element");
yield selectNode("iframe", inspector);
assertState(false, inspector,
"The button and item are disabled on an IFRAME element");
});
function assertState(isEnabled, inspector, desc) {
let doc = inspector.panelDoc;
let btn = doc.querySelector("#inspector-element-add-button");
// Force an update of the context menu to make sure menu items are updated
// according to the current selection. This normally happens when the menu is
// opened, but for the sake of this test's simplicity, we directly call the
// private update function instead.
let allMenuItems = openContextMenuAndGetAllItems(inspector);
let menuItem = allMenuItems.find(item => item.id === "node-menu-add");
ok(menuItem, "The item is in the menu");
is(!menuItem.disabled, isEnabled, desc);
is(!btn.hasAttribute("disabled"), isEnabled, desc);
}

Просмотреть файл

@ -1,84 +1,84 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that adding nodes does work as expected: the parent gets expanded, the
// new node gets selected.
const TEST_URL = URL_ROOT + "doc_inspector_add_node.html";
const PARENT_TREE_LEVEL = 3;
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Adding in element that has no children and is collapsed");
let parentNode = yield getNodeFront("#foo", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children but that has not been expanded yet");
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children that has been expanded then collapsed");
// Select again #bar and collapse it.
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
collapseNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children that is expanded");
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
});
function* testAddNode(parentNode, inspector) {
let btn = inspector.panelDoc.querySelector("#inspector-element-add-button");
let markupWindow = inspector.markup.win;
let parentContainer = inspector.markup.getContainer(parentNode);
is(parentContainer.tagLine.getAttribute("aria-level"), PARENT_TREE_LEVEL,
"Parent level should be up to date.");
info("Clicking 'add node' and expecting a markup mutation and focus event");
let onMutation = inspector.once("markupmutation");
btn.click();
let mutations = yield onMutation;
info("Expecting an inspector-updated event right after the mutation event " +
"to wait for the new node selection");
yield inspector.once("inspector-updated");
is(mutations.length, 1, "There is one mutation only");
is(mutations[0].added.length, 1, "There is one new node only");
let newNode = mutations[0].added[0];
is(newNode, inspector.selection.nodeFront,
"The new node is selected");
ok(parentContainer.expanded, "The parent node is now expanded");
is(inspector.selection.nodeFront.parentNode(), parentNode,
"The new node is inside the right parent");
let focusedElement = markupWindow.document.activeElement;
let focusedContainer = focusedElement.container;
let selectedContainer = inspector.markup._selectedContainer;
is(selectedContainer.tagLine.getAttribute("aria-level"),
PARENT_TREE_LEVEL + 1, "Added container level should be up to date.");
is(selectedContainer.node, inspector.selection.nodeFront,
"The right container is selected in the markup-view");
ok(selectedContainer.selected, "Selected container is set to selected");
is(focusedContainer.toString(), "[root container]",
"Root container is focused");
}
function collapseNode(node, inspector) {
let container = inspector.markup.getContainer(node);
container.setExpanded(false);
}
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that adding nodes does work as expected: the parent gets expanded, the
// new node gets selected.
const TEST_URL = URL_ROOT + "doc_inspector_add_node.html";
const PARENT_TREE_LEVEL = 3;
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Adding in element that has no children and is collapsed");
let parentNode = yield getNodeFront("#foo", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children but that has not been expanded yet");
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children that has been expanded then collapsed");
// Select again #bar and collapse it.
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
collapseNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children that is expanded");
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
});
function* testAddNode(parentNode, inspector) {
let btn = inspector.panelDoc.querySelector("#inspector-element-add-button");
let markupWindow = inspector.markup.win;
let parentContainer = inspector.markup.getContainer(parentNode);
is(parentContainer.tagLine.getAttribute("aria-level"), PARENT_TREE_LEVEL,
"Parent level should be up to date.");
info("Clicking 'add node' and expecting a markup mutation and focus event");
let onMutation = inspector.once("markupmutation");
btn.click();
let mutations = yield onMutation;
info("Expecting an inspector-updated event right after the mutation event " +
"to wait for the new node selection");
yield inspector.once("inspector-updated");
is(mutations.length, 1, "There is one mutation only");
is(mutations[0].added.length, 1, "There is one new node only");
let newNode = mutations[0].added[0];
is(newNode, inspector.selection.nodeFront,
"The new node is selected");
ok(parentContainer.expanded, "The parent node is now expanded");
is(inspector.selection.nodeFront.parentNode(), parentNode,
"The new node is inside the right parent");
let focusedElement = markupWindow.document.activeElement;
let focusedContainer = focusedElement.container;
let selectedContainer = inspector.markup._selectedContainer;
is(selectedContainer.tagLine.getAttribute("aria-level"),
PARENT_TREE_LEVEL + 1, "Added container level should be up to date.");
is(selectedContainer.node, inspector.selection.nodeFront,
"The right container is selected in the markup-view");
ok(selectedContainer.selected, "Selected container is set to selected");
is(focusedContainer.toString(), "[root container]",
"Root container is focused");
}
function collapseNode(node, inspector) {
let container = inspector.markup.getContainer(node);
container.setExpanded(false);
}

Просмотреть файл

@ -1,71 +1,71 @@
/* 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/ */
"use strict";
// Test that the breadcrumbs keybindings work.
const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs.html";
const TEST_DATA = [{
desc: "Pressing left should select the parent <body>",
key: "VK_LEFT",
newSelection: "body"
}, {
desc: "Pressing left again should select the parent <html>",
key: "VK_LEFT",
newSelection: "html"
}, {
desc: "Pressing left again should stay on <html>, it's the first element",
key: "VK_LEFT",
newSelection: "html"
}, {
desc: "Pressing right should go to <body>",
key: "VK_RIGHT",
newSelection: "body"
}, {
desc: "Pressing right again should go to #i2",
key: "VK_RIGHT",
newSelection: "#i2"
}, {
desc: "Pressing right again should stay on #i2, it's the last element",
key: "VK_RIGHT",
newSelection: "#i2"
}];
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URI);
info("Selecting the test node");
yield selectNode("#i2", inspector);
info("Clicking on the corresponding breadcrumbs node to focus it");
let container = inspector.panelDoc.getElementById("inspector-breadcrumbs");
let button = container.querySelector("button[checked]");
button.click();
let currentSelection = "#id2";
for (let {desc, key, newSelection} of TEST_DATA) {
info(desc);
// If the selection will change, wait for the breadcrumb to update,
// otherwise continue.
let onUpdated = null;
if (newSelection !== currentSelection) {
info("Expecting a new node to be selected");
onUpdated = inspector.once("breadcrumbs-updated");
}
EventUtils.synthesizeKey(key, {});
yield onUpdated;
let newNodeFront = yield getNodeFront(newSelection, inspector);
is(newNodeFront, inspector.selection.nodeFront,
"The current selection is correct");
is(container.getAttribute("aria-activedescendant"),
container.querySelector("button[checked]").id,
"aria-activedescendant is set correctly");
currentSelection = newSelection;
}
});
/* 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/ */
"use strict";
// Test that the breadcrumbs keybindings work.
const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs.html";
const TEST_DATA = [{
desc: "Pressing left should select the parent <body>",
key: "VK_LEFT",
newSelection: "body"
}, {
desc: "Pressing left again should select the parent <html>",
key: "VK_LEFT",
newSelection: "html"
}, {
desc: "Pressing left again should stay on <html>, it's the first element",
key: "VK_LEFT",
newSelection: "html"
}, {
desc: "Pressing right should go to <body>",
key: "VK_RIGHT",
newSelection: "body"
}, {
desc: "Pressing right again should go to #i2",
key: "VK_RIGHT",
newSelection: "#i2"
}, {
desc: "Pressing right again should stay on #i2, it's the last element",
key: "VK_RIGHT",
newSelection: "#i2"
}];
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URI);
info("Selecting the test node");
yield selectNode("#i2", inspector);
info("Clicking on the corresponding breadcrumbs node to focus it");
let container = inspector.panelDoc.getElementById("inspector-breadcrumbs");
let button = container.querySelector("button[checked]");
button.click();
let currentSelection = "#id2";
for (let {desc, key, newSelection} of TEST_DATA) {
info(desc);
// If the selection will change, wait for the breadcrumb to update,
// otherwise continue.
let onUpdated = null;
if (newSelection !== currentSelection) {
info("Expecting a new node to be selected");
onUpdated = inspector.once("breadcrumbs-updated");
}
EventUtils.synthesizeKey(key, {});
yield onUpdated;
let newNodeFront = yield getNodeFront(newSelection, inspector);
is(newNodeFront, inspector.selection.nodeFront,
"The current selection is correct");
is(container.getAttribute("aria-activedescendant"),
container.querySelector("button[checked]").id,
"aria-activedescendant is set correctly");
currentSelection = newSelection;
}
});

Просмотреть файл

@ -1,30 +1,30 @@
/* 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";
// Test that the highlighter can go inside <embed> elements
const TEST_URL = URL_ROOT + "doc_inspector_embed.html";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Get a node inside the <embed> element and select/highlight it");
let body = yield getEmbeddedBody(inspector);
yield selectAndHighlightNode(body, inspector);
let selectedNode = inspector.selection.nodeFront;
is(selectedNode.tagName.toLowerCase(), "body", "The selected node is <body>");
ok(selectedNode.baseURI.endsWith("doc_inspector_menu.html"),
"The selected node is the <body> node inside the <embed> element");
});
function* getEmbeddedBody({walker}) {
let embed = yield walker.querySelector(walker.rootNode, "embed");
let {nodes} = yield walker.children(embed);
let contentDoc = nodes[0];
let body = yield walker.querySelector(contentDoc, "body");
return body;
}
/* 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";
// Test that the highlighter can go inside <embed> elements
const TEST_URL = URL_ROOT + "doc_inspector_embed.html";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Get a node inside the <embed> element and select/highlight it");
let body = yield getEmbeddedBody(inspector);
yield selectAndHighlightNode(body, inspector);
let selectedNode = inspector.selection.nodeFront;
is(selectedNode.tagName.toLowerCase(), "body", "The selected node is <body>");
ok(selectedNode.baseURI.endsWith("doc_inspector_menu.html"),
"The selected node is the <body> node inside the <embed> element");
});
function* getEmbeddedBody({walker}) {
let embed = yield walker.querySelector(walker.rootNode, "embed");
let {nodes} = yield walker.children(embed);
let contentDoc = nodes[0];
let body = yield walker.querySelector(contentDoc, "body");
return body;
}

Просмотреть файл

@ -1,39 +1,39 @@
/* 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";
// Test that the eyedropper can copy colors to the clipboard
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const TEST_URI = "data:text/html;charset=utf-8,<style>html{background:red}</style>";
add_task(function* () {
let helper = yield openInspectorForURL(TEST_URI)
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
helper.prefix = ID;
let {show, finalize,
waitForElementAttributeSet, waitForElementAttributeRemoved} = helper;
info("Show the eyedropper with the copyOnSelect option");
yield show("html", {copyOnSelect: true});
info("Make sure to wait until the eyedropper is done taking a screenshot of the page");
yield waitForElementAttributeSet("root", "drawn", helper);
yield waitForClipboardPromise(() => {
info("Activate the eyedropper so the background color is copied");
EventUtils.synthesizeKey("VK_RETURN", {});
}, "#FF0000");
ok(true, "The clipboard contains the right value");
yield waitForElementAttributeRemoved("root", "drawn", helper);
yield waitForElementAttributeSet("root", "hidden", helper);
ok(true, "The eyedropper is now hidden");
finalize();
});
/* 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";
// Test that the eyedropper can copy colors to the clipboard
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const TEST_URI = "data:text/html;charset=utf-8,<style>html{background:red}</style>";
add_task(function* () {
let helper = yield openInspectorForURL(TEST_URI)
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
helper.prefix = ID;
let {show, finalize,
waitForElementAttributeSet, waitForElementAttributeRemoved} = helper;
info("Show the eyedropper with the copyOnSelect option");
yield show("html", {copyOnSelect: true});
info("Make sure to wait until the eyedropper is done taking a screenshot of the page");
yield waitForElementAttributeSet("root", "drawn", helper);
yield waitForClipboardPromise(() => {
info("Activate the eyedropper so the background color is copied");
EventUtils.synthesizeKey("VK_RETURN", {});
}, "#FF0000");
ok(true, "The clipboard contains the right value");
yield waitForElementAttributeRemoved("root", "drawn", helper);
yield waitForElementAttributeSet("root", "hidden", helper);
ok(true, "The eyedropper is now hidden");
finalize();
});

Просмотреть файл

@ -1,30 +1,30 @@
/* 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";
// Test that the eyedropper opens correctly even when the page defines CSP headers.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const TEST_URI = URL_ROOT + "doc_inspector_csp.html";
add_task(function* () {
let helper = yield openInspectorForURL(TEST_URI)
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
helper.prefix = ID;
let {show, hide, finalize, isElementHidden, waitForElementAttributeSet} = helper;
info("Try to display the eyedropper");
yield show("html");
let hidden = yield isElementHidden("root");
ok(!hidden, "The eyedropper is now shown");
info("Wait until the eyedropper is done taking a screenshot of the page");
yield waitForElementAttributeSet("root", "drawn", helper);
ok(true, "The image data was retrieved successfully from the window");
yield hide();
finalize();
});
/* 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";
// Test that the eyedropper opens correctly even when the page defines CSP headers.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const TEST_URI = URL_ROOT + "doc_inspector_csp.html";
add_task(function* () {
let helper = yield openInspectorForURL(TEST_URI)
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
helper.prefix = ID;
let {show, hide, finalize, isElementHidden, waitForElementAttributeSet} = helper;
info("Try to display the eyedropper");
yield show("html");
let hidden = yield isElementHidden("root");
ok(!hidden, "The eyedropper is now shown");
info("Wait until the eyedropper is done taking a screenshot of the page");
yield waitForElementAttributeSet("root", "drawn", helper);
ok(true, "The image data was retrieved successfully from the window");
yield hide();
finalize();
});

Просмотреть файл

@ -1,141 +1,141 @@
/* 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";
// Test the eyedropper mouse and keyboard handling.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const TEST_URI = `
<style>
html{width:100%;height:100%;}
</style>
<body>eye-dropper test</body>`;
const MOVE_EVENTS_DATA = [
{type: "mouse", x: 200, y: 100, expected: {x: 200, y: 100}},
{type: "mouse", x: 100, y: 200, expected: {x: 100, y: 200}},
{type: "keyboard", key: "VK_LEFT", expected: {x: 99, y: 200}},
{type: "keyboard", key: "VK_LEFT", shift: true, expected: {x: 89, y: 200}},
{type: "keyboard", key: "VK_RIGHT", expected: {x: 90, y: 200}},
{type: "keyboard", key: "VK_RIGHT", shift: true, expected: {x: 100, y: 200}},
{type: "keyboard", key: "VK_DOWN", expected: {x: 100, y: 201}},
{type: "keyboard", key: "VK_DOWN", shift: true, expected: {x: 100, y: 211}},
{type: "keyboard", key: "VK_UP", expected: {x: 100, y: 210}},
{type: "keyboard", key: "VK_UP", shift: true, expected: {x: 100, y: 200}},
// Mouse initialization for left and top snapping
{type: "mouse", x: 7, y: 7, expected: {x: 7, y: 7}},
// Left Snapping
{type: "keyboard", key: "VK_LEFT", shift: true, expected: {x: 0, y: 7},
desc: "Left Snapping to x=0"},
// Top Snapping
{type: "keyboard", key: "VK_UP", shift: true, expected: {x: 0, y: 0},
desc: "Top Snapping to y=0"},
// Mouse initialization for right snapping
{
type: "mouse",
x: (width, height) => width - 5,
y: 0,
expected: {
x: (width, height) => width - 5,
y: 0
}
},
// Right snapping
{
type: "keyboard",
key: "VK_RIGHT",
shift: true,
expected: {
x: (width, height) => width,
y: 0
},
desc: "Right snapping to x=max window width available"
},
// Mouse initialization for bottom snapping
{
type: "mouse",
x: 0,
y: (width, height) => height - 5,
expected: {
x: 0,
y: (width, height) => height - 5
}
},
// Bottom snapping
{
type: "keyboard",
key: "VK_DOWN",
shift: true,
expected: {
x: 0,
y: (width, height) => height
},
desc: "Bottom snapping to y=max window height available"
},
];
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(
"data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)({inspector, testActor});
helper.prefix = ID;
yield helper.show("html");
yield respondsToMoveEvents(helper, testActor);
yield respondsToReturnAndEscape(helper);
helper.finalize();
});
function* respondsToMoveEvents(helper, testActor) {
info("Checking that the eyedropper responds to events from the mouse and keyboard");
let {mouse} = helper;
let {width, height} = yield testActor.getBoundingClientRect("html");
for (let {type, x, y, key, shift, expected, desc} of MOVE_EVENTS_DATA) {
x = typeof x === "function" ? x(width, height) : x;
y = typeof y === "function" ? y(width, height) : y;
expected.x = typeof expected.x === "function" ?
expected.x(width, height) : expected.x;
expected.y = typeof expected.y === "function" ?
expected.y(width, height) : expected.y;
if (typeof desc === "undefined") {
info(`Simulating a ${type} event to move to ${expected.x} ${expected.y}`);
} else {
info(`Simulating ${type} event: ${desc}`);
}
if (type === "mouse") {
yield mouse.move(x, y);
} else if (type === "keyboard") {
let options = shift ? {shiftKey: true} : {};
yield EventUtils.synthesizeKey(key, options);
}
yield checkPosition(expected, helper);
}
}
function* checkPosition({x, y}, {getElementAttribute}) {
let style = yield getElementAttribute("root", "style");
is(style, `top:${y}px;left:${x}px;`,
`The eyedropper is at the expected ${x} ${y} position`);
}
function* respondsToReturnAndEscape({isElementHidden, show}) {
info("Simulating return to select the color and hide the eyedropper");
yield EventUtils.synthesizeKey("VK_RETURN", {});
let hidden = yield isElementHidden("root");
ok(hidden, "The eyedropper has been hidden");
info("Showing the eyedropper again and simulating escape to hide it");
yield show("html");
yield EventUtils.synthesizeKey("VK_ESCAPE", {});
hidden = yield isElementHidden("root");
ok(hidden, "The eyedropper has been hidden again");
}
/* 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";
// Test the eyedropper mouse and keyboard handling.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const TEST_URI = `
<style>
html{width:100%;height:100%;}
</style>
<body>eye-dropper test</body>`;
const MOVE_EVENTS_DATA = [
{type: "mouse", x: 200, y: 100, expected: {x: 200, y: 100}},
{type: "mouse", x: 100, y: 200, expected: {x: 100, y: 200}},
{type: "keyboard", key: "VK_LEFT", expected: {x: 99, y: 200}},
{type: "keyboard", key: "VK_LEFT", shift: true, expected: {x: 89, y: 200}},
{type: "keyboard", key: "VK_RIGHT", expected: {x: 90, y: 200}},
{type: "keyboard", key: "VK_RIGHT", shift: true, expected: {x: 100, y: 200}},
{type: "keyboard", key: "VK_DOWN", expected: {x: 100, y: 201}},
{type: "keyboard", key: "VK_DOWN", shift: true, expected: {x: 100, y: 211}},
{type: "keyboard", key: "VK_UP", expected: {x: 100, y: 210}},
{type: "keyboard", key: "VK_UP", shift: true, expected: {x: 100, y: 200}},
// Mouse initialization for left and top snapping
{type: "mouse", x: 7, y: 7, expected: {x: 7, y: 7}},
// Left Snapping
{type: "keyboard", key: "VK_LEFT", shift: true, expected: {x: 0, y: 7},
desc: "Left Snapping to x=0"},
// Top Snapping
{type: "keyboard", key: "VK_UP", shift: true, expected: {x: 0, y: 0},
desc: "Top Snapping to y=0"},
// Mouse initialization for right snapping
{
type: "mouse",
x: (width, height) => width - 5,
y: 0,
expected: {
x: (width, height) => width - 5,
y: 0
}
},
// Right snapping
{
type: "keyboard",
key: "VK_RIGHT",
shift: true,
expected: {
x: (width, height) => width,
y: 0
},
desc: "Right snapping to x=max window width available"
},
// Mouse initialization for bottom snapping
{
type: "mouse",
x: 0,
y: (width, height) => height - 5,
expected: {
x: 0,
y: (width, height) => height - 5
}
},
// Bottom snapping
{
type: "keyboard",
key: "VK_DOWN",
shift: true,
expected: {
x: 0,
y: (width, height) => height
},
desc: "Bottom snapping to y=max window height available"
},
];
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(
"data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)({inspector, testActor});
helper.prefix = ID;
yield helper.show("html");
yield respondsToMoveEvents(helper, testActor);
yield respondsToReturnAndEscape(helper);
helper.finalize();
});
function* respondsToMoveEvents(helper, testActor) {
info("Checking that the eyedropper responds to events from the mouse and keyboard");
let {mouse} = helper;
let {width, height} = yield testActor.getBoundingClientRect("html");
for (let {type, x, y, key, shift, expected, desc} of MOVE_EVENTS_DATA) {
x = typeof x === "function" ? x(width, height) : x;
y = typeof y === "function" ? y(width, height) : y;
expected.x = typeof expected.x === "function" ?
expected.x(width, height) : expected.x;
expected.y = typeof expected.y === "function" ?
expected.y(width, height) : expected.y;
if (typeof desc === "undefined") {
info(`Simulating a ${type} event to move to ${expected.x} ${expected.y}`);
} else {
info(`Simulating ${type} event: ${desc}`);
}
if (type === "mouse") {
yield mouse.move(x, y);
} else if (type === "keyboard") {
let options = shift ? {shiftKey: true} : {};
yield EventUtils.synthesizeKey(key, options);
}
yield checkPosition(expected, helper);
}
}
function* checkPosition({x, y}, {getElementAttribute}) {
let style = yield getElementAttribute("root", "style");
is(style, `top:${y}px;left:${x}px;`,
`The eyedropper is at the expected ${x} ${y} position`);
}
function* respondsToReturnAndEscape({isElementHidden, show}) {
info("Simulating return to select the color and hide the eyedropper");
yield EventUtils.synthesizeKey("VK_RETURN", {});
let hidden = yield isElementHidden("root");
ok(hidden, "The eyedropper has been hidden");
info("Showing the eyedropper again and simulating escape to hide it");
yield show("html");
yield EventUtils.synthesizeKey("VK_ESCAPE", {});
hidden = yield isElementHidden("root");
ok(hidden, "The eyedropper has been hidden again");
}

Просмотреть файл

@ -1,115 +1,115 @@
/* 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";
// Test the position of the eyedropper label.
// It should move around when the eyedropper is close to the edges of the viewport so as
// to always stay visible.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const HTML = `
<style>
html, body {height: 100%; margin: 0;}
body {background: linear-gradient(red, gold); display: flex; justify-content: center;
align-items: center;}
</style>
Eyedropper label position test
`;
const TEST_PAGE = "data:text/html;charset=utf-8," + encodeURI(HTML);
const TEST_DATA = [{
desc: "Move the mouse to the center of the screen",
getCoordinates: (width, height) => {
return {x: width / 2, y: height / 2};
},
expectedPositions: {top: false, right: false, left: false}
}, {
desc: "Move the mouse to the center left",
getCoordinates: (width, height) => {
return {x: 0, y: height / 2};
},
expectedPositions: {top: false, right: true, left: false}
}, {
desc: "Move the mouse to the center right",
getCoordinates: (width, height) => {
return {x: width, y: height / 2};
},
expectedPositions: {top: false, right: false, left: true}
}, {
desc: "Move the mouse to the bottom center",
getCoordinates: (width, height) => {
return {x: width / 2, y: height};
},
expectedPositions: {top: true, right: false, left: false}
}, {
desc: "Move the mouse to the bottom left",
getCoordinates: (width, height) => {
return {x: 0, y: height};
},
expectedPositions: {top: true, right: true, left: false}
}, {
desc: "Move the mouse to the bottom right",
getCoordinates: (width, height) => {
return {x: width, y: height};
},
expectedPositions: {top: true, right: false, left: true}
}, {
desc: "Move the mouse to the top left",
getCoordinates: (width, height) => {
return {x: 0, y: 0};
},
expectedPositions: {top: false, right: true, left: false}
}, {
desc: "Move the mouse to the top right",
getCoordinates: (width, height) => {
return {x: width, y: 0};
},
expectedPositions: {top: false, right: false, left: true}
}];
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(TEST_PAGE);
let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)({inspector, testActor});
helper.prefix = ID;
let {mouse, show, hide, finalize} = helper;
let {width, height} = yield testActor.getBoundingClientRect("html");
// This test fails in non-e10s windows if we use width and height. For some reasons, the
// mouse events can't be dispatched/handled properly when we try to move the eyedropper
// to the far right and/or bottom of the screen. So just removing 10px from each side
// fixes it.
width -= 10;
height -= 10;
info("Show the eyedropper on the page");
yield show("html");
info("Move the eyedropper around and check that the label appears at the right place");
for (let {desc, getCoordinates, expectedPositions} of TEST_DATA) {
info(desc);
let {x, y} = getCoordinates(width, height);
info(`Moving the mouse to ${x} ${y}`);
yield mouse.move(x, y);
yield checkLabelPositionAttributes(helper, expectedPositions);
}
info("Hide the eyedropper");
yield hide();
finalize();
});
function* checkLabelPositionAttributes(helper, positions) {
for (let position in positions) {
is((yield hasAttribute(helper, position)), positions[position],
`The label was ${positions[position] ? "" : "not "}moved to the ${position}`);
}
}
function* hasAttribute({getElementAttribute}, name) {
let value = yield getElementAttribute("root", name);
return value !== null;
}
/* 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";
// Test the position of the eyedropper label.
// It should move around when the eyedropper is close to the edges of the viewport so as
// to always stay visible.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const HTML = `
<style>
html, body {height: 100%; margin: 0;}
body {background: linear-gradient(red, gold); display: flex; justify-content: center;
align-items: center;}
</style>
Eyedropper label position test
`;
const TEST_PAGE = "data:text/html;charset=utf-8," + encodeURI(HTML);
const TEST_DATA = [{
desc: "Move the mouse to the center of the screen",
getCoordinates: (width, height) => {
return {x: width / 2, y: height / 2};
},
expectedPositions: {top: false, right: false, left: false}
}, {
desc: "Move the mouse to the center left",
getCoordinates: (width, height) => {
return {x: 0, y: height / 2};
},
expectedPositions: {top: false, right: true, left: false}
}, {
desc: "Move the mouse to the center right",
getCoordinates: (width, height) => {
return {x: width, y: height / 2};
},
expectedPositions: {top: false, right: false, left: true}
}, {
desc: "Move the mouse to the bottom center",
getCoordinates: (width, height) => {
return {x: width / 2, y: height};
},
expectedPositions: {top: true, right: false, left: false}
}, {
desc: "Move the mouse to the bottom left",
getCoordinates: (width, height) => {
return {x: 0, y: height};
},
expectedPositions: {top: true, right: true, left: false}
}, {
desc: "Move the mouse to the bottom right",
getCoordinates: (width, height) => {
return {x: width, y: height};
},
expectedPositions: {top: true, right: false, left: true}
}, {
desc: "Move the mouse to the top left",
getCoordinates: (width, height) => {
return {x: 0, y: 0};
},
expectedPositions: {top: false, right: true, left: false}
}, {
desc: "Move the mouse to the top right",
getCoordinates: (width, height) => {
return {x: width, y: 0};
},
expectedPositions: {top: false, right: false, left: true}
}];
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(TEST_PAGE);
let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)({inspector, testActor});
helper.prefix = ID;
let {mouse, show, hide, finalize} = helper;
let {width, height} = yield testActor.getBoundingClientRect("html");
// This test fails in non-e10s windows if we use width and height. For some reasons, the
// mouse events can't be dispatched/handled properly when we try to move the eyedropper
// to the far right and/or bottom of the screen. So just removing 10px from each side
// fixes it.
width -= 10;
height -= 10;
info("Show the eyedropper on the page");
yield show("html");
info("Move the eyedropper around and check that the label appears at the right place");
for (let {desc, getCoordinates, expectedPositions} of TEST_DATA) {
info(desc);
let {x, y} = getCoordinates(width, height);
info(`Moving the mouse to ${x} ${y}`);
yield mouse.move(x, y);
yield checkLabelPositionAttributes(helper, expectedPositions);
}
info("Hide the eyedropper");
yield hide();
finalize();
});
function* checkLabelPositionAttributes(helper, positions) {
for (let position in positions) {
is((yield hasAttribute(helper, position)), positions[position],
`The label was ${positions[position] ? "" : "not "}moved to the ${position}`);
}
}
function* hasAttribute({getElementAttribute}, name) {
let value = yield getElementAttribute("root", name);
return value !== null;
}

Просмотреть файл

@ -1,42 +1,42 @@
/* 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";
// Test the basic structure of the eye-dropper highlighter.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
add_task(function* () {
let helper = yield openInspectorForURL("data:text/html;charset=utf-8,eye-dropper test")
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
helper.prefix = ID;
yield isInitiallyHidden(helper);
yield canBeShownAndHidden(helper);
helper.finalize();
});
function* isInitiallyHidden({isElementHidden}) {
info("Checking that the eyedropper is hidden by default");
let hidden = yield isElementHidden("root");
ok(hidden, "The eyedropper is hidden by default");
}
function* canBeShownAndHidden({show, hide, isElementHidden, getElementAttribute}) {
info("Asking to show and hide the highlighter actually works");
yield show("html");
let hidden = yield isElementHidden("root");
ok(!hidden, "The eyedropper is now shown");
let style = yield getElementAttribute("root", "style");
is(style, "top:100px;left:100px;", "The eyedropper is correctly positioned");
yield hide();
hidden = yield isElementHidden("root");
ok(hidden, "The eyedropper is now hidden again");
}
/* 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";
// Test the basic structure of the eye-dropper highlighter.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
add_task(function* () {
let helper = yield openInspectorForURL("data:text/html;charset=utf-8,eye-dropper test")
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
helper.prefix = ID;
yield isInitiallyHidden(helper);
yield canBeShownAndHidden(helper);
helper.finalize();
});
function* isInitiallyHidden({isElementHidden}) {
info("Checking that the eyedropper is hidden by default");
let hidden = yield isElementHidden("root");
ok(hidden, "The eyedropper is hidden by default");
}
function* canBeShownAndHidden({show, hide, isElementHidden, getElementAttribute}) {
info("Asking to show and hide the highlighter actually works");
yield show("html");
let hidden = yield isElementHidden("root");
ok(!hidden, "The eyedropper is now shown");
let style = yield getElementAttribute("root", "style");
is(style, "top:100px;left:100px;", "The eyedropper is correctly positioned");
yield hide();
hidden = yield isElementHidden("root");
ok(hidden, "The eyedropper is now hidden again");
}

Просмотреть файл

@ -1,42 +1,42 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the eyedropper icons in the toolbar and in the color picker aren't displayed
// when the page isn't an HTML one.
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_xbl.xul";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Check the inspector toolbar");
let button = inspector.panelDoc.querySelector("#inspector-eyedropper-toggle");
ok(!isVisible(button), "The button is hidden in the toolbar");
info("Check the color picker");
yield selectNode("#scale", inspector);
// Find the color swatch in the rule-view.
let ruleView = inspector.ruleview.view;
let ruleViewDocument = ruleView.styleDocument;
let swatchEl = ruleViewDocument.querySelector(".ruleview-colorswatch");
info("Open the color picker");
let cPicker = ruleView.tooltips.colorPicker;
let onColorPickerReady = cPicker.once("ready");
swatchEl.click();
yield onColorPickerReady;
button = cPicker.tooltip.doc.querySelector("#eyedropper-button");
ok(isDisabled(button), "The button is disabled in the color picker");
});
function isVisible(button) {
return button.getBoxQuads().length !== 0;
}
function isDisabled(button) {
return button.disabled;
}
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that the eyedropper icons in the toolbar and in the color picker aren't displayed
// when the page isn't an HTML one.
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_xbl.xul";
add_task(function* () {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Check the inspector toolbar");
let button = inspector.panelDoc.querySelector("#inspector-eyedropper-toggle");
ok(!isVisible(button), "The button is hidden in the toolbar");
info("Check the color picker");
yield selectNode("#scale", inspector);
// Find the color swatch in the rule-view.
let ruleView = inspector.ruleview.view;
let ruleViewDocument = ruleView.styleDocument;
let swatchEl = ruleViewDocument.querySelector(".ruleview-colorswatch");
info("Open the color picker");
let cPicker = ruleView.tooltips.colorPicker;
let onColorPickerReady = cPicker.once("ready");
swatchEl.click();
yield onColorPickerReady;
button = cPicker.tooltip.doc.querySelector("#eyedropper-button");
ok(isDisabled(button), "The button is disabled in the color picker");
});
function isVisible(button) {
return button.getBoxQuads().length !== 0;
}
function isDisabled(button) {
return button.disabled;
}

Просмотреть файл

@ -1,64 +1,64 @@
/* 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";
// Test that the keybindings for Picker work alright
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
add_task(function* () {
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
yield startPicker(toolbox);
info("Selecting the simple-div1 DIV");
yield moveMouseOver("#simple-div1");
ok((yield testActor.assertHighlightedNode("#simple-div1")),
"The highlighter shows #simple-div1. OK.");
// First Child selection
info("Testing first-child selection.");
yield doKeyHover({key: "VK_RIGHT", options: {}});
ok((yield testActor.assertHighlightedNode("#useless-para")),
"The highlighter shows #useless-para. OK.");
info("Selecting the useful-para paragraph DIV");
yield moveMouseOver("#useful-para");
ok((yield testActor.assertHighlightedNode("#useful-para")),
"The highlighter shows #useful-para. OK.");
yield doKeyHover({key: "VK_RIGHT", options: {}});
ok((yield testActor.assertHighlightedNode("#bold")),
"The highlighter shows #bold. OK.");
info("Going back up to the simple-div1 DIV");
yield doKeyHover({key: "VK_LEFT", options: {}});
yield doKeyHover({key: "VK_LEFT", options: {}});
ok((yield testActor.assertHighlightedNode("#simple-div1")),
"The highlighter shows #simple-div1. OK.");
info("First child selection test Passed.");
info("Stopping the picker");
yield toolbox.highlighterUtils.stopPicker();
function doKeyHover(args) {
info("Key pressed. Waiting for element to be highlighted/hovered");
testActor.synthesizeKey(args);
return inspector.toolbox.once("picker-node-hovered");
}
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
testActor.synthesizeMouse({
options: {type: "mousemove"},
center: true,
selector: selector
});
return inspector.toolbox.once("picker-node-hovered");
}
});
/* 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";
// Test that the keybindings for Picker work alright
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
add_task(function* () {
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
yield startPicker(toolbox);
info("Selecting the simple-div1 DIV");
yield moveMouseOver("#simple-div1");
ok((yield testActor.assertHighlightedNode("#simple-div1")),
"The highlighter shows #simple-div1. OK.");
// First Child selection
info("Testing first-child selection.");
yield doKeyHover({key: "VK_RIGHT", options: {}});
ok((yield testActor.assertHighlightedNode("#useless-para")),
"The highlighter shows #useless-para. OK.");
info("Selecting the useful-para paragraph DIV");
yield moveMouseOver("#useful-para");
ok((yield testActor.assertHighlightedNode("#useful-para")),
"The highlighter shows #useful-para. OK.");
yield doKeyHover({key: "VK_RIGHT", options: {}});
ok((yield testActor.assertHighlightedNode("#bold")),
"The highlighter shows #bold. OK.");
info("Going back up to the simple-div1 DIV");
yield doKeyHover({key: "VK_LEFT", options: {}});
yield doKeyHover({key: "VK_LEFT", options: {}});
ok((yield testActor.assertHighlightedNode("#simple-div1")),
"The highlighter shows #simple-div1. OK.");
info("First child selection test Passed.");
info("Stopping the picker");
yield toolbox.highlighterUtils.stopPicker();
function doKeyHover(args) {
info("Key pressed. Waiting for element to be highlighted/hovered");
testActor.synthesizeKey(args);
return inspector.toolbox.once("picker-node-hovered");
}
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
testActor.synthesizeMouse({
options: {type: "mousemove"},
center: true,
selector: selector
});
return inspector.toolbox.once("picker-node-hovered");
}
});

Просмотреть файл

@ -1,64 +1,64 @@
/* 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";
// Test that the keybindings for Picker work alright
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
add_task(function* () {
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
yield startPicker(toolbox);
// Previously chosen child memory
info("Testing whether previously chosen child is remembered");
info("Selecting the ahoy paragraph DIV");
yield moveMouseOver("#ahoy");
yield doKeyHover({key: "VK_LEFT", options: {}});
ok((yield testActor.assertHighlightedNode("#simple-div2")),
"The highlighter shows #simple-div2. OK.");
yield doKeyHover({key: "VK_RIGHT", options: {}});
ok((yield testActor.assertHighlightedNode("#ahoy")),
"The highlighter shows #ahoy. OK.");
info("Going back up to the complex-div DIV");
yield doKeyHover({key: "VK_LEFT", options: {}});
yield doKeyHover({key: "VK_LEFT", options: {}});
ok((yield testActor.assertHighlightedNode("#complex-div")),
"The highlighter shows #complex-div. OK.");
yield doKeyHover({key: "VK_RIGHT", options: {}});
ok((yield testActor.assertHighlightedNode("#simple-div2")),
"The highlighter shows #simple-div2. OK.");
info("Previously chosen child is remembered. Passed.");
info("Stopping the picker");
yield toolbox.highlighterUtils.stopPicker();
function doKeyHover(args) {
info("Key pressed. Waiting for element to be highlighted/hovered");
let onHighlighterReady = toolbox.once("highlighter-ready");
let onPickerNodeHovered = inspector.toolbox.once("picker-node-hovered");
testActor.synthesizeKey(args);
return promise.all([onHighlighterReady, onPickerNodeHovered]);
}
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
let onHighlighterReady = toolbox.once("highlighter-ready");
let onPickerNodeHovered = inspector.toolbox.once("picker-node-hovered");
testActor.synthesizeMouse({
options: {type: "mousemove"},
center: true,
selector: selector
});
return promise.all([onHighlighterReady, onPickerNodeHovered]);
}
});
/* 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";
// Test that the keybindings for Picker work alright
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
add_task(function* () {
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
yield startPicker(toolbox);
// Previously chosen child memory
info("Testing whether previously chosen child is remembered");
info("Selecting the ahoy paragraph DIV");
yield moveMouseOver("#ahoy");
yield doKeyHover({key: "VK_LEFT", options: {}});
ok((yield testActor.assertHighlightedNode("#simple-div2")),
"The highlighter shows #simple-div2. OK.");
yield doKeyHover({key: "VK_RIGHT", options: {}});
ok((yield testActor.assertHighlightedNode("#ahoy")),
"The highlighter shows #ahoy. OK.");
info("Going back up to the complex-div DIV");
yield doKeyHover({key: "VK_LEFT", options: {}});
yield doKeyHover({key: "VK_LEFT", options: {}});
ok((yield testActor.assertHighlightedNode("#complex-div")),
"The highlighter shows #complex-div. OK.");
yield doKeyHover({key: "VK_RIGHT", options: {}});
ok((yield testActor.assertHighlightedNode("#simple-div2")),
"The highlighter shows #simple-div2. OK.");
info("Previously chosen child is remembered. Passed.");
info("Stopping the picker");
yield toolbox.highlighterUtils.stopPicker();
function doKeyHover(args) {
info("Key pressed. Waiting for element to be highlighted/hovered");
let onHighlighterReady = toolbox.once("highlighter-ready");
let onPickerNodeHovered = inspector.toolbox.once("picker-node-hovered");
testActor.synthesizeKey(args);
return promise.all([onHighlighterReady, onPickerNodeHovered]);
}
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
let onHighlighterReady = toolbox.once("highlighter-ready");
let onPickerNodeHovered = inspector.toolbox.once("picker-node-hovered");
testActor.synthesizeMouse({
options: {type: "mousemove"},
center: true,
selector: selector
});
return promise.all([onHighlighterReady, onPickerNodeHovered]);
}
});

Просмотреть файл

@ -1,71 +1,71 @@
/* 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";
// Test that the keybindings for Picker work alright
const IS_OSX = Services.appinfo.OS === "Darwin";
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
add_task(function* () {
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
yield startPicker(toolbox);
yield moveMouseOver("#another");
info("Testing enter/return key as pick-node command");
yield doKeyPick({key: "VK_RETURN", options: {}});
is(inspector.selection.nodeFront.id, "another",
"The #another node was selected. Passed.");
info("Testing escape key as cancel-picker command");
yield startPicker(toolbox);
yield moveMouseOver("#ahoy");
yield doKeyStop({key: "VK_ESCAPE", options: {}});
is(inspector.selection.nodeFront.id, "another",
"The #another DIV is still selected. Passed.");
info("Testing Ctrl+Shift+C shortcut as cancel-picker command");
yield startPicker(toolbox);
yield moveMouseOver("#ahoy");
let shortcutOpts = {key: "VK_C", options: {}};
if (IS_OSX) {
shortcutOpts.options.metaKey = true;
shortcutOpts.options.altKey = true;
} else {
shortcutOpts.options.ctrlKey = true;
shortcutOpts.options.shiftKey = true;
}
yield doKeyStop(shortcutOpts);
is(inspector.selection.nodeFront.id, "another",
"The #another DIV is still selected. Passed.");
function doKeyPick(args) {
info("Key pressed. Waiting for element to be picked");
testActor.synthesizeKey(args);
return promise.all([
inspector.selection.once("new-node-front"),
inspector.once("inspector-updated")
]);
}
function doKeyStop(args) {
info("Key pressed. Waiting for picker to be canceled");
testActor.synthesizeKey(args);
return inspector.toolbox.once("picker-stopped");
}
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
let onHighlighterReady = toolbox.once("highlighter-ready");
let onPickerNodeHovered = inspector.toolbox.once("picker-node-hovered");
testActor.synthesizeMouse({
options: {type: "mousemove"},
center: true,
selector: selector
});
return promise.all([onHighlighterReady, onPickerNodeHovered]);
}
});
/* 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";
// Test that the keybindings for Picker work alright
const IS_OSX = Services.appinfo.OS === "Darwin";
const TEST_URL = URL_ROOT + "doc_inspector_highlighter_dom.html";
add_task(function* () {
let {inspector, toolbox, testActor} = yield openInspectorForURL(TEST_URL);
yield startPicker(toolbox);
yield moveMouseOver("#another");
info("Testing enter/return key as pick-node command");
yield doKeyPick({key: "VK_RETURN", options: {}});
is(inspector.selection.nodeFront.id, "another",
"The #another node was selected. Passed.");
info("Testing escape key as cancel-picker command");
yield startPicker(toolbox);
yield moveMouseOver("#ahoy");
yield doKeyStop({key: "VK_ESCAPE", options: {}});
is(inspector.selection.nodeFront.id, "another",
"The #another DIV is still selected. Passed.");
info("Testing Ctrl+Shift+C shortcut as cancel-picker command");
yield startPicker(toolbox);
yield moveMouseOver("#ahoy");
let shortcutOpts = {key: "VK_C", options: {}};
if (IS_OSX) {
shortcutOpts.options.metaKey = true;
shortcutOpts.options.altKey = true;
} else {
shortcutOpts.options.ctrlKey = true;
shortcutOpts.options.shiftKey = true;
}
yield doKeyStop(shortcutOpts);
is(inspector.selection.nodeFront.id, "another",
"The #another DIV is still selected. Passed.");
function doKeyPick(args) {
info("Key pressed. Waiting for element to be picked");
testActor.synthesizeKey(args);
return promise.all([
inspector.selection.once("new-node-front"),
inspector.once("inspector-updated")
]);
}
function doKeyStop(args) {
info("Key pressed. Waiting for picker to be canceled");
testActor.synthesizeKey(args);
return inspector.toolbox.once("picker-stopped");
}
function moveMouseOver(selector) {
info("Waiting for element " + selector + " to be highlighted");
let onHighlighterReady = toolbox.once("highlighter-ready");
let onPickerNodeHovered = inspector.toolbox.once("picker-node-hovered");
testActor.synthesizeMouse({
options: {type: "mousemove"},
center: true,
selector: selector
});
return promise.all([onHighlighterReady, onPickerNodeHovered]);
}
});

Просмотреть файл

@ -1,74 +1,74 @@
/* 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/ */
"use strict";
// Test that depending where the user last clicked in the inspector, the right search
// field is focused when ctrl+F is pressed.
add_task(function* () {
let {inspector} = yield openInspectorForURL("data:text/html;charset=utf-8,Search!");
info("Check that by default, the inspector search field gets focused");
pressCtrlF();
isInInspectorSearchBox(inspector);
info("Click somewhere in the rule-view");
clickInRuleView(inspector);
info("Check that the rule-view search field gets focused");
pressCtrlF();
isInRuleViewSearchBox(inspector);
info("Click in the inspector again");
yield clickContainer("head", inspector);
info("Check that now we're back in the inspector, its search field gets focused");
pressCtrlF();
isInInspectorSearchBox(inspector);
info("Switch to the computed view, and click somewhere inside it");
selectComputedView(inspector);
clickInComputedView(inspector);
info("Check that the computed-view search field gets focused");
pressCtrlF();
isInComputedViewSearchBox(inspector);
info("Click in the inspector yet again");
yield clickContainer("body", inspector);
info("We're back in the inspector again, check the inspector search field focuses");
pressCtrlF();
isInInspectorSearchBox(inspector);
});
function pressCtrlF() {
EventUtils.synthesizeKey("f", {accelKey: true});
}
function clickInRuleView(inspector) {
let el = inspector.panelDoc.querySelector("#sidebar-panel-ruleview");
EventUtils.synthesizeMouseAtCenter(el, {}, inspector.panelDoc.defaultView);
}
function clickInComputedView(inspector) {
let el = inspector.panelDoc.querySelector("#sidebar-panel-computedview");
EventUtils.synthesizeMouseAtCenter(el, {}, inspector.panelDoc.defaultView);
}
function isInInspectorSearchBox(inspector) {
// Focus ends up in an anonymous child of the XUL textbox.
ok(inspector.panelDoc.activeElement.closest("#inspector-searchbox"),
"The inspector search field is focused when ctrl+F is pressed");
}
function isInRuleViewSearchBox(inspector) {
is(inspector.panelDoc.activeElement, inspector.ruleview.view.searchField,
"The rule-view search field is focused when ctrl+F is pressed");
}
function isInComputedViewSearchBox(inspector) {
is(inspector.panelDoc.activeElement, inspector.computedview.computedView.searchField,
"The computed-view search field is focused when ctrl+F is pressed");
}
/* 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/ */
"use strict";
// Test that depending where the user last clicked in the inspector, the right search
// field is focused when ctrl+F is pressed.
add_task(function* () {
let {inspector} = yield openInspectorForURL("data:text/html;charset=utf-8,Search!");
info("Check that by default, the inspector search field gets focused");
pressCtrlF();
isInInspectorSearchBox(inspector);
info("Click somewhere in the rule-view");
clickInRuleView(inspector);
info("Check that the rule-view search field gets focused");
pressCtrlF();
isInRuleViewSearchBox(inspector);
info("Click in the inspector again");
yield clickContainer("head", inspector);
info("Check that now we're back in the inspector, its search field gets focused");
pressCtrlF();
isInInspectorSearchBox(inspector);
info("Switch to the computed view, and click somewhere inside it");
selectComputedView(inspector);
clickInComputedView(inspector);
info("Check that the computed-view search field gets focused");
pressCtrlF();
isInComputedViewSearchBox(inspector);
info("Click in the inspector yet again");
yield clickContainer("body", inspector);
info("We're back in the inspector again, check the inspector search field focuses");
pressCtrlF();
isInInspectorSearchBox(inspector);
});
function pressCtrlF() {
EventUtils.synthesizeKey("f", {accelKey: true});
}
function clickInRuleView(inspector) {
let el = inspector.panelDoc.querySelector("#sidebar-panel-ruleview");
EventUtils.synthesizeMouseAtCenter(el, {}, inspector.panelDoc.defaultView);
}
function clickInComputedView(inspector) {
let el = inspector.panelDoc.querySelector("#sidebar-panel-computedview");
EventUtils.synthesizeMouseAtCenter(el, {}, inspector.panelDoc.defaultView);
}
function isInInspectorSearchBox(inspector) {
// Focus ends up in an anonymous child of the XUL textbox.
ok(inspector.panelDoc.activeElement.closest("#inspector-searchbox"),
"The inspector search field is focused when ctrl+F is pressed");
}
function isInRuleViewSearchBox(inspector) {
is(inspector.panelDoc.activeElement, inspector.ruleview.view.searchField,
"The rule-view search field is focused when ctrl+F is pressed");
}
function isInComputedViewSearchBox(inspector) {
is(inspector.panelDoc.activeElement, inspector.computedview.computedView.searchField,
"The computed-view search field is focused when ctrl+F is pressed");
}

Просмотреть файл

@ -1,90 +1,90 @@
/* 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/ */
"use strict";
// Test that when right-clicking on various text boxes throughout the inspector does use
// the toolbox's context menu (copy/cut/paste/selectAll/Undo).
add_task(function* () {
yield addTab(`data:text/html;charset=utf-8,
<style>h1 { color: red; }</style>
<h1 id="title">textbox context menu test</h1>`);
let {toolbox, inspector} = yield openInspector();
yield selectNode("h1", inspector);
info("Testing the markup-view tagname");
let container = yield focusNode("h1", inspector);
let tag = container.editor.tag;
tag.focus();
EventUtils.sendKey("return", inspector.panelWin);
yield checkTextBox(inspector.markup.doc.activeElement, toolbox);
info("Testing the markup-view attribute");
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.markup.doc.activeElement, toolbox);
info("Testing the markup-view new attribute");
// It takes 2 tabs to focus the newAttr field, the first one just moves the cursor to
// the end of the field.
EventUtils.sendKey("tab", inspector.panelWin);
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.markup.doc.activeElement, toolbox);
info("Testing the markup-view textcontent");
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.markup.doc.activeElement, toolbox);
// Blur this last markup-view field, since we're moving on to the rule-view next.
EventUtils.sendKey("escape", inspector.panelWin);
info("Testing the rule-view selector");
let ruleView = inspector.ruleview.view;
let cssRuleEditor = getRuleViewRuleEditor(ruleView, 1);
EventUtils.synthesizeMouse(cssRuleEditor.selectorText, 0, 0, {}, inspector.panelWin);
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
info("Testing the rule-view property name");
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
info("Testing the rule-view property value");
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
info("Testing the rule-view new property");
// Tabbing out of the value field triggers a ruleview-changed event that we need to wait
// for.
let onRuleViewChanged = once(ruleView, "ruleview-changed");
EventUtils.sendKey("tab", inspector.panelWin);
yield onRuleViewChanged;
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
info("Switching to the computed-view");
let onComputedViewReady = inspector.once("boxmodel-view-updated");
selectComputedView(inspector);
yield onComputedViewReady;
info("Testing the box-model region");
let margin = inspector.panelDoc.querySelector(".boxmodel-margin.boxmodel-top > span");
EventUtils.synthesizeMouseAtCenter(margin, {}, inspector.panelWin);
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
});
function* checkTextBox(textBox, {textBoxContextMenuPopup}) {
is(textBoxContextMenuPopup.state, "closed", "The menu is closed");
info("Simulating context click on the textbox and expecting the menu to open");
let onContextMenu = once(textBoxContextMenuPopup, "popupshown");
EventUtils.synthesizeMouse(textBox, 2, 2, {type: "contextmenu", button: 2},
textBox.ownerDocument.defaultView);
yield onContextMenu;
is(textBoxContextMenuPopup.state, "open", "The menu is now visible");
info("Closing the menu");
let onContextMenuHidden = once(textBoxContextMenuPopup, "popuphidden");
textBoxContextMenuPopup.hidePopup();
yield onContextMenuHidden;
is(textBoxContextMenuPopup.state, "closed", "The menu is closed again");
}
/* 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/ */
"use strict";
// Test that when right-clicking on various text boxes throughout the inspector does use
// the toolbox's context menu (copy/cut/paste/selectAll/Undo).
add_task(function* () {
yield addTab(`data:text/html;charset=utf-8,
<style>h1 { color: red; }</style>
<h1 id="title">textbox context menu test</h1>`);
let {toolbox, inspector} = yield openInspector();
yield selectNode("h1", inspector);
info("Testing the markup-view tagname");
let container = yield focusNode("h1", inspector);
let tag = container.editor.tag;
tag.focus();
EventUtils.sendKey("return", inspector.panelWin);
yield checkTextBox(inspector.markup.doc.activeElement, toolbox);
info("Testing the markup-view attribute");
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.markup.doc.activeElement, toolbox);
info("Testing the markup-view new attribute");
// It takes 2 tabs to focus the newAttr field, the first one just moves the cursor to
// the end of the field.
EventUtils.sendKey("tab", inspector.panelWin);
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.markup.doc.activeElement, toolbox);
info("Testing the markup-view textcontent");
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.markup.doc.activeElement, toolbox);
// Blur this last markup-view field, since we're moving on to the rule-view next.
EventUtils.sendKey("escape", inspector.panelWin);
info("Testing the rule-view selector");
let ruleView = inspector.ruleview.view;
let cssRuleEditor = getRuleViewRuleEditor(ruleView, 1);
EventUtils.synthesizeMouse(cssRuleEditor.selectorText, 0, 0, {}, inspector.panelWin);
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
info("Testing the rule-view property name");
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
info("Testing the rule-view property value");
EventUtils.sendKey("tab", inspector.panelWin);
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
info("Testing the rule-view new property");
// Tabbing out of the value field triggers a ruleview-changed event that we need to wait
// for.
let onRuleViewChanged = once(ruleView, "ruleview-changed");
EventUtils.sendKey("tab", inspector.panelWin);
yield onRuleViewChanged;
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
info("Switching to the computed-view");
let onComputedViewReady = inspector.once("boxmodel-view-updated");
selectComputedView(inspector);
yield onComputedViewReady;
info("Testing the box-model region");
let margin = inspector.panelDoc.querySelector(".boxmodel-margin.boxmodel-top > span");
EventUtils.synthesizeMouseAtCenter(margin, {}, inspector.panelWin);
yield checkTextBox(inspector.panelDoc.activeElement, toolbox);
});
function* checkTextBox(textBox, {textBoxContextMenuPopup}) {
is(textBoxContextMenuPopup.state, "closed", "The menu is closed");
info("Simulating context click on the textbox and expecting the menu to open");
let onContextMenu = once(textBoxContextMenuPopup, "popupshown");
EventUtils.synthesizeMouse(textBox, 2, 2, {type: "contextmenu", button: 2},
textBox.ownerDocument.defaultView);
yield onContextMenu;
is(textBoxContextMenuPopup.state, "open", "The menu is now visible");
info("Closing the menu");
let onContextMenuHidden = once(textBoxContextMenuPopup, "popuphidden");
textBoxContextMenuPopup.hidePopup();
yield onContextMenuHidden;
is(textBoxContextMenuPopup.state, "closed", "The menu is closed again");
}

Просмотреть файл

@ -1,54 +1,54 @@
/* 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 React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { DOM: dom, PropTypes } = React;
const Draggable = React.createClass({
displayName: "Draggable",
propTypes: {
onMove: PropTypes.func.isRequired,
onStart: PropTypes.func,
onStop: PropTypes.func,
style: PropTypes.object,
className: PropTypes.string
},
startDragging(ev) {
ev.preventDefault();
const doc = ReactDOM.findDOMNode(this).ownerDocument;
doc.addEventListener("mousemove", this.onMove);
doc.addEventListener("mouseup", this.onUp);
this.props.onStart && this.props.onStart();
},
onMove(ev) {
ev.preventDefault();
// Use screen coordinates so, moving mouse over iframes
// doesn't mangle (relative) coordinates.
this.props.onMove(ev.screenX, ev.screenY);
},
onUp(ev) {
ev.preventDefault();
const doc = ReactDOM.findDOMNode(this).ownerDocument;
doc.removeEventListener("mousemove", this.onMove);
doc.removeEventListener("mouseup", this.onUp);
this.props.onStop && this.props.onStop();
},
render() {
return dom.div({
style: this.props.style,
className: this.props.className,
onMouseDown: this.startDragging
});
}
});
module.exports = Draggable;
/* 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 React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { DOM: dom, PropTypes } = React;
const Draggable = React.createClass({
displayName: "Draggable",
propTypes: {
onMove: PropTypes.func.isRequired,
onStart: PropTypes.func,
onStop: PropTypes.func,
style: PropTypes.object,
className: PropTypes.string
},
startDragging(ev) {
ev.preventDefault();
const doc = ReactDOM.findDOMNode(this).ownerDocument;
doc.addEventListener("mousemove", this.onMove);
doc.addEventListener("mouseup", this.onUp);
this.props.onStart && this.props.onStart();
},
onMove(ev) {
ev.preventDefault();
// Use screen coordinates so, moving mouse over iframes
// doesn't mangle (relative) coordinates.
this.props.onMove(ev.screenX, ev.screenY);
},
onUp(ev) {
ev.preventDefault();
const doc = ReactDOM.findDOMNode(this).ownerDocument;
doc.removeEventListener("mousemove", this.onMove);
doc.removeEventListener("mouseup", this.onUp);
this.props.onStop && this.props.onStop();
},
render() {
return dom.div({
style: this.props.style,
className: this.props.className,
onMouseDown: this.startDragging
});
}
});
module.exports = Draggable;

Просмотреть файл

@ -1,207 +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 React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const Draggable = React.createFactory(require("devtools/client/shared/components/splitter/draggable"));
const { DOM: dom, PropTypes } = React;
/**
* This component represents a Splitter. The splitter supports vertical
* as well as horizontal mode.
*/
const SplitBox = React.createClass({
displayName: "SplitBox",
propTypes: {
// Custom class name. You can use more names separated by a space.
className: PropTypes.string,
// Initial size of controlled panel.
initialSize: PropTypes.number,
// Left/top panel
startPanel: PropTypes.any,
// Min panel size.
minSize: PropTypes.number,
// Max panel size.
maxSize: PropTypes.number,
// Right/bottom panel
endPanel: PropTypes.any,
// True if the right/bottom panel should be controlled.
endPanelControl: PropTypes.bool,
// Size of the splitter handle bar.
splitterSize: PropTypes.number,
// True if the splitter bar is vertical (default is vertical).
vert: PropTypes.bool
},
getDefaultProps() {
return {
splitterSize: 5,
vert: true,
endPanelControl: false
};
},
/**
* The state stores the current orientation (vertical or horizontal)
* and the current size (width/height). All these values can change
* during the component's life time.
*/
getInitialState() {
return {
vert: this.props.vert,
width: this.props.initialWidth || this.props.initialSize,
height: this.props.initialHeight || this.props.initialSize
};
},
// Dragging Events
/**
* Set 'resizing' cursor on entire document during splitter dragging.
* This avoids cursor-flickering that happens when the mouse leaves
* the splitter bar area (happens frequently).
*/
onStartMove() {
const splitBox = ReactDOM.findDOMNode(this);
const doc = splitBox.ownerDocument;
let defaultCursor = doc.documentElement.style.cursor;
doc.documentElement.style.cursor = (this.state.vert ? "ew-resize" : "ns-resize");
splitBox.classList.add("dragging");
this.setState({
defaultCursor: defaultCursor
});
},
onStopMove() {
const splitBox = ReactDOM.findDOMNode(this);
const doc = splitBox.ownerDocument;
doc.documentElement.style.cursor = this.state.defaultCursor;
splitBox.classList.remove("dragging");
},
/**
* Adjust size of the controlled panel. Depending on the current
* orientation we either remember the width or height of
* the splitter box.
*/
onMove(x, y) {
const node = ReactDOM.findDOMNode(this);
const doc = node.ownerDocument;
const win = doc.defaultView;
let size;
let { endPanelControl } = this.props;
if (this.state.vert) {
// Switch the control flag in case of RTL. Note that RTL
// has impact on vertical splitter only.
let dir = win.getComputedStyle(doc.documentElement).direction;
if (dir == "rtl") {
endPanelControl = !endPanelControl;
}
let innerOffset = x - win.mozInnerScreenX;
size = endPanelControl ?
(node.offsetLeft + node.offsetWidth) - innerOffset :
innerOffset - node.offsetLeft;
this.setState({
width: size
});
} else {
let innerOffset = y - win.mozInnerScreenY;
size = endPanelControl ?
(node.offsetTop + node.offsetHeight) - innerOffset :
innerOffset - node.offsetTop;
this.setState({
height: size
});
}
},
// Rendering
render() {
const vert = this.state.vert;
const { startPanel, endPanel, endPanelControl, minSize,
maxSize, splitterSize } = this.props;
let style = Object.assign({}, this.props.style);
// Calculate class names list.
let classNames = ["split-box"];
classNames.push(vert ? "vert" : "horz");
if (this.props.className) {
classNames = classNames.concat(this.props.className.split(" "));
}
let leftPanelStyle;
let rightPanelStyle;
// Set proper size for panels depending on the current state.
if (vert) {
leftPanelStyle = {
maxWidth: endPanelControl ? null : maxSize,
minWidth: endPanelControl ? null : minSize,
width: endPanelControl ? null : this.state.width
};
rightPanelStyle = {
maxWidth: endPanelControl ? maxSize : null,
minWidth: endPanelControl ? minSize : null,
width: endPanelControl ? this.state.width : null
};
} else {
leftPanelStyle = {
maxHeight: endPanelControl ? null : maxSize,
minHeight: endPanelControl ? null : minSize,
height: endPanelControl ? null : this.state.height
};
rightPanelStyle = {
maxHeight: endPanelControl ? maxSize : null,
minHeight: endPanelControl ? minSize : null,
height: endPanelControl ? this.state.height : null
};
}
// Calculate splitter size
let splitterStyle = {
flex: "0 0 " + splitterSize + "px"
};
return (
dom.div({
className: classNames.join(" "),
style: style },
startPanel ?
dom.div({
className: endPanelControl ? "uncontrolled" : "controlled",
style: leftPanelStyle},
startPanel
) : null,
Draggable({
className: "splitter",
style: splitterStyle,
onStart: this.onStartMove,
onStop: this.onStopMove,
onMove: this.onMove
}),
endPanel ?
dom.div({
className: endPanelControl ? "controlled" : "uncontrolled",
style: rightPanelStyle},
endPanel
) : null
)
);
}
});
module.exports = SplitBox;
/* 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 React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const Draggable = React.createFactory(require("devtools/client/shared/components/splitter/draggable"));
const { DOM: dom, PropTypes } = React;
/**
* This component represents a Splitter. The splitter supports vertical
* as well as horizontal mode.
*/
const SplitBox = React.createClass({
displayName: "SplitBox",
propTypes: {
// Custom class name. You can use more names separated by a space.
className: PropTypes.string,
// Initial size of controlled panel.
initialSize: PropTypes.number,
// Left/top panel
startPanel: PropTypes.any,
// Min panel size.
minSize: PropTypes.number,
// Max panel size.
maxSize: PropTypes.number,
// Right/bottom panel
endPanel: PropTypes.any,
// True if the right/bottom panel should be controlled.
endPanelControl: PropTypes.bool,
// Size of the splitter handle bar.
splitterSize: PropTypes.number,
// True if the splitter bar is vertical (default is vertical).
vert: PropTypes.bool
},
getDefaultProps() {
return {
splitterSize: 5,
vert: true,
endPanelControl: false
};
},
/**
* The state stores the current orientation (vertical or horizontal)
* and the current size (width/height). All these values can change
* during the component's life time.
*/
getInitialState() {
return {
vert: this.props.vert,
width: this.props.initialWidth || this.props.initialSize,
height: this.props.initialHeight || this.props.initialSize
};
},
// Dragging Events
/**
* Set 'resizing' cursor on entire document during splitter dragging.
* This avoids cursor-flickering that happens when the mouse leaves
* the splitter bar area (happens frequently).
*/
onStartMove() {
const splitBox = ReactDOM.findDOMNode(this);
const doc = splitBox.ownerDocument;
let defaultCursor = doc.documentElement.style.cursor;
doc.documentElement.style.cursor = (this.state.vert ? "ew-resize" : "ns-resize");
splitBox.classList.add("dragging");
this.setState({
defaultCursor: defaultCursor
});
},
onStopMove() {
const splitBox = ReactDOM.findDOMNode(this);
const doc = splitBox.ownerDocument;
doc.documentElement.style.cursor = this.state.defaultCursor;
splitBox.classList.remove("dragging");
},
/**
* Adjust size of the controlled panel. Depending on the current
* orientation we either remember the width or height of
* the splitter box.
*/
onMove(x, y) {
const node = ReactDOM.findDOMNode(this);
const doc = node.ownerDocument;
const win = doc.defaultView;
let size;
let { endPanelControl } = this.props;
if (this.state.vert) {
// Switch the control flag in case of RTL. Note that RTL
// has impact on vertical splitter only.
let dir = win.getComputedStyle(doc.documentElement).direction;
if (dir == "rtl") {
endPanelControl = !endPanelControl;
}
let innerOffset = x - win.mozInnerScreenX;
size = endPanelControl ?
(node.offsetLeft + node.offsetWidth) - innerOffset :
innerOffset - node.offsetLeft;
this.setState({
width: size
});
} else {
let innerOffset = y - win.mozInnerScreenY;
size = endPanelControl ?
(node.offsetTop + node.offsetHeight) - innerOffset :
innerOffset - node.offsetTop;
this.setState({
height: size
});
}
},
// Rendering
render() {
const vert = this.state.vert;
const { startPanel, endPanel, endPanelControl, minSize,
maxSize, splitterSize } = this.props;
let style = Object.assign({}, this.props.style);
// Calculate class names list.
let classNames = ["split-box"];
classNames.push(vert ? "vert" : "horz");
if (this.props.className) {
classNames = classNames.concat(this.props.className.split(" "));
}
let leftPanelStyle;
let rightPanelStyle;
// Set proper size for panels depending on the current state.
if (vert) {
leftPanelStyle = {
maxWidth: endPanelControl ? null : maxSize,
minWidth: endPanelControl ? null : minSize,
width: endPanelControl ? null : this.state.width
};
rightPanelStyle = {
maxWidth: endPanelControl ? maxSize : null,
minWidth: endPanelControl ? minSize : null,
width: endPanelControl ? this.state.width : null
};
} else {
leftPanelStyle = {
maxHeight: endPanelControl ? null : maxSize,
minHeight: endPanelControl ? null : minSize,
height: endPanelControl ? null : this.state.height
};
rightPanelStyle = {
maxHeight: endPanelControl ? maxSize : null,
minHeight: endPanelControl ? minSize : null,
height: endPanelControl ? this.state.height : null
};
}
// Calculate splitter size
let splitterStyle = {
flex: "0 0 " + splitterSize + "px"
};
return (
dom.div({
className: classNames.join(" "),
style: style },
startPanel ?
dom.div({
className: endPanelControl ? "uncontrolled" : "controlled",
style: leftPanelStyle},
startPanel
) : null,
Draggable({
className: "splitter",
style: splitterStyle,
onStart: this.onStartMove,
onStop: this.onStopMove,
onMove: this.onMove
}),
endPanel ?
dom.div({
className: endPanelControl ? "controlled" : "uncontrolled",
style: rightPanelStyle},
endPanel
) : null
)
);
}
});
module.exports = SplitBox;

Просмотреть файл

@ -7,7 +7,7 @@
#ifndef mozilla_dom_AnimationPerformanceWarning_h
#define mozilla_dom_AnimationPerformanceWarning_h
#include "mozilla/InitializerList.h"
#include <initializer_list>
class nsXPIDLString;

Просмотреть файл

@ -14,7 +14,6 @@
#include "mozilla/AnimationTarget.h"
#include "mozilla/AnimationUtils.h"
#include "mozilla/EffectSet.h"
#include "mozilla/InitializerList.h"
#include "mozilla/LayerAnimationInfo.h"
#include "mozilla/RestyleManagerHandle.h"
#include "mozilla/RestyleManagerHandleInlines.h"
@ -27,6 +26,7 @@
#include "nsRuleProcessorData.h" // For ElementRuleProcessorData etc.
#include "nsTArray.h"
#include <bitset>
#include <initializer_list>
using mozilla::dom::Animation;
using mozilla::dom::Element;

Просмотреть файл

@ -60,7 +60,6 @@ public:
{
NS_ASSERTION(mDone || mResult.isUndefined(),
"Result should be undefined when pending");
JS::ExposeValueToActiveJS(mResult);
aRetval.set(mResult);
}

Просмотреть файл

@ -884,6 +884,8 @@ public:
ErrorResult& aError);
// Note: GetAnimations will flush style while GetAnimationsUnsorted won't.
// Callers must keep this element alive because flushing style may destroy
// this element.
void GetAnimations(const AnimationFilter& filter,
nsTArray<RefPtr<Animation>>& aAnimations);
static void GetAnimationsUnsorted(Element* aElement,

Просмотреть файл

@ -7544,7 +7544,7 @@ nsContentUtils::IPCTransferableToTransferable(const IPCDataTransfer& aDataTransf
}
void
nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
nsContentUtils::TransferablesToIPCTransferables(nsIArray* aTransferables,
nsTArray<IPCDataTransfer>& aIPC,
bool aInSyncMessage,
mozilla::dom::nsIContentChild* aChild,
@ -7553,12 +7553,10 @@ nsContentUtils::TransferablesToIPCTransferables(nsISupportsArray* aTransferables
aIPC.Clear();
if (aTransferables) {
uint32_t transferableCount = 0;
aTransferables->Count(&transferableCount);
aTransferables->GetLength(&transferableCount);
for (uint32_t i = 0; i < transferableCount; ++i) {
IPCDataTransfer* dt = aIPC.AppendElement();
nsCOMPtr<nsISupports> genericItem;
aTransferables->GetElementAt(i, getter_AddRefs(genericItem));
nsCOMPtr<nsITransferable> transferable(do_QueryInterface(genericItem));
nsCOMPtr<nsITransferable> transferable = do_QueryElementAt(aTransferables, i);
TransferableToIPCTransferable(transferable, dt, aInSyncMessage, aChild, aParent);
}
}

Просмотреть файл

@ -2500,7 +2500,7 @@ public:
mozilla::dom::nsIContentParent* aContentParent,
mozilla::dom::TabChild* aTabChild);
static void TransferablesToIPCTransferables(nsISupportsArray* aTransferables,
static void TransferablesToIPCTransferables(nsIArray* aTransferables,
nsTArray<mozilla::dom::IPCDataTransfer>& aIPC,
bool aInSyncMessage,
mozilla::dom::nsIContentChild* aChild,

Просмотреть файл

@ -2924,7 +2924,9 @@ nsDocument::Timeline()
void
nsDocument::GetAnimations(nsTArray<RefPtr<Animation>>& aAnimations)
{
Element* root = GetRootElement();
// Hold a strong ref for the root element since Element::GetAnimations() calls
// FlushPendingNotifications() which may destroy the element.
RefPtr<Element> root = GetRootElement();
if (!root) {
return;
}

Просмотреть файл

@ -1905,10 +1905,7 @@ void
nsMessageManagerScriptExecutor::MarkScopesForCC()
{
for (uint32_t i = 0; i < mAnonymousGlobalScopes.Length(); ++i) {
JSObject* obj = mAnonymousGlobalScopes[i];
if (obj) {
JS::ExposeObjectToActiveJS(obj);
}
mAnonymousGlobalScopes[i].exposeToActiveJS();
}
}

Просмотреть файл

@ -1819,7 +1819,7 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
for (auto iter = tmp->mCachedXBLPrototypeHandlers->Iter();
!iter.Done();
iter.Next()) {
JS::ExposeObjectToActiveJS(iter.Data());
iter.Data().exposeToActiveJS();
}
}
if (EventListenerManager* elm = tmp->GetExistingListenerManager()) {
@ -2024,8 +2024,7 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
for (auto iter = tmp->mCachedXBLPrototypeHandlers->Iter();
!iter.Done();
iter.Next()) {
JS::Heap<JSObject*>& data = iter.Data();
aCallbacks.Trace(&data, "Cached XBL prototype handler", aClosure);
aCallbacks.Trace(&iter.Data(), "Cached XBL prototype handler", aClosure);
}
}
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
@ -2648,8 +2647,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
// We're reusing the inner window, but this still counts as a navigation,
// so all expandos and such defined on the outer window should go away. Force
// all Xray wrappers to be recomputed.
JS::Rooted<JSObject*> rootedObject(cx, GetWrapperPreserveColor());
JS::ExposeObjectToActiveJS(rootedObject);
JS::Rooted<JSObject*> rootedObject(cx, GetWrapper());
if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
return NS_ERROR_FAILURE;
}
@ -9038,7 +9036,7 @@ nsGlobalWindow::CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
JS::Handle<JSObject*> aHandler)
{
if (!mCachedXBLPrototypeHandlers) {
mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>();
mCachedXBLPrototypeHandlers = new XBLPrototypeHandlerTable();
PreserveWrapper(ToSupports(this));
}

Просмотреть файл

@ -1917,7 +1917,8 @@ protected:
nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
nsAutoPtr<nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> > mCachedXBLPrototypeHandlers;
using XBLPrototypeHandlerTable = nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>;
nsAutoPtr<XBLPrototypeHandlerTable> mCachedXBLPrototypeHandlers;
// mSuspendedDoc is only set on outer windows. It's useful when we get matched
// EnterModalState/LeaveModalState calls, in which case the outer window is

Просмотреть файл

@ -85,7 +85,6 @@ public:
*/
virtual void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) = 0;
virtual JSObject* GetWindowProxy() = 0;
virtual JSObject* GetWindowProxyPreserveColor() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIScriptContext, NS_ISCRIPTCONTEXT_IID)

Просмотреть файл

@ -2218,17 +2218,6 @@ nsJSContext::SetWindowProxy(JS::Handle<JSObject*> aWindowProxy)
JSObject*
nsJSContext::GetWindowProxy()
{
JSObject* windowProxy = GetWindowProxyPreserveColor();
if (windowProxy) {
JS::ExposeObjectToActiveJS(windowProxy);
}
return windowProxy;
}
JSObject*
nsJSContext::GetWindowProxyPreserveColor()
{
return mWindowProxy;
}

Просмотреть файл

@ -63,7 +63,6 @@ public:
virtual void SetWindowProxy(JS::Handle<JSObject*> aWindowProxy) override;
virtual JSObject* GetWindowProxy() override;
virtual JSObject* GetWindowProxyPreserveColor() override;
static void LoadStart();
static void LoadEnd();

Просмотреть файл

@ -314,18 +314,8 @@ public:
JS::Handle<JSObject*> aModuleRecord);
nsScriptLoader* Loader() const { return mLoader; }
JSObject* ModuleRecord() const
{
if (mModuleRecord) {
JS::ExposeObjectToActiveJS(mModuleRecord);
}
return mModuleRecord;
}
JS::Value Exception() const
{
JS::ExposeValueToActiveJS(mException);
return mException;
}
JSObject* ModuleRecord() const { return mModuleRecord; }
JS::Value Exception() const { return mException; }
nsIURI* BaseURL() const { return mBaseURL; }
void SetInstantiationResult(JS::Handle<JS::Value> aMaybeException);

Просмотреть файл

@ -81,27 +81,19 @@ public:
JS::Handle<JSObject*> Callback() const
{
JS::ExposeObjectToActiveJS(mCallback);
mCallback.exposeToActiveJS();
return CallbackPreserveColor();
}
JSObject* GetCreationStack() const
{
JSObject* result = mCreationStack;
if (result) {
JS::ExposeObjectToActiveJS(result);
}
return result;
return mCreationStack;
}
void MarkForCC()
{
if (mCallback) {
JS::ExposeObjectToActiveJS(mCallback);
}
if (mCreationStack) {
JS::ExposeObjectToActiveJS(mCreationStack);
}
mCallback.exposeToActiveJS();
mCreationStack.exposeToActiveJS();
}
/*

Просмотреть файл

@ -345,7 +345,7 @@ NS_IMETHODIMP JSStackFrame::GetLanguageName(nsACString& aLanguageName)
// @argument [out] aValue the value we got from the stack.
template<typename ReturnType, typename GetterOutParamType>
static void
GetValueIfNotCached(JSContext* aCx, JSObject* aStack,
GetValueIfNotCached(JSContext* aCx, const JS::Heap<JSObject*>& aStack,
JS::SavedFrameResult (*aPropGetter)(JSContext*,
JS::Handle<JSObject*>,
GetterOutParamType,
@ -365,7 +365,6 @@ GetValueIfNotCached(JSContext* aCx, JSObject* aStack,
}
*aUseCachedValue = false;
JS::ExposeObjectToActiveJS(stack);
aPropGetter(aCx, stack, aValue, JS::SavedFrameSelfHosted::Exclude);
}
@ -628,7 +627,6 @@ NS_IMETHODIMP JSStackFrame::GetFormattedStack(JSContext* aCx, nsAString& aStack)
return NS_OK;
}
JS::ExposeObjectToActiveJS(mStack);
JS::Rooted<JSObject*> stack(aCx, mStack);
JS::Rooted<JSString*> formattedStack(aCx);
@ -657,9 +655,6 @@ NS_IMETHODIMP JSStackFrame::GetFormattedStack(JSContext* aCx, nsAString& aStack)
NS_IMETHODIMP JSStackFrame::GetNativeSavedFrame(JS::MutableHandle<JS::Value> aSavedFrame)
{
if (mStack) {
JS::ExposeObjectToActiveJS(mStack);
}
aSavedFrame.setObjectOrNull(mStack);
return NS_OK;
}

Просмотреть файл

@ -168,7 +168,6 @@ BluetoothGattAttributeEvent::GetValue(
mRawValue.Clear();
}
JS::ExposeObjectToActiveJS(mValue);
aValue.set(mValue);
return;

Просмотреть файл

@ -140,7 +140,6 @@ BluetoothLeDeviceEvent::GetScanRecord(
}
mRawScanRecord.Clear();
}
JS::ExposeObjectToActiveJS(mScanRecord);
aScanRecord.set(mScanRecord);
return;

60
dom/cache/AutoUtils.cpp поставляемый
Просмотреть файл

@ -240,50 +240,40 @@ MatchInPutList(InternalRequest* aRequest,
RefPtr<InternalHeaders> cachedResponseHeaders =
TypeUtils::ToInternalHeaders(cachedResponse.headers());
AutoTArray<nsCString, 16> varyHeaders;
nsCString varyHeaders;
ErrorResult rv;
cachedResponseHeaders->GetAll(NS_LITERAL_CSTRING("vary"), varyHeaders, rv);
cachedResponseHeaders->Get(NS_LITERAL_CSTRING("vary"), varyHeaders, rv);
MOZ_ALWAYS_TRUE(!rv.Failed());
// Assume the vary headers match until we find a conflict
bool varyHeadersMatch = true;
for (uint32_t j = 0; j < varyHeaders.Length(); ++j) {
// Extract the header names inside the Vary header value.
nsAutoCString varyValue(varyHeaders[j]);
char* rawBuffer = varyValue.BeginWriting();
char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
bool bailOut = false;
for (; token;
token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
nsDependentCString header(token);
MOZ_ASSERT(!header.EqualsLiteral("*"),
"We should have already caught this in "
"TypeUtils::ToPCacheResponseWithoutBody()");
char* rawBuffer = varyHeaders.BeginWriting();
char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
for (; token;
token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
nsDependentCString header(token);
MOZ_ASSERT(!header.EqualsLiteral("*"),
"We should have already caught this in "
"TypeUtils::ToPCacheResponseWithoutBody()");
ErrorResult headerRv;
nsAutoCString value;
requestHeaders->Get(header, value, headerRv);
if (NS_WARN_IF(headerRv.Failed())) {
headerRv.SuppressException();
MOZ_ASSERT(value.IsEmpty());
}
nsAutoCString cachedValue;
cachedRequestHeaders->Get(header, value, headerRv);
if (NS_WARN_IF(headerRv.Failed())) {
headerRv.SuppressException();
MOZ_ASSERT(cachedValue.IsEmpty());
}
if (value != cachedValue) {
varyHeadersMatch = false;
bailOut = true;
break;
}
ErrorResult headerRv;
nsAutoCString value;
requestHeaders->Get(header, value, headerRv);
if (NS_WARN_IF(headerRv.Failed())) {
headerRv.SuppressException();
MOZ_ASSERT(value.IsEmpty());
}
if (bailOut) {
nsAutoCString cachedValue;
cachedRequestHeaders->Get(header, cachedValue, headerRv);
if (NS_WARN_IF(headerRv.Failed())) {
headerRv.SuppressException();
MOZ_ASSERT(cachedValue.IsEmpty());
}
if (value != cachedValue) {
varyHeadersMatch = false;
break;
}
}

21
dom/cache/TypeUtils.cpp поставляемый
Просмотреть файл

@ -45,21 +45,18 @@ namespace {
static bool
HasVaryStar(mozilla::dom::InternalHeaders* aHeaders)
{
AutoTArray<nsCString, 16> varyHeaders;
nsCString varyHeaders;
ErrorResult rv;
aHeaders->GetAll(NS_LITERAL_CSTRING("vary"), varyHeaders, rv);
aHeaders->Get(NS_LITERAL_CSTRING("vary"), varyHeaders, rv);
MOZ_ALWAYS_TRUE(!rv.Failed());
for (uint32_t i = 0; i < varyHeaders.Length(); ++i) {
nsAutoCString varyValue(varyHeaders[i]);
char* rawBuffer = varyValue.BeginWriting();
char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
for (; token;
token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
nsDependentCString header(token);
if (header.EqualsLiteral("*")) {
return true;
}
char* rawBuffer = varyHeaders.BeginWriting();
char* token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer);
for (; token;
token = nsCRT::strtok(rawBuffer, NS_HTTP_HEADER_SEPS, &rawBuffer)) {
nsDependentCString header(token);
if (header.EqualsLiteral("*")) {
return true;
}
}
return false;

Просмотреть файл

@ -230,6 +230,11 @@ protected:
Point mEnd;
};
CanvasRenderingContext2D* CanvasRenderingContext2D::sThisContext = nullptr;
bool CanvasRenderingContext2D::sThisContextWasDestroyed = false;
bool CanvasRenderingContext2D::sThisContextHadItsFilterUpdated = false;
bool
CanvasRenderingContext2D::PatternIsOpaque(CanvasRenderingContext2D::Style aStyle) const
{
@ -1084,6 +1089,10 @@ CanvasRenderingContext2D::CanvasRenderingContext2D()
CanvasRenderingContext2D::~CanvasRenderingContext2D()
{
if (sThisContext == this) {
sThisContextWasDestroyed = true;
}
RemoveDrawObserver();
RemovePostRefreshObserver();
RemoveShutdownObserver();
@ -2838,6 +2847,45 @@ private:
nsPresContext* mPresContext;
};
bool
CanvasRenderingContext2D::NeedToApplyFilter()
{
MOZ_RELEASE_ASSERT(mStyleStack.Length() >= 1);
MOZ_RELEASE_ASSERT(CurrentState().filter.mPrimitives.Length() < 1000);
sThisContext = this;
sThisContextWasDestroyed = false;
sThisContextHadItsFilterUpdated = false;
const gfx::FilterDescription& filter = EnsureUpdatedFilter();
MOZ_RELEASE_ASSERT(!sThisContextWasDestroyed);
// Do these checks in different lines so that if we crash, we can tell from
// the crashing line whether the filter was updated.
// Any well-behaved filters should be less have 1000 primitives. The check
// itself is not important, we just need it to access mPrimitives.Length(),
// since that's what's crashing.
if (sThisContextHadItsFilterUpdated) {
MOZ_RELEASE_ASSERT(mStyleStack.Length() >= 1);
MOZ_RELEASE_ASSERT(filter.mPrimitives.Length() < 1000);
} else {
MOZ_RELEASE_ASSERT(mStyleStack.Length() >= 1);
MOZ_RELEASE_ASSERT(filter.mPrimitives.Length() < 2000);
}
return filter.mPrimitives.Length() > 0;
}
const gfx::FilterDescription&
CanvasRenderingContext2D::EnsureUpdatedFilter()
{
const ContextState& state = CurrentState();
bool isWriteOnly = mCanvasElement && mCanvasElement->IsWriteOnly();
if (state.filterSourceGraphicTainted != isWriteOnly) {
UpdateFilter();
}
MOZ_ASSERT(state.filterSourceGraphicTainted == isWriteOnly);
return state.filter;
}
void
CanvasRenderingContext2D::UpdateFilter()
{
@ -2851,6 +2899,10 @@ CanvasRenderingContext2D::UpdateFilter()
return;
}
if (sThisContext == this) {
sThisContextHadItsFilterUpdated = true;
}
// The filter might reference an SVG filter that is declared inside this
// document. Flush frames so that we'll have an nsSVGFilterFrame to work
// with.

Просмотреть файл

@ -863,24 +863,13 @@ protected:
* Returns true if the result of a drawing operation should be
* drawn with a filter.
*/
bool NeedToApplyFilter()
{
return EnsureUpdatedFilter().mPrimitives.Length() > 0;
}
bool NeedToApplyFilter();
/**
* Calls UpdateFilter if the canvas's WriteOnly state has changed between the
* last call to UpdateFilter and now.
*/
const gfx::FilterDescription& EnsureUpdatedFilter() {
const ContextState& state = CurrentState();
bool isWriteOnly = mCanvasElement && mCanvasElement->IsWriteOnly();
if (state.filterSourceGraphicTainted != isWriteOnly) {
UpdateFilter();
}
MOZ_ASSERT(state.filterSourceGraphicTainted == isWriteOnly);
return state.filter;
}
const gfx::FilterDescription& EnsureUpdatedFilter();
bool NeedToCalculateBounds()
{
@ -1102,6 +1091,11 @@ protected:
AutoTArray<ContextState, 3> mStyleStack;
// Temporary instrumentation to help debug bug 1308859
static CanvasRenderingContext2D* sThisContext;
static bool sThisContextWasDestroyed;
static bool sThisContextHadItsFilterUpdated;
inline ContextState& CurrentState() {
return mStyleStack[mStyleStack.Length() - 1];
}

Просмотреть файл

@ -69,7 +69,6 @@ public:
}
JSObject* GetDataObject() const
{
JS::ExposeObjectToActiveJS(mData);
return mData;
}

Просмотреть файл

@ -13,6 +13,7 @@
#include "nsISupportsPrimitives.h"
#include "nsIScriptSecurityManager.h"
#include "mozilla/dom/DOMStringList.h"
#include "nsArray.h"
#include "nsError.h"
#include "nsIDragService.h"
#include "nsIClipboard.h"
@ -876,7 +877,7 @@ DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
return NS_OK;
}
already_AddRefed<nsISupportsArray>
already_AddRefed<nsIArray>
DataTransfer::GetTransferables(nsIDOMNode* aDragTarget)
{
MOZ_ASSERT(aDragTarget);
@ -894,12 +895,10 @@ DataTransfer::GetTransferables(nsIDOMNode* aDragTarget)
return GetTransferables(doc->GetLoadContext());
}
already_AddRefed<nsISupportsArray>
already_AddRefed<nsIArray>
DataTransfer::GetTransferables(nsILoadContext* aLoadContext)
{
nsCOMPtr<nsISupportsArray> transArray =
do_CreateInstance("@mozilla.org/supports-array;1");
nsCOMPtr<nsIMutableArray> transArray = nsArray::Create();
if (!transArray) {
return nullptr;
}
@ -908,7 +907,7 @@ DataTransfer::GetTransferables(nsILoadContext* aLoadContext)
for (uint32_t i = 0; i < count; i++) {
nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
if (transferable) {
transArray->AppendElement(transferable);
transArray->AppendElement(transferable, /*weak =*/ false);
}
}

Просмотреть файл

@ -235,9 +235,9 @@ public:
// converts the data into an array of nsITransferable objects to be used for
// drag and drop or clipboard operations.
already_AddRefed<nsISupportsArray> GetTransferables(nsIDOMNode* aDragTarget);
already_AddRefed<nsIArray> GetTransferables(nsIDOMNode* aDragTarget);
already_AddRefed<nsISupportsArray>
already_AddRefed<nsIArray>
GetTransferables(nsILoadContext* aLoadContext);
// converts the data for a single item at aIndex into an nsITransferable

Просмотреть файл

@ -1975,7 +1975,7 @@ EventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
int32_t imageX, imageY;
Element* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
nsCOMPtr<nsISupportsArray> transArray =
nsCOMPtr<nsIArray> transArray =
aDataTransfer->GetTransferables(dragTarget->AsDOMNode());
if (!transArray)
return false;

Просмотреть файл

@ -66,7 +66,6 @@ void
MessageEvent::GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
ErrorResult& aRv)
{
JS::ExposeValueToActiveJS(mData);
aData.set(mData);
if (!JS_WrapValue(aCx, aData)) {
aRv.Throw(NS_ERROR_FAILURE);

Просмотреть файл

@ -1286,16 +1286,16 @@ FetchBody<Derived>::SetMimeType()
{
// Extract mime type.
ErrorResult result;
nsTArray<nsCString> contentTypeValues;
nsCString contentTypeValues;
MOZ_ASSERT(DerivedClass()->GetInternalHeaders());
DerivedClass()->GetInternalHeaders()->GetAll(NS_LITERAL_CSTRING("Content-Type"),
contentTypeValues, result);
DerivedClass()->GetInternalHeaders()->Get(NS_LITERAL_CSTRING("Content-Type"),
contentTypeValues, result);
MOZ_ALWAYS_TRUE(!result.Failed());
// HTTP ABNF states Content-Type may have only one value.
// This is from the "parse a header value" of the fetch spec.
if (contentTypeValues.Length() == 1) {
mMimeType = contentTypeValues[0];
if (!contentTypeValues.IsVoid() && contentTypeValues.Find(",") == -1) {
mMimeType = contentTypeValues;
ToLowerCase(mMimeType);
}
}

Просмотреть файл

@ -329,7 +329,7 @@ FetchDriver::HttpFetch()
if (uploadChan) {
nsAutoCString contentType;
ErrorResult result;
mRequest->Headers()->Get(NS_LITERAL_CSTRING("content-type"), contentType, result);
mRequest->Headers()->GetFirst(NS_LITERAL_CSTRING("content-type"), contentType, result);
// This is an error because the Request constructor explicitly extracts and
// sets a content-type per spec.
if (result.Failed()) {

Просмотреть файл

@ -81,15 +81,14 @@ public:
mInternalHeaders->Delete(aName, aRv);
}
void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const
void Get(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
{
mInternalHeaders->Get(aName, aValue, aRv);
}
void GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
ErrorResult& aRv) const
void GetFirst(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
{
mInternalHeaders->GetAll(aName, aResults, aRv);
mInternalHeaders->GetFirst(aName, aValue, aRv);
}
bool Has(const nsACString& aName, ErrorResult& aRv) const

Просмотреть файл

@ -78,7 +78,36 @@ InternalHeaders::Delete(const nsACString& aName, ErrorResult& aRv)
}
void
InternalHeaders::Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const
InternalHeaders::Get(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
{
nsAutoCString lowerName;
ToLowerCase(aName, lowerName);
if (IsInvalidName(lowerName, aRv)) {
return;
}
const char* delimiter = ",";
bool firstValueFound = false;
for (uint32_t i = 0; i < mList.Length(); ++i) {
if (lowerName == mList[i].mName) {
if (firstValueFound) {
aValue += delimiter;
}
aValue += mList[i].mValue;
firstValueFound = true;
}
}
// No value found, so return null to content
if (!firstValueFound) {
aValue.SetIsVoid(true);
}
}
void
InternalHeaders::GetFirst(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const
{
nsAutoCString lowerName;
ToLowerCase(aName, lowerName);
@ -98,25 +127,6 @@ InternalHeaders::Get(const nsACString& aName, nsCString& aValue, ErrorResult& aR
aValue.SetIsVoid(true);
}
void
InternalHeaders::GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
ErrorResult& aRv) const
{
nsAutoCString lowerName;
ToLowerCase(aName, lowerName);
if (IsInvalidName(lowerName, aRv)) {
return;
}
aResults.SetLength(0);
for (uint32_t i = 0; i < mList.Length(); ++i) {
if (lowerName == mList[i].mName) {
aResults.AppendElement(mList[i].mValue);
}
}
}
bool
InternalHeaders::Has(const nsACString& aName, ErrorResult& aRv) const
{
@ -351,7 +361,7 @@ InternalHeaders::CORSHeaders(InternalHeaders* aHeaders)
ErrorResult result;
nsAutoCString acExposedNames;
aHeaders->Get(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
aHeaders->GetFirst(NS_LITERAL_CSTRING("Access-Control-Expose-Headers"), acExposedNames, result);
MOZ_ASSERT(!result.Failed());
AutoTArray<nsCString, 5> exposeNamesArray;

Просмотреть файл

@ -74,9 +74,8 @@ public:
void Append(const nsACString& aName, const nsACString& aValue,
ErrorResult& aRv);
void Delete(const nsACString& aName, ErrorResult& aRv);
void Get(const nsACString& aName, nsCString& aValue, ErrorResult& aRv) const;
void GetAll(const nsACString& aName, nsTArray<nsCString>& aResults,
ErrorResult& aRv) const;
void Get(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const;
void GetFirst(const nsACString& aName, nsACString& aValue, ErrorResult& aRv) const;
bool Has(const nsACString& aName, ErrorResult& aRv) const;
void Set(const nsACString& aName, const nsACString& aValue, ErrorResult& aRv);

Просмотреть файл

@ -139,7 +139,7 @@ FlyWebPublishedServer::OnWebSocketAccept(InternalRequest* aConnectRequest,
nsAutoCString extensions, negotiatedExtensions;
aConnectRequest->Headers()->
Get(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions, aRv);
GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Extensions"), extensions, aRv);
mozilla::net::ProcessServerWebSocketExtensions(extensions,
negotiatedExtensions);
@ -402,7 +402,7 @@ FlyWebPublishedServerChild::OnWebSocketAcceptInternal(InternalRequest* aRequest,
nsAutoCString reqProtocols;
aRequest->Headers()->
Get(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), reqProtocols, aRv);
GetFirst(NS_LITERAL_CSTRING("Sec-WebSocket-Protocol"), reqProtocols, aRv);
if (!ContainsToken(reqProtocols, NS_ConvertUTF16toUTF8(protocol))) {
// Should throw a better error here
aRv.Throw(NS_ERROR_FAILURE);

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше