org.htmlunit.javascript.host.dom.MutationObserver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of xlt Show documentation
Show all versions of xlt Show documentation
XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.
/*
* Copyright (c) 2002-2023 Gargoyle Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.htmlunit.javascript.host.dom;
import org.htmlunit.html.CharacterDataChangeEvent;
import org.htmlunit.html.CharacterDataChangeListener;
import org.htmlunit.html.HtmlAttributeChangeEvent;
import org.htmlunit.html.HtmlAttributeChangeListener;
import org.htmlunit.html.HtmlElement;
import org.htmlunit.html.HtmlPage;
import org.htmlunit.javascript.HtmlUnitScriptable;
import org.htmlunit.javascript.JavaScriptEngine;
import org.htmlunit.javascript.PostponedAction;
import org.htmlunit.javascript.configuration.JsxClass;
import org.htmlunit.javascript.configuration.JsxConstructor;
import org.htmlunit.javascript.configuration.JsxFunction;
import org.htmlunit.javascript.host.Window;
import org.htmlunit.corejs.javascript.Context;
import org.htmlunit.corejs.javascript.Function;
import org.htmlunit.corejs.javascript.NativeArray;
import org.htmlunit.corejs.javascript.NativeObject;
import org.htmlunit.corejs.javascript.ScriptRuntime;
import org.htmlunit.corejs.javascript.Scriptable;
import org.htmlunit.corejs.javascript.ScriptableObject;
import org.htmlunit.corejs.javascript.TopLevel;
/**
* A JavaScript object for {@code MutationObserver}.
*
* @author Ahmed Ashour
* @author Ronald Brill
* @author Atsushi Nakagawa
*/
@JsxClass
public class MutationObserver extends HtmlUnitScriptable implements HtmlAttributeChangeListener,
CharacterDataChangeListener {
private Function function_;
private Node node_;
private boolean attaributes_;
private boolean attributeOldValue_;
private NativeArray attributeFilter_;
private boolean characterData_;
private boolean characterDataOldValue_;
private boolean subtree_;
/**
* Creates an instance.
*/
public MutationObserver() {
}
/**
* Creates an instance.
* @param function the function to observe
*/
@JsxConstructor
public MutationObserver(final Function function) {
function_ = function;
}
/**
* Registers the {@link MutationObserver} instance to receive notifications of DOM mutations on the specified node.
* @param node the node
* @param options the options
*/
@JsxFunction
public void observe(final Node node, final NativeObject options) {
if (node == null) {
throw Context.throwAsScriptRuntimeEx(new IllegalArgumentException("Node is undefined"));
}
if (options == null) {
throw Context.throwAsScriptRuntimeEx(new IllegalArgumentException("Options is undefined"));
}
node_ = node;
attaributes_ = Boolean.TRUE.equals(options.get("attributes"));
attributeOldValue_ = Boolean.TRUE.equals(options.get("attributeOldValue"));
characterData_ = Boolean.TRUE.equals(options.get("characterData"));
characterDataOldValue_ = Boolean.TRUE.equals(options.get("characterDataOldValue"));
subtree_ = Boolean.TRUE.equals(options.get("subtree"));
attributeFilter_ = (NativeArray) options.get("attributeFilter");
final boolean childList = Boolean.TRUE.equals(options.get("childList"));
if (!attaributes_ && !childList && !characterData_) {
throw Context.throwAsScriptRuntimeEx(new IllegalArgumentException(
"One of childList, attributes, od characterData must be set"));
}
if (attaributes_ && node_.getDomNodeOrDie() instanceof HtmlElement) {
((HtmlElement) node_.getDomNodeOrDie()).addHtmlAttributeChangeListener(this);
}
if (characterData_) {
node.getDomNodeOrDie().addCharacterDataChangeListener(this);
}
}
/**
* Stops the MutationObserver instance from receiving notifications of DOM mutations.
*/
@JsxFunction
public void disconnect() {
if (attaributes_ && node_.getDomNodeOrDie() instanceof HtmlElement) {
((HtmlElement) node_.getDomNodeOrDie()).removeHtmlAttributeChangeListener(this);
}
if (characterData_) {
node_.getDomNodeOrDie().removeCharacterDataChangeListener(this);
}
}
/**
* Empties the MutationObserver instance's record queue and returns what was in there.
* @return an {@link NativeArray} of {@link MutationRecord}s
*/
@JsxFunction
public NativeArray takeRecords() {
return new NativeArray(0);
}
/**
* {@inheritDoc}
*/
@Override
public void characterDataChanged(final CharacterDataChangeEvent event) {
final ScriptableObject target = event.getCharacterData().getScriptableObject();
if (subtree_ || target == node_) {
final MutationRecord mutationRecord = new MutationRecord();
final Scriptable scope = getParentScope();
mutationRecord.setParentScope(scope);
mutationRecord.setPrototype(getPrototype(mutationRecord.getClass()));
mutationRecord.setType("characterData");
mutationRecord.setTarget(target);
if (characterDataOldValue_) {
mutationRecord.setOldValue(event.getOldValue());
}
final Window window = getWindow();
final HtmlPage owningPage = (HtmlPage) window.getDocument().getPage();
final JavaScriptEngine jsEngine =
(JavaScriptEngine) window.getWebWindow().getWebClient().getJavaScriptEngine();
jsEngine.addPostponedAction(new PostponedAction(owningPage, "MutationObserver.characterDataChanged") {
@Override
public void execute() throws Exception {
final NativeArray array = new NativeArray(new Object[] {mutationRecord});
ScriptRuntime.setBuiltinProtoAndParent(array, scope, TopLevel.Builtins.Array);
jsEngine.callFunction(owningPage, function_, scope, MutationObserver.this, new Object[] {array});
}
});
}
}
/**
* {@inheritDoc}
*/
@Override
public void attributeAdded(final HtmlAttributeChangeEvent event) {
}
/**
* {@inheritDoc}
*/
@Override
public void attributeRemoved(final HtmlAttributeChangeEvent event) {
}
/**
* {@inheritDoc}
*/
@Override
public void attributeReplaced(final HtmlAttributeChangeEvent event) {
final HtmlElement target = event.getHtmlElement();
if (subtree_ || target == node_.getDomNodeOrDie()) {
final String attributeName = event.getName();
if (attributeFilter_ == null || attributeFilter_.contains(attributeName)) {
final MutationRecord mutationRecord = new MutationRecord();
final Scriptable scope = getParentScope();
mutationRecord.setParentScope(scope);
mutationRecord.setPrototype(getPrototype(mutationRecord.getClass()));
mutationRecord.setAttributeName(attributeName);
mutationRecord.setType("attributes");
mutationRecord.setTarget(target.getScriptableObject());
if (attributeOldValue_) {
mutationRecord.setOldValue(event.getValue());
}
final Window window = getWindow();
final HtmlPage owningPage = (HtmlPage) window.getDocument().getPage();
final JavaScriptEngine jsEngine =
(JavaScriptEngine) window.getWebWindow().getWebClient().getJavaScriptEngine();
jsEngine.addPostponedAction(new PostponedAction(owningPage, "MutationObserver.attributeReplaced") {
@Override
public void execute() throws Exception {
final NativeArray array = new NativeArray(new Object[] {mutationRecord});
ScriptRuntime.setBuiltinProtoAndParent(array, scope, TopLevel.Builtins.Array);
jsEngine.callFunction(owningPage, function_, scope,
MutationObserver.this, new Object[] {array});
}
});
}
}
}
}