зеркало из https://github.com/mozilla/gecko-dev.git
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
036a7c4324
|
@ -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 {
|
||||
|
|
Двоичный файл не отображается.
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче