/*
GNU GENERAL LICENSE
Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public
License as published by the Free Software Foundation; either
verion 3 of the License, or (at your option) any later version.
This program 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
General License for more details.
You should have received a copy of the GNU General Public
along with this program. If not, see <http://www.gnu.org/licenses/>.
Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it
*/
package org.lobobrowser.html.js.xml;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lobobrowser.html.js.Executor;
import org.lobobrowser.http.HttpRequest;
import org.lobobrowser.http.ReadyState;
import org.lobobrowser.http.UserAgentContext;
import org.lobobrowser.js.AbstractScriptableDelegate;
import org.lobobrowser.js.JavaScript;
import org.lobobrowser.util.Urls;
import org.lobobrowser.w3c.events.Event;
import org.lobobrowser.w3c.events.EventListener;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.w3c.dom.Document;
import org.w3c.dom.events.EventException;
/**
* The Class XMLHttpRequest.
*/
public class XMLHttpRequest extends AbstractScriptableDelegate {
/** The Constant logger. */
private static final Logger logger = LogManager.getLogger(XMLHttpRequest.class.getName());
/** The request. */
private final HttpRequest request;
/** The pcontext. */
private final UserAgentContext pcontext;
/** The scope. */
private final Scriptable scope;
/** The code source. */
private final URL codeSource;
/** The onreadystatechange. */
private Function onreadystatechange;
/** The listener added. */
private boolean listenerAdded;
/**
* Instantiates a new XML http request.
*
* @param pcontext
* the pcontext
* @param codeSource
* the code source
* @param scope
* the scope
*/
public XMLHttpRequest(UserAgentContext pcontext, URL codeSource, Scriptable scope) {
this.request = pcontext.createHttpRequest();
this.pcontext = pcontext;
this.scope = scope;
this.codeSource = codeSource;
}
/**
* Abort.
*/
public void abort() {
request.abort();
}
/**
* Gets the all response headers.
*
* @return the all response headers
*/
public String getAllResponseHeaders() {
return request.getAllResponseHeaders();
}
/**
* Gets the ready state.
*
* @return the ready state
*/
public ReadyState getReadyState() {
return request.getReadyState();
}
/**
* Gets the response bytes.
*
* @return the response bytes
*/
public byte[] getResponseBytes() {
return request.getResponseBytes();
}
/**
* Sets the request header.
*
* @param header
* the header
* @param value
* the value
*/
public void setRequestHeader(String header, String value) {
request.setRequestHeader(header, value);
}
/**
* Gets the response header.
*
* @param headerName
* the header name
* @return the response header
*/
public String getResponseHeader(String headerName) {
return request.getResponseHeader(headerName);
}
/**
* Gets the response text.
*
* @return the response text
*/
public String getResponseText() {
return request.getResponseText();
}
/**
* Gets the response xml.
*
* @return the response xml
*/
public Document getResponseXML() {
return request.getResponseXML();
}
/**
* Gets the status.
*
* @return the status
*/
public int getStatus() {
return request.getStatus();
}
/**
* Gets the status text.
*
* @return the status text
*/
public String getStatusText() {
return request.getStatusText();
}
/**
* Gets the full url.
*
* @param relativeUrl
* the relative url
* @return the full url
* @throws MalformedURLException
* the malformed url exception
* @throws UnsupportedEncodingException
*/
private URL getFullURL(String relativeUrl) throws MalformedURLException, UnsupportedEncodingException {
return Urls.createURL(this.codeSource, relativeUrl);
}
/**
* Open.
*
* @param method
* the method
* @param url
* the url
* @param asyncFlag
* the async flag
* @param userName
* the user name
* @param password
* the password
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void open(String method, String url, boolean asyncFlag, String userName, String password)
throws IOException {
request.open(method, this.getFullURL(url), asyncFlag, userName, password);
}
/**
* Open.
*
* @param method
* the method
* @param url
* the url
* @param asyncFlag
* the async flag
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void open(String method, String url, boolean asyncFlag) throws IOException {
request.open(method, this.getFullURL(url), asyncFlag);
}
/**
* Open.
*
* @param method
* the method
* @param url
* the url
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void open(String method, String url) throws IOException {
request.open(method, this.getFullURL(url));
}
/**
* Send.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void send() throws IOException {
send((String) null);
}
/**
* Send.
*
* @param data
* the data
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void send(Document data) throws IOException {
send(data.getTextContent());
}
/**
* Send.
*
* @param content
* the content
* @throws IOException
* Signals that an I/O exception has occurred.
*/
public void send(String content) throws IOException {
request.send(content);
}
/**
* Gets the onreadystatechange.
*
* @return the onreadystatechange
*/
public Function getOnreadystatechange() {
synchronized (this) {
return this.onreadystatechange;
}
}
/**
* Adds the event listener.
*
* @param arg0
* the arg0
* @param arg1
* the arg1
* @param arg2
* the arg2
*/
public void addEventListener(String arg0, EventListener arg1, boolean arg2) {
// TODO Auto-generated method stub
}
/**
* Dispatch event.
*
* @param arg0
* the arg0
* @return true, if successful
* @throws EventException
* the event exception
*/
public boolean dispatchEvent(Event arg0) throws EventException {
// TODO Auto-generated method stub
return false;
}
/**
* Removes the event listener.
*
* @param arg0
* the arg0
* @param arg1
* the arg1
* @param arg2
* the arg2
*/
public void removeEventListener(String arg0, EventListener arg1, boolean arg2) {
// TODO Auto-generated method stub
}
/**
* Gets the timeout.
*
* @return the timeout
*/
public int getTimeout() {
// TODO Auto-generated method stub
return 0;
}
/**
* Sets the timeout.
*
* @param timeout
* the new timeout
*/
public void setTimeout(int timeout) {
// TODO Auto-generated method stub
}
/**
* Gets the with credentials.
*
* @return the with credentials
*/
public boolean getWithCredentials() {
// TODO Auto-generated method stub
return false;
}
/**
* Sets the with credentials.
*
* @param withCredentials
* the new with credentials
*/
public void setWithCredentials(boolean withCredentials) {
// TODO Auto-generated method stub
}
/**
* Override mime type.
*
* @param mime
* the mime
*/
public void overrideMimeType(String mime) {
// TODO Auto-generated method stub
}
/**
* Gets the response body.
*
* @return the response body
*/
public byte[] getResponseBody() {
// TODO Auto-generated method stub
return null;
}
/**
* Sets the onreadystatechange.
*
* @param value
* the new onreadystatechange
*/
public void setOnreadystatechange(final Function value) {
synchronized (this) {
this.onreadystatechange = value;
if ((value != null) && !this.listenerAdded) {
this.request.addReadyStateChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent arg0) {
executeReadyStateChange();
}
});
this.listenerAdded = true;
}
}
}
/**
* Execute ready state change.
*/
private void executeReadyStateChange() {
// Not called in GUI thread to ensure consistency of readyState.
try {
Function f = XMLHttpRequest.this.getOnreadystatechange();
if (f != null) {
Context ctx = Executor.createContext(this.codeSource, this.pcontext);
try {
Scriptable newScope = (Scriptable) JavaScript.getInstance().getJavascriptObject(XMLHttpRequest.this,
this.scope);
f.call(ctx, newScope, newScope, new Object[0]);
} finally {
Context.exit();
}
}
} catch (Exception err) {
logger.error("Error processing ready state change.", err);
}
}
}