All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.htmlunit.javascript.host.dom.MutationObserver Maven / Gradle / Ivy

Go to download

XLT (Xceptance LoadTest) is an extensive load and performance test tool developed and maintained by Xceptance.

There is a newer version: 8.4.1
Show newest version
/*
 * 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});
                    }
                });
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy