Merge mozilla-central to mozilla-inbound. r=merge a=merge CLOSED TREE

This commit is contained in:
Noemi Erli 2017-11-10 12:45:26 +02:00
Родитель 48988ddf27 7e070192d7
Коммит ca01158c96
125 изменённых файлов: 1606 добавлений и 14657 удалений

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

@ -334,7 +334,6 @@ testing/xpcshell/node-http2/**
# Third party services
services/common/kinto-http-client.js
services/common/kinto-offline-client.js
services/sync/tps/extensions/mozmill
# toolkit/ exclusions

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

@ -781,10 +781,6 @@ html|input.urlbar-input[textoverflow]:not([focused]) {
margin-inline-end: 0.25em !important;
}
#main-window[customizing] :-moz-any(#urlbar, .searchbar-textbox) > .urlbar-textbox-container > .textbox-input-box {
visibility: hidden;
}
/* Flexible spacer sizing (matching url bar) */
toolbarpaletteitem[place=toolbar][id^=wrapper-customizableui-special-spring],
toolbarspring {

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

@ -82,12 +82,12 @@
<xul:button anonid="preferencesButton"
label="&preferencesButton.label;"
xbl:inherits="value=userContextId"
onclick="gContainersPane.onPreferenceClick(event.originalTarget)">
oncommand="gContainersPane.onPreferenceCommand(event.originalTarget)">
</xul:button>
<xul:button anonid="removeButton"
label="&removeButton.label;"
xbl:inherits="value=userContextId"
onclick="gContainersPane.onRemoveClick(event.originalTarget)">
oncommand="gContainersPane.onRemoveCommand(event.originalTarget)">
</xul:button>
</xul:hbox>
</xul:hbox>

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

@ -40,7 +40,7 @@ let gContainersPane = {
}
},
async onRemoveClick(button) {
async onRemoveCommand(button) {
let userContextId = parseInt(button.getAttribute("value"), 10);
let count = ContextualIdentityService.countContainerTabs(userContextId);
@ -69,11 +69,11 @@ let gContainersPane = {
this._rebuildView();
},
onPreferenceClick(button) {
onPreferenceCommand(button) {
this.openPreferenceDialog(button.getAttribute("value"));
},
onAddButtonClick(button) {
onAddButtonCommand(button) {
this.openPreferenceDialog(null);
},

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

@ -39,7 +39,7 @@
</vbox>
<vbox>
<hbox flex="1">
<button id="containersAdd" onclick="gContainersPane.onAddButtonClick();" accesskey="&addButton.accesskey;" label="&addButton.label;"/>
<button id="containersAdd" oncommand="gContainersPane.onAddButtonCommand();" accesskey="&addButton.accesskey;" label="&addButton.label;"/>
</hbox>
</vbox>
</groupbox>

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

@ -14,7 +14,7 @@ add_task(async function() {
let dialogPromise = promiseLoadSubDialog(CONTAINERS_URL);
let addButton = doc.getElementById("containersAdd");
addButton.click();
addButton.doCommand();
let dialog = await dialogPromise;

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

@ -249,7 +249,10 @@ add_task(async function testClearHistory() {
let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory");
ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled");
let historyCleared = promiseObserver("satchel-storage-changed");
controller.doCommand("cmd_clearhistory");
await historyCleared;
let count = await countEntries();
ok(count == 0, "History cleared");
});
@ -262,3 +265,13 @@ add_task(async function asyncCleanup() {
gBrowser.selectedBrowser.loadURI("about:blank");
await promiseRemoveEngine();
});
function promiseObserver(topic) {
return new Promise(resolve => {
let obs = (aSubject, aTopic, aData) => {
Services.obs.removeObserver(obs, aTopic);
resolve(aSubject);
};
Services.obs.addObserver(obs, topic);
});
}

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

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const {utils: Cu} = Components;
const {results: Cr, utils: Cu} = Components;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -82,26 +82,36 @@ this.Bootstrap = {
}
// record the value of the default branch before setting it
switch (realPrefType) {
case Services.prefs.PREF_STRING:
studyPrefsChanged[prefName] = defaultBranch.getCharPref(prefName);
break;
try {
switch (realPrefType) {
case Services.prefs.PREF_STRING:
studyPrefsChanged[prefName] = defaultBranch.getCharPref(prefName);
break;
case Services.prefs.PREF_INT:
studyPrefsChanged[prefName] = defaultBranch.getIntPref(prefName);
break;
case Services.prefs.PREF_INT:
studyPrefsChanged[prefName] = defaultBranch.getIntPref(prefName);
break;
case Services.prefs.PREF_BOOL:
studyPrefsChanged[prefName] = defaultBranch.getBoolPref(prefName);
break;
case Services.prefs.PREF_BOOL:
studyPrefsChanged[prefName] = defaultBranch.getBoolPref(prefName);
break;
case Services.prefs.PREF_INVALID:
case Services.prefs.PREF_INVALID:
studyPrefsChanged[prefName] = null;
break;
default:
// This should never happen
log.error(`Error getting startup pref ${prefName}; unknown value type ${experimentPrefType}.`);
}
} catch (e) {
if (e.result == Cr.NS_ERROR_UNEXPECTED) {
// There is a value for the pref on the user branch but not on the default branch. This is ok.
studyPrefsChanged[prefName] = null;
break;
default:
// This should never happen
log.error(`Error getting startup pref ${prefName}; unknown value type ${experimentPrefType}.`);
} else {
// rethrow
throw e;
}
}
// now set the new default value

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

@ -252,3 +252,19 @@ decorate_task(
);
},
);
// Test that startup prefs are handled correctly when there is a value on the user branch but not the default branch.
decorate_task(
withPrefEnv({
set: [
["extensions.shield-recipe-client.startupExperimentPrefs.testing.does-not-exist", "foo"],
["testing.does-not-exist", "foo"],
],
}),
withBootstrap,
withStub(PreferenceExperiments, "recordOriginalValues"),
async function testInitExperimentPrefsNoDefaultValue(Bootstrap) {
Bootstrap.initExperimentPrefs();
ok(true, "initExperimentPrefs should not throw for non-existant prefs");
},
);

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

@ -69,6 +69,10 @@
margin-inline-end: 0;
}
:root[customizing] .urlbar-input-box {
visibility: hidden;
}
#urlbar-container {
-moz-box-align: center;
}

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

@ -119,8 +119,7 @@ else
esac
export CCACHE="$topsrcdir/sccache2/sccache${suffix}"
export SCCACHE_VERBOSE_STATS=1
mk_add_options MOZ_PREFLIGHT_ALL+=build/sccache.mk
mk_add_options MOZ_POSTFLIGHT_ALL+=build/sccache.mk
mk_add_options MOZBUILD_MANAGE_SCCACHE_DAEMON=${topsrcdir}/sccache2/sccache
mk_add_options "UPLOAD_EXTRA_FILES+=sccache.log.gz"
case "$platform" in
win*)

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

@ -1,15 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
preflight_all:
# Terminate any sccache server that might still be around
-$(TOPSRCDIR)/sccache2/sccache --stop-server > /dev/null 2>&1
# Start a new server, ensuring it gets the jobserver file descriptors
# from make (but don't use the + prefix when make -n is used, so that
# the command doesn't run in that case)
$(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)env RUST_LOG=sccache::compiler=debug SCCACHE_ERROR_LOG=$(OBJDIR)/dist/sccache.log $(TOPSRCDIR)/sccache2/sccache --start-server
postflight_all:
# Terminate sccache server. This prints sccache stats.
-$(TOPSRCDIR)/sccache2/sccache --stop-server

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

@ -12,14 +12,10 @@
# Options:
# MOZ_OBJDIR - Destination object directory
# MOZ_MAKE_FLAGS - Flags to pass to $(MAKE)
# MOZ_PREFLIGHT_ALL - Makefiles to run before building.
# MOZ_POSTFLIGHT_ALL - Makefiles to run after building.
#
#######################################################################
# Defines
comma := ,
ifdef MACH
ifndef NO_BUILDSTATUS_MESSAGES
define BUILDSTATUS
@ -44,8 +40,6 @@ TOPSRCDIR := $(CWD)
endif
endif
SH := /bin/sh
PERL ?= perl
PYTHON ?= $(shell which python2.7 > /dev/null 2>&1 && echo python2.7 || echo python)
CONFIG_GUESS := $(shell $(TOPSRCDIR)/build/autoconf/config.guess)
@ -153,15 +147,16 @@ endif
# helper target for mobile
build_and_deploy: build package install
#####################################################
# Preflight, before building any project
ifdef MOZ_PREFLIGHT_ALL
build preflight_all::
set -e; \
for mkfile in $(MOZ_PREFLIGHT_ALL); do \
$(MAKE) -f $(TOPSRCDIR)/$$mkfile preflight_all TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \
done
# In automation, manage an sccache daemon. The starting of the server
# needs to be in a make file so sccache inherits the jobserver.
ifdef MOZBUILD_MANAGE_SCCACHE_DAEMON
build::
# Terminate any sccache server that might still be around.
-$(MOZBUILD_MANAGE_SCCACHE_DAEMON) --stop-server > /dev/null 2>&1
# Start a new server, ensuring it gets the jobserver file descriptors
# from make (but don't use the + prefix when make -n is used, so that
# the command doesn't run in that case)
$(if $(findstring n,$(filter-out --%, $(MAKEFLAGS))),,+)env RUST_LOG=sccache::compiler=debug SCCACHE_ERROR_LOG=$(OBJDIR)/dist/sccache.log $(MOZBUILD_MANAGE_SCCACHE_DAEMON) --start-server
endif
####################################
@ -280,25 +275,17 @@ build:: $(OBJDIR)/Makefile $(OBJDIR)/config.status
$(OBJDIR_TARGETS):: $(OBJDIR)/Makefile $(OBJDIR)/config.status
+$(MOZ_MAKE) $@
####################################
# Postflight, after building all projects
ifdef MOZ_AUTOMATION
build::
$(MAKE) -f $(TOPSRCDIR)/client.mk automation/build
endif
ifdef MOZ_POSTFLIGHT_ALL
build postflight_all::
set -e; \
for mkfile in $(MOZ_POSTFLIGHT_ALL); do \
$(MAKE) -f $(TOPSRCDIR)/$$mkfile postflight_all TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \
done
ifdef MOZBUILD_MANAGE_SCCACHE_DAEMON
build::
# Terminate sccache server. This prints sccache stats.
-$(MOZBUILD_MANAGE_SCCACHE_DAEMON) --stop-server
endif
echo-variable-%:
@echo $($*)
# This makefile doesn't support parallel execution. It does pass
# MOZ_MAKE_FLAGS to sub-make processes, so they will correctly execute
# in parallel.
@ -307,6 +294,4 @@ echo-variable-%:
.PHONY: \
build \
configure \
preflight_all \
postflight_all \
$(OBJDIR_TARGETS)

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

@ -59,8 +59,7 @@ endif
.PHONY: FORCE
# Extra define to trigger some workarounds. We should strive to limit the
# use of those. As of writing the only ones are in
# toolkit/content/buildconfig.html and browser/locales/jar.mn.
# use of those. As of writing the only one is in browser/locales/jar.mn.
ACDEFINES += -DBUILD_FASTER
# Files under the faster/ sub-directory, however, are not meant to use the

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

@ -5,37 +5,61 @@
"use strict";
add_task(function* () {
info("Test JSON row selection started");
add_task(async function () {
info("Test 1 JSON row selection started");
// Create a tall JSON so that there is a scrollbar.
let numRows = 1e3;
let json = JSON.stringify(Array(numRows).fill().map((_, i) => i));
let tab = yield addJsonViewTab("data:application/json," + json);
let tab = await addJsonViewTab("data:application/json," + json);
is(yield getElementCount(".treeRow"), numRows, "Got the expected number of rows.");
yield assertRowSelected(null);
yield evalInContent("var scroller = $('.jsonPanelBox .panelContent')");
ok(yield evalInContent("scroller.clientHeight < scroller.scrollHeight"),
is(await getElementCount(".treeRow"), numRows, "Got the expected number of rows.");
await assertRowSelected(null);
await evalInContent("var scroller = $('.jsonPanelBox .panelContent')");
ok(await evalInContent("scroller.clientHeight < scroller.scrollHeight"),
"There is a scrollbar.");
is(yield evalInContent("scroller.scrollTop"), 0, "Initially scrolled to the top.");
is(await evalInContent("scroller.scrollTop"), 0, "Initially scrolled to the top.");
// Click to select last row.
yield evalInContent("$('.treeRow:last-child').click()");
yield assertRowSelected(numRows);
is(yield evalInContent("scroller.scrollTop + scroller.clientHeight"),
yield evalInContent("scroller.scrollHeight"), "Scrolled to the bottom.");
await evalInContent("$('.treeRow:last-child').click()");
await assertRowSelected(numRows);
is(await evalInContent("scroller.scrollTop + scroller.clientHeight"),
await evalInContent("scroller.scrollHeight"), "Scrolled to the bottom.");
// Click to select 2nd row.
yield evalInContent("$('.treeRow:nth-child(2)').click()");
yield assertRowSelected(2);
ok(yield evalInContent("scroller.scrollTop > 0"), "Not scrolled to the top.");
await evalInContent("$('.treeRow:nth-child(2)').click()");
await assertRowSelected(2);
ok(await evalInContent("scroller.scrollTop > 0"), "Not scrolled to the top.");
// Synthetize up arrow key to select first row.
yield evalInContent("$('.treeTable').focus()");
yield BrowserTestUtils.synthesizeKey("VK_UP", {}, tab.linkedBrowser);
yield assertRowSelected(1);
is(yield evalInContent("scroller.scrollTop"), 0, "Scrolled to the top.");
await evalInContent("$('.treeTable').focus()");
await BrowserTestUtils.synthesizeKey("VK_UP", {}, tab.linkedBrowser);
await assertRowSelected(1);
is(await evalInContent("scroller.scrollTop"), 0, "Scrolled to the top.");
});
add_task(async function () {
info("Test 2 JSON row selection started");
let numRows = 4;
let tab = await addJsonViewTab("data:application/json,[0,1,2,3]");
is(await getElementCount(".treeRow"), numRows, "Got the expected number of rows.");
await assertRowSelected(null);
// Click to select first row.
await clickJsonNode(".treeRow:first-child");
await assertRowSelected(1);
// Synthetize multiple down arrow keydowns to select following rows.
for (let i = 2; i < numRows; ++i) {
await BrowserTestUtils.synthesizeKey("VK_DOWN", {type: "keydown"}, tab.linkedBrowser);
await assertRowSelected(i);
}
// Now synthetize the keyup, this shouldn't change selected row.
await BrowserTestUtils.synthesizeKey("VK_DOWN", {type: "keyup"}, tab.linkedBrowser);
await assertRowSelected(numRows - 1);
});
async function assertRowSelected(rowNum) {

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

@ -32,54 +32,47 @@ registerCleanupFunction(() => {
* will be considered to have failed, and the returned promise will be rejected.
* If this parameter is not passed or is negative, it will be ignored.
*/
function addJsonViewTab(url, timeout = -1) {
async function addJsonViewTab(url, timeout = -1) {
info("Adding a new JSON tab with URL: '" + url + "'");
let deferred = defer();
addTab(url).then(tab => {
let browser = tab.linkedBrowser;
let tab = await addTab(url);
let browser = tab.linkedBrowser;
// Load devtools/shared/frame-script-utils.js
getFrameScript();
// Load devtools/shared/frame-script-utils.js
getFrameScript();
// Load frame script with helpers for JSON View tests.
let rootDir = getRootDirectory(gTestPath);
let frameScriptUrl = rootDir + "doc_frame_script.js";
browser.messageManager.loadFrameScript(frameScriptUrl, false);
// Load frame script with helpers for JSON View tests.
let rootDir = getRootDirectory(gTestPath);
let frameScriptUrl = rootDir + "doc_frame_script.js";
browser.messageManager.loadFrameScript(frameScriptUrl, false);
// Check if there is a JSONView object.
if (!content.window.wrappedJSObject.JSONView) {
deferred.reject("JSON Viewer did not load.");
return;
}
// Check if there is a JSONView object.
if (!content.window.wrappedJSObject.JSONView) {
throw new Error("JSON Viewer did not load.");
}
// Resolve if the JSONView is fully loaded or wait
// for an initialization event.
if (content.window.wrappedJSObject.JSONView.initialized) {
deferred.resolve(tab);
} else {
waitForContentMessage("Test:JsonView:JSONViewInitialized").then(() => {
deferred.resolve(tab);
});
}
// Resolve if the JSONView is fully loaded.
if (content.window.wrappedJSObject.JSONView.initialized) {
return tab;
}
// Add a timeout.
if (timeout >= 0) {
new Promise(resolve => {
if (content.window.document.readyState === "complete") {
resolve();
} else {
waitForContentMessage("Test:JsonView:load").then(resolve);
}
}).then(() => {
setTimeout(() => {
deferred.reject("JSON Viewer did not load.");
}, timeout);
});
}
});
// Otherwise wait for an initialization event, possibly with a time limit.
const onJSONViewInitialized =
waitForContentMessage("Test:JsonView:JSONViewInitialized")
.then(() => tab);
return deferred.promise;
if (!(timeout >= 0)) {
return onJSONViewInitialized;
}
if (content.window.document.readyState !== "complete") {
await waitForContentMessage("Test:JsonView:load");
}
let onTimeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error("JSON Viewer did not load.")), timeout));
return Promise.race([onJSONViewInitialized, onTimeout]);
}
/**
@ -165,9 +158,7 @@ function sendString(str, selector) {
}
function waitForTime(delay) {
let deferred = defer();
setTimeout(deferred.resolve, delay);
return deferred.promise;
return new Promise(resolve => setTimeout(resolve, delay));
}
function waitForFilter() {

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

@ -130,7 +130,6 @@ define(function (require, exports, module) {
this.toggle = this.toggle.bind(this);
this.isExpanded = this.isExpanded.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.onKeyUp = this.onKeyUp.bind(this);
this.onClickRow = this.onClickRow.bind(this);
this.getSelectedRow = this.getSelectedRow.bind(this);
this.selectRow = this.selectRow.bind(this);
@ -228,13 +227,10 @@ define(function (require, exports, module) {
// Event Handlers
onKeyDown(event) {
if (["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(
event.key)) {
event.preventDefault();
if (!["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"].includes(event.key)) {
return;
}
}
onKeyUp(event) {
let row = this.getSelectedRow(this.rows);
if (!row) {
return;
@ -265,8 +261,6 @@ define(function (require, exports, module) {
this.selectRow(previousRow);
}
break;
default:
return;
}
event.preventDefault();
@ -471,7 +465,6 @@ define(function (require, exports, module) {
role: "tree",
tabIndex: 0,
onKeyDown: this.onKeyDown,
onKeyUp: this.onKeyUp,
"aria-label": this.props.label || "",
"aria-activedescendant": this.state.selected,
cellPadding: 0,

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

@ -428,6 +428,17 @@ DevToolsStartup.prototype = {
}
},
/**
* Check if the user is a DevTools user by looking at our selfxss pref.
* This preference is incremented everytime the console is used (up to 5).
*
* @return {Boolean} true if the user can be considered as a devtools user.
*/
isDevToolsUser() {
let selfXssCount = Services.prefs.getIntPref("devtools.selfxss.count", 0);
return selfXssCount > 0;
},
/**
* Depending on some runtime parameters (command line arguments as well as existing
* preferences), the DEVTOOLS_ENABLED_PREF might be forced to true.
@ -442,7 +453,8 @@ DevToolsStartup.prototype = {
}
let hasToolbarPref = Services.prefs.getBoolPref(TOOLBAR_VISIBLE_PREF, false);
if (hasDevToolsFlag || hasToolbarPref) {
if (hasDevToolsFlag || hasToolbarPref || this.isDevToolsUser()) {
Services.prefs.setBoolPref(DEVTOOLS_ENABLED_PREF, true);
}
},

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

@ -87,10 +87,11 @@ protected:
MOZ_RELEASE_ASSERT(aContainer,
"This constructor shouldn't be used when pointing nowhere");
if (!mRef) {
MOZ_ASSERT(mOffset.value() == 0);
MOZ_ASSERT(!mParent->IsContainerNode() || mOffset.value() == 0);
return;
}
MOZ_ASSERT(mOffset.value() > 0);
MOZ_ASSERT(mParent == mRef->GetParentNode());
MOZ_ASSERT(mParent->GetChildAt(mOffset.value() - 1) == mRef);
}

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

@ -49,19 +49,10 @@ HTMLOptionElement::~HTMLOptionElement()
{
}
NS_IMPL_ISUPPORTS_INHERITED(HTMLOptionElement, nsGenericHTMLElement,
nsIDOMHTMLOptionElement)
NS_IMPL_ISUPPORTS_INHERITED0(HTMLOptionElement, nsGenericHTMLElement)
NS_IMPL_ELEMENT_CLONE(HTMLOptionElement)
NS_IMETHODIMP
HTMLOptionElement::GetForm(nsIDOMHTMLFormElement** aForm)
{
NS_IF_ADDREF(*aForm = GetForm());
return NS_OK;
}
mozilla::dom::HTMLFormElement*
HTMLOptionElement::GetForm()
{
@ -115,15 +106,7 @@ HTMLOptionElement::UpdateDisabledState(bool aNotify)
}
}
NS_IMETHODIMP
HTMLOptionElement::GetSelected(bool* aValue)
{
NS_ENSURE_ARG_POINTER(aValue);
*aValue = Selected();
return NS_OK;
}
NS_IMETHODIMP
void
HTMLOptionElement::SetSelected(bool aValue)
{
// Note: The select content obj maintains all the PresState
@ -141,21 +124,6 @@ HTMLOptionElement::SetSelected(bool aValue)
} else {
SetSelectedInternal(aValue, true);
}
return NS_OK;
}
NS_IMPL_BOOL_ATTR(HTMLOptionElement, DefaultSelected, selected)
// GetText returns a whitespace compressed .textContent value.
NS_IMPL_STRING_ATTR_WITH_FALLBACK(HTMLOptionElement, Label, label, GetText)
NS_IMPL_STRING_ATTR_WITH_FALLBACK(HTMLOptionElement, Value, value, GetText)
NS_IMPL_BOOL_ATTR(HTMLOptionElement, Disabled, disabled)
NS_IMETHODIMP
HTMLOptionElement::GetIndex(int32_t* aIndex)
{
*aIndex = Index();
return NS_OK;
}
int32_t
@ -179,18 +147,6 @@ HTMLOptionElement::Index()
return index;
}
bool
HTMLOptionElement::Selected() const
{
return mIsSelected;
}
bool
HTMLOptionElement::DefaultSelected() const
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::selected);
}
nsChangeHint
HTMLOptionElement::GetAttributeChangeHint(const nsAtom* aAttribute,
int32_t aModType) const
@ -288,7 +244,7 @@ HTMLOptionElement::AfterSetAttr(int32_t aNameSpaceID, nsAtom* aName,
aValue, aOldValue, aSubjectPrincipal, aNotify);
}
NS_IMETHODIMP
void
HTMLOptionElement::GetText(nsAString& aText)
{
nsAutoString text;
@ -310,14 +266,12 @@ HTMLOptionElement::GetText(nsAString& aText)
// XXX No CompressWhitespace for nsAString. Sad.
text.CompressWhitespace(true, true);
aText = text;
return NS_OK;
}
NS_IMETHODIMP
HTMLOptionElement::SetText(const nsAString& aText)
void
HTMLOptionElement::SetText(const nsAString& aText, ErrorResult& aRv)
{
return nsContentUtils::SetNodeTextContent(this, aText, true);
aRv = nsContentUtils::SetNodeTextContent(this, aText, true);
}
nsresult
@ -435,11 +389,7 @@ HTMLOptionElement::Option(const GlobalObject& aGlobal,
}
}
option->SetSelected(aSelected, aError);
if (aError.Failed()) {
return nullptr;
}
option->SetSelected(aSelected);
option->SetSelectedChanged(false);
return option.forget();

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

@ -9,7 +9,6 @@
#include "mozilla/Attributes.h"
#include "nsGenericHTMLElement.h"
#include "nsIDOMHTMLOptionElement.h"
#include "mozilla/dom/HTMLFormElement.h"
namespace mozilla {
@ -17,8 +16,7 @@ namespace dom {
class HTMLSelectElement;
class HTMLOptionElement final : public nsGenericHTMLElement,
public nsIDOMHTMLOptionElement
class HTMLOptionElement final : public nsGenericHTMLElement
{
public:
explicit HTMLOptionElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
@ -36,13 +34,15 @@ public:
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIDOMHTMLOptionElement
using mozilla::dom::Element::SetText;
using mozilla::dom::Element::GetText;
NS_DECL_NSIDOMHTMLOPTIONELEMENT
bool Selected() const;
bool DefaultSelected() const;
bool Selected() const
{
return mIsSelected;
}
void SetSelected(bool aValue);
void SetSelectedChanged(bool aValue)
{
@ -106,35 +106,39 @@ public:
HTMLFormElement* GetForm();
// The XPCOM GetLabel is OK for us
void GetLabel(DOMString& aLabel)
{
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::label, aLabel)) {
GetText(aLabel);
}
}
void SetLabel(const nsAString& aLabel, ErrorResult& aError)
{
SetHTMLAttr(nsGkAtoms::label, aLabel, aError);
}
// The XPCOM DefaultSelected is OK for us
bool DefaultSelected() const
{
return HasAttr(kNameSpaceID_None, nsGkAtoms::selected);
}
void SetDefaultSelected(bool aValue, ErrorResult& aRv)
{
SetHTMLBoolAttr(nsGkAtoms::selected, aValue, aRv);
}
// The XPCOM Selected is OK for us
void SetSelected(bool aValue, ErrorResult& aRv)
void GetValue(nsAString& aValue)
{
aRv = SetSelected(aValue);
if (!GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue)) {
GetText(aValue);
}
}
// The XPCOM GetValue is OK for us
void SetValue(const nsAString& aValue, ErrorResult& aRv)
{
SetHTMLAttr(nsGkAtoms::value, aValue, aRv);
}
// The XPCOM GetText is OK for us
void SetText(const nsAString& aValue, ErrorResult& aRv)
{
aRv = SetText(aValue);
}
void GetText(nsAString& aText);
void SetText(const nsAString& aText, ErrorResult& aRv);
int32_t Index();

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

@ -17,8 +17,6 @@
#include "nsGenericHTMLElement.h"
#include "nsTArray.h"
class nsIDOMHTMLOptionElement;
namespace mozilla {
namespace dom {

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

@ -513,7 +513,7 @@ HTMLSelectElement::GetFirstOptionIndex(nsIContent* aOptions)
int32_t listIndex = -1;
HTMLOptionElement* optElement = HTMLOptionElement::FromContent(aOptions);
if (optElement) {
GetOptionIndex(optElement, 0, true, &listIndex);
mOptions->GetOptionIndex(optElement->AsElement(), 0, true, &listIndex);
return listIndex;
}
@ -711,15 +711,6 @@ HTMLSelectElement::SetSelectedIndexInternal(int32_t aIndex, bool aNotify)
return rv;
}
NS_IMETHODIMP
HTMLSelectElement::GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
int32_t aStartIndex, bool aForward,
int32_t* aIndex)
{
nsCOMPtr<nsINode> option = do_QueryInterface(aOption);
return mOptions->GetOptionIndex(option->AsElement(), aStartIndex, aForward, aIndex);
}
bool
HTMLSelectElement::IsOptionSelectedByIndex(int32_t aIndex)
{
@ -1032,8 +1023,7 @@ HTMLSelectElement::GetValue(DOMString& aValue)
return;
}
DebugOnly<nsresult> rv = option->GetValue(aValue);
MOZ_ASSERT(NS_SUCCEEDED(rv));
option->GetValue(aValue);
}
void
@ -1479,8 +1469,8 @@ HTMLSelectElement::RestoreStateTo(SelectState* aNewSelected)
HTMLOptionElement* option = Item(i);
if (option) {
nsAutoString value;
nsresult rv = option->GetValue(value);
if (NS_SUCCEEDED(rv) && aNewSelected->ContainsOption(i, value)) {
option->GetValue(value);
if (aNewSelected->ContainsOption(i, value)) {
SetOptionsSelectedByIndex(i, i, IS_SELECTED | SET_DISABLED | NOTIFY);
}
}
@ -1579,7 +1569,7 @@ HTMLSelectElement::SubmitNamesValues(HTMLFormSubmission* aFormSubmission)
}
nsString value;
MOZ_ALWAYS_SUCCEEDS(option->GetValue(value));
option->GetValue(value);
if (keyGenProcessor) {
nsString tmp(value);
@ -1659,7 +1649,7 @@ HTMLSelectElement::IsValueMissing() const
// Check for a placeholder label option, don't count it as a valid value.
if (i == 0 && !Multiple() && Size() <= 1 && option->GetParent() == this) {
nsAutoString value;
MOZ_ALWAYS_SUCCEEDS(option->GetValue(value));
option->GetValue(value);
if (value.IsEmpty()) {
continue;
}

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

@ -360,19 +360,6 @@ public:
int32_t aEndIndex,
uint32_t aOptionsMask);
/**
* Finds the index of a given option element
*
* @param aOption the option to get the index of
* @param aStartIndex the index to start looking at
* @param aForward TRUE to look forward, FALSE to look backward
* @return the option index
*/
NS_IMETHOD GetOptionIndex(nsIDOMHTMLOptionElement* aOption,
int32_t aStartIndex,
bool aForward,
int32_t* aIndex);
/**
* Called when an attribute is about to be changed
*/

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

@ -16,7 +16,6 @@ XPIDL_SOURCES += [
'nsIDOMHTMLHtmlElement.idl',
'nsIDOMHTMLInputElement.idl',
'nsIDOMHTMLMediaElement.idl',
'nsIDOMHTMLOptionElement.idl',
'nsIDOMHTMLScriptElement.idl',
'nsIDOMMozBrowserFrame.idl',
'nsIDOMTimeRanges.idl',

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

@ -1,31 +0,0 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIDOMHTMLElement.idl"
/**
* The nsIDOMHTMLOptionElement interface is the interface to a [X]HTML
* option element.
*
* This interface is trying to follow the DOM Level 2 HTML specification:
* http://www.w3.org/TR/DOM-Level-2-HTML/
*
* with changes from the work-in-progress WHATWG HTML specification:
* http://www.whatwg.org/specs/web-apps/current-work/
*/
[uuid(c2b3e9ff-6b36-4158-ace3-05a9c5b8e1c1)]
interface nsIDOMHTMLOptionElement : nsISupports
{
attribute boolean disabled;
readonly attribute nsIDOMHTMLFormElement form;
attribute DOMString label;
attribute boolean defaultSelected;
attribute boolean selected;
attribute DOMString value;
attribute DOMString text;
readonly attribute long index;
};

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

@ -233,13 +233,13 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
}
if (NS_FAILED(aRv)) {
recorder->NotifyError(aRv);
mSession->DoSessionEndTask(aRv);
return;
}
nsresult rv = recorder->CreateAndDispatchBlobEvent(aBlob);
if (NS_FAILED(rv)) {
recorder->NotifyError(aRv);
mSession->DoSessionEndTask(aRv);
}
if (mDestroyRunnable &&
@ -275,11 +275,18 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
NS_IMETHOD
Run() override
{
MOZ_ASSERT(NS_IsMainThread());
mSession->MaybeCreateMutableBlobStorage();
for (uint32_t i = 0; i < mBuffer.Length(); i++) {
if (!mBuffer[i].IsEmpty()) {
mSession->mMutableBlobStorage->Append(mBuffer[i].Elements(),
mBuffer[i].Length());
if (mBuffer[i].IsEmpty()) {
continue;
}
nsresult rv = mSession->mMutableBlobStorage->Append(mBuffer[i].Elements(),
mBuffer[i].Length());
if (NS_WARN_IF(NS_FAILED(rv))) {
mSession->DoSessionEndTask(rv);
break;
}
}
@ -318,10 +325,9 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
class DispatchStartEventRunnable : public Runnable
{
public:
DispatchStartEventRunnable(Session* aSession, const nsAString& aEventName)
explicit DispatchStartEventRunnable(Session* aSession)
: Runnable("dom::MediaRecorder::Session::DispatchStartEventRunnable")
, mSession(aSession)
, mEventName(aEventName)
{ }
NS_IMETHOD Run() override
@ -332,15 +338,13 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
NS_ENSURE_TRUE(mSession->mRecorder, NS_OK);
RefPtr<MediaRecorder> recorder = mSession->mRecorder;
recorder->SetMimeType(mSession->mMimeType);
recorder->DispatchSimpleEvent(mEventName);
recorder->DispatchSimpleEvent(NS_LITERAL_STRING("start"));
return NS_OK;
}
private:
RefPtr<Session> mSession;
nsString mEventName;
};
// To ensure that MediaRecorder has tracks to record.
@ -376,8 +380,8 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
NS_IMETHOD Run() override
{
LOG(LogLevel::Debug, ("Session.DestroyRunnable session refcnt = (%d) stopIssued %d s=(%p)",
(int)mSession->mRefCnt, mSession->mStopIssued, mSession.get()));
LOG(LogLevel::Debug, ("Session.DestroyRunnable session refcnt = (%d) s=(%p)",
static_cast<int>(mSession->mRefCnt), mSession.get()));
MOZ_ASSERT(NS_IsMainThread() && mSession);
RefPtr<MediaRecorder> recorder = mSession->mRecorder;
if (!recorder) {
@ -389,7 +393,9 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
// MediaRecorder is not associated with this Session anymore, then, it's
// safe to delete this Session.
// Also avoid to run if this session already call stop before
if (!mSession->mStopIssued) {
if (mSession->mRunningState.isOk() &&
mSession->mRunningState.unwrap() != RunningState::Stopping &&
mSession->mRunningState.unwrap() != RunningState::Stopped) {
recorder->StopForSessionDestruction();
if (NS_FAILED(NS_DispatchToMainThread(new DestroyRunnable(mSession.forget())))) {
MOZ_ASSERT(false, "NS_DispatchToMainThread failed");
@ -397,6 +403,10 @@ class MediaRecorder::Session: public PrincipalChangeObserver<MediaStreamTrack>,
return NS_OK;
}
if (mSession->mRunningState.isOk()) {
mSession->mRunningState = RunningState::Stopped;
}
// Dispatch stop event and clear MIME type.
mSession->mMimeType = NS_LITERAL_STRING("");
recorder->SetMimeType(mSession->mMimeType);
@ -484,9 +494,7 @@ public:
Session(MediaRecorder* aRecorder, int32_t aTimeSlice)
: mRecorder(aRecorder)
, mTimeSlice(aTimeSlice)
, mStopIssued(false)
, mIsStartEventFired(false)
, mNeedSessionEndTask(true)
, mRunningState(RunningState::Idling)
{
MOZ_ASSERT(NS_IsMainThread());
@ -563,16 +571,20 @@ public:
{
LOG(LogLevel::Debug, ("Session.Stop %p", this));
MOZ_ASSERT(NS_IsMainThread());
mStopIssued = true;
if (mEncoder) {
mEncoder->Stop();
}
if (mNeedSessionEndTask) {
LOG(LogLevel::Debug, ("Session.Stop mNeedSessionEndTask %p", this));
if (mRunningState.isOk() &&
mRunningState.unwrap() == RunningState::Idling) {
LOG(LogLevel::Debug, ("Session.Stop Explicit end task %p", this));
// End the Session directly if there is no ExtractRunnable.
DoSessionEndTask(NS_OK);
} else if (mRunningState.isOk() &&
(mRunningState.unwrap() == RunningState::Starting ||
mRunningState.unwrap() == RunningState::Running)) {
mRunningState = RunningState::Stopping;
}
}
@ -714,7 +726,7 @@ private:
void MediaStreamReady(DOMMediaStream* aStream) {
MOZ_RELEASE_ASSERT(aStream);
if (mStopIssued) {
if (!mRunningState.isOk() || mRunningState.unwrap() != RunningState::Idling) {
return;
}
@ -832,6 +844,11 @@ private:
LOG(LogLevel::Debug, ("Session.InitEncoder %p", this));
MOZ_ASSERT(NS_IsMainThread());
if (!mRunningState.isOk() || mRunningState.unwrap() != RunningState::Idling) {
MOZ_ASSERT_UNREACHABLE("Double-init");
return;
}
// Create a TaskQueue to read encode media data from MediaEncoder.
MOZ_RELEASE_ASSERT(!mEncoderThread);
RefPtr<SharedThreadPool> pool =
@ -944,19 +961,36 @@ private:
mEncoder->ConnectMediaStreamTrack(track);
}
// Set mNeedSessionEndTask to false because the
// ExtractRunnable/DestroyRunnable will take the response to
// end the session.
mNeedSessionEndTask = false;
// Set mRunningState to Running so that ExtractRunnable/DestroyRunnable will
// take the responsibility to end the session.
mRunningState = RunningState::Starting;
}
// application should get blob and onstop event
void DoSessionEndTask(nsresult rv)
{
MOZ_ASSERT(NS_IsMainThread());
if (!mIsStartEventFired) {
NS_DispatchToMainThread(
new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
if (mRunningState.isErr()) {
// We have already ended with an error.
return;
}
if (mRunningState.isOk() &&
mRunningState.unwrap() == RunningState::Stopped) {
// We have already ended gracefully.
return;
}
if (mRunningState.isOk() &&
(mRunningState.unwrap() == RunningState::Idling ||
mRunningState.unwrap() == RunningState::Starting)) {
NS_DispatchToMainThread(new DispatchStartEventRunnable(this));
}
if (rv == NS_OK) {
mRunningState = RunningState::Stopped;
} else {
mRunningState = Err(rv);
}
if (NS_FAILED(rv)) {
@ -980,8 +1014,6 @@ private:
MOZ_ASSERT(false, "NS_DispatchToMainThread DestroyRunnable failed");
}
}
mNeedSessionEndTask = false;
}
void MediaEncoderInitialized()
@ -990,7 +1022,9 @@ private:
// Pull encoded metadata from MediaEncoder
nsTArray<nsTArray<uint8_t> > encodedBuf;
nsresult rv = mEncoder->GetEncodedMetadata(&encodedBuf, mMimeType);
nsString mime;
nsresult rv = mEncoder->GetEncodedMetadata(&encodedBuf, mime);
if (NS_FAILED(rv)) {
MOZ_ASSERT(false);
return;
@ -999,18 +1033,37 @@ private:
// Append pulled data into cache buffer.
NS_DispatchToMainThread(new StoreEncodedBufferRunnable(this,
Move(encodedBuf)));
RefPtr<Session> self = this;
NS_DispatchToMainThread(NewRunnableFrom([self, mime]() {
if (!self->mRecorder) {
MOZ_ASSERT_UNREACHABLE("Recorder should be live");
return NS_OK;
}
if (self->mRunningState.isOk()) {
auto state = self->mRunningState.unwrap();
if (state == RunningState::Starting || state == RunningState::Stopping) {
if (state == RunningState::Starting) {
// We set it to Running in the runnable since we can only assign
// mRunningState on main thread. We set it before running the start
// event runnable since that dispatches synchronously (and may cause
// js calls to methods depending on mRunningState).
self->mRunningState = RunningState::Running;
}
self->mMimeType = mime;
self->mRecorder->SetMimeType(self->mMimeType);
auto startEvent = MakeRefPtr<DispatchStartEventRunnable>(self);
startEvent->Run();
}
}
return NS_OK;
}));
}
void MediaEncoderDataAvailable()
{
MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn());
if (!mIsStartEventFired) {
NS_DispatchToMainThread(
new DispatchStartEventRunnable(this, NS_LITERAL_STRING("start")));
mIsStartEventFired = true;
}
Extract(false, nullptr);
}
@ -1117,6 +1170,14 @@ private:
}
private:
enum class RunningState {
Idling, // Session has been created
Starting, // MediaEncoder started, waiting for data
Running, // MediaEncoder has produced data
Stopping, // Stop() has been called
Stopped, // Session has stopped without any error
};
// Hold reference to MediaRecorder that ensure MediaRecorder is alive
// if there is an active session. Access ONLY on main thread.
RefPtr<MediaRecorder> mRecorder;
@ -1149,14 +1210,10 @@ private:
// push encoded data to onDataAvailable, instead, it passive wait the client
// side pull encoded data by calling requestData API.
const int32_t mTimeSlice;
// Indicate this session's stop has been called.
bool mStopIssued;
// Indicate the session had fire start event. Encoding thread only.
bool mIsStartEventFired;
// False if the InitEncoder called successfully, ensure the
// ExtractRunnable/DestroyRunnable will end the session.
// The session's current main thread state. The error type gets setwhen ending
// a recording with an error. An NS_OK error is invalid.
// Main thread only.
bool mNeedSessionEndTask;
Result<RunningState, nsresult> mRunningState;
};
NS_IMPL_ISUPPORTS_INHERITED0(MediaRecorder::Session::PushBlobRunnable, Runnable)

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

@ -11,6 +11,7 @@
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/HTMLLinkElement.h"
#include "mozilla/dom/HTMLObjectElement.h"
#include "mozilla/dom/HTMLOptionElement.h"
#include "mozilla/dom/HTMLSharedElement.h"
#include "mozilla/dom/HTMLTextAreaElement.h"
#include "mozilla/dom/TabParent.h"
@ -29,7 +30,6 @@
#include "nsIDOMHTMLDocument.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMHTMLMediaElement.h"
#include "nsIDOMHTMLOptionElement.h"
#include "nsIDOMHTMLScriptElement.h"
#include "nsIDOMMozNamedAttrMap.h"
#include "nsIDOMNode.h"
@ -1166,14 +1166,15 @@ PersistNodeFixup::FixupNode(nsIDOMNode *aNodeIn,
return rv;
}
nsCOMPtr<nsIDOMHTMLOptionElement> nodeAsOption = do_QueryInterface(aNodeIn);
dom::HTMLOptionElement* nodeAsOption = dom::HTMLOptionElement::FromContent(content);
if (nodeAsOption) {
rv = GetNodeToFixup(aNodeIn, aNodeOut);
if (NS_SUCCEEDED(rv) && *aNodeOut) {
nsCOMPtr<nsIDOMHTMLOptionElement> outElt = do_QueryInterface(*aNodeOut);
bool selected;
nodeAsOption->GetSelected(&selected);
outElt->SetDefaultSelected(selected);
nsCOMPtr<nsIContent> outContent = do_QueryInterface(*aNodeOut);
dom::HTMLOptionElement* outElt = dom::HTMLOptionElement::FromContent(outContent);
bool selected = nodeAsOption->Selected();
IgnoredErrorResult ignored;
outElt->SetDefaultSelected(selected, ignored);
}
return rv;
}

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

@ -13,19 +13,18 @@
[HTMLConstructor, NamedConstructor=Option(optional DOMString text = "", optional DOMString value, optional boolean defaultSelected = false, optional boolean selected = false)]
interface HTMLOptionElement : HTMLElement {
[CEReactions, SetterThrows]
attribute boolean disabled;
[CEReactions, SetterThrows]
attribute boolean disabled;
readonly attribute HTMLFormElement? form;
[CEReactions, SetterThrows]
attribute DOMString label;
[CEReactions, SetterThrows]
attribute boolean defaultSelected;
[SetterThrows]
attribute boolean selected;
[CEReactions, SetterThrows]
attribute DOMString value;
[CEReactions, SetterThrows]
attribute DOMString label;
[CEReactions, SetterThrows]
attribute boolean defaultSelected;
attribute boolean selected;
[CEReactions, SetterThrows]
attribute DOMString value;
[CEReactions, SetterThrows]
attribute DOMString text;
[CEReactions, SetterThrows]
attribute DOMString text;
readonly attribute long index;
};

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

@ -32,17 +32,14 @@ namespace mozilla {
using namespace dom;
CreateElementTransaction::CreateElementTransaction(EditorBase& aEditorBase,
nsAtom& aTag,
nsINode& aParent,
int32_t aOffsetInParent,
nsIContent* aChildAtOffset)
CreateElementTransaction::CreateElementTransaction(
EditorBase& aEditorBase,
nsAtom& aTag,
const EditorRawDOMPoint& aPointToInsert)
: EditTransactionBase()
, mEditorBase(&aEditorBase)
, mTag(&aTag)
, mParent(&aParent)
, mOffsetInParent(aOffsetInParent)
, mRefNode(aChildAtOffset)
, mPointToInsert(aPointToInsert)
{
}
@ -53,9 +50,8 @@ CreateElementTransaction::~CreateElementTransaction()
NS_IMPL_CYCLE_COLLECTION_INHERITED(CreateElementTransaction,
EditTransactionBase,
mEditorBase,
mParent,
mNewNode,
mRefNode)
mPointToInsert,
mNewNode)
NS_IMPL_ADDREF_INHERITED(CreateElementTransaction, EditTransactionBase)
NS_IMPL_RELEASE_INHERITED(CreateElementTransaction, EditTransactionBase)
@ -66,7 +62,8 @@ NS_INTERFACE_MAP_END_INHERITING(EditTransactionBase)
NS_IMETHODIMP
CreateElementTransaction::DoTransaction()
{
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) || NS_WARN_IF(!mParent)) {
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mTag) ||
NS_WARN_IF(!mPointToInsert.IsSet())) {
return NS_ERROR_NOT_INITIALIZED;
}
@ -77,24 +74,12 @@ CreateElementTransaction::DoTransaction()
mEditorBase->MarkNodeDirty(GetAsDOMNode(mNewNode));
// Insert the new node
ErrorResult rv;
if (mOffsetInParent == -1) {
mParent->AppendChild(*mNewNode, rv);
return rv.StealNSResult();
ErrorResult error;
InsertNewNode(error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
mOffsetInParent = std::min(mOffsetInParent,
static_cast<int32_t>(mParent->GetChildCount()));
if (!mRefNode) {
// Note, it's ok for mRefNode to be null. That means append
mRefNode = mParent->GetChildAt(mOffsetInParent);
}
nsCOMPtr<nsIContent> refNode = mRefNode;
mParent->InsertBefore(*mNewNode, refNode, rv);
NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
// Only set selection to insertion point if editor gives permission
if (!mEditorBase->GetShouldTxnSetSelection()) {
// Do nothing - DOM range gravity will adjust selection
@ -111,41 +96,77 @@ CreateElementTransaction::DoTransaction()
// in this method?
return NS_ERROR_FAILURE;
}
rv = selection->Collapse(afterNewNode);
NS_ASSERTION(!rv.Failed(),
"selection could not be collapsed after insert");
selection->Collapse(afterNewNode, error);
if (error.Failed()) {
NS_WARNING("selection could not be collapsed after insert");
error.SuppressException();
}
return NS_OK;
}
void
CreateElementTransaction::InsertNewNode(ErrorResult& aError)
{
if (mPointToInsert.IsSetAndValid()) {
if (mPointToInsert.IsEndOfContainer()) {
mPointToInsert.Container()->AppendChild(*mNewNode, aError);
NS_WARNING_ASSERTION(!aError.Failed(), "Failed to append the new node");
return;
}
mPointToInsert.Container()->InsertBefore(*mNewNode,
mPointToInsert.GetChildAtOffset(),
aError);
NS_WARNING_ASSERTION(!aError.Failed(), "Failed to insert the new node");
return;
}
if (NS_WARN_IF(mPointToInsert.GetChildAtOffset() &&
mPointToInsert.Container() !=
mPointToInsert.GetChildAtOffset()->GetParentNode())) {
aError.Throw(NS_ERROR_FAILURE);
return;
}
// If mPointToInsert has only offset and it's not valid, we need to treat
// it as pointing end of the container.
mPointToInsert.Container()->AppendChild(*mNewNode, aError);
NS_WARNING_ASSERTION(!aError.Failed(), "Failed to append the new node");
}
NS_IMETHODIMP
CreateElementTransaction::UndoTransaction()
{
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) {
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mPointToInsert.IsSet())) {
return NS_ERROR_NOT_INITIALIZED;
}
ErrorResult rv;
mParent->RemoveChild(*mNewNode, rv);
return rv.StealNSResult();
ErrorResult error;
mPointToInsert.Container()->RemoveChild(*mNewNode, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
NS_IMETHODIMP
CreateElementTransaction::RedoTransaction()
{
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mParent)) {
if (NS_WARN_IF(!mEditorBase) || NS_WARN_IF(!mPointToInsert.IsSet())) {
return NS_ERROR_NOT_INITIALIZED;
}
// First, reset mNewNode so it has no attributes or content
// XXX We never actually did this, we only cleared mNewNode's contents if it
// was a CharacterData node (which it's not, it's an Element)
// XXX Don't we need to set selection like DoTransaction()?
// Now, reinsert mNewNode
ErrorResult rv;
nsCOMPtr<nsIContent> refNode = mRefNode;
mParent->InsertBefore(*mNewNode, refNode, rv);
return rv.StealNSResult();
ErrorResult error;
InsertNewNode(error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
NS_IMETHODIMP

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

@ -6,6 +6,7 @@
#ifndef CreateElementTransaction_h
#define CreateElementTransaction_h
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EditTransactionBase.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
@ -32,17 +33,14 @@ public:
* Initialize the transaction.
* @param aEditorBase The provider of basic editing functionality.
* @param aTag The tag (P, HR, TABLE, etc.) for the new element.
* @param aParent The node into which the new element will be
* inserted.
* @param aOffsetInParent The location in aParent to insert the new element.
* If eAppend, the new element is appended as the last
* child.
* @param aPointToInsert The new node will be inserted before the child at
* aPointToInsert. If this refers end of the container
* or after, the new node will be appended to the
* container.
*/
CreateElementTransaction(EditorBase& aEditorBase,
nsAtom& aTag,
nsINode& aParent,
int32_t aOffsetInParent,
nsIContent* aChildAtOffset);
const EditorRawDOMPoint& aPointToInsert);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CreateElementTransaction,
@ -57,24 +55,22 @@ public:
protected:
virtual ~CreateElementTransaction();
/**
* InsertNewNode() inserts mNewNode before the child node at mPointToInsert.
*/
void InsertNewNode(ErrorResult& aError);
// The document into which the new node will be inserted.
RefPtr<EditorBase> mEditorBase;
// The tag (mapping to object type) for the new element.
RefPtr<nsAtom> mTag;
// The node into which the new node will be inserted.
nsCOMPtr<nsINode> mParent;
// The index in mParent for the new node.
int32_t mOffsetInParent;
// The DOM point we will insert mNewNode.
RangeBoundary mPointToInsert;
// The new node to insert.
nsCOMPtr<dom::Element> mNewNode;
// The node we will insert mNewNode before. We compute this ourselves if it
// is not set by the constructor.
nsCOMPtr<nsIContent> mRefNode;
};
} // namespace mozilla

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

@ -1419,11 +1419,15 @@ EditorBase::SetSpellcheckUserOverride(bool enable)
already_AddRefed<Element>
EditorBase::CreateNode(nsAtom* aTag,
nsINode* aParent,
int32_t aPosition,
nsIContent* aChildAtPosition)
EditorRawDOMPoint& aPointToInsert)
{
MOZ_ASSERT(aTag && aParent);
MOZ_ASSERT(aTag);
MOZ_ASSERT(aPointToInsert.IsSetAndValid());
// XXX We need to offset at new node to mRangeUpdater. Therefore, we need
// to compute the offset now but this is expensive. So, if it's possible,
// we need to redesign mRangeUpdater as avoiding using indices.
int32_t offset = static_cast<int32_t>(aPointToInsert.Offset());
AutoRules beginRulesSniffing(this, EditAction::createNode, nsIEditor::eNext);
@ -1431,28 +1435,31 @@ EditorBase::CreateNode(nsAtom* aTag,
AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
listener->WillCreateNode(nsDependentAtomString(aTag),
GetAsDOMNode(aParent), aPosition);
GetAsDOMNode(aPointToInsert.GetChildAtOffset()));
}
}
nsCOMPtr<Element> ret;
RefPtr<CreateElementTransaction> transaction =
CreateTxnForCreateElement(*aTag, *aParent, aPosition,
aChildAtPosition);
CreateTxnForCreateElement(*aTag, aPointToInsert);
nsresult rv = DoTransaction(transaction);
if (NS_SUCCEEDED(rv)) {
ret = transaction->GetNewNode();
MOZ_ASSERT(ret);
// Now, aPointToInsert may be invalid. I.e., ChildAtOffset() keeps
// referring the next sibling of new node but Offset() refers the
// new node. Let's make refer the new node.
aPointToInsert.Set(ret);
}
mRangeUpdater.SelAdjCreateNode(aParent, aPosition);
mRangeUpdater.SelAdjCreateNode(aPointToInsert.Container(), offset);
{
AutoActionListenerArray listeners(mActionListeners);
for (auto& listener : listeners) {
listener->DidCreateNode(nsDependentAtomString(aTag), GetAsDOMNode(ret),
GetAsDOMNode(aParent), aPosition, rv);
listener->DidCreateNode(nsDependentAtomString(aTag),
GetAsDOMNode(ret), rv);
}
}
@ -4214,16 +4221,29 @@ EditorBase::DeleteSelectionAndCreateElement(nsAtom& aTag)
RefPtr<Selection> selection = GetSelection();
NS_ENSURE_TRUE(selection, nullptr);
nsCOMPtr<nsINode> node = selection->GetAnchorNode();
uint32_t offset = selection->AnchorOffset();
nsIContent* child = selection->GetChildAtAnchorOffset();
nsCOMPtr<Element> newElement = CreateNode(&aTag, node, offset, child);
EditorRawDOMPoint pointToInsert(selection->GetChildAtAnchorOffset());
if (!pointToInsert.IsSet()) {
// Perhaps, the anchor point is in a text node.
pointToInsert.Set(selection->GetAnchorNode(), selection->AnchorOffset());
if (NS_WARN_IF(!pointToInsert.IsSet())) {
return nullptr;
}
}
RefPtr<Element> newElement = CreateNode(&aTag, pointToInsert);
// We want the selection to be just after the new node
rv = selection->Collapse(node, offset + 1);
NS_ENSURE_SUCCESS(rv, nullptr);
DebugOnly<bool> advanced = pointToInsert.AdvanceOffset();
NS_WARNING_ASSERTION(advanced,
"Failed to move offset next to the new element");
ErrorResult error;
selection->Collapse(pointToInsert, error);
if (NS_WARN_IF(error.Failed())) {
// XXX Even if it succeeded to create new element, this returns error
// when Selection.Collapse() fails something. This could occur with
// mutation observer or mutation event listener.
error.SuppressException();
return nullptr;
}
return newElement.forget();
}
@ -4368,13 +4388,10 @@ EditorBase::CreateTxnForRemoveAttribute(Element& aElement,
already_AddRefed<CreateElementTransaction>
EditorBase::CreateTxnForCreateElement(nsAtom& aTag,
nsINode& aParent,
int32_t aPosition,
nsIContent* aChildAtPosition)
const EditorRawDOMPoint& aPointToInsert)
{
RefPtr<CreateElementTransaction> transaction =
new CreateElementTransaction(*this, aTag, aParent, aPosition,
aChildAtPosition);
new CreateElementTransaction(*this, aTag, aPointToInsert);
return transaction.forget();
}

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

@ -394,17 +394,39 @@ protected:
CreateTxnForRemoveAttribute(Element& aElement, nsAtom& aAttribute);
/**
* Create a transaction for creating a new child node of aParent of type aTag.
* Create a transaction for creating a new child node of the container of
* aPointToInsert of type aTag.
*
* @param aTag The element name to create.
* @param aPointToInsert The insertion point of new element. If this refers
* end of the container or after, the transaction
* will append the element to the container.
* Otherwise, will insert the element before the
* child node referred by this.
* @return A CreateElementTransaction which are initialized
* with the arguments.
*/
already_AddRefed<CreateElementTransaction>
CreateTxnForCreateElement(nsAtom& aTag,
nsINode& aParent,
int32_t aPosition,
nsIContent* aChildAtPosition);
const EditorRawDOMPoint& aPointToInsert);
already_AddRefed<Element> CreateNode(nsAtom* aTag, nsINode* aParent,
int32_t aPosition,
nsIContent* aChildAtPosition = nullptr);
/**
* Create an element node whose name is aTag at before aPointToInsert. When
* this succeed to create an element node, this sets aPointToInsert to the
* new element because the relation of child and offset may be broken.
* If the caller needs to collapse the selection to next to the new element
* node, it should call |aPointToInsert.AdvanceOffset()| after calling this.
*
* @param aTag The element name to create.
* @param aPointToInsert The insertion point of new element. If this refers
* end of the container or after, the transaction
* will append the element to the container.
* Otherwise, will insert the element before the
* child node referred by this.
* @return The created new element node.
*/
already_AddRefed<Element> CreateNode(nsAtom* aTag,
EditorRawDOMPoint& aPointToInsert);
/**
* Create a transaction for inserting aNode as a child of aParent.

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

@ -59,15 +59,16 @@ public:
aPointedNode && aPointedNode->IsContent() ?
aPointedNode->GetParentNode() : nullptr,
aPointedNode && aPointedNode->IsContent() ?
GetRef(aPointedNode->AsContent()) : nullptr)
GetRef(aPointedNode->GetParentNode(),
aPointedNode->AsContent()) : nullptr)
{
}
EditorDOMPointBase(nsINode* aConatiner,
EditorDOMPointBase(nsINode* aContainer,
nsIContent* aPointedNode,
int32_t aOffset)
: RangeBoundaryBase<ParentType, RefType>(aConatiner,
GetRef(aPointedNode),
: RangeBoundaryBase<ParentType, RefType>(aContainer,
GetRef(aContainer, aPointedNode),
aOffset)
{
}
@ -84,10 +85,18 @@ public:
}
private:
static nsIContent* GetRef(nsIContent* aPointedNode)
static nsIContent* GetRef(nsINode* aContainerNode, nsIContent* aPointedNode)
{
MOZ_ASSERT(aPointedNode);
return aPointedNode ? aPointedNode->GetPreviousSibling() : nullptr;
// If referring one of a child of the container, the 'ref' should be the
// previous sibling of the referring child.
if (aPointedNode) {
return aPointedNode->GetPreviousSibling();
}
// If referring after the last child, the 'ref' should be the last child.
if (aContainerNode && aContainerNode->IsContainerNode()) {
return aContainerNode->GetLastChild();
}
return nullptr;
}
};

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

@ -3396,23 +3396,29 @@ HTMLEditRules::WillMakeList(Selection* aSelection,
address_of(child));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<Element> theList =
mHTMLEditor->CreateNode(listType, container, offset, child);
EditorRawDOMPoint atListItemToInsertBefore(container, child, offset);
RefPtr<Element> theList =
mHTMLEditor->CreateNode(listType, atListItemToInsertBefore);
NS_ENSURE_STATE(theList);
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<Element> theListItem =
mHTMLEditor->CreateNode(itemType, theList, 0, theList->GetFirstChild());
EditorRawDOMPoint atFirstListItemToInsertBefore(theList, 0);
RefPtr<Element> theListItem =
mHTMLEditor->CreateNode(itemType, atFirstListItemToInsertBefore);
NS_ENSURE_STATE(theListItem);
// remember our new block for postprocessing
mNewBlock = theListItem;
// put selection in new list item
*aHandled = true;
rv = aSelection->Collapse(theListItem, 0);
ErrorResult error;
aSelection->Collapse(EditorRawDOMPoint(theListItem, 0), error);
// Don't restore the selection
selectionRestorer.Abort();
return rv;
if (NS_WARN_IF(!error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
// if there is only one node in the array, and it is a list, div, or
@ -3498,13 +3504,9 @@ HTMLEditRules::WillMakeList(Selection* aSelection,
mHTMLEditor->SplitNode(*curParent->AsContent(), offset, rv);
NS_ENSURE_TRUE(!rv.Failed(), rv.StealNSResult());
newBlock = splitNode ? splitNode->AsElement() : nullptr;
int32_t offset;
nsCOMPtr<nsINode> parent = EditorBase::GetNodeLocation(curParent,
&offset);
NS_ENSURE_STATE(mHTMLEditor);
curList = mHTMLEditor->CreateNode(listType, parent, offset,
curParent ? curParent->AsContent()
: nullptr);
EditorRawDOMPoint atCurParent(curParent);
curList = mHTMLEditor->CreateNode(listType, atCurParent);
NS_ENSURE_STATE(curList);
}
// move list item to new list
@ -3575,8 +3577,8 @@ HTMLEditRules::WillMakeList(Selection* aSelection,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
curList = mHTMLEditor->CreateNode(listType, curParent, offset,
curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curList = mHTMLEditor->CreateNode(listType, atCurChild);
// remember our new block for postprocessing
mNewBlock = curList;
// curList is now the correct thing to put curNode in
@ -3781,13 +3783,15 @@ HTMLEditRules::MakeBasicBlock(Selection& aSelection, nsAtom& blockType)
NS_ENSURE_SUCCESS(rv, rv);
// We don't need to act on this node any more
arrayOfNodes.RemoveElement(brNode);
child = nullptr;
// XXX We need to recompute child here because SplitAsNeeded() and
// EditorBase::SplitNodeDeep() don't compute child in some cases.
child = container->GetChildAt(offset);
}
// Make sure we can put a block here
rv = SplitAsNeeded(blockType, container, offset, address_of(child));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<Element> block =
htmlEditor->CreateNode(&blockType, container, offset, child);
EditorRawDOMPoint atChild(container, child, offset);
RefPtr<Element> block = htmlEditor->CreateNode(&blockType, atChild);
NS_ENSURE_STATE(block);
// Remember our new block for postprocessing
mNewBlock = block;
@ -3928,9 +3932,9 @@ HTMLEditRules::WillCSSIndent(Selection* aSelection,
rv = SplitAsNeeded(*nsGkAtoms::div, container, offset, address_of(child));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<Element> theBlock = mHTMLEditor->CreateNode(nsGkAtoms::div,
container, offset,
child);
EditorRawDOMPoint atChild(container, child, offset);
RefPtr<Element> theBlock =
mHTMLEditor->CreateNode(nsGkAtoms::div, atChild);
NS_ENSURE_STATE(theBlock);
// remember our new block for postprocessing
mNewBlock = theBlock;
@ -3945,10 +3949,15 @@ HTMLEditRules::WillCSSIndent(Selection* aSelection,
}
// put selection in new block
*aHandled = true;
rv = aSelection->Collapse(theBlock, 0);
EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
ErrorResult error;
aSelection->Collapse(atStartOfTheBlock, error);
// Don't restore the selection
selectionRestorer.Abort();
return rv;
if (NS_WARN_IF(!error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
// Ok, now go through all the nodes and put them in a blockquote,
@ -4025,8 +4034,9 @@ HTMLEditRules::WillCSSIndent(Selection* aSelection,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
curParent, offset, curChild);
atCurChild);
NS_ENSURE_STATE(curList);
// curList is now the correct thing to put curNode in
// remember our new block for postprocessing
@ -4058,8 +4068,8 @@ HTMLEditRules::WillCSSIndent(Selection* aSelection,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
curQuote = mHTMLEditor->CreateNode(nsGkAtoms::div, curParent,
offset, curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curQuote = mHTMLEditor->CreateNode(nsGkAtoms::div, atCurChild);
NS_ENSURE_STATE(curQuote);
ChangeIndentation(*curQuote, Change::plus);
// remember our new block for postprocessing
@ -4127,9 +4137,9 @@ HTMLEditRules::WillHTMLIndent(Selection* aSelection,
address_of(child));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
nsCOMPtr<Element> theBlock = mHTMLEditor->CreateNode(nsGkAtoms::blockquote,
container, offset,
child);
EditorRawDOMPoint atChild(container, child, offset);
RefPtr<Element> theBlock =
mHTMLEditor->CreateNode(nsGkAtoms::blockquote, atChild);
NS_ENSURE_STATE(theBlock);
// remember our new block for postprocessing
mNewBlock = theBlock;
@ -4143,10 +4153,15 @@ HTMLEditRules::WillHTMLIndent(Selection* aSelection,
}
// put selection in new block
*aHandled = true;
rv = aSelection->Collapse(theBlock, 0);
EditorRawDOMPoint atStartOfTheBlock(theBlock, 0);
ErrorResult error;
aSelection->Collapse(atStartOfTheBlock, error);
// Don't restore the selection
selectionRestorer.Abort();
return rv;
if (NS_WARN_IF(!error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
// Ok, now go through all the nodes and put them in a blockquote,
@ -4224,8 +4239,9 @@ HTMLEditRules::WillHTMLIndent(Selection* aSelection,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
curParent, offset, curChild);
atCurChild);
NS_ENSURE_STATE(curList);
// curList is now the correct thing to put curNode in
// remember our new block for postprocessing
@ -4269,8 +4285,9 @@ HTMLEditRules::WillHTMLIndent(Selection* aSelection,
offset, address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curList = mHTMLEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
curParent, offset, curChild);
atCurChild);
NS_ENSURE_STATE(curList);
}
NS_ENSURE_STATE(mHTMLEditor);
@ -4301,8 +4318,8 @@ HTMLEditRules::WillHTMLIndent(Selection* aSelection,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
curQuote = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, curParent,
offset, curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curQuote = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, atCurChild);
NS_ENSURE_STATE(curQuote);
// remember our new block for postprocessing
mNewBlock = curQuote;
@ -4916,8 +4933,8 @@ HTMLEditRules::WillAlign(Selection& aSelection,
NS_ENSURE_SUCCESS(rv, rv);
}
}
nsCOMPtr<Element> div = htmlEditor->CreateNode(nsGkAtoms::div, parent,
offset, child);
EditorRawDOMPoint atChild(parent, child, offset);
RefPtr<Element> div = htmlEditor->CreateNode(nsGkAtoms::div, atChild);
NS_ENSURE_STATE(div);
// Remember our new block for postprocessing
mNewBlock = div;
@ -4928,10 +4945,14 @@ HTMLEditRules::WillAlign(Selection& aSelection,
// Put in a moz-br so that it won't get deleted
rv = CreateMozBR(div->AsDOMNode(), 0);
NS_ENSURE_SUCCESS(rv, rv);
rv = aSelection.Collapse(div, 0);
EditorRawDOMPoint atStartOfDiv(div, 0);
ErrorResult error;
aSelection.Collapse(atStartOfDiv, error);
// Don't restore the selection
selectionRestorer.Abort();
NS_ENSURE_SUCCESS(rv, rv);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
return NS_OK;
}
@ -5022,8 +5043,8 @@ HTMLEditRules::WillAlign(Selection& aSelection,
rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
curDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParent, offset,
curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curDiv = htmlEditor->CreateNode(nsGkAtoms::div, atCurChild);
NS_ENSURE_STATE(curDiv);
// Remember our new block for postprocessing
mNewBlock = curDiv;
@ -5099,8 +5120,9 @@ HTMLEditRules::AlignBlockContents(nsIDOMNode* aNode,
} else {
// else we need to put in a div, set the alignment, and toss in all the children
NS_ENSURE_STATE(mHTMLEditor);
RefPtr<Element> divElem = mHTMLEditor->CreateNode(nsGkAtoms::div, node, 0,
node->GetFirstChild());
EditorRawDOMPoint atStartOfNode(node, 0);
RefPtr<Element> divElem =
mHTMLEditor->CreateNode(nsGkAtoms::div, atStartOfNode);
NS_ENSURE_STATE(divElem);
// set up the alignment on the div
NS_ENSURE_STATE(mHTMLEditor);
@ -6618,10 +6640,11 @@ HTMLEditRules::ReturnInHeader(Selection& aSelection,
// Create a paragraph
nsAtom& paraAtom = DefaultParagraphSeparator();
// We want a wrapper element even if we separate with <br>
nsCOMPtr<Element> pNode =
htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ? nsGkAtoms::p
: &paraAtom,
headerParent, offset + 1);
EditorRawDOMPoint nextToHeader(headerParent, offset + 1);
RefPtr<Element> pNode =
htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ?
nsGkAtoms::p : &paraAtom,
nextToHeader);
NS_ENSURE_STATE(pNode);
// Append a <br> to it
@ -6899,10 +6922,11 @@ HTMLEditRules::ReturnInListItem(Selection& aSelection,
// Time to insert a paragraph
nsAtom& paraAtom = DefaultParagraphSeparator();
// We want a wrapper even if we separate with <br>
nsCOMPtr<Element> pNode =
htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ? nsGkAtoms::p
: &paraAtom,
listParent, offset + 1);
EditorRawDOMPoint atNextListItem(listParent, offset + 1);
RefPtr<Element> pNode =
htmlEditor->CreateNode(&paraAtom == nsGkAtoms::br ?
nsGkAtoms::p : &paraAtom,
atNextListItem);
NS_ENSURE_STATE(pNode);
// Append a <br> to it
@ -6948,9 +6972,11 @@ HTMLEditRules::ReturnInListItem(Selection& aSelection,
nsAtom* listAtom = nodeAtom == nsGkAtoms::dt ? nsGkAtoms::dd
: nsGkAtoms::dt;
nsCOMPtr<Element> newListItem =
htmlEditor->CreateNode(listAtom, list, itemOffset + 1,
aListItem.GetNextSibling());
MOZ_DIAGNOSTIC_ASSERT(itemOffset != -1);
EditorRawDOMPoint atNextListItem(list, aListItem.GetNextSibling(),
itemOffset + 1);
RefPtr<Element> newListItem =
htmlEditor->CreateNode(listAtom, atNextListItem);
NS_ENSURE_STATE(newListItem);
rv = htmlEditor->DeleteNode(&aListItem);
NS_ENSURE_SUCCESS(rv, rv);
@ -7053,8 +7079,8 @@ HTMLEditRules::MakeBlockquote(nsTArray<OwningNonNull<nsINode>>& aNodeArray)
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_STATE(mHTMLEditor);
curBlock = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, curParent,
offset, curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curBlock = mHTMLEditor->CreateNode(nsGkAtoms::blockquote, atCurChild);
NS_ENSURE_STATE(curBlock);
// remember our new block for postprocessing
mNewBlock = curBlock;
@ -7223,9 +7249,9 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<Element> theBlock =
htmlEditor->CreateNode(&aBlockTag, curParent, offset,
curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
RefPtr<Element> theBlock =
htmlEditor->CreateNode(&aBlockTag, atCurChild);
NS_ENSURE_STATE(theBlock);
// Remember our new block for postprocessing
mNewBlock = theBlock;
@ -7245,8 +7271,8 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
curBlock = htmlEditor->CreateNode(&aBlockTag, curParent, offset,
curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curBlock = htmlEditor->CreateNode(&aBlockTag, atCurChild);
NS_ENSURE_STATE(curBlock);
// Remember our new block for postprocessing
mNewBlock = curBlock;
@ -7273,8 +7299,8 @@ HTMLEditRules::ApplyBlockStyle(nsTArray<OwningNonNull<nsINode>>& aNodeArray,
nsresult rv = SplitAsNeeded(aBlockTag, curParent, offset,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
curBlock = htmlEditor->CreateNode(&aBlockTag, curParent, offset,
curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curBlock = htmlEditor->CreateNode(&aBlockTag, atCurChild);
NS_ENSURE_STATE(curBlock);
// Remember our new block for postprocessing
mNewBlock = curBlock;
@ -8518,24 +8544,21 @@ HTMLEditRules::InsertBRIfNeededInternal(nsINode& aNode,
NS_IMETHODIMP
HTMLEditRules::WillCreateNode(const nsAString& aTag,
nsIDOMNode* aParent,
int32_t aPosition)
nsIDOMNode* aNextSiblingOfNewNode)
{
return NS_OK;
}
NS_IMETHODIMP
HTMLEditRules::DidCreateNode(const nsAString& aTag,
nsIDOMNode* aNode,
nsIDOMNode* aParent,
int32_t aPosition,
nsIDOMNode* aNewNode,
nsresult aResult)
{
if (!mListenerEnabled) {
return NS_OK;
}
// assumption that Join keeps the righthand node
nsresult rv = mUtilRange->SelectNode(aNode);
nsresult rv = mUtilRange->SelectNode(aNewNode);
NS_ENSURE_SUCCESS(rv, rv);
return UpdateDocChangeRange(mUtilRange);
}
@ -9028,8 +9051,9 @@ HTMLEditRules::WillAbsolutePosition(Selection& aSelection,
rv = SplitAsNeeded(*nsGkAtoms::div, parent, offset,
address_of(child));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<Element> positionedDiv =
htmlEditor->CreateNode(nsGkAtoms::div, parent, offset, child);
EditorRawDOMPoint atChild(parent, child, offset);
RefPtr<Element> positionedDiv =
htmlEditor->CreateNode(nsGkAtoms::div, atChild);
NS_ENSURE_STATE(positionedDiv);
// Remember our new block for postprocessing
mNewBlock = positionedDiv;
@ -9084,16 +9108,15 @@ HTMLEditRules::WillAbsolutePosition(Selection& aSelection,
SplitAsNeeded(*curParent->NodeInfo()->NameAtom(), curParent, offset);
NS_ENSURE_SUCCESS(rv, rv);
if (!curPositionedDiv) {
nsCOMPtr<nsINode> curParentParent = curParent->GetParentNode();
int32_t parentOffset = curParentParent
? curParentParent->IndexOf(curParent) : -1;
curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParentParent,
parentOffset);
EditorRawDOMPoint atCurParent(curParent);
curPositionedDiv =
htmlEditor->CreateNode(nsGkAtoms::div, atCurParent);
mNewBlock = curPositionedDiv;
}
EditorRawDOMPoint atEndOfCurPositionedDiv(curPositionedDiv,
curPositionedDiv->Length());
curList = htmlEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
curPositionedDiv, -1,
curPositionedDiv->GetLastChild());
atEndOfCurPositionedDiv);
NS_ENSURE_STATE(curList);
// curList is now the correct thing to put curNode in. Remember our
// new block for postprocessing.
@ -9128,18 +9151,15 @@ HTMLEditRules::WillAbsolutePosition(Selection& aSelection,
offset);
NS_ENSURE_SUCCESS(rv, rv);
if (!curPositionedDiv) {
nsCOMPtr<nsINode> curParentParent = curParent->GetParentNode();
int32_t parentOffset = curParentParent ?
curParentParent->IndexOf(curParent) : -1;
curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div,
curParentParent,
parentOffset,
curParent->AsContent());
EditorRawDOMPoint atCurParent(curParent);
curPositionedDiv =
htmlEditor->CreateNode(nsGkAtoms::div, atCurParent);
mNewBlock = curPositionedDiv;
}
EditorRawDOMPoint atEndOfCurPositionedDiv(curPositionedDiv,
curPositionedDiv->Length());
curList = htmlEditor->CreateNode(curParent->NodeInfo()->NameAtom(),
curPositionedDiv, -1,
curPositionedDiv->GetLastChild());
atEndOfCurPositionedDiv);
NS_ENSURE_STATE(curList);
}
rv = htmlEditor->MoveNode(listItem, curList, -1);
@ -9160,8 +9180,8 @@ HTMLEditRules::WillAbsolutePosition(Selection& aSelection,
rv = SplitAsNeeded(*nsGkAtoms::div, curParent, offset,
address_of(curChild));
NS_ENSURE_SUCCESS(rv, rv);
curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div, curParent,
offset, curChild);
EditorRawDOMPoint atCurChild(curParent, curChild, offset);
curPositionedDiv = htmlEditor->CreateNode(nsGkAtoms::div, atCurChild);
NS_ENSURE_STATE(curPositionedDiv);
// Remember our new block for postprocessing
mNewBlock = curPositionedDiv;

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

@ -106,10 +106,9 @@ public:
// nsIEditActionListener methods
NS_IMETHOD WillCreateNode(const nsAString& aTag, nsIDOMNode* aParent,
int32_t aPosition) override;
NS_IMETHOD DidCreateNode(const nsAString& aTag, nsIDOMNode* aNode,
nsIDOMNode* aParent, int32_t aPosition,
NS_IMETHOD WillCreateNode(const nsAString& aTag,
nsIDOMNode* aNextSiblingOfNewNode) override;
NS_IMETHOD DidCreateNode(const nsAString& aTag, nsIDOMNode* aNewNode,
nsresult aResult) override;
NS_IMETHOD WillInsertNode(nsIDOMNode* aNode, nsIDOMNode* aParent,
int32_t aPosition) override;

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

@ -6,6 +6,7 @@
#include "mozilla/HTMLEditor.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/EditorDOMPoint.h"
#include "mozilla/EventStates.h"
#include "mozilla/TextEvents.h"
@ -2021,11 +2022,13 @@ HTMLEditor::MakeOrChangeList(const nsAString& aListType,
}
// make a list
nsCOMPtr<Element> newList = CreateNode(listAtom, parent, offset, child);
MOZ_DIAGNOSTIC_ASSERT(child);
EditorRawDOMPoint atChild(parent, child, offset);
RefPtr<Element> newList = CreateNode(listAtom, atChild);
NS_ENSURE_STATE(newList);
// make a list item
nsCOMPtr<Element> newItem = CreateNode(nsGkAtoms::li, newList, 0,
newList->GetFirstChild());
EditorRawDOMPoint atStartOfNewList(newList, 0);
RefPtr<Element> newItem = CreateNode(nsGkAtoms::li, atStartOfNewList);
NS_ENSURE_STATE(newItem);
rv = selection->Collapse(newItem, 0);
NS_ENSURE_SUCCESS(rv, rv);
@ -2164,7 +2167,9 @@ HTMLEditor::InsertBasicBlock(const nsAString& aBlockType)
}
// make a block
nsCOMPtr<Element> newBlock = CreateNode(blockAtom, parent, offset, child);
MOZ_DIAGNOSTIC_ASSERT(child);
EditorRawDOMPoint atChild(parent, child, offset);
RefPtr<Element> newBlock = CreateNode(blockAtom, atChild);
NS_ENSURE_STATE(newBlock);
// reposition selection to inside the block
@ -2238,8 +2243,9 @@ HTMLEditor::Indent(const nsAString& aIndent)
}
// make a blockquote
nsCOMPtr<Element> newBQ =
CreateNode(nsGkAtoms::blockquote, parent, offset, child);
MOZ_DIAGNOSTIC_ASSERT(child);
EditorRawDOMPoint atChild(parent, child, offset);
RefPtr<Element> newBQ = CreateNode(nsGkAtoms::blockquote, atChild);
NS_ENSURE_STATE(newBQ);
// put a space in it so layout will draw the list item
rv = selection->Collapse(newBQ, 0);
@ -4532,9 +4538,9 @@ HTMLEditor::CopyLastEditableChildStyles(nsINode* aPreviousBlock,
childElement->NodeInfo()->NameAtom());
NS_ENSURE_STATE(newStyles);
} else {
EditorRawDOMPoint atStartOfNewBlock(newBlock, 0);
deepestStyle = newStyles =
CreateNode(childElement->NodeInfo()->NameAtom(), newBlock, 0,
newBlock->GetFirstChild());
CreateNode(childElement->NodeInfo()->NameAtom(), atStartOfNewBlock);
NS_ENSURE_STATE(newStyles);
}
CloneAttributes(newStyles, childElement);

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

@ -242,7 +242,8 @@ TextEditor::SetDocumentCharacterSet(const nsACString& characterSet)
}
// Create a new meta charset tag
RefPtr<Element> metaElement = CreateNode(nsGkAtoms::meta, headNode, 0);
EditorRawDOMPoint atStartOfHeadNode(headNode, 0);
RefPtr<Element> metaElement = CreateNode(nsGkAtoms::meta, atStartOfHeadNode);
if (NS_WARN_IF(!metaElement)) {
return NS_OK;
}
@ -443,14 +444,15 @@ TextEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
int32_t theOffset = *aInOutOffset;
RefPtr<Element> brNode;
if (IsTextNode(node)) {
int32_t offset;
nsCOMPtr<nsINode> tmp = GetNodeLocation(node, &offset);
NS_ENSURE_TRUE(tmp, NS_ERROR_FAILURE);
EditorRawDOMPoint atNode(node);
if (NS_WARN_IF(!atNode.IsSetAndValid())) {
return NS_ERROR_FAILURE;
}
if (!theOffset) {
// we are already set to go
} else if (theOffset == static_cast<int32_t>(node->Length())) {
// update offset to point AFTER the text node
offset++;
atNode.AdvanceOffset();
} else {
// split the text node
ErrorResult rv;
@ -458,17 +460,19 @@ TextEditor::CreateBRImpl(nsCOMPtr<nsIDOMNode>* aInOutParent,
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
tmp = GetNodeLocation(node, &offset);
atNode.Clear();
atNode.Set(node);
}
// create br
brNode = CreateNode(nsGkAtoms::br, tmp, offset);
brNode = CreateNode(nsGkAtoms::br, atNode);
if (NS_WARN_IF(!brNode)) {
return NS_ERROR_FAILURE;
}
*aInOutParent = GetAsDOMNode(tmp);
*aInOutOffset = offset+1;
*aInOutParent = GetAsDOMNode(atNode.Container());
*aInOutOffset = atNode.Offset() + 1;
} else {
brNode = CreateNode(nsGkAtoms::br, node, theOffset);
EditorRawDOMPoint atTheOffset(node, theOffset);
brNode = CreateNode(nsGkAtoms::br, atTheOffset);
if (NS_WARN_IF(!brNode)) {
return NS_ERROR_FAILURE;
}

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

@ -30,31 +30,23 @@ interface nsIEditActionListener : nsISupports{
/**
* Called before the editor creates a node.
* @param aTag The tag name of the DOM Node to create.
* @param aParent The node to insert the new object into
* @param aPosition The place in aParent to insert the new node
* 0=first child, 1=second child, etc.
* any number > number of current children = last child
* @param aTag The tag name of the DOM Node to create.
* @param aNextSiblingOfNewNode The node which will be next sibling of
* new node. If the new node will be appended,
* this is null.
*/
void WillCreateNode(in DOMString aTag,
in nsIDOMNode aParent,
in long aPosition);
in nsIDOMNode aNextSiblingOfNewNode);
/**
* Called after the editor creates a node.
* @param aTag The tag name of the DOM Node to create.
* @param aNode The DOM Node that was created.
* @param aParent The node to insert the new object into
* @param aPosition The place in aParent to insert the new node
* 0=first child, 1=second child, etc.
* any number > number of current children = last child
* @param aNewNode The DOM Node that was created.
* @param aResult The result of the create node operation.
*/
void DidCreateNode(in DOMString aTag,
in nsIDOMNode aNode,
in nsIDOMNode aParent,
in long aPosition,
in nsresult aResult);
in nsIDOMNode aNewNode,
in nsresult aResult);
/**
* Called before the editor inserts a node.

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

@ -3373,13 +3373,16 @@ nsTextServicesDocument::WillJoinNodes(nsIDOMNode *aLeftNode,
// -------------------------------
NS_IMETHODIMP
nsTextServicesDocument::WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition)
nsTextServicesDocument::WillCreateNode(const nsAString& aTag,
nsIDOMNode* aNextSiblingOfNewNode)
{
return NS_OK;
}
NS_IMETHODIMP
nsTextServicesDocument::DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition, nsresult aResult)
nsTextServicesDocument::DidCreateNode(const nsAString& aTag,
nsIDOMNode* aNewNode,
nsresult aResult)
{
return NS_OK;
}

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

@ -118,8 +118,11 @@ public:
nsIDOMNode *aParent,
nsresult aResult) override;
// these listen methods are unused:
NS_IMETHOD WillCreateNode(const nsAString& aTag, nsIDOMNode *aParent, int32_t aPosition) override;
NS_IMETHOD DidCreateNode(const nsAString& aTag, nsIDOMNode *aNode, nsIDOMNode *aParent, int32_t aPosition, nsresult aResult) override;
NS_IMETHOD WillCreateNode(const nsAString& aTag,
nsIDOMNode* aNextSiblingOfNewNode) override;
NS_IMETHOD DidCreateNode(const nsAString& aTag,
nsIDOMNode* aNewNode,
nsresult aResult) override;
NS_IMETHOD WillInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString) override;
NS_IMETHOD DidInsertText(nsIDOMCharacterData *aTextNode, int32_t aOffset, const nsAString &aString, nsresult aResult) override;
NS_IMETHOD WillDeleteText(nsIDOMCharacterData *aTextNode, int32_t aOffset, int32_t aLength) override;

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

@ -1054,13 +1054,17 @@ mozInlineSpellChecker::IgnoreWords(const char16_t **aWordsToIgnore,
return ScheduleSpellCheck(Move(status));
}
NS_IMETHODIMP mozInlineSpellChecker::WillCreateNode(const nsAString & aTag, nsIDOMNode *aParent, int32_t aPosition)
NS_IMETHODIMP
mozInlineSpellChecker::WillCreateNode(const nsAString& aTag,
nsIDOMNode* aNextSiblingOfNewNode)
{
return NS_OK;
}
NS_IMETHODIMP mozInlineSpellChecker::DidCreateNode(const nsAString & aTag, nsIDOMNode *aNode, nsIDOMNode *aParent,
int32_t aPosition, nsresult aResult)
NS_IMETHODIMP
mozInlineSpellChecker::DidCreateNode(const nsAString& aTag,
nsIDOMNode* aNewNode,
nsresult aResult)
{
return NS_OK;
}

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

@ -1,128 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* 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/. */
#ifndef jsfixedsizehash_h_
#define jsfixedsizehash_h_
#include "ds/LifoAlloc.h"
namespace js {
/*
* Class representing a hash set with fixed capacity, with newer entries
* evicting older entries. Each entry has several hashes and can be stored in
* different buckets, with the choice of which to evict on insertion being
* managed via LRU. For tables with a relatively small size, using different
* hashes increases utilization and makes it less likely that entries will keep
* evicting each other due to wanting to use the same bucket.
*
* T indicates the type of hash elements, HashPolicy must have the following
* contents:
*
* Lookup - As for HashMap / HashSet.
*
* bool match(T, Lookup) - As for HashMap / HashSet.
*
* NumHashes - Number of different hashes generated for each entry.
*
* void hash(Lookup, HashNumber[NumHashes]) - Compute all hashes for an entry.
*
* void clear(T*) - Clear an entry, such that isCleared() holds afterwards.
*
* bool isCleared(T) - Test whether an entry has been cleared.
*/
template <class T, class HashPolicy, size_t Capacity>
class FixedSizeHashSet
{
T entries[Capacity];
uint32_t lastOperations[Capacity];
uint32_t numOperations;
static const size_t NumHashes = HashPolicy::NumHashes;
static_assert(Capacity > 0, "an empty fixed-size hash set is meaningless");
public:
typedef typename HashPolicy::Lookup Lookup;
FixedSizeHashSet()
: entries(), lastOperations(), numOperations(0)
{
MOZ_ASSERT(HashPolicy::isCleared(entries[0]));
}
bool lookup(const Lookup& lookup, T* pentry)
{
size_t bucket;
if (lookupReference(lookup, &bucket)) {
*pentry = entries[bucket];
lastOperations[bucket] = numOperations++;
return true;
}
return false;
}
void insert(const Lookup& lookup, const T& entry)
{
size_t buckets[NumHashes];
getBuckets(lookup, buckets);
size_t min = buckets[0];
for (size_t i = 0; i < NumHashes; i++) {
const T& entry = entries[buckets[i]];
if (HashPolicy::isCleared(entry)) {
entries[buckets[i]] = entry;
lastOperations[buckets[i]] = numOperations++;
return;
}
if (i && lastOperations[min] > lastOperations[buckets[i]])
min = buckets[i];
}
entries[min] = entry;
lastOperations[min] = numOperations++;
}
template <typename S>
void remove(const S& s)
{
size_t bucket;
if (lookupReference(s, &bucket))
HashPolicy::clear(&entries[bucket]);
}
private:
template <typename S>
bool lookupReference(const S& s, size_t* pbucket)
{
size_t buckets[NumHashes];
getBuckets(s, buckets);
for (size_t i = 0; i < NumHashes; i++) {
const T& entry = entries[buckets[i]];
if (!HashPolicy::isCleared(entry) && HashPolicy::match(entry, s)) {
*pbucket = buckets[i];
return true;
}
}
return false;
}
template <typename S>
void getBuckets(const S& s, size_t buckets[NumHashes])
{
HashNumber hashes[NumHashes];
HashPolicy::hash(s, hashes);
for (size_t i = 0; i < NumHashes; i++)
buckets[i] = hashes[i] % Capacity;
}
};
} /* namespace js */
#endif /* jsfixedsizehash_h_ */

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

@ -0,0 +1,28 @@
// |jit-test| --arm-asm-nop-fill=1
//
try {
enableSingleStepProfiling();
disableSingleStepProfiling();
} catch (e) {
// Early quit on plateforms not supporting single step profiling.
quit();
}
load(libdir + "asm.js");
var ffi = function(enable) {
enableGeckoProfiling();
enableSingleStepProfiling();
}
var f = asmLink(asmCompile('global', 'ffis',
USE_ASM + `
var ffi=ffis.ffi;
function f(i) {
i=i|0;
ffi(i|0);
} return f
`), null, {
ffi
});
f(0);
f(+1);

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

@ -1592,34 +1592,6 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
return true;
}
// Lazy script caching is only supported for leaf functions. If a
// script with inner functions was returned by the cache, those inner
// functions would be delazified when deep cloning the script, even if
// they have never executed.
//
// Additionally, the lazy script cache is not used during incremental
// GCs, to avoid resurrecting dead scripts after incremental sweeping
// has started.
if (canRelazify && !JS::IsIncrementalGCInProgress(cx)) {
LazyScriptCache::Lookup lookup(cx, lazy);
cx->caches().lazyScriptCache.lookup(lookup, script.address());
}
if (script) {
RootedScope enclosingScope(cx, lazy->enclosingScope());
RootedScript clonedScript(cx, CloneScriptIntoFunction(cx, enclosingScope, fun, script));
if (!clonedScript)
return false;
clonedScript->setSourceObject(lazy->sourceObject());
fun->initAtom(script->functionNonDelazifying()->displayAtom());
if (!lazy->maybeScript())
lazy->initScript(clonedScript);
return true;
}
MOZ_ASSERT(lazy->scriptSource()->hasSourceData());
// Parse and compile the script from source.
@ -1654,9 +1626,6 @@ JSFunction::createScriptForLazilyInterpretedFunction(JSContext* cx, HandleFuncti
// script is encountered later a match can be determined.
script->setColumn(lazy->column());
LazyScriptCache::Lookup lookup(cx, lazy);
cx->caches().lazyScriptCache.insert(lookup, script);
// Remember the lazy script on the compiled script, so it can be
// stored on the function again in case of re-lazification.
// Only functions without inner functions are re-lazified.

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

@ -3205,8 +3205,6 @@ JSScript::finalize(FreeOp* fop)
if (scriptData_)
scriptData_->decRefCount();
fop->runtime()->caches().lazyScriptCache.remove(this);
// In most cases, our LazyScript's script pointer will reference this
// script, and thus be nulled out by normal weakref processing. However, if
// we unlazified the LazyScript during incremental sweeping, it will have a
@ -4529,76 +4527,6 @@ JSScript::mayReadFrameArgsDirectly()
return argumentsHasVarBinding() || hasRest();
}
static inline void
LazyScriptHash(uint32_t lineno, uint32_t column, uint32_t begin, uint32_t end,
HashNumber hashes[3])
{
HashNumber hash = lineno;
hash = RotateLeft(hash, 4) ^ column;
hash = RotateLeft(hash, 4) ^ begin;
hash = RotateLeft(hash, 4) ^ end;
hashes[0] = hash;
hashes[1] = RotateLeft(hashes[0], 4) ^ begin;
hashes[2] = RotateLeft(hashes[1], 4) ^ end;
}
void
LazyScriptHashPolicy::hash(const Lookup& lookup, HashNumber hashes[3])
{
LazyScript* lazy = lookup.lazy;
LazyScriptHash(lazy->lineno(), lazy->column(), lazy->begin(), lazy->end(), hashes);
}
void
LazyScriptHashPolicy::hash(JSScript* script, HashNumber hashes[3])
{
LazyScriptHash(script->lineno(), script->column(), script->sourceStart(), script->sourceEnd(), hashes);
}
bool
LazyScriptHashPolicy::match(JSScript* script, const Lookup& lookup)
{
JSContext* cx = lookup.cx;
LazyScript* lazy = lookup.lazy;
// To be a match, the script and lazy script need to have the same line
// and column and to be at the same position within their respective
// source blobs, and to have the same source contents and version.
//
// While the surrounding code in the source may differ, this is
// sufficient to ensure that compiling the lazy script will yield an
// identical result to compiling the original script.
//
// Note that the filenames and origin principals of the lazy script and
// original script can differ. If there is a match, these will be fixed
// up in the resulting clone by the caller.
if (script->lineno() != lazy->lineno() ||
script->column() != lazy->column() ||
script->getVersion() != lazy->version() ||
script->sourceStart() != lazy->begin() ||
script->sourceEnd() != lazy->end())
{
return false;
}
UncompressedSourceCache::AutoHoldEntry holder;
size_t scriptBegin = script->sourceStart();
size_t length = script->sourceEnd() - scriptBegin;
ScriptSource::PinnedChars scriptChars(cx, script->scriptSource(), holder, scriptBegin, length);
if (!scriptChars.get())
return false;
MOZ_ASSERT(scriptBegin == lazy->begin());
ScriptSource::PinnedChars lazyChars(cx, lazy->scriptSource(), holder, scriptBegin, length);
if (!lazyChars.get())
return false;
return !memcmp(scriptChars.get(), lazyChars.get(), length);
}
void
JSScript::AutoDelazify::holdScript(JS::HandleFunction fun)
{

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

@ -13,7 +13,6 @@
#include "jsobj.h"
#include "jsscript.h"
#include "ds/FixedSizeHash.h"
#include "frontend/SourceNotes.h"
#include "gc/Tracer.h"
#include "js/RootingAPI.h"
@ -86,33 +85,6 @@ struct EvalCacheHashPolicy
typedef HashSet<EvalCacheEntry, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
struct LazyScriptHashPolicy
{
struct Lookup {
JSContext* cx;
LazyScript* lazy;
Lookup(JSContext* cx, LazyScript* lazy)
: cx(cx), lazy(lazy)
{}
};
static const size_t NumHashes = 3;
static void hash(const Lookup& lookup, HashNumber hashes[NumHashes]);
static bool match(JSScript* script, const Lookup& lookup);
// Alternate methods for use when removing scripts from the hash without an
// explicit LazyScript lookup.
static void hash(JSScript* script, HashNumber hashes[NumHashes]);
static bool match(JSScript* script, JSScript* lookup) { return script == lookup; }
static void clear(JSScript** pscript) { *pscript = nullptr; }
static bool isCleared(JSScript* script) { return !script; }
};
typedef FixedSizeHashSet<JSScript*, LazyScriptHashPolicy, 769> LazyScriptCache;
/*
* Cache for speeding up repetitive creation of objects in the VM.
* When an object is created which matches the criteria in the 'key' section
@ -256,7 +228,6 @@ class RuntimeCaches
js::NewObjectCache newObjectCache;
js::UncompressedSourceCache uncompressedSourceCache;
js::EvalCache evalCache;
LazyScriptCache lazyScriptCache;
bool init();

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

@ -28,7 +28,6 @@
#include "builtin/AtomicsObject.h"
#include "builtin/Intl.h"
#include "builtin/Promise.h"
#include "ds/FixedSizeHash.h"
#include "frontend/NameCollections.h"
#include "gc/GCRuntime.h"
#include "gc/Tracer.h"

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

@ -759,6 +759,8 @@ CodeRange::CodeRange(uint32_t funcIndex, JitExitOffsets offsets)
u.funcIndex_ = funcIndex;
u.jitExit.beginToUntrustedFPStart_ = offsets.untrustedFPStart - begin_;
u.jitExit.beginToUntrustedFPEnd_ = offsets.untrustedFPEnd - begin_;
MOZ_ASSERT(jitExitUntrustedFPStart() == offsets.untrustedFPStart);
MOZ_ASSERT(jitExitUntrustedFPEnd() == offsets.untrustedFPEnd);
}
CodeRange::CodeRange(Trap trap, CallableOffsets offsets)

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

@ -1021,8 +1021,8 @@ class CodeRange
uint8_t beginToTierEntry_;
} func;
struct {
uint8_t beginToUntrustedFPStart_;
uint8_t beginToUntrustedFPEnd_;
uint16_t beginToUntrustedFPStart_;
uint16_t beginToUntrustedFPEnd_;
} jitExit;
};
};

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

@ -10,7 +10,6 @@
#include "nsListControlFrame.h"
#include "nsCheckboxRadioFrame.h" // for COMPARE macro
#include "nsGkAtoms.h"
#include "nsIDOMHTMLOptionElement.h"
#include "nsComboboxControlFrame.h"
#include "nsIPresShell.h"
#include "nsIDOMMouseEvent.h"
@ -231,12 +230,9 @@ void nsListControlFrame::PaintFocus(DrawTarget* aDrawTarget, nsPoint aPt)
fRect += aPt;
bool lastItemIsSelected = false;
if (focusedContent) {
nsCOMPtr<nsIDOMHTMLOptionElement> domOpt =
do_QueryInterface(focusedContent);
if (domOpt) {
domOpt->GetSelected(&lastItemIsSelected);
}
HTMLOptionElement* domOpt = HTMLOptionElement::FromContentOrNull(focusedContent);
if (domOpt) {
lastItemIsSelected = domOpt->Selected();
}
// set up back stop colors and then ask L&F service for the real colors
@ -1857,7 +1853,8 @@ nsListControlFrame::MouseDown(nsIDOMEvent* aMouseEvent)
if (mComboboxFrame->IsOpenInParentProcess()) {
nsCOMPtr<nsIDOMEventTarget> etarget;
aMouseEvent->GetTarget(getter_AddRefs(etarget));
nsCOMPtr<nsIDOMHTMLOptionElement> option = do_QueryInterface(etarget);
nsCOMPtr<nsIContent> econtent = do_QueryInterface(etarget);
HTMLOptionElement* option = HTMLOptionElement::FromContentOrNull(econtent);
if (option) {
return NS_OK;
}
@ -2453,11 +2450,11 @@ nsListControlFrame::KeyPress(nsIDOMEvent* aKeyEvent)
}
nsAutoString text;
if (NS_FAILED(optionElement->GetText(text)) ||
!StringBeginsWith(
nsContentUtils::TrimWhitespace<
nsContentUtils::IsHTMLWhitespaceOrNBSP>(text, false),
incrementalString, nsCaseInsensitiveStringComparator())) {
optionElement->GetText(text);
if (!StringBeginsWith(
nsContentUtils::TrimWhitespace<
nsContentUtils::IsHTMLWhitespaceOrNBSP>(text, false),
incrementalString, nsCaseInsensitiveStringComparator())) {
continue;
}

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

@ -39,7 +39,7 @@ class PublicSuffixPatterns {
}
} catch (IOException e) {
Log.e("Patterns", "IOException during loading public suffix list");
throw new IllegalStateException("resource publicsuffixlist could not be opened but is bundled with app", e);
} finally {
IOUtils.safeStreamClose(reader);
}

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

@ -161,12 +161,12 @@ class LoggingManager(object):
# complaining about "no handlers could be found for logger XXX."
self.root_logger.addHandler(logging.NullHandler())
self.mach_logger = logging.getLogger('mach')
self.mach_logger.setLevel(logging.DEBUG)
mach_logger = logging.getLogger('mach')
mach_logger.setLevel(logging.DEBUG)
self.structured_filter = ConvertToStructuredFilter()
self.structured_loggers = [self.mach_logger]
self.structured_loggers = [mach_logger]
self._terminal = None
@ -256,10 +256,43 @@ class LoggingManager(object):
self.terminal_handler.removeFilter(self.structured_filter)
self.root_logger.removeHandler(self.terminal_handler)
def register_structured_logger(self, logger):
def register_structured_logger(self, logger, terminal=True, json=True):
"""Register a structured logger.
This needs to be called for all structured loggers that don't chain up
to the mach logger in order for their output to be captured.
"""
self.structured_loggers.append(logger)
if terminal and self.terminal_handler:
logger.addHandler(self.terminal_handler)
if json:
for handler in self.json_handlers:
logger.addHandler(handler)
def enable_all_structured_loggers(self, terminal=True, json=True):
"""Enable logging of all structured messages from all loggers.
``terminal`` and ``json`` determine which log handlers to operate
on. By default, all known handlers are operated on.
"""
# Remove current handlers from all loggers so we don't double
# register handlers.
for logger in self.root_logger.manager.loggerDict.values():
# Some entries might be logging.PlaceHolder.
if not isinstance(logger, logging.Logger):
continue
if terminal:
logger.removeHandler(self.terminal_handler)
if json:
for handler in self.json_handlers:
logger.removeHandler(handler)
# Wipe out existing registered structured loggers since they
# all propagate to root logger.
self.structured_loggers = []
self.register_structured_logger(self.root_logger, terminal=terminal,
json=json)

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

@ -8,36 +8,67 @@ import getpass
import json
import logging
import os
import platform
import subprocess
import sys
import time
import which
from collections import (
Counter,
namedtuple,
OrderedDict,
)
from textwrap import (
TextWrapper,
)
try:
import psutil
except Exception:
psutil = None
from mach.mixin.logging import LoggingMixin
from mozsystemmonitor.resourcemonitor import SystemResourceMonitor
import mozpack.path as mozpath
from ..base import MozbuildObject
from ..testing import install_test_files
from ..base import (
BuildEnvironmentNotFoundException,
MozbuildObject,
)
from ..backend import (
get_backend_class,
)
from ..testing import (
install_test_files,
)
from ..compilation.warnings import (
WarningsCollector,
WarningsDatabase,
)
from ..shellutil import (
quote as shell_quote,
)
from ..util import (
mkdir,
resolve_target_to_make,
)
FINDER_SLOW_MESSAGE = '''
===================
PERFORMANCE WARNING
The OS X Finder application (file indexing used by Spotlight) used a lot of CPU
during the build - an average of %f%% (100%% is 1 core). This made your build
slower.
Consider adding ".noindex" to the end of your object directory name to have
Finder ignore it. Or, add an indexing exclusion through the Spotlight System
Preferences.
===================
'''.strip()
from textwrap import TextWrapper
INSTALL_TESTS_CLOBBER = ''.join([TextWrapper().fill(line) + '\n' for line in
'''
@ -485,6 +516,275 @@ class BuildMonitor(MozbuildObject):
return ccache_stats
class TerminalLoggingHandler(logging.Handler):
"""Custom logging handler that works with terminal window dressing.
This class should probably live elsewhere, like the mach core. Consider
this a proving ground for its usefulness.
"""
def __init__(self):
logging.Handler.__init__(self)
self.fh = sys.stdout
self.footer = None
def flush(self):
self.acquire()
try:
self.fh.flush()
finally:
self.release()
def emit(self, record):
msg = self.format(record)
self.acquire()
try:
if self.footer:
self.footer.clear()
self.fh.write(msg)
self.fh.write('\n')
if self.footer:
self.footer.draw()
# If we don't flush, the footer may not get drawn.
self.fh.flush()
finally:
self.release()
class Footer(object):
"""Handles display of a footer in a terminal.
This class implements the functionality common to all mach commands
that render a footer.
"""
def __init__(self, terminal):
# terminal is a blessings.Terminal.
self._t = terminal
self._fh = sys.stdout
def clear(self):
"""Removes the footer from the current terminal."""
self._fh.write(self._t.move_x(0))
self._fh.write(self._t.clear_eol())
def write(self, parts):
"""Write some output in the footer, accounting for terminal width.
parts is a list of 2-tuples of (encoding_function, input).
None means no encoding."""
# We don't want to write more characters than the current width of the
# terminal otherwise wrapping may result in weird behavior. We can't
# simply truncate the line at terminal width characters because a)
# non-viewable escape characters count towards the limit and b) we
# don't want to truncate in the middle of an escape sequence because
# subsequent output would inherit the escape sequence.
max_width = self._t.width
written = 0
write_pieces = []
for part in parts:
try:
func, part = part
encoded = getattr(self._t, func)(part)
except ValueError:
encoded = part
len_part = len(part)
len_spaces = len(write_pieces)
if written + len_part + len_spaces > max_width:
write_pieces.append(part[0:max_width - written - len_spaces])
written += len_part
break
write_pieces.append(encoded)
written += len_part
with self._t.location():
self._t.move(self._t.height-1,0)
self._fh.write(' '.join(write_pieces))
class BuildProgressFooter(Footer):
"""Handles display of a build progress indicator in a terminal.
When mach builds inside a blessings-supported terminal, it will render
progress information collected from a BuildMonitor. This class converts the
state of BuildMonitor into terminal output.
"""
def __init__(self, terminal, monitor):
Footer.__init__(self, terminal)
self.tiers = monitor.tiers.tier_status.viewitems()
def draw(self):
"""Draws this footer in the terminal."""
if not self.tiers:
return
# The drawn terminal looks something like:
# TIER: static export libs tools
parts = [('bold', 'TIER:')]
append = parts.append
for tier, status in self.tiers:
if status is None:
append(tier)
elif status == 'finished':
append(('green', tier))
else:
append(('underline_yellow', tier))
self.write(parts)
class OutputManager(LoggingMixin):
"""Handles writing job output to a terminal or log."""
def __init__(self, log_manager, footer):
self.populate_logger()
self.footer = None
terminal = log_manager.terminal
# TODO convert terminal footer to config file setting.
if not terminal or os.environ.get('MACH_NO_TERMINAL_FOOTER', None):
return
if os.environ.get('INSIDE_EMACS', None):
return
self.t = terminal
self.footer = footer
self._handler = TerminalLoggingHandler()
self._handler.setFormatter(log_manager.terminal_formatter)
self._handler.footer = self.footer
old = log_manager.replace_terminal_handler(self._handler)
self._handler.level = old.level
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.footer:
self.footer.clear()
# Prevents the footer from being redrawn if logging occurs.
self._handler.footer = None
def write_line(self, line):
if self.footer:
self.footer.clear()
print(line)
if self.footer:
self.footer.draw()
def refresh(self):
if not self.footer:
return
self.footer.clear()
self.footer.draw()
class BuildOutputManager(OutputManager):
"""Handles writing build output to a terminal, to logs, etc."""
def __init__(self, log_manager, monitor, footer):
self.monitor = monitor
OutputManager.__init__(self, log_manager, footer)
def __exit__(self, exc_type, exc_value, traceback):
OutputManager.__exit__(self, exc_type, exc_value, traceback)
# Ensure the resource monitor is stopped because leaving it running
# could result in the process hanging on exit because the resource
# collection child process hasn't been told to stop.
self.monitor.stop_resource_recording()
def on_line(self, line):
warning, state_changed, relevant = self.monitor.on_line(line)
if relevant:
self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
elif state_changed:
have_handler = hasattr(self, 'handler')
if have_handler:
self.handler.acquire()
try:
self.refresh()
finally:
if have_handler:
self.handler.release()
class StaticAnalysisFooter(Footer):
"""Handles display of a static analysis progress indicator in a terminal.
"""
def __init__(self, terminal, monitor):
Footer.__init__(self, terminal)
self.monitor = monitor
def draw(self):
"""Draws this footer in the terminal."""
monitor = self.monitor
total = monitor.num_files
processed = monitor.num_files_processed
percent = '(%.2f%%)' % (processed * 100.0 / total)
parts = [
('dim', 'Processing'),
('yellow', str(processed)),
('dim', 'of'),
('yellow', str(total)),
('dim', 'files'),
('green', percent)
]
if monitor.current_file:
parts.append(('bold', monitor.current_file))
self.write(parts)
class StaticAnalysisOutputManager(OutputManager):
"""Handles writing static analysis output to a terminal."""
def __init__(self, log_manager, monitor, footer):
self.monitor = monitor
OutputManager.__init__(self, log_manager, footer)
def on_line(self, line):
warning, relevant = self.monitor.on_line(line)
if warning:
self.log(logging.INFO, 'compiler_warning', warning,
'Warning: {flag} in {filename}: {message}')
if relevant:
self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
else:
have_handler = hasattr(self, 'handler')
if have_handler:
self.handler.acquire()
try:
self.refresh()
finally:
if have_handler:
self.handler.release()
class CCacheStats(object):
"""Holds statistics from ccache.
@ -678,6 +978,328 @@ class CCacheStats(object):
class BuildDriver(MozbuildObject):
"""Provides a high-level API for build actions."""
def build(self, what=None, disable_extra_make_dependencies=None, jobs=0,
directory=None, verbose=False, keep_going=False, mach_context=None):
"""Invoke the build backend.
``what`` defines the thing to build. If not defined, the default
target is used.
"""
warnings_path = self._get_state_filename('warnings.json')
monitor = self._spawn(BuildMonitor)
monitor.init(warnings_path)
ccache_start = monitor.ccache_stats()
footer = BuildProgressFooter(self.log_manager.terminal, monitor)
# Disable indexing in objdir because it is not necessary and can slow
# down builds.
mkdir(self.topobjdir, not_indexed=True)
with BuildOutputManager(self.log_manager, monitor, footer) as output:
monitor.start()
if directory is not None and not what:
print('Can only use -C/--directory with an explicit target '
'name.')
return 1
if directory is not None:
disable_extra_make_dependencies=True
directory = mozpath.normsep(directory)
if directory.startswith('/'):
directory = directory[1:]
status = None
monitor.start_resource_recording()
if what:
top_make = os.path.join(self.topobjdir, 'Makefile')
if not os.path.exists(top_make):
print('Your tree has not been configured yet. Please run '
'|mach build| with no arguments.')
return 1
# Collect target pairs.
target_pairs = []
for target in what:
path_arg = self._wrap_path_argument(target)
if directory is not None:
make_dir = os.path.join(self.topobjdir, directory)
make_target = target
else:
make_dir, make_target = \
resolve_target_to_make(self.topobjdir,
path_arg.relpath())
if make_dir is None and make_target is None:
return 1
# See bug 886162 - we don't want to "accidentally" build
# the entire tree (if that's really the intent, it's
# unlikely they would have specified a directory.)
if not make_dir and not make_target:
print("The specified directory doesn't contain a "
"Makefile and the first parent with one is the "
"root of the tree. Please specify a directory "
"with a Makefile or run |mach build| if you "
"want to build the entire tree.")
return 1
target_pairs.append((make_dir, make_target))
# Possibly add extra make depencies using dumbmake.
if not disable_extra_make_dependencies:
from dumbmake.dumbmake import (dependency_map,
add_extra_dependencies)
depfile = os.path.join(self.topsrcdir, 'build',
'dumbmake-dependencies')
with open(depfile) as f:
dm = dependency_map(f.readlines())
new_pairs = list(add_extra_dependencies(target_pairs, dm))
self.log(logging.DEBUG, 'dumbmake',
{'target_pairs': target_pairs,
'new_pairs': new_pairs},
'Added extra dependencies: will build {new_pairs} ' +
'instead of {target_pairs}.')
target_pairs = new_pairs
# Ensure build backend is up to date. The alternative is to
# have rules in the invoked Makefile to rebuild the build
# backend. But that involves make reinvoking itself and there
# are undesired side-effects of this. See bug 877308 for a
# comprehensive history lesson.
self._run_make(directory=self.topobjdir, target='backend',
line_handler=output.on_line, log=False,
print_directory=False, keep_going=keep_going)
# Build target pairs.
for make_dir, make_target in target_pairs:
# We don't display build status messages during partial
# tree builds because they aren't reliable there. This
# could potentially be fixed if the build monitor were more
# intelligent about encountering undefined state.
status = self._run_make(directory=make_dir, target=make_target,
line_handler=output.on_line, log=False, print_directory=False,
ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'},
keep_going=keep_going)
if status != 0:
break
else:
# Try to call the default backend's build() method. This will
# run configure to determine BUILD_BACKENDS if it hasn't run
# yet.
config = None
try:
config = self.config_environment
except Exception:
config_rc = self.configure(buildstatus_messages=True,
line_handler=output.on_line)
if config_rc != 0:
return config_rc
# Even if configure runs successfully, we may have trouble
# getting the config_environment for some builds, such as
# OSX Universal builds. These have to go through client.mk
# regardless.
try:
config = self.config_environment
except Exception:
pass
if config:
active_backend = config.substs.get('BUILD_BACKENDS', [None])[0]
if active_backend:
backend_cls = get_backend_class(active_backend)(config)
status = backend_cls.build(self, output, jobs, verbose)
# If the backend doesn't specify a build() method, then just
# call client.mk directly.
if status is None:
status = self._run_make(srcdir=True, filename='client.mk',
line_handler=output.on_line, log=False, print_directory=False,
allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
silent=not verbose, keep_going=keep_going)
self.log(logging.WARNING, 'warning_summary',
{'count': len(monitor.warnings_database)},
'{count} compiler warnings present.')
monitor.finish(record_usage=status == 0)
# Print the collected compiler warnings. This is redundant with
# inline output from the compiler itself. However, unlike inline
# output, this list is sorted and grouped by file, making it
# easier to triage output.
#
# Only do this if we had a successful build. If the build failed,
# there are more important things in the log to look for than
# whatever code we warned about.
if not status:
# Suppress warnings for 3rd party projects in local builds
# until we suppress them for real.
# TODO remove entries/feature once we stop generating warnings
# in these directories.
pathToThirdparty = os.path.join(self.topsrcdir,
"tools",
"rewriting",
"ThirdPartyPaths.txt")
if os.path.exists(pathToThirdparty):
with open(pathToThirdparty) as f:
# Normalize the path (no trailing /)
LOCAL_SUPPRESS_DIRS = tuple(d.rstrip('/') for d in f.read().splitlines())
else:
# For application based on gecko like thunderbird
LOCAL_SUPPRESS_DIRS = ()
suppressed_by_dir = Counter()
for warning in sorted(monitor.instance_warnings):
path = mozpath.normsep(warning['filename'])
if path.startswith(self.topsrcdir):
path = path[len(self.topsrcdir) + 1:]
warning['normpath'] = path
if (path.startswith(LOCAL_SUPPRESS_DIRS) and
'MOZ_AUTOMATION' not in os.environ):
for d in LOCAL_SUPPRESS_DIRS:
if path.startswith(d):
suppressed_by_dir[d] += 1
break
continue
if warning['column'] is not None:
self.log(logging.WARNING, 'compiler_warning', warning,
'warning: {normpath}:{line}:{column} [{flag}] '
'{message}')
else:
self.log(logging.WARNING, 'compiler_warning', warning,
'warning: {normpath}:{line} [{flag}] {message}')
for d, count in sorted(suppressed_by_dir.items()):
self.log(logging.WARNING, 'suppressed_warning',
{'dir': d, 'count': count},
'(suppressed {count} warnings in {dir})')
high_finder, finder_percent = monitor.have_high_finder_usage()
if high_finder:
print(FINDER_SLOW_MESSAGE % finder_percent)
ccache_end = monitor.ccache_stats()
ccache_diff = None
if ccache_start and ccache_end:
ccache_diff = ccache_end - ccache_start
if ccache_diff:
self.log(logging.INFO, 'ccache',
{'msg': ccache_diff.hit_rate_message()}, "{msg}")
notify_minimum_time = 300
try:
notify_minimum_time = int(os.environ.get('MACH_NOTIFY_MINTIME', '300'))
except ValueError:
# Just stick with the default
pass
if monitor.elapsed > notify_minimum_time:
# Display a notification when the build completes.
self.notify('Build complete' if not status else 'Build failed')
if status:
return status
long_build = monitor.elapsed > 600
if long_build:
output.on_line('We know it took a while, but your build finally finished successfully!')
else:
output.on_line('Your build was successful!')
if monitor.have_resource_usage:
excessive, swap_in, swap_out = monitor.have_excessive_swapping()
# if excessive:
# print(EXCESSIVE_SWAP_MESSAGE)
print('To view resource usage of the build, run |mach '
'resource-usage|.')
telemetry_handler = getattr(mach_context,
'telemetry_handler', None)
telemetry_data = monitor.get_resource_usage()
# Record build configuration data. For now, we cherry pick
# items we need rather than grabbing everything, in order
# to avoid accidentally disclosing PII.
telemetry_data['substs'] = {}
try:
for key in ['MOZ_ARTIFACT_BUILDS', 'MOZ_USING_CCACHE', 'MOZ_USING_SCCACHE']:
value = self.substs.get(key, False)
telemetry_data['substs'][key] = value
except BuildEnvironmentNotFoundException:
pass
# Grab ccache stats if available. We need to be careful not
# to capture information that can potentially identify the
# user (such as the cache location)
if ccache_diff:
telemetry_data['ccache'] = {}
for key in [key[0] for key in ccache_diff.STATS_KEYS]:
try:
telemetry_data['ccache'][key] = ccache_diff._values[key]
except KeyError:
pass
if telemetry_handler:
telemetry_handler(mach_context, telemetry_data)
# Only for full builds because incremental builders likely don't
# need to be burdened with this.
if not what:
try:
# Fennec doesn't have useful output from just building. We should
# arguably make the build action useful for Fennec. Another day...
if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
print('To take your build for a test drive, run: |mach run|')
app = self.substs['MOZ_BUILD_APP']
if app in ('browser', 'mobile/android'):
print('For more information on what to do now, see '
'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')
except Exception:
# Ignore Exceptions in case we can't find config.status (such
# as when doing OSX Universal builds)
pass
return status
def configure(self, options=None, buildstatus_messages=False,
line_handler=None):
def on_line(line):
self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
line_handler = line_handler or on_line
options = ' '.join(shell_quote(o) for o in options or ())
append_env = {b'CONFIGURE_ARGS': options.encode('utf-8')}
# Only print build status messages when we have an active
# monitor.
if not buildstatus_messages:
append_env[b'NO_BUILDSTATUS_MESSAGES'] = b'1'
status = self._run_make(srcdir=True, filename='client.mk',
target='configure', line_handler=line_handler, log=False,
print_directory=False, allow_parallel=False, ensure_exit_code=False,
append_env=append_env)
if not status:
print('Configure complete!')
print('Be sure to run |mach build| to pick up any changes');
return status
def install_tests(self, test_objs):
"""Install test files."""

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

@ -5,8 +5,6 @@
from __future__ import absolute_import, print_function, unicode_literals
import argparse
import collections
import errno
import hashlib
import itertools
import json
@ -29,26 +27,18 @@ from mach.decorators import (
SubCommand,
)
from mach.mixin.logging import LoggingMixin
from mach.main import Mach
from mozbuild.base import (
BuildEnvironmentNotFoundException,
MachCommandBase,
MachCommandConditions as conditions,
MozbuildObject,
MozconfigFindException,
MozconfigLoadException,
ObjdirMismatchException,
)
from mozbuild.util import ensureParentDir
from mozbuild.backend import (
backends,
get_backend_class,
)
from mozbuild.shellutil import quote as shell_quote
BUILD_WHAT_HELP = '''
@ -59,19 +49,6 @@ targets as needed. BUILDING ONLY PARTS OF THE TREE CAN RESULT IN BAD TREE
STATE. USE AT YOUR OWN RISK.
'''.strip()
FINDER_SLOW_MESSAGE = '''
===================
PERFORMANCE WARNING
The OS X Finder application (file indexing used by Spotlight) used a lot of CPU
during the build - an average of %f%% (100%% is 1 core). This made your build
slower.
Consider adding ".noindex" to the end of your object directory name to have
Finder ignore it. Or, add an indexing exclusion through the Spotlight System
Preferences.
===================
'''.strip()
EXCESSIVE_SWAP_MESSAGE = '''
===================
@ -91,217 +68,6 @@ warning heuristic.
'''
class TerminalLoggingHandler(logging.Handler):
"""Custom logging handler that works with terminal window dressing.
This class should probably live elsewhere, like the mach core. Consider
this a proving ground for its usefulness.
"""
def __init__(self):
logging.Handler.__init__(self)
self.fh = sys.stdout
self.footer = None
def flush(self):
self.acquire()
try:
self.fh.flush()
finally:
self.release()
def emit(self, record):
msg = self.format(record)
self.acquire()
try:
if self.footer:
self.footer.clear()
self.fh.write(msg)
self.fh.write('\n')
if self.footer:
self.footer.draw()
# If we don't flush, the footer may not get drawn.
self.fh.flush()
finally:
self.release()
class Footer(object):
"""Handles display of a footer in a terminal.
This class implements the functionality common to all mach commands
that render a footer.
"""
def __init__(self, terminal):
# terminal is a blessings.Terminal.
self._t = terminal
self._fh = sys.stdout
def clear(self):
"""Removes the footer from the current terminal."""
self._fh.write(self._t.move_x(0))
self._fh.write(self._t.clear_eol())
def write(self, parts):
"""Write some output in the footer, accounting for terminal width.
parts is a list of 2-tuples of (encoding_function, input).
None means no encoding."""
# We don't want to write more characters than the current width of the
# terminal otherwise wrapping may result in weird behavior. We can't
# simply truncate the line at terminal width characters because a)
# non-viewable escape characters count towards the limit and b) we
# don't want to truncate in the middle of an escape sequence because
# subsequent output would inherit the escape sequence.
max_width = self._t.width
written = 0
write_pieces = []
for part in parts:
try:
func, part = part
encoded = getattr(self._t, func)(part)
except ValueError:
encoded = part
len_part = len(part)
len_spaces = len(write_pieces)
if written + len_part + len_spaces > max_width:
write_pieces.append(part[0:max_width - written - len_spaces])
written += len_part
break
write_pieces.append(encoded)
written += len_part
with self._t.location():
self._t.move(self._t.height-1,0)
self._fh.write(' '.join(write_pieces))
class BuildProgressFooter(Footer):
"""Handles display of a build progress indicator in a terminal.
When mach builds inside a blessings-supported terminal, it will render
progress information collected from a BuildMonitor. This class converts the
state of BuildMonitor into terminal output.
"""
def __init__(self, terminal, monitor):
Footer.__init__(self, terminal)
self.tiers = monitor.tiers.tier_status.viewitems()
def draw(self):
"""Draws this footer in the terminal."""
if not self.tiers:
return
# The drawn terminal looks something like:
# TIER: static export libs tools
parts = [('bold', 'TIER:')]
append = parts.append
for tier, status in self.tiers:
if status is None:
append(tier)
elif status == 'finished':
append(('green', tier))
else:
append(('underline_yellow', tier))
self.write(parts)
class OutputManager(LoggingMixin):
"""Handles writing job output to a terminal or log."""
def __init__(self, log_manager, footer):
self.populate_logger()
self.footer = None
terminal = log_manager.terminal
# TODO convert terminal footer to config file setting.
if not terminal or os.environ.get('MACH_NO_TERMINAL_FOOTER', None):
return
if os.environ.get('INSIDE_EMACS', None):
return
self.t = terminal
self.footer = footer
self._handler = TerminalLoggingHandler()
self._handler.setFormatter(log_manager.terminal_formatter)
self._handler.footer = self.footer
old = log_manager.replace_terminal_handler(self._handler)
self._handler.level = old.level
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.footer:
self.footer.clear()
# Prevents the footer from being redrawn if logging occurs.
self._handler.footer = None
def write_line(self, line):
if self.footer:
self.footer.clear()
print(line)
if self.footer:
self.footer.draw()
def refresh(self):
if not self.footer:
return
self.footer.clear()
self.footer.draw()
class BuildOutputManager(OutputManager):
"""Handles writing build output to a terminal, to logs, etc."""
def __init__(self, log_manager, monitor, footer):
self.monitor = monitor
OutputManager.__init__(self, log_manager, footer)
def __exit__(self, exc_type, exc_value, traceback):
OutputManager.__exit__(self, exc_type, exc_value, traceback)
# Ensure the resource monitor is stopped because leaving it running
# could result in the process hanging on exit because the resource
# collection child process hasn't been told to stop.
self.monitor.stop_resource_recording()
def on_line(self, line):
warning, state_changed, relevant = self.monitor.on_line(line)
if relevant:
self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
elif state_changed:
have_handler = hasattr(self, 'handler')
if have_handler:
self.handler.acquire()
try:
self.refresh()
finally:
if have_handler:
self.handler.release()
class StoreDebugParamsAndWarnAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
sys.stderr.write('The --debugparams argument is deprecated. Please ' +
@ -387,331 +153,38 @@ class Build(MachCommandBase):
there are build actions not captured by either. If things don't appear to
be rebuilding, perform a vanilla `mach build` to rebuild the world.
"""
import which
from mozbuild.controller.building import BuildMonitor
from mozbuild.util import (
mkdir,
resolve_target_to_make,
from mozbuild.controller.building import (
BuildDriver,
)
self.log_manager.register_structured_logger(logging.getLogger('mozbuild'))
self.log_manager.enable_all_structured_loggers()
warnings_path = self._get_state_filename('warnings.json')
monitor = self._spawn(BuildMonitor)
monitor.init(warnings_path)
ccache_start = monitor.ccache_stats()
footer = BuildProgressFooter(self.log_manager.terminal, monitor)
# Disable indexing in objdir because it is not necessary and can slow
# down builds.
mkdir(self.topobjdir, not_indexed=True)
with BuildOutputManager(self.log_manager, monitor, footer) as output:
monitor.start()
if directory is not None and not what:
print('Can only use -C/--directory with an explicit target '
'name.')
return 1
if directory is not None:
disable_extra_make_dependencies=True
directory = mozpath.normsep(directory)
if directory.startswith('/'):
directory = directory[1:]
status = None
monitor.start_resource_recording()
if what:
top_make = os.path.join(self.topobjdir, 'Makefile')
if not os.path.exists(top_make):
print('Your tree has not been configured yet. Please run '
'|mach build| with no arguments.')
return 1
# Collect target pairs.
target_pairs = []
for target in what:
path_arg = self._wrap_path_argument(target)
if directory is not None:
make_dir = os.path.join(self.topobjdir, directory)
make_target = target
else:
make_dir, make_target = \
resolve_target_to_make(self.topobjdir,
path_arg.relpath())
if make_dir is None and make_target is None:
return 1
# See bug 886162 - we don't want to "accidentally" build
# the entire tree (if that's really the intent, it's
# unlikely they would have specified a directory.)
if not make_dir and not make_target:
print("The specified directory doesn't contain a "
"Makefile and the first parent with one is the "
"root of the tree. Please specify a directory "
"with a Makefile or run |mach build| if you "
"want to build the entire tree.")
return 1
target_pairs.append((make_dir, make_target))
# Possibly add extra make depencies using dumbmake.
if not disable_extra_make_dependencies:
from dumbmake.dumbmake import (dependency_map,
add_extra_dependencies)
depfile = os.path.join(self.topsrcdir, 'build',
'dumbmake-dependencies')
with open(depfile) as f:
dm = dependency_map(f.readlines())
new_pairs = list(add_extra_dependencies(target_pairs, dm))
self.log(logging.DEBUG, 'dumbmake',
{'target_pairs': target_pairs,
'new_pairs': new_pairs},
'Added extra dependencies: will build {new_pairs} ' +
'instead of {target_pairs}.')
target_pairs = new_pairs
# Ensure build backend is up to date. The alternative is to
# have rules in the invoked Makefile to rebuild the build
# backend. But that involves make reinvoking itself and there
# are undesired side-effects of this. See bug 877308 for a
# comprehensive history lesson.
self._run_make(directory=self.topobjdir, target='backend',
line_handler=output.on_line, log=False,
print_directory=False, keep_going=keep_going)
# Build target pairs.
for make_dir, make_target in target_pairs:
# We don't display build status messages during partial
# tree builds because they aren't reliable there. This
# could potentially be fixed if the build monitor were more
# intelligent about encountering undefined state.
status = self._run_make(directory=make_dir, target=make_target,
line_handler=output.on_line, log=False, print_directory=False,
ensure_exit_code=False, num_jobs=jobs, silent=not verbose,
append_env={b'NO_BUILDSTATUS_MESSAGES': b'1'},
keep_going=keep_going)
if status != 0:
break
else:
# Try to call the default backend's build() method. This will
# run configure to determine BUILD_BACKENDS if it hasn't run
# yet.
config = None
try:
config = self.config_environment
except Exception:
config_rc = self.configure(buildstatus_messages=True,
line_handler=output.on_line)
if config_rc != 0:
return config_rc
# Even if configure runs successfully, we may have trouble
# getting the config_environment for some builds, such as
# OSX Universal builds. These have to go through client.mk
# regardless.
try:
config = self.config_environment
except Exception:
pass
if config:
active_backend = config.substs.get('BUILD_BACKENDS', [None])[0]
if active_backend:
backend_cls = get_backend_class(active_backend)(config)
status = backend_cls.build(self, output, jobs, verbose)
# If the backend doesn't specify a build() method, then just
# call client.mk directly.
if status is None:
status = self._run_make(srcdir=True, filename='client.mk',
line_handler=output.on_line, log=False, print_directory=False,
allow_parallel=False, ensure_exit_code=False, num_jobs=jobs,
silent=not verbose, keep_going=keep_going)
self.log(logging.WARNING, 'warning_summary',
{'count': len(monitor.warnings_database)},
'{count} compiler warnings present.')
# Print the collected compiler warnings. This is redundant with
# inline output from the compiler itself. However, unlike inline
# output, this list is sorted and grouped by file, making it
# easier to triage output.
#
# Only do this if we had a successful build. If the build failed,
# there are more important things in the log to look for than
# whatever code we warned about.
if not status:
# Suppress warnings for 3rd party projects in local builds
# until we suppress them for real.
# TODO remove entries/feature once we stop generating warnings
# in these directories.
pathToThirdparty = os.path.join(self.topsrcdir,
"tools",
"rewriting",
"ThirdPartyPaths.txt")
if os.path.exists(pathToThirdparty):
with open(pathToThirdparty) as f:
# Normalize the path (no trailing /)
LOCAL_SUPPRESS_DIRS = tuple(d.rstrip('/') for d in f.read().splitlines())
else:
# For application based on gecko like thunderbird
LOCAL_SUPPRESS_DIRS = ()
suppressed_by_dir = collections.Counter()
for warning in sorted(monitor.instance_warnings):
path = mozpath.normsep(warning['filename'])
if path.startswith(self.topsrcdir):
path = path[len(self.topsrcdir) + 1:]
warning['normpath'] = path
if (path.startswith(LOCAL_SUPPRESS_DIRS) and
'MOZ_AUTOMATION' not in os.environ):
for d in LOCAL_SUPPRESS_DIRS:
if path.startswith(d):
suppressed_by_dir[d] += 1
break
continue
if warning['column'] is not None:
self.log(logging.WARNING, 'compiler_warning', warning,
'warning: {normpath}:{line}:{column} [{flag}] '
'{message}')
else:
self.log(logging.WARNING, 'compiler_warning', warning,
'warning: {normpath}:{line} [{flag}] {message}')
for d, count in sorted(suppressed_by_dir.items()):
self.log(logging.WARNING, 'suppressed_warning',
{'dir': d, 'count': count},
'(suppressed {count} warnings in {dir})')
monitor.finish(record_usage=status==0)
high_finder, finder_percent = monitor.have_high_finder_usage()
if high_finder:
print(FINDER_SLOW_MESSAGE % finder_percent)
ccache_end = monitor.ccache_stats()
ccache_diff = None
if ccache_start and ccache_end:
ccache_diff = ccache_end - ccache_start
if ccache_diff:
self.log(logging.INFO, 'ccache',
{'msg': ccache_diff.hit_rate_message()}, "{msg}")
notify_minimum_time = 300
try:
notify_minimum_time = int(os.environ.get('MACH_NOTIFY_MINTIME', '300'))
except ValueError:
# Just stick with the default
pass
if monitor.elapsed > notify_minimum_time:
# Display a notification when the build completes.
self.notify('Build complete' if not status else 'Build failed')
if status:
return status
long_build = monitor.elapsed > 600
if long_build:
output.on_line('We know it took a while, but your build finally finished successfully!')
else:
output.on_line('Your build was successful!')
if monitor.have_resource_usage:
excessive, swap_in, swap_out = monitor.have_excessive_swapping()
# if excessive:
# print(EXCESSIVE_SWAP_MESSAGE)
print('To view resource usage of the build, run |mach '
'resource-usage|.')
telemetry_handler = getattr(self._mach_context,
'telemetry_handler', None)
telemetry_data = monitor.get_resource_usage()
# Record build configuration data. For now, we cherry pick
# items we need rather than grabbing everything, in order
# to avoid accidentally disclosing PII.
telemetry_data['substs'] = {}
try:
for key in ['MOZ_ARTIFACT_BUILDS', 'MOZ_USING_CCACHE', 'MOZ_USING_SCCACHE']:
value = self.substs.get(key, False)
telemetry_data['substs'][key] = value
except BuildEnvironmentNotFoundException:
pass
# Grab ccache stats if available. We need to be careful not
# to capture information that can potentially identify the
# user (such as the cache location)
if ccache_diff:
telemetry_data['ccache'] = {}
for key in [key[0] for key in ccache_diff.STATS_KEYS]:
try:
telemetry_data['ccache'][key] = ccache_diff._values[key]
except KeyError:
pass
telemetry_handler(self._mach_context, telemetry_data)
# Only for full builds because incremental builders likely don't
# need to be burdened with this.
if not what:
try:
# Fennec doesn't have useful output from just building. We should
# arguably make the build action useful for Fennec. Another day...
if self.substs['MOZ_BUILD_APP'] != 'mobile/android':
print('To take your build for a test drive, run: |mach run|')
app = self.substs['MOZ_BUILD_APP']
if app in ('browser', 'mobile/android'):
print('For more information on what to do now, see '
'https://developer.mozilla.org/docs/Developer_Guide/So_You_Just_Built_Firefox')
except Exception:
# Ignore Exceptions in case we can't find config.status (such
# as when doing OSX Universal builds)
pass
return status
driver = self._spawn(BuildDriver)
return driver.build(
what=what,
disable_extra_make_dependencies=disable_extra_make_dependencies,
jobs=jobs,
directory=directory,
verbose=verbose,
keep_going=keep_going,
mach_context=self._mach_context)
@Command('configure', category='build',
description='Configure the tree (run configure and config.status).')
@CommandArgument('options', default=None, nargs=argparse.REMAINDER,
help='Configure options')
def configure(self, options=None, buildstatus_messages=False, line_handler=None):
def on_line(line):
self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
from mozbuild.controller.building import (
BuildDriver,
)
line_handler = line_handler or on_line
self.log_manager.enable_all_structured_loggers()
driver = self._spawn(BuildDriver)
options = ' '.join(shell_quote(o) for o in options or ())
append_env = {b'CONFIGURE_ARGS': options.encode('utf-8')}
# Only print build status messages when we have an active
# monitor.
if not buildstatus_messages:
append_env[b'NO_BUILDSTATUS_MESSAGES'] = b'1'
status = self._run_make(srcdir=True, filename='client.mk',
target='configure', line_handler=line_handler, log=False,
print_directory=False, allow_parallel=False, ensure_exit_code=False,
append_env=append_env)
if not status:
print('Configure complete!')
print('Be sure to run |mach build| to pick up any changes');
return status
return driver.configure(
options=options,
buildstatus_messages=buildstatus_messages,
line_handler=line_handler)
@Command('resource-usage', category='post-build',
description='Show information about system resource usage for a build.')
@ -2121,62 +1594,6 @@ class StaticAnalysisMonitor(object):
return (warning, True)
class StaticAnalysisFooter(Footer):
"""Handles display of a static analysis progress indicator in a terminal.
"""
def __init__(self, terminal, monitor):
Footer.__init__(self, terminal)
self.monitor = monitor
def draw(self):
"""Draws this footer in the terminal."""
monitor = self.monitor
total = monitor.num_files
processed = monitor.num_files_processed
percent = '(%.2f%%)' % (processed * 100.0 / total)
parts = [
('dim', 'Processing'),
('yellow', str(processed)),
('dim', 'of'),
('yellow', str(total)),
('dim', 'files'),
('green', percent)
]
if monitor.current_file:
parts.append(('bold', monitor.current_file))
self.write(parts)
class StaticAnalysisOutputManager(OutputManager):
"""Handles writing static analysis output to a terminal."""
def __init__(self, log_manager, monitor, footer):
self.monitor = monitor
OutputManager.__init__(self, log_manager, footer)
def on_line(self, line):
warning, relevant = self.monitor.on_line(line)
if warning:
self.log(logging.INFO, 'compiler_warning', warning,
'Warning: {flag} in {filename}: {message}')
if relevant:
self.log(logging.INFO, 'build_output', {'line': line}, '{line}')
else:
have_handler = hasattr(self, 'handler')
if have_handler:
self.handler.acquire()
try:
self.refresh()
finally:
if have_handler:
self.handler.release()
@CommandProvider
class StaticAnalysis(MachCommandBase):
"""Utilities for running C++ static analysis checks."""
@ -2213,7 +1630,14 @@ class StaticAnalysis(MachCommandBase):
'of each translation unit are always displayed')
def check(self, source=None, jobs=2, strip=1, verbose=False,
checks='-*', fix=False, header_filter=''):
from mozbuild.controller.building import (
StaticAnalysisFooter,
StaticAnalysisOutputManager,
)
self._set_log_level(verbose)
self.log_manager.enable_all_structured_loggers()
rc = self._build_compile_db(verbose=verbose)
if rc != 0:
return rc
@ -2246,8 +1670,6 @@ class StaticAnalysis(MachCommandBase):
if fix:
common_args.append('-fix')
self.log_manager.register_structured_logger(logging.getLogger('mozbuild'))
compile_db = json.loads(open(self._compile_db, 'r').read())
total = 0
import re

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

@ -31,6 +31,10 @@ function add_ocsp_test(aHost, aExpectedResult, aOCSPResponseToServe,
do_get_profile();
Services.prefs.setBoolPref("security.ssl.enable_ocsp_stapling", true);
Services.prefs.setIntPref("security.OCSP.enabled", 1);
// Sometimes this test will fail on android due to an OCSP request timing out.
// That aspect of OCSP requests is not what we're testing here, so we can just
// bump the timeout and hopefully avoid these failures.
Services.prefs.setIntPref("security.OCSP.timeoutMilliseconds.soft", 5000);
Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 4);
var args = [["good", "default-ee", "unused", 0],
["expiredresponse", "default-ee", "unused", 0],

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

@ -6,11 +6,6 @@ module.exports = {
],
globals: {
// Globals specific to mozmill
"assert": false,
"controller": false,
"findElement": false,
"mozmill": false,
// Injected into tests via tps.jsm
"Addons": false,
"Bookmarks": false,
@ -22,7 +17,6 @@ module.exports = {
"Passwords": false,
"Phase": false,
"Prefs": false,
"RunMozmillTest": false,
"STATE_DISABLED": false,
"STATE_ENABLED": false,
"Sync": false,

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

@ -1,30 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://tps/tps.jsm");
var setupModule = function(module) {
module.controller = mozmill.getBrowserController();
assert.ok(true, "SetupModule passes");
};
var setupTest = function(module) {
assert.ok(true, "SetupTest passes");
};
var testTestStep = function() {
assert.ok(true, "test Passes");
controller.open("http://www.mozilla.org");
TPS.Login();
TPS.Sync(ACTIONS.ACTION_SYNC_WIPE_CLIENT);
};
var teardownTest = function() {
assert.ok(true, "teardownTest passes");
};
var teardownModule = function() {
assert.ok(true, "teardownModule passes");
};

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

@ -1,15 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var setupModule = function(module) {
module.controller = mozmill.getBrowserController();
};
var testGetNode = function() {
controller.open("about:support");
controller.waitForPageLoad();
var appbox = findElement.ID(controller.tabs.activeTab, "application-box");
assert.waitFor(() => appbox.getNode().textContent == "Firefox", "correct app name");
};

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

@ -1,24 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* The list of phases mapped to their corresponding profiles. The object
* here must be in strict JSON format, as it will get parsed by the Python
* testrunner (no single quotes, extra comma's, etc).
*/
var phases = { "phase1": "profile1",
"phase2": "profile2" };
/*
* Test phases
*/
Phase("phase1", [
[RunMozmillTest, "mozmill_sanity.js"],
]);
Phase("phase2", [
[Sync],
[RunMozmillTest, "mozmill_sanity2.js"],
]);

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

@ -166,7 +166,7 @@ add_task(async function test_repairs_skip_if_cant_vaidate() {
};
let requestor = {
async startRepairs(validationInfo, flowID) {
assert.ok(false, "Never should start repairs");
ok(false, "Never should start repairs");
},
tryServerOnlyRepairs() {
return false;

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

@ -1,2 +0,0 @@
resource mozmill resource/

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

@ -1,25 +0,0 @@
<?xml version="1.0"?>
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>mozmill@mozilla.com</em:id>
<em:name>Mozmill</em:name>
<em:version>2.0.8</em:version>
<em:description>UI Automation tool for Mozilla applications</em:description>
<em:unpack>true</em:unpack>
<em:creator>Mozilla Automation and Testing Team</em:creator>
<em:contributor>Adam Christian</em:contributor>
<em:contributor>Mikeal Rogers</em:contributor>
<em:targetApplication>
<Description>
<em:id>toolkit@mozilla.org</em:id>
<em:minVersion>10.0</em:minVersion>
<em:maxVersion>38.*</em:maxVersion>
</Description>
</em:targetApplication>
<em:multiprocessCompatible>true</em:multiprocessCompatible>
</Description>
</RDF>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,537 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ["ID", "Link", "XPath", "Selector", "Name", "Anon", "AnonXPath",
"Lookup", "_byID", "_byName", "_byAttrib", "_byAnonAttrib",
];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
var strings = {}; Cu.import('resource://mozmill/stdlib/strings.js', strings);
var arrays = {}; Cu.import('resource://mozmill/stdlib/arrays.js', arrays);
var json2 = {}; Cu.import('resource://mozmill/stdlib/json2.js', json2);
var withs = {}; Cu.import('resource://mozmill/stdlib/withs.js', withs);
var dom = {}; Cu.import('resource://mozmill/stdlib/dom.js', dom);
var objects = {}; Cu.import('resource://mozmill/stdlib/objects.js', objects);
var countQuotes = function (str) {
var count = 0;
var i = 0;
while (i < str.length) {
i = str.indexOf('"', i);
if (i != -1) {
count++;
i++;
} else {
break;
}
}
return count;
};
/**
* smartSplit()
*
* Takes a lookup string as input and returns
* a list of each node in the string
*/
var smartSplit = function (str) {
// Ensure we have an even number of quotes
if (countQuotes(str) % 2 != 0) {
throw new Error ("Invalid Lookup Expression");
}
/**
* This regex matches a single "node" in a lookup string.
* In otherwords, it matches the part between the two '/'s
*
* Regex Explanation:
* \/ - start matching at the first forward slash
* ([^\/"]*"[^"]*")* - match as many pairs of quotes as possible until we hit a slash (ignore slashes inside quotes)
* [^\/]* - match the remainder of text outside of last quote but before next slash
*/
var re = /\/([^\/"]*"[^"]*")*[^\/]*/g
var ret = []
var match = re.exec(str);
while (match != null) {
ret.push(match[0].replace(/^\//, ""));
match = re.exec(str);
}
return ret;
};
/**
* defaultDocuments()
*
* Returns a list of default documents in which to search for elements
* if no document is provided
*/
function defaultDocuments() {
var win = Services.wm.getMostRecentWindow("navigator:browser");
return [
win.document,
utils.getBrowserObject(win).selectedBrowser.contentWindow.document
];
};
/**
* nodeSearch()
*
* Takes an optional document, callback and locator string
* Returns a handle to the located element or null
*/
function nodeSearch(doc, func, string) {
if (doc != undefined) {
var documents = [doc];
} else {
var documents = defaultDocuments();
}
var e = null;
var element = null;
//inline function to recursively find the element in the DOM, cross frame.
var search = function (win, func, string) {
if (win == null) {
return;
}
//do the lookup in the current window
element = func.call(win, string);
if (!element || (element.length == 0)) {
var frames = win.frames;
for (var i = 0; i < frames.length; i++) {
search(frames[i], func, string);
}
} else {
e = element;
}
};
for (var i = 0; i < documents.length; ++i) {
var win = documents[i].defaultView;
search(win, func, string);
if (e) {
break;
}
}
return e;
};
/**
* Selector()
*
* Finds an element by selector string
*/
function Selector(_document, selector, index) {
if (selector == undefined) {
throw new Error('Selector constructor did not recieve enough arguments.');
}
this.selector = selector;
this.getNodeForDocument = function (s) {
return this.document.querySelectorAll(s);
};
var nodes = nodeSearch(_document, this.getNodeForDocument, this.selector);
return nodes ? nodes[index || 0] : null;
};
/**
* ID()
*
* Finds an element by ID
*/
function ID(_document, nodeID) {
if (nodeID == undefined) {
throw new Error('ID constructor did not recieve enough arguments.');
}
this.getNodeForDocument = function (nodeID) {
return this.document.getElementById(nodeID);
};
return nodeSearch(_document, this.getNodeForDocument, nodeID);
};
/**
* Link()
*
* Finds a link by innerHTML
*/
function Link(_document, linkName) {
if (linkName == undefined) {
throw new Error('Link constructor did not recieve enough arguments.');
}
this.getNodeForDocument = function (linkName) {
var getText = function (el) {
var text = "";
if (el.nodeType == 3) { //textNode
if (el.data != undefined) {
text = el.data;
} else {
text = el.innerHTML;
}
text = text.replace(/n|r|t/g, " ");
}
else if (el.nodeType == 1) { //elementNode
for (var i = 0; i < el.childNodes.length; i++) {
var child = el.childNodes.item(i);
text += getText(child);
}
if (el.tagName == "P" || el.tagName == "BR" ||
el.tagName == "HR" || el.tagName == "DIV") {
text += "\n";
}
}
return text;
};
//sometimes the windows won't have this function
try {
var links = this.document.getElementsByTagName('a');
} catch (e) {
// ADD LOG LINE mresults.write('Error: '+ e, 'lightred');
}
for (var i = 0; i < links.length; i++) {
var el = links[i];
//if (getText(el).indexOf(this.linkName) != -1) {
if (el.innerHTML.indexOf(linkName) != -1) {
return el;
}
}
return null;
};
return nodeSearch(_document, this.getNodeForDocument, linkName);
};
/**
* XPath()
*
* Finds an element by XPath
*/
function XPath(_document, expr) {
if (expr == undefined) {
throw new Error('XPath constructor did not recieve enough arguments.');
}
this.getNodeForDocument = function (s) {
var aNode = this.document;
var aExpr = s;
var xpe = null;
if (this.document.defaultView == null) {
xpe = new getMethodInWindows('XPathEvaluator')();
} else {
xpe = new this.document.defaultView.XPathEvaluator();
}
var nsResolver = xpe.createNSResolver(aNode.ownerDocument == null ? aNode.documentElement
: aNode.ownerDocument.documentElement);
var result = xpe.evaluate(aExpr, aNode, nsResolver, 0, null);
var found = [];
var res;
while (res = result.iterateNext()) {
found.push(res);
}
return found[0];
};
return nodeSearch(_document, this.getNodeForDocument, expr);
};
/**
* Name()
*
* Finds an element by Name
*/
function Name(_document, nName) {
if (nName == undefined) {
throw new Error('Name constructor did not recieve enough arguments.');
}
this.getNodeForDocument = function (s) {
try{
var els = this.document.getElementsByName(s);
if (els.length > 0) {
return els[0];
}
} catch (e) {
}
return null;
};
return nodeSearch(_document, this.getNodeForDocument, nName);
};
var _returnResult = function (results) {
if (results.length == 0) {
return null
}
else if (results.length == 1) {
return results[0];
} else {
return results;
}
}
var _forChildren = function (element, name, value) {
var results = [];
var nodes = Array.from(element.childNodes).filter(e => e);
for (var i in nodes) {
var n = nodes[i];
if (n[name] == value) {
results.push(n);
}
}
return results;
}
var _forAnonChildren = function (_document, element, name, value) {
var results = [];
var nodes = Array.from(_document.getAnoymousNodes(element)).filter(e => e);
for (var i in nodes ) {
var n = nodes[i];
if (n[name] == value) {
results.push(n);
}
}
return results;
}
var _byID = function (_document, parent, value) {
return _returnResult(_forChildren(parent, 'id', value));
}
var _byName = function (_document, parent, value) {
return _returnResult(_forChildren(parent, 'tagName', value));
}
var _byAttrib = function (parent, attributes) {
var results = [];
var nodes = parent.childNodes;
for (var i in nodes) {
var n = nodes[i];
let requirementPass = 0;
let requirementLength = 0;
for (var a in attributes) {
requirementLength++;
try {
if (n.getAttribute(a) == attributes[a]) {
requirementPass++;
}
} catch (e) {
// Workaround any bugs in custom attribute crap in XUL elements
}
}
if (requirementPass == requirementLength) {
results.push(n);
}
}
return _returnResult(results)
}
var _byAnonAttrib = function (_document, parent, attributes) {
var results = [];
if (objects.getLength(attributes) == 1) {
for (var i in attributes) {
var k = i;
var v = attributes[i];
}
var result = _document.getAnonymousElementByAttribute(parent, k, v);
if (result) {
return result;
}
}
var nodes = Array.from(_document.getAnonymousNodes(parent)).filter(n => n.getAttribute);
function resultsForNodes (nodes) {
for (var i in nodes) {
var n = nodes[i];
requirementPass = 0;
requirementLength = 0;
for (var a in attributes) {
requirementLength++;
if (n.getAttribute(a) == attributes[a]) {
requirementPass++;
}
}
if (requirementPass == requirementLength) {
results.push(n);
}
}
}
resultsForNodes(nodes);
if (results.length == 0) {
resultsForNodes(Array.from(parent.childNodes).filter(n => n != undefined && n.getAttribute));
}
return _returnResult(results)
}
var _byIndex = function (_document, parent, i) {
if (parent instanceof Array) {
return parent[i];
}
return parent.childNodes[i];
}
var _anonByName = function (_document, parent, value) {
return _returnResult(_forAnonChildren(_document, parent, 'tagName', value));
}
var _anonByAttrib = function (_document, parent, value) {
return _byAnonAttrib(_document, parent, value);
}
var _anonByIndex = function (_document, parent, i) {
return _document.getAnonymousNodes(parent)[i];
}
/**
* Lookup()
*
* Finds an element by Lookup expression
*/
function Lookup(_document, expression) {
if (expression == undefined) {
throw new Error('Lookup constructor did not recieve enough arguments.');
}
var expSplit = smartSplit(expression).filter(e => e != '');
expSplit.unshift(_document);
var nCases = {'id':_byID, 'name':_byName, 'attrib':_byAttrib, 'index':_byIndex};
var aCases = {'name':_anonByName, 'attrib':_anonByAttrib, 'index':_anonByIndex};
/**
* Reduces the lookup expression
* @param {Object} parentNode
* Parent node (previousValue of the formerly executed reduce callback)
* @param {String} exp
* Lookup expression for the parents child node
*
* @returns {Object} Node found by the given expression
*/
var reduceLookup = function (parentNode, exp) {
// Abort in case the parent node was not found
if (!parentNode) {
return false;
}
// Handle case where only index is provided
var cases = nCases;
// Handle ending index before any of the expression gets mangled
if (withs.endsWith(exp, ']')) {
var expIndex = json2.JSON.parse(strings.vslice(exp, '[', ']'));
}
// Handle anon
if (withs.startsWith(exp, 'anon')) {
exp = strings.vslice(exp, '(', ')');
cases = aCases;
}
if (withs.startsWith(exp, '[')) {
try {
var obj = json2.JSON.parse(strings.vslice(exp, '[', ']'));
} catch (e) {
throw new SyntaxError(e + '. String to be parsed was || ' +
strings.vslice(exp, '[', ']') + ' ||');
}
var r = cases['index'](_document, parentNode, obj);
if (r == null) {
throw new SyntaxError('Expression "' + exp +
'" returned null. Anonymous == ' + (cases == aCases));
}
return r;
}
for (var c in cases) {
if (withs.startsWith(exp, c)) {
try {
var obj = json2.JSON.parse(strings.vslice(exp, '(', ')'))
} catch (e) {
throw new SyntaxError(e + '. String to be parsed was || ' +
strings.vslice(exp, '(', ')') + ' ||');
}
var result = cases[c](_document, parentNode, obj);
}
}
if (!result) {
if (withs.startsWith(exp, '{')) {
try {
var obj = json2.JSON.parse(exp);
} catch (e) {
throw new SyntaxError(e + '. String to be parsed was || ' + exp + ' ||');
}
if (cases == aCases) {
var result = _anonByAttrib(_document, parentNode, obj);
} else {
var result = _byAttrib(parentNode, obj);
}
}
}
// Final return
if (expIndex) {
// TODO: Check length and raise error
return result[expIndex];
} else {
// TODO: Check length and raise error
return result;
}
// Maybe we should cause an exception here
return false;
};
return expSplit.reduce(reduceLookup);
};

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,283 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ["controller", "utils", "elementslib", "os",
"getBrowserController", "newBrowserController",
"getAddonsController", "getPreferencesController",
"newMail3PaneController", "getMail3PaneController",
"wm", "platform", "getAddrbkController",
"getMsgComposeController", "getDownloadsController",
"Application", "findElement",
"getPlacesController", 'isMac', 'isLinux', 'isWindows',
"firePythonCallback", "getAddons"
];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// imports
var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
var controller = {}; Cu.import('resource://mozmill/driver/controller.js', controller);
var elementslib = {}; Cu.import('resource://mozmill/driver/elementslib.js', elementslib);
var findElement = {}; Cu.import('resource://mozmill/driver/mozelement.js', findElement);
var os = {}; Cu.import('resource://mozmill/stdlib/os.js', os);
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
var windows = {}; Cu.import('resource://mozmill/modules/windows.js', windows);
const DEBUG = false;
// This is a useful "check" timer. See utils.js, good for debugging
if (DEBUG) {
utils.startTimer();
}
var assert = new assertions.Assert();
// platform information
var platform = os.getPlatform();
var isMac = false;
var isWindows = false;
var isLinux = false;
if (platform == "darwin"){
isMac = true;
}
if (platform == "winnt"){
isWindows = true;
}
if (platform == "linux"){
isLinux = true;
}
var wm = Services.wm;
var appInfo = Services.appinfo;
var Application = utils.applicationName;
/**
* Retrieves the list with information about installed add-ons.
*
* @returns {String} JSON data of installed add-ons
*/
function getAddons() {
var addons = null;
AddonManager.getAllAddons(function (addonList) {
var tmp_list = [ ];
addonList.forEach(function (addon) {
var tmp = { };
// We have to filter out properties of type 'function' of the addon
// object, which will break JSON.stringify() and result in incomplete
// addon information.
for (var key in addon) {
if (typeof(addon[key]) !== "function") {
tmp[key] = addon[key];
}
}
tmp_list.push(tmp);
});
addons = tmp_list;
});
try {
// Sychronize with getAllAddons so we do not return too early
assert.waitFor(function () {
return !!addons;
})
return addons;
} catch (e) {
return null;
}
}
/**
* Retrieves application details for the Mozmill report
*
* @return {String} JSON data of application details
*/
function getApplicationDetails() {
var locale = Services.locale.getAppLocaleAsLangTag();
// Put all our necessary information into JSON and return it:
// appinfo, startupinfo, and addons
var details = {
application_id: appInfo.ID,
application_name: Application,
application_version: appInfo.version,
application_locale: locale,
platform_buildid: appInfo.platformBuildID,
platform_version: appInfo.platformVersion,
addons: getAddons(),
startupinfo: getStartupInfo(),
paths: {
appdata: Services.dirsvc.get('UAppData', Ci.nsIFile).path,
profile: Services.dirsvc.get('ProfD', Ci.nsIFile).path
}
};
return JSON.stringify(details);
}
// get startup time if available
// see http://blog.mozilla.com/tglek/2011/04/26/measuring-startup-speed-correctly/
function getStartupInfo() {
var startupInfo = {};
try {
var _startupInfo = Services.startup.getStartupInfo();
for (var time in _startupInfo) {
// convert from Date object to ms since epoch
startupInfo[time] = _startupInfo[time].getTime();
}
} catch (e) {
startupInfo = null;
}
return startupInfo;
}
function newBrowserController () {
return new controller.MozMillController(utils.getMethodInWindows('OpenBrowserWindow')());
}
function getBrowserController () {
var browserWindow = wm.getMostRecentWindow("navigator:browser");
if (browserWindow == null) {
return newBrowserController();
} else {
return new controller.MozMillController(browserWindow);
}
}
function getPlacesController () {
utils.getMethodInWindows('PlacesCommandHook').showPlacesOrganizer('AllBookmarks');
return new controller.MozMillController(wm.getMostRecentWindow(''));
}
function getAddonsController () {
if (Application == 'SeaMonkey') {
utils.getMethodInWindows('toEM')();
}
else if (Application == 'Thunderbird') {
utils.getMethodInWindows('openAddonsMgr')();
}
else if (Application == 'Sunbird') {
utils.getMethodInWindows('goOpenAddons')();
} else {
utils.getMethodInWindows('BrowserOpenAddonsMgr')();
}
return new controller.MozMillController(wm.getMostRecentWindow(''));
}
function getDownloadsController() {
utils.getMethodInWindows('BrowserDownloadsUI')();
return new controller.MozMillController(wm.getMostRecentWindow(''));
}
function getPreferencesController() {
if (Application == 'Thunderbird') {
utils.getMethodInWindows('openOptionsDialog')();
} else {
utils.getMethodInWindows('openPreferences')();
}
return new controller.MozMillController(wm.getMostRecentWindow(''));
}
// Thunderbird functions
function newMail3PaneController () {
return new controller.MozMillController(utils.getMethodInWindows('toMessengerWindow')());
}
function getMail3PaneController () {
var mail3PaneWindow = wm.getMostRecentWindow("mail:3pane");
if (mail3PaneWindow == null) {
return newMail3PaneController();
} else {
return new controller.MozMillController(mail3PaneWindow);
}
}
// Thunderbird - Address book window
function newAddrbkController () {
utils.getMethodInWindows("toAddressBook")();
utils.sleep(2000);
var addyWin = wm.getMostRecentWindow("mail:addressbook");
return new controller.MozMillController(addyWin);
}
function getAddrbkController () {
var addrbkWindow = wm.getMostRecentWindow("mail:addressbook");
if (addrbkWindow == null) {
return newAddrbkController();
} else {
return new controller.MozMillController(addrbkWindow);
}
}
function firePythonCallback (filename, method, args, kwargs) {
let obj = {'filename': filename, 'method': method};
obj['args'] = args || [];
obj['kwargs'] = kwargs || {};
broker.sendMessage("firePythonCallback", obj);
}
function timer (name) {
this.name = name;
this.timers = {};
this.actions = [];
frame.timers.push(this);
}
timer.prototype.start = function (name) {
this.timers[name].startTime = (new Date).getTime();
}
timer.prototype.stop = function (name) {
var t = this.timers[name];
t.endTime = (new Date).getTime();
t.totalTime = (t.endTime - t.startTime);
}
timer.prototype.end = function () {
frame.events.fireEvent("timer", this);
frame.timers.remove(this);
}
// Initialization
/**
* Initialize Mozmill
*/
function initialize() {
windows.init();
}
initialize();

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

@ -1,58 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['addListener', 'addObject',
'removeListener',
'sendMessage', 'log', 'pass', 'fail'];
var listeners = {};
// add a listener for a specific message type
function addListener(msgType, listener) {
if (listeners[msgType] === undefined) {
listeners[msgType] = [];
}
listeners[msgType].push(listener);
}
// add each method in an object as a message listener
function addObject(object) {
for (var msgType in object) {
addListener(msgType, object[msgType]);
}
}
// remove a listener for all message types
function removeListener(listener) {
for (var msgType in listeners) {
for (let i = 0; i < listeners.length; ++i) {
if (listeners[msgType][i] == listener) {
listeners[msgType].splice(i, 1); // remove listener from array
}
}
}
}
function sendMessage(msgType, obj) {
if (listeners[msgType] === undefined) {
return;
}
for (let i = 0; i < listeners[msgType].length; ++i) {
listeners[msgType][i](obj);
}
}
function log(obj) {
sendMessage('log', obj);
}
function pass(obj) {
sendMessage('pass', obj);
}
function fail(obj) {
sendMessage('fail', obj);
}

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

@ -1,672 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['Assert', 'Expect'];
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
var stack = {}; Cu.import('resource://mozmill/modules/stack.js', stack);
/**
* @name assertions
* @namespace Defines expect and assert methods to be used for assertions.
*/
/**
* The Assert class implements fatal assertions, and can be used in cases
* when a failing test has to directly abort the current test function. All
* remaining tasks will not be performed.
*
*/
var Assert = function () {}
Assert.prototype = {
// The following deepEquals implementation is from Narwhal under this license:
// http://wiki.commonjs.org/wiki/Unit_Testing/1.0
//
// THIS IS NOT TESTED NOR LIKELY TO WORK OUTSIDE V8!
//
// Originally from narwhal.js (http://narwhaljs.org)
// Copyright (c) 2009 Thomas Robinson <280north.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the 'Software'), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
_deepEqual: function (actual, expected) {
// 7.1. All identical values are equivalent, as determined by ===.
if (actual === expected) {
return true;
// 7.2. If the expected value is a Date object, the actual value is
// equivalent if it is also a Date object that refers to the same time.
} else if (actual instanceof Date && expected instanceof Date) {
return actual.getTime() === expected.getTime();
// 7.3. Other pairs that do not both pass typeof value == 'object',
// equivalence is determined by ==.
} else if (typeof actual != 'object' && typeof expected != 'object') {
return actual == expected;
// 7.4. For all other Object pairs, including Array objects, equivalence is
// determined by having the same number of owned properties (as verified
// with Object.prototype.hasOwnProperty.call), the same set of keys
// (although not necessarily the same order), equivalent values for every
// corresponding key, and an identical 'prototype' property. Note: this
// accounts for both named and indexed properties on Arrays.
} else {
return this._objEquiv(actual, expected);
}
},
_objEquiv: function (a, b) {
if (a == null || a == undefined || b == null || b == undefined)
return false;
// an identical 'prototype' property.
if (a.prototype !== b.prototype) return false;
function isArguments(object) {
return Object.prototype.toString.call(object) == '[object Arguments]';
}
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
if (isArguments(a)) {
if (!isArguments(b)) {
return false;
}
a = pSlice.call(a);
b = pSlice.call(b);
return this._deepEqual(a, b);
}
try {
var ka = Object.keys(a),
kb = Object.keys(b),
key, i;
} catch (e) {//happens when one is a string literal and the other isn't
return false;
}
// having the same number of owned properties (keys incorporates
// hasOwnProperty)
if (ka.length != kb.length)
return false;
//the same set of keys (although not necessarily the same order),
ka.sort();
kb.sort();
//~~~cheap key test
for (i = ka.length - 1; i >= 0; i--) {
if (ka[i] != kb[i])
return false;
}
//equivalent values for every corresponding key, and
//~~~possibly expensive deep test
for (i = ka.length - 1; i >= 0; i--) {
key = ka[i];
if (!this._deepEqual(a[key], b[key])) return false;
}
return true;
},
_expectedException : function Assert__expectedException(actual, expected) {
if (!actual || !expected) {
return false;
}
if (expected instanceof RegExp) {
return expected.test(actual);
} else if (actual instanceof expected) {
return true;
} else if (expected.call({}, actual) === true) {
return true;
} else if (actual.name === expected.name) {
return true;
}
return false;
},
/**
* Log a test as failing by throwing an AssertionException.
*
* @param {object} aResult
* Test result details used for reporting.
* <dl>
* <dd>fileName</dd>
* <dt>Name of the file in which the assertion failed.</dt>
* <dd>functionName</dd>
* <dt>Function in which the assertion failed.</dt>
* <dd>lineNumber</dd>
* <dt>Line number of the file in which the assertion failed.</dt>
* <dd>message</dd>
* <dt>Message why the assertion failed.</dt>
* </dl>
* @throws {errors.AssertionError}
*
*/
_logFail: function Assert__logFail(aResult) {
throw new errors.AssertionError(aResult.message,
aResult.fileName,
aResult.lineNumber,
aResult.functionName,
aResult.name);
},
/**
* Log a test as passing by adding a pass frame.
*
* @param {object} aResult
* Test result details used for reporting.
* <dl>
* <dd>fileName</dd>
* <dt>Name of the file in which the assertion failed.</dt>
* <dd>functionName</dd>
* <dt>Function in which the assertion failed.</dt>
* <dd>lineNumber</dd>
* <dt>Line number of the file in which the assertion failed.</dt>
* <dd>message</dd>
* <dt>Message why the assertion failed.</dt>
* </dl>
*/
_logPass: function Assert__logPass(aResult) {
broker.pass({pass: aResult});
},
/**
* Test the condition and mark test as passed or failed
*
* @param {boolean} aCondition
* Condition to test.
* @param {string} aMessage
* Message to show for the test result
* @param {string} aDiagnosis
* Diagnose message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
_test: function Assert__test(aCondition, aMessage, aDiagnosis) {
let diagnosis = aDiagnosis || "";
let message = aMessage || "";
if (diagnosis)
message = aMessage ? message + " - " + diagnosis : diagnosis;
// Build result data
let frame = stack.findCallerFrame(Components.stack);
let result = {
'fileName' : frame.filename.replace(/(.*)-> /, ""),
'functionName' : frame.name,
'lineNumber' : frame.lineNumber,
'message' : message
};
// Log test result
if (aCondition) {
this._logPass(result);
}
else {
result.stack = Components.stack;
this._logFail(result);
}
return aCondition;
},
/**
* Perform an always passing test
*
* @param {string} aMessage
* Message to show for the test result.
* @returns {boolean} Always returns true.
*/
pass: function Assert_pass(aMessage) {
return this._test(true, aMessage, undefined);
},
/**
* Perform an always failing test
*
* @param {string} aMessage
* Message to show for the test result.
* @throws {errors.AssertionError}
*
* @returns {boolean} Always returns false.
*/
fail: function Assert_fail(aMessage) {
return this._test(false, aMessage, undefined);
},
/**
* Test if the value pass
*
* @param {boolean|string|number|object} aValue
* Value to test.
* @param {string} aMessage
* Message to show for the test result.
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
ok: function Assert_ok(aValue, aMessage) {
let condition = !!aValue;
let diagnosis = "got '" + aValue + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if both specified values are identical.
*
* @param {boolean|string|number|object} aValue
* Value to test.
* @param {boolean|string|number|object} aExpected
* Value to strictly compare with.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
equal: function Assert_equal(aValue, aExpected, aMessage) {
let condition = (aValue === aExpected);
let diagnosis = "'" + aValue + "' should equal '" + aExpected + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if both specified values are not identical.
*
* @param {boolean|string|number|object} aValue
* Value to test.
* @param {boolean|string|number|object} aExpected
* Value to strictly compare with.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
notEqual: function Assert_notEqual(aValue, aExpected, aMessage) {
let condition = (aValue !== aExpected);
let diagnosis = "'" + aValue + "' should not equal '" + aExpected + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if an object equals another object
*
* @param {object} aValue
* The object to test.
* @param {object} aExpected
* The object to strictly compare with.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
deepEqual: function equal(aValue, aExpected, aMessage) {
let condition = this._deepEqual(aValue, aExpected);
try {
var aValueString = JSON.stringify(aValue);
} catch (e) {
var aValueString = String(aValue);
}
try {
var aExpectedString = JSON.stringify(aExpected);
} catch (e) {
var aExpectedString = String(aExpected);
}
let diagnosis = "'" + aValueString + "' should equal '" +
aExpectedString + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if an object does not equal another object
*
* @param {object} aValue
* The object to test.
* @param {object} aExpected
* The object to strictly compare with.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
notDeepEqual: function notEqual(aValue, aExpected, aMessage) {
let condition = !this._deepEqual(aValue, aExpected);
try {
var aValueString = JSON.stringify(aValue);
} catch (e) {
var aValueString = String(aValue);
}
try {
var aExpectedString = JSON.stringify(aExpected);
} catch (e) {
var aExpectedString = String(aExpected);
}
let diagnosis = "'" + aValueString + "' should not equal '" +
aExpectedString + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if the regular expression matches the string.
*
* @param {string} aString
* String to test.
* @param {RegEx} aRegex
* Regular expression to use for testing that a match exists.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
match: function Assert_match(aString, aRegex, aMessage) {
// XXX Bug 634948
// Regex objects are transformed to strings when evaluated in a sandbox
// For now lets re-create the regex from its string representation
let pattern = "";
let flags = "";
try {
let matches = aRegex.toString().match(/\/(.*)\/(.*)/);
pattern = matches[1];
flags = matches[2];
} catch (e) {
}
let regex = new RegExp(pattern, flags);
let condition = (aString.match(regex) !== null);
let diagnosis = "'" + regex + "' matches for '" + aString + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if the regular expression does not match the string.
*
* @param {string} aString
* String to test.
* @param {RegEx} aRegex
* Regular expression to use for testing that a match does not exist.
* @param {string} aMessage
* Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
notMatch: function Assert_notMatch(aString, aRegex, aMessage) {
// XXX Bug 634948
// Regex objects are transformed to strings when evaluated in a sandbox
// For now lets re-create the regex from its string representation
let pattern = flags = "";
try {
let matches = aRegex.toString().match(/\/(.*)\/(.*)/);
pattern = matches[1];
flags = matches[2];
} catch (e) {
}
let regex = new RegExp(pattern, flags);
let condition = (aString.match(regex) === null);
let diagnosis = "'" + regex + "' doesn't match for '" + aString + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if a code block throws an exception.
*
* @param {string} block
* function to call to test for exception
* @param {RegEx} error
* the expected error class
* @param {string} message
* message to present if assertion fails
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
throws : function Assert_throws(block, /*optional*/error, /*optional*/message) {
return this._throws.apply(this, [true].concat(Array.prototype.slice.call(arguments)));
},
/**
* Test if a code block doesn't throw an exception.
*
* @param {string} block
* function to call to test for exception
* @param {RegEx} error
* the expected error class
* @param {string} message
* message to present if assertion fails
* @throws {errors.AssertionError}
*
* @returns {boolean} Result of the test.
*/
doesNotThrow : function Assert_doesNotThrow(block, /*optional*/error, /*optional*/message) {
return this._throws.apply(this, [false].concat(Array.prototype.slice.call(arguments)));
},
/* Tests whether a code block throws the expected exception
class. helper for throws() and doesNotThrow()
adapted from node.js's assert._throws()
https://github.com/joyent/node/blob/master/lib/assert.js
*/
_throws : function Assert__throws(shouldThrow, block, expected, message) {
var actual;
if (typeof expected === 'string') {
message = expected;
expected = null;
}
try {
block();
} catch (e) {
actual = e;
}
message = (expected && expected.name ? ' (' + expected.name + ').' : '.') +
(message ? ' ' + message : '.');
if (shouldThrow && !actual) {
return this._test(false, message, 'Missing expected exception');
}
if (!shouldThrow && this._expectedException(actual, expected)) {
return this._test(false, message, 'Got unwanted exception');
}
if ((shouldThrow && actual && expected &&
!this._expectedException(actual, expected)) || (!shouldThrow && actual)) {
throw actual;
}
return this._test(true, message);
},
/**
* Test if the string contains the pattern.
*
* @param {String} aString String to test.
* @param {String} aPattern Pattern to look for in the string
* @param {String} aMessage Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {Boolean} Result of the test.
*/
contain: function Assert_contain(aString, aPattern, aMessage) {
let condition = (aString.indexOf(aPattern) !== -1);
let diagnosis = "'" + aString + "' should contain '" + aPattern + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Test if the string does not contain the pattern.
*
* @param {String} aString String to test.
* @param {String} aPattern Pattern to look for in the string
* @param {String} aMessage Message to show for the test result
* @throws {errors.AssertionError}
*
* @returns {Boolean} Result of the test.
*/
notContain: function Assert_notContain(aString, aPattern, aMessage) {
let condition = (aString.indexOf(aPattern) === -1);
let diagnosis = "'" + aString + "' should not contain '" + aPattern + "'";
return this._test(condition, aMessage, diagnosis);
},
/**
* Waits for the callback evaluates to true
*
* @param {Function} aCallback
* Callback for evaluation
* @param {String} aMessage
* Message to show for result
* @param {Number} aTimeout
* Timeout in waiting for evaluation
* @param {Number} aInterval
* Interval between evaluation attempts
* @param {Object} aThisObject
* this object
* @throws {errors.AssertionError}
*
* @returns {Boolean} Result of the test.
*/
waitFor: function Assert_waitFor(aCallback, aMessage, aTimeout, aInterval, aThisObject) {
var timeout = aTimeout || 5000;
var interval = aInterval || 100;
var self = {
timeIsUp: false,
result: aCallback.call(aThisObject)
};
var deadline = Date.now() + timeout;
function wait() {
if (self.result !== true) {
self.result = aCallback.call(aThisObject);
self.timeIsUp = Date.now() > deadline;
}
}
var hwindow = Services.appShell.hiddenDOMWindow;
var timeoutInterval = hwindow.setInterval(wait, interval);
var thread = Services.tm.currentThread;
Services.tm.spinEventLoopUntil(() => {
let type = typeof(self.result);
if (type !== 'boolean') {
throw TypeError("waitFor() callback has to return a boolean" +
" instead of '" + type + "'");
}
return self.result === true || self.timeIsUp;
});
hwindow.clearInterval(timeoutInterval);
if (self.result !== true && self.timeIsUp) {
aMessage = aMessage || arguments.callee.name + ": Timeout exceeded for '" + aCallback + "'";
throw new errors.TimeoutError(aMessage);
}
broker.pass({'function':'assert.waitFor()'});
return true;
}
}
/* non-fatal assertions */
var Expect = function () {}
Expect.prototype = new Assert();
/**
* Log a test as failing by adding a fail frame.
*
* @param {object} aResult
* Test result details used for reporting.
* <dl>
* <dd>fileName</dd>
* <dt>Name of the file in which the assertion failed.</dt>
* <dd>functionName</dd>
* <dt>Function in which the assertion failed.</dt>
* <dd>lineNumber</dd>
* <dt>Line number of the file in which the assertion failed.</dt>
* <dd>message</dd>
* <dt>Message why the assertion failed.</dt>
* </dl>
*/
Expect.prototype._logFail = function Expect__logFail(aResult) {
broker.fail({fail: aResult});
}
/**
* Waits for the callback evaluates to true
*
* @param {Function} aCallback
* Callback for evaluation
* @param {String} aMessage
* Message to show for result
* @param {Number} aTimeout
* Timeout in waiting for evaluation
* @param {Number} aInterval
* Interval between evaluation attempts
* @param {Object} aThisObject
* this object
*/
Expect.prototype.waitFor = function Expect_waitFor(aCallback, aMessage, aTimeout, aInterval, aThisObject) {
let condition = true;
let message = aMessage;
try {
Assert.prototype.waitFor.apply(this, arguments);
}
catch (ex) {
if (!(ex instanceof errors.AssertionError)) {
throw ex;
}
message = ex.message;
condition = false;
}
return this._test(condition, message);
}

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

@ -1,290 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* @namespace Defines the Mozmill driver for global actions
*/
var driver = exports;
Cu.import("resource://gre/modules/Services.jsm");
// Temporarily include utils module to re-use sleep
var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
var mozmill = {}; Cu.import("resource://mozmill/driver/mozmill.js", mozmill);
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
/**
* Gets the topmost browser window. If there are none at that time, optionally
* opens one. Otherwise will raise an exception if none are found.
*
* @memberOf driver
* @param {Boolean] [aOpenIfNone=true] Open a new browser window if none are found.
* @returns {DOMWindow}
*/
function getBrowserWindow(aOpenIfNone) {
// Set default
if (typeof aOpenIfNone === 'undefined') {
aOpenIfNone = true;
}
// If implicit open is off, turn on strict checking, and vice versa.
let win = getTopmostWindowByType("navigator:browser", !aOpenIfNone);
// Can just assume automatic open here. If we didn't want it and nothing found,
// we already raised above when getTopmostWindow was called.
if (!win)
win = openBrowserWindow();
return win;
}
/**
* Retrieves the hidden window on OS X
*
* @memberOf driver
* @returns {DOMWindow} The hidden window
*/
function getHiddenWindow() {
return Services.appShell.hiddenDOMWindow;
}
/**
* Opens a new browser window
*
* @memberOf driver
* @returns {DOMWindow}
*/
function openBrowserWindow() {
// On OS X we have to be able to create a new browser window even with no other
// window open. Therefore we have to use the hidden window. On other platforms
// at least one remaining browser window has to exist.
var win = mozmill.isMac ? getHiddenWindow() :
getTopmostWindowByType("navigator:browser", true);
return win.OpenBrowserWindow();
}
/**
* Pause the test execution for the given amount of time
*
* @type utils.sleep
* @memberOf driver
*/
var sleep = utils.sleep;
/**
* Wait until the given condition via the callback returns true.
*
* @type utils.waitFor
* @memberOf driver
*/
var waitFor = assertions.Assert.waitFor;
//
// INTERNAL WINDOW ENUMERATIONS
//
/**
* Internal function to build a list of DOM windows using a given enumerator
* and filter.
*
* @private
* @memberOf driver
* @param {nsISimpleEnumerator} aEnumerator Window enumerator to use.
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] Throw an error if no windows found
*
* @returns {DOMWindow[]} The windows found, in the same order as the enumerator.
*/
function _getWindows(aEnumerator, aFilterCallback, aStrict) {
// Set default
if (typeof aStrict === 'undefined')
aStrict = true;
let windows = [];
while (aEnumerator.hasMoreElements()) {
let window = aEnumerator.getNext();
if (!aFilterCallback || aFilterCallback(window)) {
windows.push(window);
}
}
// If this list is empty and we're strict, throw an error
if (windows.length === 0 && aStrict) {
var message = 'No windows were found';
// We'll throw a more detailed error if a filter was used.
if (aFilterCallback && aFilterCallback.name)
message += ' using filter "' + aFilterCallback.name + '"';
throw new Error(message);
}
return windows;
}
//
// FILTER CALLBACKS
//
/**
* Generator of a closure to filter a window based by a method
*
* @memberOf driver
* @param {String} aName Name of the method in the window object.
* @returns {Boolean} True if the condition is met.
*/
function windowFilterByMethod(aName) {
return function byMethod(aWindow) { return (aName in aWindow); }
}
/**
* Generator of a closure to filter a window based by the its title
*
* @param {String} aTitle Title of the window.
* @returns {Boolean} True if the condition is met.
*/
function windowFilterByTitle(aTitle) {
return function byTitle(aWindow) { return (aWindow.document.title === aTitle); }
}
/**
* Generator of a closure to filter a window based by the its type
*
* @memberOf driver
* @param {String} aType Type of the window.
* @returns {Boolean} True if the condition is met.
*/
function windowFilterByType(aType) {
return function byType(aWindow) {
var type = aWindow.document.documentElement.getAttribute("windowtype");
return (type === aType);
}
}
//
// WINDOW LIST RETRIEVAL FUNCTIONS
//
/**
* Retrieves a sorted list of open windows based on their age (newest to oldest),
* optionally matching filter criteria.
*
* @memberOf driver
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] Throw an error if no windows found
*
* @returns {DOMWindow[]} List of windows.
*/
function getWindowsByAge(aFilterCallback, aStrict) {
var windows = _getWindows(Services.wm.getEnumerator(""),
aFilterCallback, aStrict);
// Reverse the list, since naturally comes back old->new
return windows.reverse();
}
/**
* Retrieves a sorted list of open windows based on their z order (topmost first),
* optionally matching filter criteria.
*
* @memberOf driver
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] Throw an error if no windows found
*
* @returns {DOMWindow[]} List of windows.
*/
function getWindowsByZOrder(aFilterCallback, aStrict) {
return _getWindows(Services.wm.getZOrderDOMWindowEnumerator("", true),
aFilterCallback, aStrict);
}
//
// SINGLE WINDOW RETRIEVAL FUNCTIONS
//
/**
* Retrieves the last opened window, optionally matching filter criteria.
*
* @memberOf driver
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] If true, throws error if no window found.
*
* @returns {DOMWindow} The window, or null if none found and aStrict == false
*/
function getNewestWindow(aFilterCallback, aStrict) {
var windows = getWindowsByAge(aFilterCallback, aStrict);
return windows.length ? windows[0] : null;
}
/**
* Retrieves the topmost window, optionally matching filter criteria.
*
* @memberOf driver
* @param {Function} [aFilterCallback] Function which is used to filter windows.
* @param {Boolean} [aStrict=true] If true, throws error if no window found.
*
* @returns {DOMWindow} The window, or null if none found and aStrict == false
*/
function getTopmostWindow(aFilterCallback, aStrict) {
var windows = getWindowsByZOrder(aFilterCallback, aStrict);
return windows.length ? windows[0] : null;
}
/**
* Retrieves the topmost window given by the window type
*
* XXX: Bug 462222
* This function has to be used instead of getTopmostWindow until the
* underlying platform bug has been fixed.
*
* @memberOf driver
* @param {String} [aWindowType=null] Window type to query for
* @param {Boolean} [aStrict=true] Throw an error if no windows found
*
* @returns {DOMWindow} The window, or null if none found and aStrict == false
*/
function getTopmostWindowByType(aWindowType, aStrict) {
if (typeof aStrict === 'undefined')
aStrict = true;
var win = Services.wm.getMostRecentWindow(aWindowType);
if (win === null && aStrict) {
var message = 'No windows of type "' + aWindowType + '" were found';
throw new errors.UnexpectedError(message);
}
return win;
}
// Export of functions
driver.getBrowserWindow = getBrowserWindow;
driver.getHiddenWindow = getHiddenWindow;
driver.openBrowserWindow = openBrowserWindow;
driver.sleep = sleep;
driver.waitFor = waitFor;
driver.windowFilterByMethod = windowFilterByMethod;
driver.windowFilterByTitle = windowFilterByTitle;
driver.windowFilterByType = windowFilterByType;
driver.getWindowsByAge = getWindowsByAge;
driver.getNewestWindow = getNewestWindow;
driver.getTopmostWindowByType = getTopmostWindowByType;
// XXX Bug: 462222
// Currently those functions cannot be used. So they shouldn't be exported.
//driver.getWindowsByZOrder = getWindowsByZOrder;
//driver.getTopmostWindow = getTopmostWindow;

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

@ -1,102 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['BaseError',
'ApplicationQuitError',
'AssertionError',
'TimeoutError'];
/**
* Creates a new instance of a base error
*
* @class Represents the base for custom errors
* @param {string} [aMessage=Error().message]
* The error message to show
* @param {string} [aFileName=Error().fileName]
* The file name where the error has been raised
* @param {string} [aLineNumber=Error().lineNumber]
* The line number of the file where the error has been raised
* @param {string} [aFunctionName=undefined]
* The function name in which the error has been raised
*/
function BaseError(aMessage, aFileName, aLineNumber, aFunctionName) {
this.name = this.constructor.name;
var err = new Error();
if (err.stack) {
this.stack = err.stack;
}
this.message = aMessage || err.message;
this.fileName = aFileName || err.fileName;
this.lineNumber = aLineNumber || err.lineNumber;
this.functionName = aFunctionName;
}
/**
* Creates a new instance of an application quit error used by Mozmill to
* indicate that the application is going to shutdown
*
* @class Represents an error object thrown when the application is going to shutdown
* @param {string} [aMessage=Error().message]
* The error message to show
* @param {string} [aFileName=Error().fileName]
* The file name where the error has been raised
* @param {string} [aLineNumber=Error().lineNumber]
* The line number of the file where the error has been raised
* @param {string} [aFunctionName=undefined]
* The function name in which the error has been raised
*/
function ApplicationQuitError(aMessage, aFileName, aLineNumber, aFunctionName) {
BaseError.apply(this, arguments);
}
ApplicationQuitError.prototype = Object.create(BaseError.prototype, {
constructor : { value : ApplicationQuitError }
});
/**
* Creates a new instance of an assertion error
*
* @class Represents an error object thrown by failing assertions
* @param {string} [aMessage=Error().message]
* The error message to show
* @param {string} [aFileName=Error().fileName]
* The file name where the error has been raised
* @param {string} [aLineNumber=Error().lineNumber]
* The line number of the file where the error has been raised
* @param {string} [aFunctionName=undefined]
* The function name in which the error has been raised
*/
function AssertionError(aMessage, aFileName, aLineNumber, aFunctionName) {
BaseError.apply(this, arguments);
}
AssertionError.prototype = Object.create(BaseError.prototype, {
constructor : { value : AssertionError }
});
/**
* Creates a new instance of a timeout error
*
* @class Represents an error object thrown by failing assertions
* @param {string} [aMessage=Error().message]
* The error message to show
* @param {string} [aFileName=Error().fileName]
* The file name where the error has been raised
* @param {string} [aLineNumber=Error().lineNumber]
* The line number of the file where the error has been raised
* @param {string} [aFunctionName=undefined]
* The function name in which the error has been raised
*/
function TimeoutError(aMessage, aFileName, aLineNumber, aFunctionName) {
AssertionError.apply(this, arguments);
}
TimeoutError.prototype = Object.create(AssertionError.prototype, {
constructor : { value : TimeoutError }
});

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

@ -1,787 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['Collector','Runner','events', 'runTestFile', 'log',
'timers', 'persisted', 'shutdownApplication'];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
const TIMEOUT_SHUTDOWN_HTTPD = 15000;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import('resource://mozmill/stdlib/httpd.js');
var broker = {}; Cu.import('resource://mozmill/driver/msgbroker.js', broker);
var assertions = {}; Cu.import('resource://mozmill/modules/assertions.js', assertions);
var errors = {}; Cu.import('resource://mozmill/modules/errors.js', errors);
var os = {}; Cu.import('resource://mozmill/stdlib/os.js', os);
var strings = {}; Cu.import('resource://mozmill/stdlib/strings.js', strings);
var arrays = {}; Cu.import('resource://mozmill/stdlib/arrays.js', arrays);
var withs = {}; Cu.import('resource://mozmill/stdlib/withs.js', withs);
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
var securableModule = {};
Cu.import('resource://mozmill/stdlib/securable-module.js', securableModule);
var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
var httpd = null;
var persisted = {};
var assert = new assertions.Assert();
var expect = new assertions.Expect();
var mozmill = undefined;
var mozelement = undefined;
var modules = undefined;
var timers = [];
/**
* Shutdown or restart the application
*
* @param {boolean} [aFlags=undefined]
* Additional flags how to handle the shutdown or restart.
* @see https://developer.mozilla.org/nsIAppStartup#Attributes
*/
function shutdownApplication(aFlags) {
var flags = Ci.nsIAppStartup.eForceQuit;
if (aFlags) {
flags |= aFlags;
}
// Send a request to shutdown the application. That will allow us and other
// components to finish up with any shutdown code. Please note that we don't
// care if other components or add-ons want to prevent this via cancelQuit,
// we really force the shutdown.
let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
createInstance(Components.interfaces.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, "quit-application-requested");
// Use a timer to trigger the application restart, which will allow us to
// send an ACK packet via jsbridge if the method has been called via Python.
var event = {
notify: function(timer) {
Services.startup.quit(flags);
}
}
var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback(event, 100, Ci.nsITimer.TYPE_ONE_SHOT);
}
function stateChangeBase(possibilties, restrictions, target, cmeta, v) {
if (possibilties) {
if (!arrays.inArray(possibilties, v)) {
// TODO Error value not in this.poss
return;
}
}
if (restrictions) {
for (var i in restrictions) {
var r = restrictions[i];
if (!r(v)) {
// TODO error value did not pass restriction
return;
}
}
}
// Fire jsbridge notification, logging notification, listener notifications
events[target] = v;
events.fireEvent(cmeta, target);
}
var events = {
appQuit : false,
currentModule : null,
currentState : null,
currentTest : null,
shutdownRequested : false,
userShutdown : null,
userShutdownTimer : null,
listeners : {},
globalListeners : []
}
events.setState = function (v) {
return stateChangeBase(['dependencies', 'setupModule', 'teardownModule',
'test', 'setupTest', 'teardownTest', 'collection'],
null, 'currentState', 'setState', v);
}
events.toggleUserShutdown = function (obj){
if (!this.userShutdown) {
this.userShutdown = obj;
var event = {
notify: function(timer) {
events.toggleUserShutdown(obj);
}
}
this.userShutdownTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.userShutdownTimer.initWithCallback(event, obj.timeout, Ci.nsITimer.TYPE_ONE_SHOT);
} else {
this.userShutdownTimer.cancel();
// If the application is not going to shutdown, the user shutdown failed and
// we have to force a shutdown.
if (!events.appQuit) {
this.fail({'function':'events.toggleUserShutdown',
'message':'Shutdown expected but none detected before timeout',
'userShutdown': obj});
var flags = Ci.nsIAppStartup.eAttemptQuit;
if (events.isRestartShutdown()) {
flags |= Ci.nsIAppStartup.eRestart;
}
shutdownApplication(flags);
}
}
}
events.isUserShutdown = function () {
return this.userShutdown ? this.userShutdown["user"] : false;
}
events.isRestartShutdown = function () {
return this.userShutdown.restart;
}
events.startShutdown = function (obj) {
events.fireEvent('shutdown', obj);
if (obj["user"]) {
events.toggleUserShutdown(obj);
} else {
shutdownApplication(obj.flags);
}
}
events.setTest = function (test) {
test.__start__ = Date.now();
test.__passes__ = [];
test.__fails__ = [];
events.currentTest = test;
var obj = {'filename': events.currentModule.__file__,
'name': test.__name__}
events.fireEvent('setTest', obj);
}
events.endTest = function (test) {
// use the current test unless specified
if (test === undefined) {
test = events.currentTest;
}
// If no test is set it has already been reported. Beside that we don't want
// to report it a second time.
if (!test || test.status === 'done')
return;
// report the end of a test
test.__end__ = Date.now();
test.status = 'done';
var obj = {'filename': events.currentModule.__file__,
'passed': test.__passes__.length,
'failed': test.__fails__.length,
'passes': test.__passes__,
'fails' : test.__fails__,
'name' : test.__name__,
'time_start': test.__start__,
'time_end': test.__end__}
if (test.skipped) {
obj['skipped'] = true;
obj.skipped_reason = test.skipped_reason;
}
if (test.meta) {
obj.meta = test.meta;
}
// Report the test result only if the test is a true test or if it is failing
if (withs.startsWith(test.__name__, "test") || test.__fails__.length > 0) {
events.fireEvent('endTest', obj);
}
}
events.setModule = function (aModule) {
aModule.__start__ = Date.now();
aModule.__status__ = 'running';
var result = stateChangeBase(null,
[function (aModule) {return (aModule.__file__ != undefined)}],
'currentModule', 'setModule', aModule);
return result;
}
events.endModule = function (aModule) {
// It should only reported once, so check if it already has been done
if (aModule.__status__ === 'done')
return;
aModule.__end__ = Date.now();
aModule.__status__ = 'done';
var obj = {
'filename': aModule.__file__,
'time_start': aModule.__start__,
'time_end': aModule.__end__
}
events.fireEvent('endModule', obj);
}
events.pass = function (obj) {
// a low level event, such as a keystroke, succeeds
if (events.currentTest) {
events.currentTest.__passes__.push(obj);
}
for (var timer of timers) {
timer.actions.push(
{"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__,
"obj": obj,
"result": "pass"}
);
}
events.fireEvent('pass', obj);
}
events.fail = function (obj) {
var error = obj.exception;
if (error) {
// Error objects aren't enumerable https://bugzilla.mozilla.org/show_bug.cgi?id=637207
obj.exception = {
name: error.name,
message: error.message,
lineNumber: error.lineNumber,
fileName: error.fileName,
stack: error.stack
};
}
// a low level event, such as a keystroke, fails
if (events.currentTest) {
events.currentTest.__fails__.push(obj);
}
for (var timer of timers) {
timer.actions.push(
{"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__,
"obj": obj,
"result": "fail"}
);
}
events.fireEvent('fail', obj);
}
events.skip = function (reason) {
// this is used to report skips associated with setupModule and nothing else
events.currentTest.skipped = true;
events.currentTest.skipped_reason = reason;
for (var timer of timers) {
timer.actions.push(
{"currentTest": events.currentModule.__file__ + "::" + events.currentTest.__name__,
"obj": reason,
"result": "skip"}
);
}
events.fireEvent('skip', reason);
}
events.fireEvent = function (name, obj) {
if (events.appQuit) {
// dump('* Event discarded: ' + name + ' ' + JSON.stringify(obj) + '\n');
return;
}
if (this.listeners[name]) {
for (var i in this.listeners[name]) {
this.listeners[name][i](obj);
}
}
for (var listener of this.globalListeners) {
listener(name, obj);
}
}
events.addListener = function (name, listener) {
if (this.listeners[name]) {
this.listeners[name].push(listener);
} else if (name == '') {
this.globalListeners.push(listener)
} else {
this.listeners[name] = [listener];
}
}
events.removeListener = function (listener) {
for (var listenerIndex in this.listeners) {
var e = this.listeners[listenerIndex];
for (var i in e){
if (e[i] == listener) {
this.listeners[listenerIndex] = arrays.remove(e, i);
}
}
}
for (var i in this.globalListeners) {
if (this.globalListeners[i] == listener) {
this.globalListeners = arrays.remove(this.globalListeners, i);
}
}
}
events.persist = function () {
try {
events.fireEvent('persist', persisted);
} catch (e) {
events.fireEvent('error', "persist serialization failed.")
}
}
events.firePythonCallback = function (obj) {
obj['test'] = events.currentModule.__file__;
events.fireEvent('firePythonCallback', obj);
}
events.screenshot = function (obj) {
// Find the name of the test function
for (var attr in events.currentModule) {
if (events.currentModule[attr] == events.currentTest) {
var testName = attr;
break;
}
}
obj['test_file'] = events.currentModule.__file__;
obj['test_name'] = testName;
events.fireEvent('screenshot', obj);
}
var log = function (obj) {
events.fireEvent('log', obj);
}
// Register the listeners
broker.addObject({'endTest': events.endTest,
'fail': events.fail,
'firePythonCallback': events.firePythonCallback,
'log': log,
'pass': events.pass,
'persist': events.persist,
'screenshot': events.screenshot,
'shutdown': events.startShutdown,
});
try {
Cu.import('resource://jsbridge/modules/Events.jsm');
events.addListener('', function (name, obj) {
Events.fireEvent('mozmill.' + name, obj);
});
} catch (e) {
Services.console.logStringMessage("Event module of JSBridge not available.");
}
/**
* Observer for notifications when the application is going to shutdown
*/
function AppQuitObserver() {
this.runner = null;
Services.obs.addObserver(this, "quit-application-requested");
}
AppQuitObserver.prototype = {
observe: function (aSubject, aTopic, aData) {
switch (aTopic) {
case "quit-application-requested":
Services.obs.removeObserver(this, "quit-application-requested");
// If we observe a quit notification make sure to send the
// results of the current test. In those cases we don't reach
// the equivalent code in runTestModule()
events.pass({'message': 'AppQuitObserver: ' + JSON.stringify(aData),
'userShutdown': events.userShutdown});
if (this.runner) {
this.runner.end();
}
if (httpd) {
httpd.stop();
}
events.appQuit = true;
break;
}
}
}
var appQuitObserver = new AppQuitObserver();
/**
* The collector handles HTTPd.js and initilizing the module
*/
function Collector() {
this.test_modules_by_filename = {};
this.testing = [];
}
Collector.prototype.addHttpResource = function (aDirectory, aPath) {
var fp = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
fp.initWithPath(os.abspath(aDirectory, this.current_file));
return httpd.addHttpResource(fp, aPath);
}
Collector.prototype.initTestModule = function (filename, testname) {
var test_module = this.loadFile(filename, this);
var has_restarted = !(testname == null);
test_module.__tests__ = [];
for (var i in test_module) {
if (typeof(test_module[i]) == "function") {
test_module[i].__name__ = i;
// Only run setupModule if we are a single test OR if we are the first
// test of a restart chain (don't run it prior to members in a restart
// chain)
if (i == "setupModule" && !has_restarted) {
test_module.__setupModule__ = test_module[i];
} else if (i == "setupTest") {
test_module.__setupTest__ = test_module[i];
} else if (i == "teardownTest") {
test_module.__teardownTest__ = test_module[i];
} else if (i == "teardownModule") {
test_module.__teardownModule__ = test_module[i];
} else if (withs.startsWith(i, "test")) {
if (testname && (i != testname)) {
continue;
}
testname = null;
test_module.__tests__.push(test_module[i]);
}
}
}
test_module.collector = this;
test_module.status = 'loaded';
this.test_modules_by_filename[filename] = test_module;
return test_module;
}
Collector.prototype.loadFile = function (path, collector) {
var moduleLoader = new securableModule.Loader({
rootPaths: ["resource://mozmill/modules/"],
defaultPrincipal: "system",
globals : { Cc: Cc,
Ci: Ci,
Cu: Cu,
Cr: Components.results}
});
// load a test module from a file and add some candy
var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(path);
var uri = Services.io.newFileURI(file).spec;
this.loadTestResources();
var systemPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
var module = new Components.utils.Sandbox(systemPrincipal);
module.assert = assert;
module.Cc = Cc;
module.Ci = Ci;
module.Cr = Components.results;
module.Cu = Cu;
module.collector = collector;
module.driver = moduleLoader.require("driver");
module.elementslib = mozelement;
module.errors = errors;
module.expect = expect;
module.findElement = mozelement;
module.log = log;
module.mozmill = mozmill;
module.persisted = persisted;
module.require = function (mod) {
var loader = new securableModule.Loader({
rootPaths: [Services.io.newFileURI(file.parent).spec,
"resource://mozmill/modules/"],
defaultPrincipal: "system",
globals : { assert: assert,
expect: expect,
mozmill: mozmill,
elementslib: mozelement, // This a quick hack to maintain backwards compatibility with 1.5.x
findElement: mozelement,
persisted: persisted,
Cc: Cc,
Ci: Ci,
Cu: Cu,
log: log }
});
if (modules != undefined) {
loader.modules = modules;
}
var retval = loader.require(mod);
modules = loader.modules;
return retval;
}
if (collector != undefined) {
collector.current_file = file;
collector.current_path = path;
}
try {
Services.scriptloader.loadSubScript(uri, module, "UTF-8");
} catch (e) {
var obj = {
'filename': path,
'passed': 0,
'failed': 1,
'passes': [],
'fails' : [{'exception' : {
message: e.message,
filename: e.filename,
lineNumber: e.lineNumber}}],
'name' :'<TOP_LEVEL>'
};
events.fail({'exception': e});
events.fireEvent('endTest', obj);
}
module.__file__ = path;
module.__uri__ = uri;
return module;
}
Collector.prototype.loadTestResources = function () {
// load resources we want in our tests
if (mozmill === undefined) {
mozmill = {};
Cu.import("resource://mozmill/driver/mozmill.js", mozmill);
}
if (mozelement === undefined) {
mozelement = {};
Cu.import("resource://mozmill/driver/mozelement.js", mozelement);
}
}
/**
*
*/
function Httpd(aPort) {
this.http_port = aPort;
while (true) {
try {
var srv = new HttpServer();
srv.registerContentType("sjs", "sjs");
srv.identity.setPrimary("http", "localhost", this.http_port);
srv.start(this.http_port);
this._httpd = srv;
break;
}
catch (e) {
// Failure most likely due to port conflict
this.http_port++;
}
}
}
Httpd.prototype.addHttpResource = function (aDir, aPath) {
var path = aPath ? ("/" + aPath + "/") : "/";
try {
this._httpd.registerDirectory(path, aDir);
return 'http://localhost:' + this.http_port + path;
}
catch (e) {
throw Error("Failure to register directory: " + aDir.path);
}
};
Httpd.prototype.stop = function () {
if (!this._httpd) {
return;
}
var shutdown = false;
this._httpd.stop(function () { shutdown = true; });
assert.waitFor(function () {
return shutdown;
}, "Local HTTP server has been stopped", TIMEOUT_SHUTDOWN_HTTPD);
this._httpd = null;
};
function startHTTPd() {
if (!httpd) {
// Ensure that we start the HTTP server only once during a session
httpd = new Httpd(43336);
}
}
function Runner() {
this.collector = new Collector();
this.ended = false;
var m = {}; Cu.import('resource://mozmill/driver/mozmill.js', m);
this.platform = m.platform;
events.fireEvent('startRunner', true);
}
Runner.prototype.end = function () {
if (!this.ended) {
this.ended = true;
appQuitObserver.runner = null;
events.endTest();
events.endModule(events.currentModule);
events.fireEvent('endRunner', true);
events.persist();
}
};
Runner.prototype.runTestFile = function (filename, name) {
var module = this.collector.initTestModule(filename, name);
this.runTestModule(module);
};
Runner.prototype.runTestModule = function (module) {
appQuitObserver.runner = this;
events.setModule(module);
// If setupModule passes, run all the tests. Otherwise mark them as skipped.
if (this.execFunction(module.__setupModule__, module)) {
for (var test of module.__tests__) {
if (events.shutdownRequested) {
break;
}
// If setupTest passes, run the test. Otherwise mark it as skipped.
if (this.execFunction(module.__setupTest__, module)) {
this.execFunction(test);
} else {
this.skipFunction(test, module.__setupTest__.__name__ + " failed");
}
this.execFunction(module.__teardownTest__, module);
}
} else {
for (var test of module.__tests__) {
this.skipFunction(test, module.__setupModule__.__name__ + " failed");
}
}
this.execFunction(module.__teardownModule__, module);
events.endModule(module);
};
Runner.prototype.execFunction = function (func, arg) {
if (typeof func !== "function" || events.shutdownRequested) {
return true;
}
var isTest = withs.startsWith(func.__name__, "test");
events.setState(isTest ? "test" : func.__name);
events.setTest(func);
// skip excluded platforms
if (func.EXCLUDED_PLATFORMS != undefined) {
if (arrays.inArray(func.EXCLUDED_PLATFORMS, this.platform)) {
events.skip("Platform exclusion");
events.endTest(func);
return false;
}
}
// skip function if requested
if (func.__force_skip__ != undefined) {
events.skip(func.__force_skip__);
events.endTest(func);
return false;
}
// execute the test function
try {
func(arg);
} catch (e) {
if (e instanceof errors.ApplicationQuitError) {
events.shutdownRequested = true;
} else {
events.fail({'exception': e, 'test': func})
}
}
// If a user shutdown has been requested and the function already returned,
// we can assume that a shutdown will not happen anymore. We should force a
// shutdown then, to prevent the next test from being executed.
if (events.isUserShutdown()) {
events.shutdownRequested = true;
events.toggleUserShutdown(events.userShutdown);
}
events.endTest(func);
return events.currentTest.__fails__.length == 0;
};
function runTestFile(filename, name) {
var runner = new Runner();
runner.runTestFile(filename, name);
runner.end();
return true;
}
Runner.prototype.skipFunction = function (func, message) {
events.setTest(func);
events.skip(message);
events.endTest(func);
};

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

@ -1,71 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* @namespace Defines useful methods to work with localized content
*/
var l10n = exports;
Cu.import("resource://gre/modules/Services.jsm");
/**
* Retrieve the localized content for a given DTD entity
*
* @memberOf l10n
* @param {String[]} aDTDs Array of URLs for DTD files.
* @param {String} aEntityId ID of the entity to get the localized content of.
*
* @returns {String} Localized content
*/
function getEntity(aDTDs, aEntityId) {
// Add xhtml11.dtd to prevent missing entity errors with XHTML files
aDTDs.push("resource:///res/dtd/xhtml11.dtd");
// Build a string of external entities
var references = "";
for (let i = 0; i < aDTDs.length; i++) {
var id = 'dtd' + i;
references += '<!ENTITY % ' + id + ' SYSTEM "' + aDTDs[i] + '">%' + id + ';';
}
var header = '<?xml version="1.0"?><!DOCTYPE elem [' + references + ']>';
var element = '<elem id="entity">&' + aEntityId + ';</elem>';
var content = header + element;
var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
createInstance(Ci.nsIDOMParser);
var doc = parser.parseFromString(content, 'text/xml');
var node = doc.querySelector('elem[id="entity"]');
if (!node) {
throw new Error("Unkown entity '" + aEntityId + "'");
}
return node.textContent;
}
/**
* Retrieve the localized content for a given property
*
* @memberOf l10n
* @param {String} aURL URL of the .properties file.
* @param {String} aProperty The property to get the value of.
*
* @returns {String} Value of the requested property
*/
function getProperty(aURL, aProperty) {
var bundle = Services.strings.createBundle(aURL);
try {
return bundle.GetStringFromName(aProperty);
} catch (ex) {
throw new Error("Unkown property '" + aProperty + "'");
}
}
// Export of functions
l10n.getEntity = getEntity;
l10n.getProperty = getProperty;

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

@ -1,43 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['findCallerFrame'];
/**
* @namespace Defines utility methods for handling stack frames
*/
/**
* Find the frame to use for logging the test result. If a start frame has
* been specified, we walk down the stack until a frame with the same filename
* as the start frame has been found. The next file in the stack will be the
* frame to use for logging the result.
*
* @memberOf stack
* @param {Object} [aStartFrame=Components.stack] Frame to start from walking up the stack.
* @returns {Object} Frame of the stack to use for logging the result.
*/
function findCallerFrame(aStartFrame) {
let frame = Components.stack;
let filename = frame.filename.replace(/(.*)-> /, "");
// If a start frame has been specified, walk up the stack until we have
// found the corresponding file
if (aStartFrame) {
filename = aStartFrame.filename.replace(/(.*)-> /, "");
while (frame.caller &&
frame.filename && (frame.filename.indexOf(filename) == -1)) {
frame = frame.caller;
}
}
// Walk even up more until the next file has been found
while (frame.caller &&
(!frame.filename || (frame.filename.indexOf(filename) != -1)))
frame = frame.caller;
return frame;
}

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

@ -1,292 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ["init", "map"];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
// imports
var utils = {}; Cu.import('resource://mozmill/stdlib/utils.js', utils);
var uuidgen = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
/**
* The window map is used to store information about the current state of
* open windows, e.g. loaded state
*/
var map = {
_windows : { },
/**
* Check if a given window id is contained in the map of windows
*
* @param {Number} aWindowId
* Outer ID of the window to check.
* @returns {Boolean} True if the window is part of the map, otherwise false.
*/
contains : function (aWindowId) {
return (aWindowId in this._windows);
},
/**
* Retrieve the value of the specified window's property.
*
* @param {Number} aWindowId
* Outer ID of the window to check.
* @param {String} aProperty
* Property to retrieve the value from
* @return {Object} Value of the window's property
*/
getValue : function (aWindowId, aProperty) {
if (!this.contains(aWindowId)) {
return undefined;
} else {
var win = this._windows[aWindowId];
return (aProperty in win) ? win[aProperty]
: undefined;
}
},
/**
* Remove the entry for a given window
*
* @param {Number} aWindowId
* Outer ID of the window to check.
*/
remove : function (aWindowId) {
if (this.contains(aWindowId)) {
delete this._windows[aWindowId];
}
// dump("* current map: " + JSON.stringify(this._windows) + "\n");
},
/**
* Update the property value of a given window
*
* @param {Number} aWindowId
* Outer ID of the window to check.
* @param {String} aProperty
* Property to update the value for
* @param {Object}
* Value to set
*/
update : function (aWindowId, aProperty, aValue) {
if (!this.contains(aWindowId)) {
this._windows[aWindowId] = { };
}
this._windows[aWindowId][aProperty] = aValue;
// dump("* current map: " + JSON.stringify(this._windows) + "\n");
},
/**
* Update the internal loaded state of the given content window. To identify
* an active (re)load action we make use of an uuid.
*
* @param {Window} aId - The outer id of the window to update
* @param {Boolean} aIsLoaded - Has the window been loaded
*/
updatePageLoadStatus : function (aId, aIsLoaded) {
this.update(aId, "loaded", aIsLoaded);
var uuid = this.getValue(aId, "id_load_in_transition");
// If no uuid has been set yet or when the page gets unloaded create a new id
if (!uuid || !aIsLoaded) {
uuid = uuidgen.generateUUID();
this.update(aId, "id_load_in_transition", uuid);
}
// dump("*** Page status updated: id=" + aId + ", loaded=" + aIsLoaded + ", uuid=" + uuid + "\n");
},
/**
* This method only applies to content windows, where we have to check if it has
* been successfully loaded or reloaded. An uuid allows us to wait for the next
* load action triggered by e.g. controller.open().
*
* @param {Window} aId - The outer id of the content window to check
*
* @returns {Boolean} True if the content window has been loaded
*/
hasPageLoaded : function (aId) {
var load_current = this.getValue(aId, "id_load_in_transition");
var load_handled = this.getValue(aId, "id_load_handled");
var isLoaded = this.contains(aId) && this.getValue(aId, "loaded") &&
(load_current !== load_handled);
if (isLoaded) {
// Backup the current uuid so we can check later if another page load happened.
this.update(aId, "id_load_handled", load_current);
}
// dump("** Page has been finished loading: id=" + aId + ", status=" + isLoaded + ", uuid=" + load_current + "\n");
return isLoaded;
}
};
// Observer when a new top-level window is ready
var windowReadyObserver = {
observe: function (aSubject, aTopic, aData) {
// Not in all cases we get a ChromeWindow. So ensure we really operate
// on such an instance. Otherwise load events will not be handled.
var win = utils.getChromeWindow(aSubject);
// var id = utils.getWindowId(win);
// dump("*** 'toplevel-window-ready' observer notification: id=" + id + "\n");
attachEventListeners(win);
}
};
// Observer when a top-level window is closed
var windowCloseObserver = {
observe: function (aSubject, aTopic, aData) {
var id = utils.getWindowId(aSubject);
// dump("*** 'outer-window-destroyed' observer notification: id=" + id + "\n");
map.remove(id);
}
};
// Bug 915554
// Support for the old Private Browsing Mode (eg. ESR17)
// TODO: remove once ESR17 is no longer supported
var enterLeavePrivateBrowsingObserver = {
observe: function (aSubject, aTopic, aData) {
handleAttachEventListeners();
}
};
/**
* Attach event listeners
*
* @param {ChromeWindow} aWindow
* Window to attach listeners on.
*/
function attachEventListeners(aWindow) {
// These are the event handlers
var pageShowHandler = function (aEvent) {
var doc = aEvent.originalTarget;
// Only update the flag if we have a document as target
// see https://bugzilla.mozilla.org/show_bug.cgi?id=690829
if ("defaultView" in doc) {
var id = utils.getWindowId(doc.defaultView);
// dump("*** 'pageshow' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
map.updatePageLoadStatus(id, true);
}
// We need to add/remove the unload/pagehide event listeners to preserve caching.
aWindow.addEventListener("beforeunload", beforeUnloadHandler, true);
aWindow.addEventListener("pagehide", pageHideHandler, true);
};
var DOMContentLoadedHandler = function (aEvent) {
var doc = aEvent.originalTarget;
// Only update the flag if we have a document as target
if ("defaultView" in doc) {
var id = utils.getWindowId(doc.defaultView);
// dump("*** 'DOMContentLoaded' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
// We only care about error pages for DOMContentLoaded
var errorRegex = /about:.+(error)|(blocked)\?/;
if (errorRegex.exec(doc.baseURI)) {
// Wait about 1s to be sure the DOM is ready
utils.sleep(1000);
map.updatePageLoadStatus(id, true);
}
// We need to add/remove the unload event listener to preserve caching.
aWindow.addEventListener("beforeunload", beforeUnloadHandler, true);
}
};
// beforeunload is still needed because pagehide doesn't fire before the page is unloaded.
// still use pagehide for cases when beforeunload doesn't get fired
var beforeUnloadHandler = function (aEvent) {
var doc = aEvent.originalTarget;
// Only update the flag if we have a document as target
if ("defaultView" in doc) {
var id = utils.getWindowId(doc.defaultView);
// dump("*** 'beforeunload' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
map.updatePageLoadStatus(id, false);
}
aWindow.removeEventListener("beforeunload", beforeUnloadHandler, true);
};
var pageHideHandler = function (aEvent) {
var doc = aEvent.originalTarget;
// Only update the flag if we have a document as target
if ("defaultView" in doc) {
var id = utils.getWindowId(doc.defaultView);
// dump("*** 'pagehide' event: id=" + id + ", baseURI=" + doc.baseURI + "\n");
map.updatePageLoadStatus(id, false);
}
// If event.persisted is true the beforeUnloadHandler would never fire
// and we have to remove the event handler here to avoid memory leaks.
if (aEvent.persisted)
aWindow.removeEventListener("beforeunload", beforeUnloadHandler, true);
};
var onWindowLoaded = function (aEvent) {
var id = utils.getWindowId(aWindow);
// dump("*** 'load' event: id=" + id + ", baseURI=" + aWindow.document.baseURI + "\n");
map.update(id, "loaded", true);
// Note: Error pages will never fire a "pageshow" event. For those we
// have to wait for the "DOMContentLoaded" event. That's the final state.
// Error pages will always have a baseURI starting with
// "about:" followed by "error" or "blocked".
aWindow.addEventListener("DOMContentLoaded", DOMContentLoadedHandler, true);
// Page is ready
aWindow.addEventListener("pageshow", pageShowHandler, true);
// Leave page (use caching)
aWindow.addEventListener("pagehide", pageHideHandler, true);
};
// If the window has already been finished loading, call the load handler
// directly. Otherwise attach it to the current window.
if (aWindow.document.readyState === 'complete') {
onWindowLoaded();
} else {
aWindow.addEventListener("load", onWindowLoaded);
}
}
// Attach event listeners to all already open top-level windows
function handleAttachEventListeners() {
var enumerator = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator).getEnumerator("");
while (enumerator.hasMoreElements()) {
var win = enumerator.getNext();
attachEventListeners(win);
}
}
function init() {
// Activate observer for new top level windows
var observerService = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
observerService.addObserver(windowReadyObserver, "toplevel-window-ready");
observerService.addObserver(windowCloseObserver, "outer-window-destroyed");
observerService.addObserver(enterLeavePrivateBrowsingObserver, "private-browsing");
handleAttachEventListeners();
}

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

@ -1,823 +0,0 @@
// Export all available functions for Mozmill
var EXPORTED_SYMBOLS = ["disableNonTestMouseEvents","sendMouseEvent", "sendChar",
"sendString", "sendKey", "synthesizeMouse", "synthesizeTouch",
"synthesizeMouseAtPoint", "synthesizeTouchAtPoint",
"synthesizeMouseAtCenter", "synthesizeTouchAtCenter",
"synthesizeWheel", "synthesizeKey",
"synthesizeMouseExpectEvent", "synthesizeKeyExpectEvent",
"synthesizeText",
"synthesizeComposition", "synthesizeQuerySelectedText"];
var Ci = Components.interfaces;
var Cc = Components.classes;
var window = Cc["@mozilla.org/appshell/appShellService;1"]
.getService(Ci.nsIAppShellService).hiddenDOMWindow;
var _EU_Ci = Ci;
var navigator = window.navigator;
var KeyEvent = window.KeyEvent;
var parent = window.parent;
function is(aExpression1, aExpression2, aMessage) {
if (aExpression1 !== aExpression2) {
throw new Error(aMessage);
}
}
/**
* EventUtils provides some utility methods for creating and sending DOM events.
* Current methods:
* sendMouseEvent
* sendChar
* sendString
* sendKey
* synthesizeMouse
* synthesizeMouseAtCenter
* synthesizeWheel
* synthesizeKey
* synthesizeMouseExpectEvent
* synthesizeKeyExpectEvent
*
* When adding methods to this file, please add a performance test for it.
*/
/**
* Send a mouse event to the node aTarget (aTarget can be an id, or an
* actual node) . The "event" passed in to aEvent is just a JavaScript
* object with the properties set that the real mouse event object should
* have. This includes the type of the mouse event.
* E.g. to send an click event to the node with id 'node' you might do this:
*
* sendMouseEvent({type:'click'}, 'node');
*/
function getElement(id) {
return ((typeof(id) == "string") ?
document.getElementById(id) : id);
};
this.$ = this.getElement;
function sendMouseEvent(aEvent, aTarget, aWindow) {
if (['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout'].indexOf(aEvent.type) == -1) {
throw new Error("sendMouseEvent doesn't know about event type '" + aEvent.type + "'");
}
if (!aWindow) {
aWindow = window;
}
if (!(aTarget instanceof aWindow.Element)) {
aTarget = aWindow.document.getElementById(aTarget);
}
var event = aWindow.document.createEvent('MouseEvent');
var typeArg = aEvent.type;
var canBubbleArg = true;
var cancelableArg = true;
var viewArg = aWindow;
var detailArg = aEvent.detail || (aEvent.type == 'click' ||
aEvent.type == 'mousedown' ||
aEvent.type == 'mouseup' ? 1 :
aEvent.type == 'dblclick'? 2 : 0);
var screenXArg = aEvent.screenX || 0;
var screenYArg = aEvent.screenY || 0;
var clientXArg = aEvent.clientX || 0;
var clientYArg = aEvent.clientY || 0;
var ctrlKeyArg = aEvent.ctrlKey || false;
var altKeyArg = aEvent.altKey || false;
var shiftKeyArg = aEvent.shiftKey || false;
var metaKeyArg = aEvent.metaKey || false;
var buttonArg = aEvent.button || 0;
var relatedTargetArg = aEvent.relatedTarget || null;
event.initMouseEvent(typeArg, canBubbleArg, cancelableArg, viewArg, detailArg,
screenXArg, screenYArg, clientXArg, clientYArg,
ctrlKeyArg, altKeyArg, shiftKeyArg, metaKeyArg,
buttonArg, relatedTargetArg);
SpecialPowers.dispatchEvent(aWindow, aTarget, event);
}
/**
* Send the char aChar to the focused element. This method handles casing of
* chars (sends the right charcode, and sends a shift key for uppercase chars).
* No other modifiers are handled at this point.
*
* For now this method only works for ASCII characters and emulates the shift
* key state on US keyboard layout.
*/
function sendChar(aChar, aWindow) {
var hasShift;
// Emulate US keyboard layout for the shiftKey state.
switch (aChar) {
case "!":
case "@":
case "#":
case "$":
case "%":
case "^":
case "&":
case "*":
case "(":
case ")":
case "_":
case "+":
case "{":
case "}":
case ":":
case "\"":
case "|":
case "<":
case ">":
case "?":
hasShift = true;
break;
default:
hasShift = (aChar == aChar.toUpperCase());
break;
}
synthesizeKey(aChar, { shiftKey: hasShift }, aWindow);
}
/**
* Send the string aStr to the focused element.
*
* For now this method only works for ASCII characters and emulates the shift
* key state on US keyboard layout.
*/
function sendString(aStr, aWindow) {
for (var i = 0; i < aStr.length; ++i) {
sendChar(aStr.charAt(i), aWindow);
}
}
/**
* Send the non-character key aKey to the focused node.
* The name of the key should be the part that comes after "DOM_VK_" in the
* KeyEvent constant name for this key.
* No modifiers are handled at this point.
*/
function sendKey(aKey, aWindow) {
var keyName = "VK_" + aKey.toUpperCase();
synthesizeKey(keyName, { shiftKey: false }, aWindow);
}
/**
* Parse the key modifier flags from aEvent. Used to share code between
* synthesizeMouse and synthesizeKey.
*/
function _parseModifiers(aEvent)
{
const nsIDOMWindowUtils = _EU_Ci.nsIDOMWindowUtils;
var mval = 0;
if (aEvent.shiftKey) {
mval |= nsIDOMWindowUtils.MODIFIER_SHIFT;
}
if (aEvent.ctrlKey) {
mval |= nsIDOMWindowUtils.MODIFIER_CONTROL;
}
if (aEvent.altKey) {
mval |= nsIDOMWindowUtils.MODIFIER_ALT;
}
if (aEvent.metaKey) {
mval |= nsIDOMWindowUtils.MODIFIER_META;
}
if (aEvent.accelKey) {
mval |= (navigator.platform.indexOf("Mac") >= 0) ?
nsIDOMWindowUtils.MODIFIER_META : nsIDOMWindowUtils.MODIFIER_CONTROL;
}
if (aEvent.altGrKey) {
mval |= nsIDOMWindowUtils.MODIFIER_ALTGRAPH;
}
if (aEvent.capsLockKey) {
mval |= nsIDOMWindowUtils.MODIFIER_CAPSLOCK;
}
if (aEvent.fnKey) {
mval |= nsIDOMWindowUtils.MODIFIER_FN;
}
if (aEvent.numLockKey) {
mval |= nsIDOMWindowUtils.MODIFIER_NUMLOCK;
}
if (aEvent.scrollLockKey) {
mval |= nsIDOMWindowUtils.MODIFIER_SCROLLLOCK;
}
if (aEvent.symbolLockKey) {
mval |= nsIDOMWindowUtils.MODIFIER_SYMBOLLOCK;
}
if (aEvent.osKey) {
mval |= nsIDOMWindowUtils.MODIFIER_OS;
}
return mval;
}
/**
* Synthesize a mouse event on a target. The actual client point is determined
* by taking the aTarget's client box and offseting it by aOffsetX and
* aOffsetY. This allows mouse clicks to be simulated by calling this method.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
*
* If the type is specified, an mouse event of that type is fired. Otherwise,
* a mousedown followed by a mouse up is performed.
*
* aWindow is optional, and defaults to the current window object.
*
* Returns whether the event had preventDefault() called on it.
*/
function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
aEvent, aWindow);
}
function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
synthesizeTouchAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
aEvent, aWindow);
}
/*
* Synthesize a mouse event at a particular point in aWindow.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
*
* If the type is specified, an mouse event of that type is fired. Otherwise,
* a mousedown followed by a mouse up is performed.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
var defaultPrevented = false;
if (utils) {
var button = aEvent.button || 0;
var clickCount = aEvent.clickCount || 1;
var modifiers = _parseModifiers(aEvent);
var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
if (("type" in aEvent) && aEvent.type) {
defaultPrevented = utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers, false, pressure, inputSource);
}
else {
utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource);
utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource);
}
}
return defaultPrevented;
}
function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (utils) {
var id = aEvent.id || 0;
var rx = aEvent.rx || 1;
var ry = aEvent.rx || 1;
var angle = aEvent.angle || 0;
var force = aEvent.force || 1;
var modifiers = _parseModifiers(aEvent);
if (("type" in aEvent) && aEvent.type) {
utils.sendTouchEvent(aEvent.type, [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
}
else {
utils.sendTouchEvent("touchstart", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
utils.sendTouchEvent("touchend", [id], [left], [top], [rx], [ry], [angle], [force], 1, modifiers);
}
}
}
// Call synthesizeMouse with coordinates at the center of aTarget.
function synthesizeMouseAtCenter(aTarget, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
synthesizeMouse(aTarget, rect.width / 2, rect.height / 2, aEvent,
aWindow);
}
function synthesizeTouchAtCenter(aTarget, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
synthesizeTouch(aTarget, rect.width / 2, rect.height / 2, aEvent,
aWindow);
}
/**
* Synthesize a wheel event on a target. The actual client point is determined
* by taking the aTarget's client box and offseting it by aOffsetX and
* aOffsetY.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, deltaX, deltaY, deltaZ,
* deltaMode, lineOrPageDeltaX, lineOrPageDeltaY, isMomentum, isPixelOnlyDevice,
* isCustomizedByPrefs, expectedOverflowDeltaX, expectedOverflowDeltaY
*
* deltaMode must be defined, others are ok even if undefined.
*
* expectedOverflowDeltaX and expectedOverflowDeltaY take integer value. The
* value is just checked as 0 or positive or negative.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeWheel(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return;
}
var modifiers = _parseModifiers(aEvent);
var options = 0;
if (aEvent.isPixelOnlyDevice &&
(aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL)) {
options |= utils.WHEEL_EVENT_CAUSED_BY_NO_LINE_OR_PAGE_DELTA_DEVICE;
}
if (aEvent.isMomentum) {
options |= utils.WHEEL_EVENT_CAUSED_BY_MOMENTUM;
}
if (aEvent.isCustomizedByPrefs) {
options |= utils.WHEEL_EVENT_CUSTOMIZED_BY_USER_PREFS;
}
if (typeof aEvent.expectedOverflowDeltaX !== "undefined") {
if (aEvent.expectedOverflowDeltaX === 0) {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_ZERO;
} else if (aEvent.expectedOverflowDeltaX > 0) {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_POSITIVE;
} else {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_X_NEGATIVE;
}
}
if (typeof aEvent.expectedOverflowDeltaY !== "undefined") {
if (aEvent.expectedOverflowDeltaY === 0) {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_ZERO;
} else if (aEvent.expectedOverflowDeltaY > 0) {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_POSITIVE;
} else {
options |= utils.WHEEL_EVENT_EXPECTED_OVERFLOW_DELTA_Y_NEGATIVE;
}
}
var isPixelOnlyDevice =
aEvent.isPixelOnlyDevice && aEvent.deltaMode == WheelEvent.DOM_DELTA_PIXEL;
// Avoid the JS warnings "reference to undefined property"
if (!aEvent.deltaX) {
aEvent.deltaX = 0;
}
if (!aEvent.deltaY) {
aEvent.deltaY = 0;
}
if (!aEvent.deltaZ) {
aEvent.deltaZ = 0;
}
var lineOrPageDeltaX =
aEvent.lineOrPageDeltaX != null ? aEvent.lineOrPageDeltaX :
aEvent.deltaX > 0 ? Math.floor(aEvent.deltaX) :
Math.ceil(aEvent.deltaX);
var lineOrPageDeltaY =
aEvent.lineOrPageDeltaY != null ? aEvent.lineOrPageDeltaY :
aEvent.deltaY > 0 ? Math.floor(aEvent.deltaY) :
Math.ceil(aEvent.deltaY);
var rect = aTarget.getBoundingClientRect();
utils.sendWheelEvent(rect.left + aOffsetX, rect.top + aOffsetY,
aEvent.deltaX, aEvent.deltaY, aEvent.deltaZ,
aEvent.deltaMode, modifiers,
lineOrPageDeltaX, lineOrPageDeltaY, options);
}
function _computeKeyCodeFromChar(aChar)
{
if (aChar.length != 1) {
return 0;
}
const nsIDOMKeyEvent = _EU_Ci.nsIDOMKeyEvent;
if (aChar >= 'a' && aChar <= 'z') {
return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'a'.charCodeAt(0);
}
if (aChar >= 'A' && aChar <= 'Z') {
return nsIDOMKeyEvent.DOM_VK_A + aChar.charCodeAt(0) - 'A'.charCodeAt(0);
}
if (aChar >= '0' && aChar <= '9') {
return nsIDOMKeyEvent.DOM_VK_0 + aChar.charCodeAt(0) - '0'.charCodeAt(0);
}
// returns US keyboard layout's keycode
switch (aChar) {
case '~':
case '`':
return nsIDOMKeyEvent.DOM_VK_BACK_QUOTE;
case '!':
return nsIDOMKeyEvent.DOM_VK_1;
case '@':
return nsIDOMKeyEvent.DOM_VK_2;
case '#':
return nsIDOMKeyEvent.DOM_VK_3;
case '$':
return nsIDOMKeyEvent.DOM_VK_4;
case '%':
return nsIDOMKeyEvent.DOM_VK_5;
case '^':
return nsIDOMKeyEvent.DOM_VK_6;
case '&':
return nsIDOMKeyEvent.DOM_VK_7;
case '*':
return nsIDOMKeyEvent.DOM_VK_8;
case '(':
return nsIDOMKeyEvent.DOM_VK_9;
case ')':
return nsIDOMKeyEvent.DOM_VK_0;
case '-':
case '_':
return nsIDOMKeyEvent.DOM_VK_SUBTRACT;
case '+':
case '=':
return nsIDOMKeyEvent.DOM_VK_EQUALS;
case '{':
case '[':
return nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET;
case '}':
case ']':
return nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET;
case '|':
case '\\':
return nsIDOMKeyEvent.DOM_VK_BACK_SLASH;
case ':':
case ';':
return nsIDOMKeyEvent.DOM_VK_SEMICOLON;
case '\'':
case '"':
return nsIDOMKeyEvent.DOM_VK_QUOTE;
case '<':
case ',':
return nsIDOMKeyEvent.DOM_VK_COMMA;
case '>':
case '.':
return nsIDOMKeyEvent.DOM_VK_PERIOD;
case '?':
case '/':
return nsIDOMKeyEvent.DOM_VK_SLASH;
default:
return 0;
}
}
/**
* isKeypressFiredKey() returns TRUE if the given key should cause keypress
* event when widget handles the native key event. Otherwise, FALSE.
*
* aDOMKeyCode should be one of consts of nsIDOMKeyEvent::DOM_VK_*, or a key
* name begins with "VK_", or a character.
*/
function isKeypressFiredKey(aDOMKeyCode)
{
if (typeof(aDOMKeyCode) == "string") {
if (aDOMKeyCode.indexOf("VK_") == 0) {
aDOMKeyCode = KeyEvent["DOM_" + aDOMKeyCode];
if (!aDOMKeyCode) {
throw new Error(`Unknown key: ${aDOMKeyCode}`);
}
} else {
// If the key generates a character, it must cause a keypress event.
return true;
}
}
switch (aDOMKeyCode) {
case KeyEvent.DOM_VK_SHIFT:
case KeyEvent.DOM_VK_CONTROL:
case KeyEvent.DOM_VK_ALT:
case KeyEvent.DOM_VK_CAPS_LOCK:
case KeyEvent.DOM_VK_NUM_LOCK:
case KeyEvent.DOM_VK_SCROLL_LOCK:
case KeyEvent.DOM_VK_META:
return false;
default:
return true;
}
}
/**
* Synthesize a key event. It is targeted at whatever would be targeted by an
* actual keypress by the user, typically the focused element.
*
* aKey should be either a character or a keycode starting with VK_ such as
* VK_ENTER.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, type, location
*
* Sets one of KeyboardEvent.DOM_KEY_LOCATION_* to location. Otherwise,
* DOMWindowUtils will choose good location from the keycode.
*
* If the type is specified, a key event of that type is fired. Otherwise,
* a keydown, a keypress and then a keyup event are fired in sequence.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeKey(aKey, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (utils) {
var keyCode = 0, charCode = 0;
if (aKey.indexOf("VK_") == 0) {
keyCode = KeyEvent["DOM_" + aKey];
if (!keyCode) {
throw new Error(`Unknown key: ${aKey}`);
}
} else {
charCode = aKey.charCodeAt(0);
keyCode = _computeKeyCodeFromChar(aKey.charAt(0));
}
var modifiers = _parseModifiers(aEvent);
var flags = 0;
if (aEvent.location != undefined) {
switch (aEvent.location) {
case KeyboardEvent.DOM_KEY_LOCATION_STANDARD:
flags |= utils.KEY_FLAG_LOCATION_STANDARD;
break;
case KeyboardEvent.DOM_KEY_LOCATION_LEFT:
flags |= utils.KEY_FLAG_LOCATION_LEFT;
break;
case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
flags |= utils.KEY_FLAG_LOCATION_RIGHT;
break;
case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
flags |= utils.KEY_FLAG_LOCATION_NUMPAD;
break;
}
}
if (!("type" in aEvent) || !aEvent.type) {
// Send keydown + (optional) keypress + keyup events.
var keyDownDefaultHappened =
utils.sendKeyEvent("keydown", keyCode, 0, modifiers, flags);
if (isKeypressFiredKey(keyCode)) {
if (!keyDownDefaultHappened) {
flags |= utils.KEY_FLAG_PREVENT_DEFAULT;
}
utils.sendKeyEvent("keypress", keyCode, charCode, modifiers, flags);
}
utils.sendKeyEvent("keyup", keyCode, 0, modifiers, flags);
} else if (aEvent.type == "keypress") {
// Send standalone keypress event.
utils.sendKeyEvent(aEvent.type, keyCode, charCode, modifiers, flags);
} else {
// Send other standalone event than keypress.
utils.sendKeyEvent(aEvent.type, keyCode, 0, modifiers, flags);
}
}
}
var _gSeenEvent = false;
/**
* Indicate that an event with an original target of aExpectedTarget and
* a type of aExpectedEvent is expected to be fired, or not expected to
* be fired.
*/
function _expectEvent(aExpectedTarget, aExpectedEvent, aTestName)
{
if (!aExpectedTarget || !aExpectedEvent)
return null;
_gSeenEvent = false;
var type = (aExpectedEvent.charAt(0) == "!") ?
aExpectedEvent.substring(1) : aExpectedEvent;
var eventHandler = function(event) {
var epassed = (!_gSeenEvent && event.originalTarget == aExpectedTarget &&
event.type == type);
is(epassed, true, aTestName + " " + type + " event target " + (_gSeenEvent ? "twice" : ""));
_gSeenEvent = true;
};
aExpectedTarget.addEventListener(type, eventHandler);
return eventHandler;
}
/**
* Check if the event was fired or not. The event handler aEventHandler
* will be removed.
*/
function _checkExpectedEvent(aExpectedTarget, aExpectedEvent, aEventHandler, aTestName)
{
if (aEventHandler) {
var expectEvent = (aExpectedEvent.charAt(0) != "!");
var type = expectEvent ? aExpectedEvent : aExpectedEvent.substring(1);
aExpectedTarget.removeEventListener(type, aEventHandler);
var desc = type + " event";
if (!expectEvent)
desc += " not";
is(_gSeenEvent, expectEvent, aTestName + " " + desc + " fired");
}
_gSeenEvent = false;
}
/**
* Similar to synthesizeMouse except that a test is performed to see if an
* event is fired at the right target as a result.
*
* aExpectedTarget - the expected originalTarget of the event.
* aExpectedEvent - the expected type of the event, such as 'select'.
* aTestName - the test name when outputing results
*
* To test that an event is not fired, use an expected type preceded by an
* exclamation mark, such as '!select'. This might be used to test that a
* click on a disabled element doesn't fire certain events for instance.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeMouseExpectEvent(aTarget, aOffsetX, aOffsetY, aEvent,
aExpectedTarget, aExpectedEvent, aTestName,
aWindow)
{
var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow);
_checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
}
/**
* Similar to synthesizeKey except that a test is performed to see if an
* event is fired at the right target as a result.
*
* aExpectedTarget - the expected originalTarget of the event.
* aExpectedEvent - the expected type of the event, such as 'select'.
* aTestName - the test name when outputing results
*
* To test that an event is not fired, use an expected type preceded by an
* exclamation mark, such as '!select'.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeKeyExpectEvent(key, aEvent, aExpectedTarget, aExpectedEvent,
aTestName, aWindow)
{
var eventHandler = _expectEvent(aExpectedTarget, aExpectedEvent, aTestName);
synthesizeKey(key, aEvent, aWindow);
_checkExpectedEvent(aExpectedTarget, aExpectedEvent, eventHandler, aTestName);
}
function disableNonTestMouseEvents(aDisable)
{
var domutils = _getDOMWindowUtils();
domutils.disableNonTestMouseEvents(aDisable);
}
function _getDOMWindowUtils(aWindow)
{
if (!aWindow) {
aWindow = window;
}
// we need parent.SpecialPowers for:
// layout/base/tests/test_reftests_with_caret.html
// chrome: toolkit/content/tests/chrome/test_findbar.xul
// chrome: toolkit/content/tests/chrome/test_popup_anchor.xul
if ("SpecialPowers" in window && window.SpecialPowers != undefined) {
return SpecialPowers.getDOMWindowUtils(aWindow);
}
if ("SpecialPowers" in parent && parent.SpecialPowers != undefined) {
return parent.SpecialPowers.getDOMWindowUtils(aWindow);
}
//TODO: this is assuming we are in chrome space
return aWindow.QueryInterface(_EU_Ci.nsIInterfaceRequestor).
getInterface(_EU_Ci.nsIDOMWindowUtils);
}
// Must be synchronized with nsIDOMWindowUtils.
const COMPOSITION_ATTR_RAWINPUT = 0x02;
const COMPOSITION_ATTR_SELECTEDRAWTEXT = 0x03;
const COMPOSITION_ATTR_CONVERTEDTEXT = 0x04;
const COMPOSITION_ATTR_SELECTEDCONVERTEDTEXT = 0x05;
/**
* Synthesize a composition event.
*
* @param aEvent The composition event information. This must
* have |type| member. The value must be
* "compositionstart", "compositionend" or
* "compositionupdate".
* And also this may have |data| and |locale| which
* would be used for the value of each property of
* the composition event. Note that the data would
* be ignored if the event type were
* "compositionstart".
* @param aWindow Optional (If null, current |window| will be used)
*/
function synthesizeComposition(aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return;
}
utils.sendCompositionEvent(aEvent.type, aEvent.data ? aEvent.data : "",
aEvent.locale ? aEvent.locale : "");
}
/**
* Synthesize a text event.
*
* @param aEvent The text event's information, this has |composition|
* and |caret| members. |composition| has |string| and
* |clauses| members. |clauses| must be array object. Each
* object has |length| and |attr|. And |caret| has |start| and
* |length|. See the following tree image.
*
* aEvent
* +-- composition
* | +-- string
* | +-- clauses[]
* | +-- length
* | +-- attr
* +-- caret
* +-- start
* +-- length
*
* Set the composition string to |composition.string|. Set its
* clauses information to the |clauses| array.
*
* When it's composing, set the each clauses' length to the
* |composition.clauses[n].length|. The sum of the all length
* values must be same as the length of |composition.string|.
* Set nsIDOMWindowUtils.COMPOSITION_ATTR_* to the
* |composition.clauses[n].attr|.
*
* When it's not composing, set 0 to the
* |composition.clauses[0].length| and
* |composition.clauses[0].attr|.
*
* Set caret position to the |caret.start|. It's offset from
* the start of the composition string. Set caret length to
* |caret.length|. If it's larger than 0, it should be wide
* caret. However, current nsEditor doesn't support wide
* caret, therefore, you should always set 0 now.
*
* @param aWindow Optional (If null, current |window| will be used)
*/
function synthesizeText(aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return;
}
if (!aEvent.composition || !aEvent.composition.clauses ||
!aEvent.composition.clauses[0]) {
return;
}
var firstClauseLength = aEvent.composition.clauses[0].length;
var firstClauseAttr = aEvent.composition.clauses[0].attr;
var secondClauseLength = 0;
var secondClauseAttr = 0;
var thirdClauseLength = 0;
var thirdClauseAttr = 0;
if (aEvent.composition.clauses[1]) {
secondClauseLength = aEvent.composition.clauses[1].length;
secondClauseAttr = aEvent.composition.clauses[1].attr;
if (aEvent.composition.clauses[2]) {
thirdClauseLength = aEvent.composition.clauses[2].length;
thirdClauseAttr = aEvent.composition.clauses[2].attr;
}
}
var caretStart = -1;
var caretLength = 0;
if (aEvent.caret) {
caretStart = aEvent.caret.start;
caretLength = aEvent.caret.length;
}
utils.sendTextEvent(aEvent.composition.string,
firstClauseLength, firstClauseAttr,
secondClauseLength, secondClauseAttr,
thirdClauseLength, thirdClauseAttr,
caretStart, caretLength);
}
/**
* Synthesize a query selected text event.
*
* @param aWindow Optional (If null, current |window| will be used)
* @return An nsIQueryContentEventResult object. If this failed,
* the result might be null.
*/
function synthesizeQuerySelectedText(aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
if (!utils) {
return null;
}
return utils.sendQueryContentEvent(utils.QUERY_SELECTED_TEXT, 0, 0, 0, 0);
}

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

@ -1,78 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['inArray', 'getSet', 'indexOf',
'remove', 'rindexOf', 'compare'];
function remove(array, from, to) {
var rest = array.slice((to || from) + 1 || array.length);
array.length = from < 0 ? array.length + from : from;
return array.push.apply(array, rest);
}
function inArray(array, value) {
for (var i in array) {
if (value == array[i]) {
return true;
}
}
return false;
}
function getSet(array) {
var narray = [];
for (var i in array) {
if (!inArray(narray, array[i])) {
narray.push(array[i]);
}
}
return narray;
}
function indexOf(array, v, offset) {
for (var i in array) {
if (offset == undefined || i >= offset) {
if (!isNaN(i) && array[i] == v) {
return new Number(i);
}
}
}
return -1;
}
function rindexOf (array, v) {
var l = array.length;
for (var i in array) {
if (!isNaN(i)) {
var i = new Number(i);
}
if (!isNaN(i) && array[l - i] == v) {
return l - i;
}
}
return -1;
}
function compare (array, carray) {
if (array.length != carray.length) {
return false;
}
for (var i in array) {
if (array[i] != carray[i]) {
return false;
}
}
return true;
}

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

@ -1,24 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['getAttributes'];
var getAttributes = function (node) {
var attributes = {};
for (var i in node.attributes) {
if (!isNaN(i)) {
try {
var attr = node.attributes[i];
attributes[attr.name] = attr.value;
}
catch (e) {
}
}
}
return attributes;
}

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -1,469 +0,0 @@
/*
http://www.JSON.org/json2.js
2008-05-25
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects without a toJSON
method. It can be a function or an array.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the object holding the key.
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array, then it will be used to
select the members to be serialized. It filters the results such
that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
*/
/*jslint evil: true */
/*global JSON */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", call,
charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, getUTCMinutes,
getUTCMonth, getUTCSeconds, hasOwnProperty, join, lastIndex, length,
parse, propertyIsEnumerable, prototype, push, replace, slice, stringify,
test, toJSON, toString
*/
var EXPORTED_SYMBOLS = ["JSON"];
// Create a JSON object only if one does not already exist. We create the
// object in a closure to avoid creating global variables.
JSON = function () {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
Date.prototype.toJSON = function (key) {
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapeable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapeable.lastIndex = 0;
return escapeable.test(string) ?
'"' + string.replace(escapeable, function (a) {
var c = meta[a];
if (typeof c === 'string') {
return c;
}
return '\\u' + ('0000' +
(+(a.charCodeAt(0))).toString(16)).slice(-4);
}) + '"' :
'"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// If the object has a dontEnum length property, we'll treat it as an array.
if (typeof value.length === 'number' &&
!(value.propertyIsEnumerable('length'))) {
// The object is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' :
gap ? '[\n' + gap +
partial.join(',\n' + gap) + '\n' +
mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
k = rep[i];
if (typeof k === 'string') {
v = str(k, value, rep);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = str(k, value, rep);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' :
gap ? '{\n' + gap + partial.join(',\n' + gap) + '\n' +
mind + '}' : '{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// Return the JSON object containing the stringify and parse methods.
return {
stringify: function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
},
parse: function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' + ('0000' +
(+(a.charCodeAt(0))).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/.
test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@').
replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
}
};
}();

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

@ -1,54 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['getLength', ];//'compare'];
var getLength = function (obj) {
var len = 0;
for (let i in obj) {
len++;
}
return len;
}
// var logging = {}; Components.utils.import('resource://mozmill/stdlib/logging.js', logging);
// var objectsLogger = logging.getLogger('objectsLogger');
// var compare = function (obj1, obj2, depth, recursion) {
// if (depth == undefined) {
// var depth = 4;
// }
// if (recursion == undefined) {
// var recursion = 0;
// }
//
// if (recursion > depth) {
// return true;
// }
//
// if (typeof(obj1) != typeof(obj2)) {
// return false;
// }
//
// if (typeof(obj1) == "object" && typeof(obj2) == "object") {
// if ([x for (x in obj1)].length != [x for (x in obj2)].length) {
// return false;
// }
// for (i in obj1) {
// recursion++;
// var result = compare(obj1[i], obj2[i], depth, recursion);
// objectsLogger.info(i+' in recursion '+result);
// if (result == false) {
// return false;
// }
// }
// } else {
// if (obj1 != obj2) {
// return false;
// }
// }
// return true;
// }

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

@ -1,57 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['listDirectory', 'getFileForPath', 'abspath', 'getPlatform'];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
function listDirectory(file) {
// file is the given directory (nsIFile)
var entries = file.directoryEntries;
var array = [];
while (entries.hasMoreElements()) {
var entry = entries.getNext();
entry.QueryInterface(Ci.nsIFile);
array.push(entry);
}
return array;
}
function getFileForPath(path) {
var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
file.initWithPath(path);
return file;
}
function abspath(rel, file) {
var relSplit = rel.split('/');
if (relSplit[0] == '..' && !file.isDirectory()) {
file = file.parent;
}
for (var p of relSplit) {
if (p == '..') {
file = file.parent;
} else if (p == '.') {
if (!file.isDirectory()) {
file = file.parent;
}
} else {
file.append(p);
}
}
return file.path;
}
function getPlatform() {
return Services.appinfo.OS.toLowerCase();
}

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

@ -1,370 +0,0 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Jetpack.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Atul Varma <atul@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
(function(global) {
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/NetUtil.jsm");
var exports = {};
var ios = Cc['@mozilla.org/network/io-service;1']
.getService(Ci.nsIIOService);
var systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
.createInstance(Ci.nsIPrincipal);
function resolvePrincipal(principal, defaultPrincipal) {
if (principal === undefined)
return defaultPrincipal;
if (principal == "system")
return systemPrincipal;
return principal;
}
// The base URI to we use when we're given relative URLs, if any.
var baseURI = null;
if (global.window)
baseURI = ios.newURI(global.location.href);
exports.baseURI = baseURI;
// The "parent" chrome URI to use if we're loading code that
// needs chrome privileges but may not have a filename that
// matches any of SpiderMonkey's defined system filename prefixes.
// The latter is needed so that wrappers can be automatically
// made for the code. For more information on this, see
// bug 418356:
//
// https://bugzilla.mozilla.org/show_bug.cgi?id=418356
var parentChromeURIString;
if (baseURI)
// We're being loaded from a chrome-privileged document, so
// use its URL as the parent string.
parentChromeURIString = baseURI.spec;
else
// We're being loaded from a chrome-privileged JS module or
// SecurableModule, so use its filename (which may itself
// contain a reference to a parent).
parentChromeURIString = Components.stack.filename;
function maybeParentifyFilename(filename) {
var doParentifyFilename = true;
try {
// TODO: Ideally we should just make
// nsIChromeRegistry.wrappersEnabled() available from script
// and use it here. Until that's in the platform, though,
// we'll play it safe and parentify the filename unless
// we're absolutely certain things will be ok if we don't.
var filenameURI = ios.newURI(filename,
null,
baseURI);
if (filenameURI.scheme == 'chrome' &&
filenameURI.pathQueryRef.indexOf('/content/') == 0)
// Content packages will always have wrappers made for them;
// if automatic wrappers have been disabled for the
// chrome package via a chrome manifest flag, then
// this still works too, to the extent that the
// content package is insecure anyways.
doParentifyFilename = false;
} catch (e) {}
if (doParentifyFilename)
return parentChromeURIString + " -> " + filename;
return filename;
}
function getRootDir(urlStr) {
// TODO: This feels hacky, and like there will be edge cases.
return urlStr.slice(0, urlStr.lastIndexOf("/") + 1);
}
exports.SandboxFactory = function SandboxFactory(defaultPrincipal) {
// Unless specified otherwise, use a principal with limited
// privileges.
this._defaultPrincipal = resolvePrincipal(defaultPrincipal,
"http://www.mozilla.org");
},
exports.SandboxFactory.prototype = {
createSandbox: function createSandbox(options) {
var principal = resolvePrincipal(options.principal,
this._defaultPrincipal);
return {
_sandbox: new Cu.Sandbox(principal),
_principal: principal,
get globalScope() {
return this._sandbox;
},
defineProperty: function defineProperty(name, value) {
this._sandbox[name] = value;
},
getProperty: function getProperty(name) {
return this._sandbox[name];
},
evaluate: function evaluate(options) {
if (typeof(options) == 'string')
options = {contents: options};
options = {__proto__: options};
if (typeof(options.contents) != 'string')
throw new Error('Expected string for options.contents');
if (options.lineNo === undefined)
options.lineNo = 1;
if (options.jsVersion === undefined)
options.jsVersion = "1.8";
if (typeof(options.filename) != 'string')
options.filename = '<string>';
if (this._principal == systemPrincipal)
options.filename = maybeParentifyFilename(options.filename);
return Cu.evalInSandbox(options.contents,
this._sandbox,
options.jsVersion,
options.filename,
options.lineNo);
}
};
}
};
exports.Loader = function Loader(options) {
options = {__proto__: options};
if (options.fs === undefined) {
var rootPaths = options.rootPath || options.rootPaths;
if (rootPaths) {
if (rootPaths.constructor.name != "Array")
rootPaths = [rootPaths];
var fses = rootPaths.map(path => new exports.LocalFileSystem(path));
options.fs = new exports.CompositeFileSystem(fses);
} else
options.fs = new exports.LocalFileSystem();
}
if (options.sandboxFactory === undefined)
options.sandboxFactory = new exports.SandboxFactory(
options.defaultPrincipal
);
if (options.modules === undefined)
options.modules = {};
if (options.globals === undefined)
options.globals = {};
this.fs = options.fs;
this.sandboxFactory = options.sandboxFactory;
this.sandboxes = {};
this.modules = options.modules;
this.globals = options.globals;
};
exports.Loader.prototype = {
_makeRequire: function _makeRequire(rootDir) {
var self = this;
return function require(module) {
if (module == "chrome") {
var chrome = { Cc: Components.classes,
Ci: Components.interfaces,
Cu: Components.utils,
Cr: Components.results,
Cm: Components.manager,
components: Components
};
return chrome;
}
var path = self.fs.resolveModule(rootDir, module);
if (!path)
throw new Error('Module "' + module + '" not found');
if (!(path in self.modules)) {
var options = self.fs.getFile(path);
if (options.filename === undefined)
options.filename = path;
var exports = {};
var sandbox = self.sandboxFactory.createSandbox(options);
self.sandboxes[path] = sandbox;
for (name in self.globals)
sandbox.defineProperty(name, self.globals[name]);
sandbox.defineProperty('require', self._makeRequire(path));
sandbox.evaluate("var exports = {};");
let ES5 = self.modules.es5;
if (ES5) {
let { Object, Array, Function } = sandbox.globalScope;
ES5.init(Object, Array, Function);
}
self.modules[path] = sandbox.getProperty("exports");
sandbox.evaluate(options);
}
return self.modules[path];
};
},
// This is only really used by unit tests and other
// development-related facilities, allowing access to symbols
// defined in the global scope of a module.
findSandboxForModule: function findSandboxForModule(module) {
var path = this.fs.resolveModule(null, module);
if (!path)
throw new Error('Module "' + module + '" not found');
if (!(path in this.sandboxes))
this.require(module);
if (!(path in this.sandboxes))
throw new Error('Internal error: path not in sandboxes: ' +
path);
return this.sandboxes[path];
},
require: function require(module) {
return (this._makeRequire(null))(module);
},
runScript: function runScript(options, extraOutput) {
if (typeof(options) == 'string')
options = {contents: options};
options = {__proto__: options};
var sandbox = this.sandboxFactory.createSandbox(options);
if (extraOutput)
extraOutput.sandbox = sandbox;
for (name in this.globals)
sandbox.defineProperty(name, this.globals[name]);
sandbox.defineProperty('require', this._makeRequire(null));
return sandbox.evaluate(options);
}
};
exports.CompositeFileSystem = function CompositeFileSystem(fses) {
this.fses = fses;
this._pathMap = {};
};
exports.CompositeFileSystem.prototype = {
resolveModule: function resolveModule(base, path) {
for (var i = 0; i < this.fses.length; i++) {
var fs = this.fses[i];
var absPath = fs.resolveModule(base, path);
if (absPath) {
this._pathMap[absPath] = fs;
return absPath;
}
}
return null;
},
getFile: function getFile(path) {
return this._pathMap[path].getFile(path);
}
};
exports.LocalFileSystem = function LocalFileSystem(root) {
if (root === undefined) {
if (!baseURI)
throw new Error("Need a root path for module filesystem");
root = baseURI;
}
if (typeof(root) == 'string')
root = ios.newURI(root, null, baseURI);
if (root instanceof Ci.nsIFile)
root = ios.newFileURI(root);
if (!(root instanceof Ci.nsIURI))
throw new Error('Expected nsIFile, nsIURI, or string for root');
this.root = root.spec;
this._rootURI = root;
this._rootURIDir = getRootDir(root.spec);
};
exports.LocalFileSystem.prototype = {
resolveModule: function resolveModule(base, path) {
path = path + ".js";
var baseURI;
if (!base)
baseURI = this._rootURI;
else
baseURI = ios.newURI(base);
var newURI = ios.newURI(path, null, baseURI);
var channel = NetUtil.newChannel({
uri: newURI,
loadUsingSystemPrincipal: true
});
try {
channel.open2().close();
} catch (e) {
if (e.result != Cr.NS_ERROR_FILE_NOT_FOUND) {
throw e;
}
return null;
}
return newURI.spec;
},
getFile: function getFile(path) {
var channel = NetUtil.newChannel({
uri: path,
loadUsingSystemPrincipal: true
});
var iStream = channel.open2();
var ciStream = Cc["@mozilla.org/intl/converter-input-stream;1"].
createInstance(Ci.nsIConverterInputStream);
var bufLen = 0x8000;
ciStream.init(iStream, "UTF-8", bufLen,
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
var chunk = {};
var data = "";
while (ciStream.readString(bufLen, chunk) > 0)
data += chunk.value;
ciStream.close();
iStream.close();
return {contents: data};
}
};
if (global.window) {
// We're being loaded in a chrome window, or a web page with
// UniversalXPConnect privileges.
global.SecurableModule = exports;
} else if (global.exports) {
// We're being loaded in a SecurableModule.
for (name in exports) {
global.exports[name] = exports[name];
}
} else {
// We're being loaded in a JS module.
global.EXPORTED_SYMBOLS = [];
for (name in exports) {
global.EXPORTED_SYMBOLS.push(name);
global[name] = exports[name];
}
}
})(this);

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

@ -1,17 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at http://mozilla.org/MPL/2.0/. */
var EXPORTED_SYMBOLS = ['trim', 'vslice'];
var arrays = {}; Components.utils.import('resource://mozmill/stdlib/arrays.js', arrays);
var trim = function (str) {
return (str.replace(/^[\s\xA0]+/, "").replace(/[\s\xA0]+$/, ""));
}
var vslice = function (str, svalue, evalue) {
var sindex = arrays.indexOf(str, svalue);
var eindex = arrays.rindexOf(str, evalue);
return str.slice(sindex + 1, eindex);
}

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

@ -1,146 +0,0 @@
/*
Copyright (c) 2006 Lawrence Oluyede <l.oluyede@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
startsWith(str, prefix[, start[, end]]) -> bool
Return true if str ends with the specified prefix, false otherwise.
With optional start, test str beginning at that position.
With optional end, stop comparing str at that position.
prefix can also be an array of strings to try.
*/
var EXPORTED_SYMBOLS = ['startsWith', 'endsWith'];
function startsWith(str, prefix, start, end) {
if (arguments.length < 2) {
throw new TypeError('startsWith() requires at least 2 arguments');
}
// check if start and end are null/undefined or a 'number'
if ((start == null) || (isNaN(new Number(start)))) {
start = 0;
}
if ((end == null) || (isNaN(new Number(end)))) {
end = Number.MAX_VALUE;
}
// if it's an array
if (typeof prefix == "object") {
for (var i = 0, j = prefix.length; i < j; i++) {
var res = _stringTailMatch(str, prefix[i], start, end, true);
if (res) {
return true;
}
}
return false;
}
return _stringTailMatch(str, prefix, start, end, true);
}
/*
endsWith(str, suffix[, start[, end]]) -> bool
Return true if str ends with the specified suffix, false otherwise.
With optional start, test str beginning at that position.
With optional end, stop comparing str at that position.
suffix can also be an array of strings to try.
*/
function endsWith(str, suffix, start, end) {
if (arguments.length < 2) {
throw new TypeError('endsWith() requires at least 2 arguments');
}
// check if start and end are null/undefined or a 'number'
if ((start == null) || (isNaN(new Number(start)))) {
start = 0;
}
if ((end == null) || (isNaN(new Number(end)))) {
end = Number.MAX_VALUE;
}
// if it's an array
if (typeof suffix == "object") {
for (var i = 0, j = suffix.length; i < j; i++) {
var res = _stringTailMatch(str, suffix[i], start, end, false);
if (res) {
return true;
}
}
return false;
}
return _stringTailMatch(str, suffix, start, end, false);
}
/*
Matches the end (direction == false) or start (direction == true) of str
against substr, using the start and end arguments. Returns false
if not found and true if found.
*/
function _stringTailMatch(str, substr, start, end, fromStart) {
var len = str.length;
var slen = substr.length;
var indices = _adjustIndices(start, end, len);
start = indices[0]; end = indices[1]; len = indices[2];
if (fromStart) {
if (start + slen > len) {
return false;
}
} else {
if (end - start < slen || start > len) {
return false;
}
if (end - slen > start) {
start = end - slen;
}
}
if (end - start >= slen) {
return str.substr(start, slen) == substr;
}
return false;
}
function _adjustIndices(start, end, len)
{
if (end > len) {
end = len;
} else if (end < 0) {
end += len;
}
if (end < 0) {
end = 0;
}
if (start < 0) {
start += len;
}
if (start < 0) {
start = 0;
}
return [start, end, len];
}

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

@ -51,9 +51,6 @@ var hh = Cc["@mozilla.org/network/protocol;1?name=http"]
var prefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch);
var mozmillInit = {};
Cu.import("resource://mozmill/driver/mozmill.js", mozmillInit);
XPCOMUtils.defineLazyGetter(this, "fileProtocolHandler", () => {
let fileHandler = Services.io.getProtocolHandler("file");
return fileHandler.QueryInterface(Ci.nsIFileProtocolHandler);
@ -581,23 +578,6 @@ var TPS = {
}
},
MozmillEndTestListener: function TPS__MozmillEndTestListener(obj) {
Logger.logInfo("mozmill endTest: " + JSON.stringify(obj));
if (obj.failed > 0) {
this.DumpError("mozmill test failed, name: " + obj.name + ", reason: " + JSON.stringify(obj.fails));
} else if ("skipped" in obj && obj.skipped) {
this.DumpError("mozmill test failed, name: " + obj.name + ", reason: " + obj.skipped_reason);
} else {
CommonUtils.namedTimer(function() {
this.FinishAsyncOperation();
}, 2000, this, "postmozmilltest");
}
},
MozmillSetTestListener: function TPS__MozmillSetTestListener(obj) {
Logger.logInfo("mozmill setTest: " + obj.name);
},
async Cleanup() {
try {
await this.WipeServer();
@ -1045,25 +1025,6 @@ var TPS = {
this._enabledEngines = names;
},
RunMozmillTest: function TPS__RunMozmillTest(testfile) {
var mozmillfile = Cc["@mozilla.org/file/local;1"]
.createInstance(Ci.nsIFile);
if (hh.oscpu.toLowerCase().indexOf("windows") > -1) {
let re = /\/(\w)\/(.*)/;
this.config.testdir = this.config.testdir.replace(re, "$1://$2").replace(/\//g, "\\");
}
mozmillfile.initWithPath(this.config.testdir);
mozmillfile.appendRelativePath(testfile);
Logger.logInfo("Running mozmill test " + mozmillfile.path);
var frame = {};
Cu.import("resource://mozmill/modules/frame.js", frame);
frame.events.addListener("setTest", this.MozmillSetTestListener.bind(this));
frame.events.addListener("endTest", this.MozmillEndTestListener.bind(this));
this.StartAsyncOperation();
frame.runTestFile(mozmillfile.path, null);
},
/**
* Returns a promise that resolves when a specific observer notification is
* resolved. This is similar to the various waitFor* functions, although is

16
servo/Cargo.lock сгенерированный
Просмотреть файл

@ -631,7 +631,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"metadeps 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"metadeps 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -854,11 +854,6 @@ dependencies = [
"regex 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "error-chain"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "error-chain"
version = "0.10.0"
@ -1713,10 +1708,10 @@ dependencies = [
[[package]]
name = "metadeps"
version = "1.1.1"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"error-chain 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
"toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -3590,7 +3585,7 @@ version = "2.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
"metadeps 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"metadeps 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -3729,7 +3724,6 @@ dependencies = [
"checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180"
"checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b"
"checksum error-chain 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d9435d864e017c3c6afeac1654189b06cdb491cf2ff73dbf0d73b0f292f42ff8"
"checksum error-chain 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "318cb3c71ee4cdea69fdc9e15c173b245ed6063e1709029e8fd32525a881120f"
"checksum euclid 0.15.3 (registry+https://github.com/rust-lang/crates.io-index)" = "01a550d73de4eac12cee6316ccef84a9f71493c3839015bfda6465a53e31b879"
"checksum expat-sys 2.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c470ccb972f2088549b023db8029ed9da9426f5affbf9b62efff7009ab8ed5b1"
"checksum extra-default 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca1434e1920ba21b45516c16b5edbd82e8f2e4349b12a7a53eb9903ed2928d56"
@ -3793,7 +3787,7 @@ dependencies = [
"checksum markup5ever 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2cf89d3e0486c32c9d99521455ddf9a438910a1ce2bd376936086edc15dff5fc"
"checksum matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "efd7622e3022e1a6eaa602c4cea8912254e5582c9c692e9167714182244801b1"
"checksum memchr 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1dbccc0e46f1ea47b9f17e6d67c5a96bd27030519c519c9c91327e31275a47b4"
"checksum metadeps 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "829fffe7ea1d747e23f64be972991bc516b2f1ac2ae4a3b33d8bea150c410151"
"checksum metadeps 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b122901b3a675fac8cecf68dcb2f0d3036193bc861d1ac0e1c337f7d5254c2"
"checksum mime 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9d69889cdc6336ed56b174514ce876c4c3dc564cc23dd872e7bca589bb2a36c8"
"checksum mime_guess 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "76da6df85047af8c0edfa53f48eb1073012ce1cc95c8fedc0a374f659a89dd65"
"checksum miniz-sys 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "28eaee17666671fa872e567547e8428e83308ebe5808cdf6a0e28397dbe2c726"

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

@ -10,7 +10,6 @@ use platform::font::{FontHandle, FontTable};
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use smallvec::SmallVec;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::RefCell;
use std::collections::HashMap;

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

@ -12,7 +12,6 @@
use hyper::method::Method;
use net_traits::request::{CredentialsMode, Origin, Request};
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use time::{self, Timespec};
/// Union type for CORS cache entries

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

@ -21,7 +21,6 @@ use net_traits::request::{CredentialsMode, Destination, Referrer, Request, Reque
use net_traits::request::{ResponseTainting, Origin, Window};
use net_traits::response::{Response, ResponseBody, ResponseType};
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::borrow::Cow;
use std::fmt;
use std::fs::File;

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

@ -39,7 +39,6 @@ use net_traits::request::{ResponseTainting, ServiceWorkersMode};
use net_traits::response::{HttpsState, Response, ResponseBody, ResponseType};
use resource_thread::AuthCache;
use servo_url::{ImmutableOrigin, ServoUrl};
use std::ascii::AsciiExt;
use std::collections::HashSet;
use std::error::Error;
use std::io::{self, Read, Write};

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

@ -20,7 +20,6 @@ use net_traits::{CookieSource, MessageData, NetworkError};
use net_traits::{WebSocketDomAction, WebSocketNetworkEvent};
use net_traits::request::{Destination, RequestInit, RequestMode};
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::io::{self, Write};
use std::net::TcpStream;
use std::sync::{Arc, Mutex};

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

@ -9,7 +9,6 @@ use hyper::header::{AccessControlExposeHeaders, ContentType, Headers};
use hyper::status::StatusCode;
use hyper_serde::Serde;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use std::sync::{Arc, Mutex};
/// [Response type](https://fetch.spec.whatwg.org/#concept-response-type)

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

@ -7,7 +7,6 @@
use cssparser::CowRcStr;
use html5ever::{LocalName, Namespace};
use servo_atoms::Atom;
use std::ascii::AsciiExt;
use std::borrow::{Borrow, Cow, ToOwned};
use std::default::Default;
use std::fmt;

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

@ -16,7 +16,6 @@ use ipc_channel::ipc;
use net_traits::{CoreResourceMsg, IpcSend};
use net_traits::blob_url_store::{BlobBuf, get_blob_origin};
use net_traits::filemanager_thread::{FileManagerThreadMsg, ReadFileProgress, RelativePos};
use std::ascii::AsciiExt;
use std::mem;
use std::ops::Index;
use std::path::PathBuf;

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

@ -16,7 +16,6 @@ use dom::window::Window;
use dom_struct::dom_struct;
use servo_arc::Arc;
use servo_url::ServoUrl;
use std::ascii::AsciiExt;
use style::attr::AttrValue;
use style::properties::{DeclarationSource, Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
use style::properties::{parse_one_declaration_into, parse_style_attribute, SourcePropertyDeclaration};

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

@ -117,7 +117,6 @@ use servo_arc::Arc;
use servo_atoms::Atom;
use servo_config::prefs::PREFS;
use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::{Cell, Ref, RefMut};
use std::collections::{HashMap, HashSet, VecDeque};

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

@ -93,7 +93,6 @@ use selectors::matching::{ElementSelectorFlags, MatchingContext, RelevantLinkSta
use selectors::sink::Push;
use servo_arc::Arc;
use servo_atoms::Atom;
use std::ascii::AsciiExt;
use std::borrow::Cow;
use std::cell::{Cell, Ref};
use std::default::Default;

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

@ -30,7 +30,6 @@ use dom::nodelist::NodeList;
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
use std::ascii::AsciiExt;
use std::collections::HashSet;
use std::default::Default;
use std::rc::Rc;

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

@ -26,7 +26,6 @@ use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy;
use script_traits::{MozBrowserEvent, ScriptMsg};
use servo_arc::Arc;
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Cell;
use std::default::Default;

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