/*
GNU LESSER GENERAL PUBLIC LICENSE
Copyright (C) 2006 The Lobo Project
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Contact info: lobochief@users.sourceforge.net
*/
/*
* Created on Oct 8, 2005
*/
package org.lobobrowser.html.domimpl;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.logging.Level;
import org.lobobrowser.html.js.Executor;
import org.lobobrowser.html.js.Window;
import org.lobobrowser.html.js.Window.JSRunnableTask;
import org.lobobrowser.ua.NetworkRequest;
import org.lobobrowser.ua.UserAgentContext;
import org.lobobrowser.ua.UserAgentContext.Request;
import org.lobobrowser.ua.UserAgentContext.RequestKind;
import org.lobobrowser.util.SecurityUtil;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.w3c.dom.Document;
import org.w3c.dom.html.HTMLScriptElement;
public class HTMLScriptElementImpl extends HTMLElementImpl implements HTMLScriptElement {
public HTMLScriptElementImpl() {
super("SCRIPT", true);
}
public HTMLScriptElementImpl(final String name) {
super(name, true);
}
private String text;
public String getText() {
final String t = this.text;
if (t == null) {
return this.getRawInnerText(true);
} else {
return t;
}
}
public void setText(final String text) {
this.text = text;
}
public String getHtmlFor() {
return this.getAttribute("htmlFor");
}
public void setHtmlFor(final String htmlFor) {
this.setAttribute("htmlFor", htmlFor);
}
public String getEvent() {
return this.getAttribute("event");
}
public void setEvent(final String event) {
this.setAttribute("event", event);
}
private boolean defer;
public boolean getDefer() {
return this.defer;
}
public void setDefer(final boolean defer) {
this.defer = defer;
}
public String getSrc() {
return this.getAttribute("src");
}
public void setSrc(final String src) {
this.setAttribute("src", src);
}
public String getType() {
return this.getAttribute("type");
}
public void setType(final String type) {
this.setAttribute("type", type);
}
private static final String[] jsTypes = {
"application/ecmascript",
"application/javascript",
"application/x-ecmascript",
"application/x-javascript",
"text/ecmascript",
"text/javascript",
"text/javascript1.0",
"text/javascript1.1",
"text/javascript1.2",
"text/javascript1.3",
"text/javascript1.4",
"text/javascript1.5",
"text/jscript",
"text/livescript",
"text/x-ecmascript",
"text/x-javascript"
};
protected final void processScript() {
final String scriptType = getType();
if (scriptType != null) {
if (Arrays.stream(jsTypes).noneMatch(e -> e.equals(scriptType))) {
((HTMLDocumentImpl) HTMLScriptElementImpl.this.document).markJobsFinished(1, false);
return;
}
}
final UserAgentContext bcontext = this.getUserAgentContext();
if (bcontext == null) {
throw new IllegalStateException("No user agent context.");
}
final Document docObj = this.document;
if (!(docObj instanceof HTMLDocumentImpl)) {
throw new IllegalStateException("no valid document");
}
final HTMLDocumentImpl doc = (HTMLDocumentImpl) docObj;
if (bcontext.isScriptingEnabled()) {
String text;
final String scriptURI;
int baseLineNumber;
final String src = this.getSrc();
if (src == null) {
final Request request = new Request(doc.getDocumentURL(), RequestKind.JavaScript);
if (bcontext.isRequestPermitted(request)) {
text = this.getText();
scriptURI = doc.getBaseURI();
baseLineNumber = 1; // TODO: Line number of inner text??
} else {
text = null;
scriptURI = null;
baseLineNumber = -1;
}
} else {
this.informExternalScriptLoading();
try {
final URL scriptURL = doc.getFullURL(src);
scriptURI = scriptURL.toExternalForm();
// Perform a synchronous request
final NetworkRequest request = bcontext.createHttpRequest();
SecurityUtil.doPrivileged(() -> {
// Code might have restrictions on accessing
// items from elsewhere.
try {
request.open("GET", scriptURI, false);
request.send(null, new Request(scriptURL, RequestKind.JavaScript));
} catch (final java.io.IOException thrown) {
logger.log(Level.WARNING, "processScript()", thrown);
}
return null;
});
final int status = request.getStatus();
if ((status != 200) && (status != 0)) {
this.warn("Script at [" + scriptURI + "] failed to load; HTTP status: " + status + ".");
return;
}
text = request.getResponseText();
baseLineNumber = 1;
} catch (final MalformedURLException mfe) {
throw new IllegalArgumentException(mfe);
}
}
final Window window = doc.getWindow();
if (text != null) {
final String textSub = text.substring(0, Math.min(50, text.length())).replaceAll("\n", "");
window.addJSTaskUnchecked(new JSRunnableTask(0, "script: " + textSub, new Runnable() {
public void run() {
// final Context ctx = Executor.createContext(HTMLScriptElementImpl.this.getDocumentURL(), bcontext);
final Context ctx = Executor.createContext(HTMLScriptElementImpl.this.getDocumentURL(), bcontext, window.getContextFactory());
try {
final Scriptable scope = window.getWindowScope();
if (scope == null) {
throw new IllegalStateException("Scriptable (scope) instance was null");
}
try {
ctx.evaluateString(scope, text, scriptURI, baseLineNumber, null);
// Why catch this?
// } catch (final EcmaError ecmaError) {
// logger.log(Level.WARNING,
// "Javascript error at " + ecmaError.sourceName() + ":" + ecmaError.lineNumber() + ": " + ecmaError.getMessage(),
// ecmaError);
} catch (final Exception err) {
Executor.logJSException(err);
}
} finally {
Context.exit();
doc.markJobsFinished(1, false);
}
}
}));
} else {
doc.markJobsFinished(1, false);
}
} else {
doc.markJobsFinished(1, false);
}
}
@Override
protected void appendInnerTextImpl(final StringBuffer buffer) {
// nop
}
@Override
protected void handleDocumentAttachmentChanged() {
if (isAttachedToDocument()) {
((HTMLDocumentImpl) document).addJob(() -> processScript(), false);
} else {
// TODO What does script element do when detached?
}
super.handleDocumentAttachmentChanged();
}
}