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

com.gargoylesoftware.htmlunit.html.HtmlScript Maven / Gradle / Ivy

Go to download

Vaadin is a web application framework for Rich Internet Applications (RIA). Vaadin enables easy development and maintenance of fast and secure rich web applications with a stunning look and feel and a wide browser support. It features a server-side architecture with the majority of the logic running on the server. Ajax technology is used at the browser-side to ensure a rich and interactive user experience.

There is a newer version: 1.2.0
Show newest version
/*
 * Copyright (c) 2002-2011 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
 * http://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 com.gargoylesoftware.htmlunit.html;

import java.io.PrintWriter;
import java.util.Map;

import net.sourceforge.htmlunit.corejs.javascript.BaseFunction;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.BrowserVersionFeatures;
import com.gargoylesoftware.htmlunit.ElementNotFoundException;
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.SgmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlPage.JavaScriptLoadResult;
import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
import com.gargoylesoftware.htmlunit.javascript.PostponedAction;
import com.gargoylesoftware.htmlunit.javascript.host.Event;
import com.gargoylesoftware.htmlunit.javascript.host.EventHandler;
import com.gargoylesoftware.htmlunit.javascript.host.Window;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLScriptElement;
import com.gargoylesoftware.htmlunit.protocol.javascript.JavaScriptURLConnection;
import com.gargoylesoftware.htmlunit.xml.XmlPage;

/**
 * Wrapper for the HTML element "script".
* When a script tag references an external script (with attribute src) it gets executed when the node * is added to the DOM tree. When the script code is nested, it gets executed when the text node * containing the script is added to the HtmlScript.
* The ScriptFilter feature of NekoHtml can't be used because it doesn't allow immediate access to the DOM * (i.e. document.write("<span id='mySpan'/>"); document.getElementById("mySpan").tagName; * can't work with a filter). * * @version $Revision: 6357 $ * @author Mike Bowler * @author Christian Sell * @author Marc Guillemot * @author David K. Taylor * @author Ahmed Ashour * @author Daniel Gredler * @author Dmitri Zoubkov * @author Sudhan Moghe * @author Ronald Brill * @see DOM Level 1 * @see DOM Level 2 */ public class HtmlScript extends HtmlElement { private static final Log LOG = LogFactory.getLog(HtmlScript.class); /** The HTML tag represented by this element. */ public static final String TAG_NAME = "script"; /** Invalid source attribute which should be ignored (used by JS libraries like jQuery). */ private static final String SLASH_SLASH_COLON = "//:"; /** * Creates an instance of HtmlScript * * @param namespaceURI the URI that identifies an XML namespace * @param qualifiedName the qualified name of the element type to instantiate * @param page the HtmlPage that contains this element * @param attributes the initial attributes */ HtmlScript(final String namespaceURI, final String qualifiedName, final SgmlPage page, final Map attributes) { super(namespaceURI, qualifiedName, page, attributes); } /** * Returns the value of the attribute "charset". Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute "charset" * or an empty string if that attribute isn't defined. */ public final String getCharsetAttribute() { return getAttribute("charset"); } /** * Returns the value of the attribute "type". Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute "type" * or an empty string if that attribute isn't defined. */ public final String getTypeAttribute() { return getAttribute("type"); } /** * Returns the value of the attribute "language". Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute "language" * or an empty string if that attribute isn't defined. */ public final String getLanguageAttribute() { return getAttribute("language"); } /** * Returns the value of the attribute "src". Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute "src" * or an empty string if that attribute isn't defined. */ public final String getSrcAttribute() { return getAttribute("src"); } /** * Returns the value of the attribute "event". * @return the value of the attribute "event" */ public final String getEventAttribute() { return getAttribute("event"); } /** * Returns the value of the attribute "for". * @return the value of the attribute "for" */ public final String getHtmlForAttribute() { return getAttribute("for"); } /** * Returns the value of the attribute "defer". Refer to the * HTML 4.01 * documentation for details on the use of this attribute. * * @return the value of the attribute "defer" * or an empty string if that attribute isn't defined. */ public final String getDeferAttribute() { return getAttribute("defer"); } /** * Returns true if this script is deferred. * @return true if this script is deferred */ protected boolean isDeferred() { return getDeferAttribute() != ATTRIBUTE_NOT_DEFINED; } /** * {@inheritDoc} */ @Override public boolean mayBeDisplayed() { return false; } /** * If setting the src attribute, this method executes the new JavaScript if necessary * (behavior varies by browser version). {@inheritDoc} */ @Override public void setAttributeNS(final String namespaceURI, final String qualifiedName, final String attributeValue) { final String oldValue = getAttributeNS(namespaceURI, qualifiedName); super.setAttributeNS(namespaceURI, qualifiedName, attributeValue); if (namespaceURI == null && "src".equals(qualifiedName)) { final boolean ie = getPage().getWebClient().getBrowserVersion().hasFeature(BrowserVersionFeatures.GENERATED_5); if (ie || (oldValue.length() == 0 && getFirstChild() == null)) { // Always execute if IE; // if FF, only execute if the "src" attribute // was undefined and there was no inline code. executeScriptIfNeeded(); } } } /** * Executes the onreadystatechange handler when simulating IE, as well as executing * the script itself, if necessary. {@inheritDoc} */ @Override protected void onAllChildrenAddedToPage(final boolean postponed) { if (getOwnerDocument() instanceof XmlPage) { return; } if (LOG.isDebugEnabled()) { LOG.debug("Script node added: " + asXml()); } final PostponedAction action = new PostponedAction(getPage()) { @Override public void execute() { final boolean ie = getPage().getWebClient().getBrowserVersion().hasFeature(BrowserVersionFeatures.GENERATED_6); if (ie) { if (!isDeferred()) { if (!SLASH_SLASH_COLON.equals(getSrcAttribute())) { setAndExecuteReadyState(READY_STATE_LOADING); executeScriptIfNeeded(); setAndExecuteReadyState(READY_STATE_LOADED); } else { setAndExecuteReadyState(READY_STATE_COMPLETE); executeScriptIfNeeded(); } } } else { executeScriptIfNeeded(); } } }; if (postponed && StringUtils.isBlank(getTextContent())) { final JavaScriptEngine engine = getPage().getWebClient().getJavaScriptEngine(); engine.addPostponedAction(action); } else { try { action.execute(); } catch (final Exception e) { if (e instanceof RuntimeException) { throw (RuntimeException) e; } throw new RuntimeException(e); } } } /** * Executes this script node as inline script if necessary and/or possible. */ private void executeInlineScriptIfNeeded() { if (!isExecutionNeeded()) { return; } final String src = getSrcAttribute(); if (src != DomElement.ATTRIBUTE_NOT_DEFINED) { return; } final String forr = getHtmlForAttribute(); String event = getEventAttribute(); // The event name can be like "onload" or "onload()". if (event.endsWith("()")) { event = event.substring(0, event.length() - 2); } final boolean ie = getPage().getWebClient().getBrowserVersion().hasFeature(BrowserVersionFeatures.GENERATED_7); final String scriptCode = getScriptCode(); if (ie && event != ATTRIBUTE_NOT_DEFINED && forr != ATTRIBUTE_NOT_DEFINED) { if ("window".equals(forr)) { // everything fine, accepted by IE and FF final Window window = (Window) getPage().getEnclosingWindow().getScriptObject(); final BaseFunction function = new EventHandler(this, event, scriptCode); window.jsxFunction_attachEvent(event, function); } else { try { final HtmlElement elt = ((HtmlPage) getPage()).getHtmlElementById(forr); elt.setEventHandler(event, scriptCode); } catch (final ElementNotFoundException e) { LOG.warn(" if (browser.hasFeature(BrowserVersionFeatures.HTMLSCRIPT_SRC_JAVASCRIPT)) { String code = StringUtils.removeStart(src, JavaScriptURLConnection.JAVASCRIPT_PREFIX).trim(); final int len = code.length(); if (len > 2) { if ((code.charAt(0) == '\'' && code.charAt(len - 1) == '\'') || (code.charAt(0) == '"' && code.charAt(len - 1) == '"')) { code = code.substring(1, len - 1); if (LOG.isDebugEnabled()) { LOG.debug("Executing JavaScript: " + code); } page.executeJavaScriptIfPossible(code, code, getStartLineNumber()); } } } } else { // if (LOG.isDebugEnabled()) { LOG.debug("Loading external JavaScript: " + src); } try { final JavaScriptLoadResult result = page.loadExternalJavaScriptFile(src, getCharsetAttribute()); if (result == JavaScriptLoadResult.SUCCESS) { executeEventIfBrowserHasFeature(Event.TYPE_LOAD, BrowserVersionFeatures.EVENT_ONLOAD_EXTERNAL_JAVASCRIPT); } else if (result == JavaScriptLoadResult.DOWNLOAD_ERROR) { executeEventIfBrowserHasFeature(Event.TYPE_ERROR, BrowserVersionFeatures.EVENT_ONERROR_EXTERNAL_JAVASCRIPT); } } catch (final FailingHttpStatusCodeException e) { executeEventIfBrowserHasFeature(Event.TYPE_ERROR, BrowserVersionFeatures.EVENT_ONERROR_EXTERNAL_JAVASCRIPT); throw e; } } } else if (getFirstChild() != null) { // executeInlineScriptIfNeeded(); } } private void executeEventIfBrowserHasFeature(final String type, final BrowserVersionFeatures feature) { if (getPage().getWebClient().getBrowserVersion().hasFeature(feature)) { final HTMLScriptElement script = (HTMLScriptElement) getScriptObject(); final Event event = new Event(HtmlScript.this, type); script.executeEvent(event); } } /** * Indicates if script execution is necessary and/or possible. * * @return true if the script should be executed */ private boolean isExecutionNeeded() { final SgmlPage page = getPage(); if (!isDirectlyAttachedToPage()) { return false; } // If JavaScript is disabled, we don't need to execute. if (!page.getWebClient().isJavaScriptEnabled()) { return false; } // If innerHTML or outerHTML is being parsed if (page instanceof HtmlPage && ((HtmlPage) page).isParsingHtmlSnippet()) { return false; } // If the script node is nested in an iframe, a noframes, or a noscript node, we don't need to execute. for (DomNode o = this; o != null; o = o.getParentNode()) { if (o instanceof HtmlInlineFrame || o instanceof HtmlNoFrames) { return false; } } // If the underlying page no longer owns its window, the client has moved on (possibly // because another script set window.location.href), and we don't need to execute. if (page.getEnclosingWindow() != null && page.getEnclosingWindow().getEnclosedPage() != page) { return false; } // If the script language is not JavaScript, we can't execute. if (!isJavaScript(getTypeAttribute(), getLanguageAttribute())) { final String t = getTypeAttribute(); final String l = getLanguageAttribute(); LOG.warn("Script is not JavaScript (type: " + t + ", language: " + l + "). Skipping execution."); return false; } // If the script's root ancestor node is not the page, the the script is not a part of the page. // If it isn't yet part of the page, don't execute the script; it's probably just being cloned. DomNode root = this; while (root.getParentNode() != null) { root = root.getParentNode(); } if (root != getPage()) { return false; } return true; } /** * Returns true if a script with the specified type and language attributes is actually JavaScript. * According to W3C recommendation * are content types case insensitive. * IE supports only a limited number of values for the type attribute. For testing you can * use http://www.robinlionheart.com/stds/html4/scripts. * @param typeAttribute the type attribute specified in the script tag * @param languageAttribute the language attribute specified in the script tag * @return true if the script is JavaScript */ boolean isJavaScript(final String typeAttribute, final String languageAttribute) { if (StringUtils.isNotEmpty(typeAttribute)) { if ("text/javascript".equalsIgnoreCase(typeAttribute) || "text/ecmascript".equalsIgnoreCase(typeAttribute)) { return true; } final boolean appJavascriptSupported = getPage().getWebClient().getBrowserVersion() .hasFeature(BrowserVersionFeatures.HTMLSCRIPT_APPLICATION_JAVASCRIPT); if (appJavascriptSupported && ("application/javascript".equalsIgnoreCase(typeAttribute) || "application/ecmascript".equalsIgnoreCase(typeAttribute) || "application/x-javascript".equalsIgnoreCase(typeAttribute))) { return true; } return false; } if (StringUtils.isNotEmpty(languageAttribute)) { return StringUtils.startsWithIgnoreCase(languageAttribute, "javascript"); } return true; } /** * Sets the readyState to the specified state and executes the * onreadystatechange handler when simulating IE. * @param state this script ready state */ protected void setAndExecuteReadyState(final String state) { if (getPage().getWebClient().getBrowserVersion() .hasFeature(BrowserVersionFeatures.EVENT_ONREADY_STATE_CHANGE)) { setReadyState(state); final HTMLScriptElement script = (HTMLScriptElement) getScriptObject(); final Event event = new Event(this, Event.TYPE_READY_STATE_CHANGE); script.executeEvent(event); } } /** * @see com.gargoylesoftware.htmlunit.html.HtmlInput#asText() * @return an empty string as the content of script is not visible by itself */ // we need to preserve this method as it is there since many versions with the above documentation. @Override public String asText() { return ""; } /** * Indicates if a node without children should be written in expanded form as XML * (i.e. with closing tag rather than with "/>") * @return true to make generated XML readable as HTML */ @Override protected boolean isEmptyXmlTagExpanded() { return true; } /** * {@inheritDoc} */ @Override protected void printChildrenAsXml(final String indent, final PrintWriter printWriter) { final DomCharacterData textNode = (DomCharacterData) getFirstChild(); if (textNode != null) { printWriter.println("//"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy