001: package com.meterware.httpunit;
002:
003: /********************************************************************************************************************
004: * $Id: WebWindow.java,v 1.19 2004/06/30 23:54:49 russgold Exp $
005: *
006: * Copyright (c) 2002-2004, Russell Gold
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
009: * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
010: * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
011: * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
012: *
013: * The above copyright notice and this permission notice shall be included in all copies or substantial portions
014: * of the Software.
015: *
016: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
017: * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
018: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
019: * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
020: * DEALINGS IN THE SOFTWARE.
021: *
022: *******************************************************************************************************************/
023: import java.io.IOException;
024: import java.net.HttpURLConnection;
025: import java.net.MalformedURLException;
026: import java.util.List;
027:
028: import org.xml.sax.SAXException;
029:
030: /**
031: * A window managed by a {@link com.meterware.httpunit.WebClient WebClient}.
032: *
033: * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
034: **/
035: public class WebWindow {
036:
037: /** The client which created this window. **/
038: private WebClient _client;
039:
040: /** A map of frame names to current contents. **/
041: private FrameHolder _frameContents;
042:
043: /** The name of the window, set via JavaScript. **/
044: private String _name = "";
045:
046: /** The web response containing the reference that opened this window **/
047: private WebResponse _opener;
048:
049: /** True if this window has been closed. **/
050: private boolean _closed;
051:
052: static final String NO_NAME = "$$HttpUnit_Window$$_";
053:
054: /**
055: * Returns the web client associated with this window.
056: */
057: public WebClient getClient() {
058: return _client;
059: }
060:
061: /**
062: * Returns true if this window has been closed.
063: */
064: public boolean isClosed() {
065: return _closed;
066: }
067:
068: /**
069: * Closes this window.
070: */
071: public void close() {
072: if (!_closed)
073: _client.close(this );
074: _closed = true;
075: }
076:
077: /**
078: * Returns the name of this window. Windows created through normal HTML or browser commands have empty names,
079: * but JavaScript can set the name. A name may be used as a target for a request.
080: */
081: public String getName() {
082: return _name;
083: }
084:
085: /**
086: * Returns the web response that contained the script which opened this window.
087: */
088: public WebResponse getOpener() {
089: return _opener;
090: }
091:
092: /**
093: * Submits a GET method request and returns a response.
094: * @exception SAXException thrown if there is an error parsing the retrieved page
095: **/
096: public WebResponse getResponse(String urlString)
097: throws MalformedURLException, IOException, SAXException {
098: return getResponse(new GetMethodWebRequest(urlString));
099: }
100:
101: /**
102: * Submits a web request and returns a response. This is an alternate name for the getResponse method.
103: */
104: public WebResponse sendRequest(WebRequest request)
105: throws MalformedURLException, IOException, SAXException {
106: return getResponse(request);
107: }
108:
109: /**
110: * Submits a web request and returns a response, using all state developed so far as stored in
111: * cookies as requested by the server.
112: * @exception SAXException thrown if there is an error parsing the retrieved page
113: **/
114: public WebResponse getResponse(WebRequest request)
115: throws MalformedURLException, IOException, SAXException {
116: final RequestContext requestContext = new RequestContext();
117: final WebResponse response = getSubframeResponse(request,
118: requestContext);
119: requestContext.runScripts();
120: return response == null ? null : response.getWindow()
121: .getFrameContents(response.getFrame()); // javascript might replace the response in its frame
122: }
123:
124: WebResponse getSubframeResponse(WebRequest request,
125: RequestContext requestContext) throws IOException,
126: SAXException {
127: WebResponse response = getResource(request);
128:
129: return response == null ? null : updateWindow(request
130: .getTarget(), response, requestContext);
131: }
132:
133: /**
134: * Updates this web client based on a received response. This includes updating
135: * cookies and frames.
136: **/
137: WebResponse updateWindow(String requestTarget,
138: WebResponse response, RequestContext requestContext)
139: throws MalformedURLException, IOException, SAXException {
140: _client.updateClient(response);
141: if (getClient().getClientProperties().isAutoRefresh()
142: && response.getRefreshRequest() != null) {
143: return getResponse(response.getRefreshRequest());
144: } else if (shouldFollowRedirect(response)) {
145: delay(HttpUnitOptions.getRedirectDelay());
146: return getResponse(new RedirectWebRequest(response));
147: } else {
148: _client.updateFrameContents(this , requestTarget, response,
149: requestContext);
150: return response;
151: }
152: }
153:
154: /**
155: * Returns the resource specified by the request. Does not update the window or load included framesets.
156: * May return null if the resource is a JavaScript URL which would normally leave the client unchanged.
157: */
158: public WebResponse getResource(WebRequest request)
159: throws IOException {
160: _client.tellListeners(request);
161:
162: WebResponse response = null;
163: String urlString = request.getURLString().trim();
164: if (urlString.startsWith("about:")) {
165: response = new DefaultWebResponse(_client, _frameContents
166: .getTargetFrame(request), null, "");
167: } else if (!HttpUnitUtils.isJavaScriptURL(urlString)) {
168: response = _client.newResponse(request, _frameContents
169: .getTargetFrame(request));
170: } else {
171: WebRequestSource wrs = request.getWebRequestSource();
172: String result = (wrs == null) ? getCurrentPage()
173: .getScriptableObject()
174: .evaluateExpression(urlString) : wrs
175: .getScriptableDelegate().evaluateExpression(
176: urlString);
177: if (result != null)
178: response = new DefaultWebResponse(_client,
179: _frameContents.getTargetFrame(request), request
180: .getURL(), result);
181: }
182:
183: if (response != null)
184: _client.tellListeners(response);
185: return response;
186: }
187:
188: /**
189: * Returns the name of the currently active frames.
190: **/
191: public String[] getFrameNames() {
192: final List names = _frameContents.getActiveFrameNames();
193: return (String[]) names.toArray(new String[names.size()]);
194: }
195:
196: /**
197: * Returns true if the specified frame name is defined in this window.
198: */
199: public boolean hasFrame(String frameName) {
200: return _frameContents.get(frameName) != null;
201: }
202:
203: boolean hasFrame(FrameSelector frame) {
204: return _frameContents.get(frame) != null;
205: }
206:
207: /**
208: * Returns the response associated with the specified frame name.
209: * Throws a runtime exception if no matching frame is defined.
210: **/
211: public WebResponse getFrameContents(String frameName) {
212: WebResponse response = _frameContents.get(frameName);
213: if (response == null)
214: throw new NoSuchFrameException(frameName);
215: return response;
216: }
217:
218: /**
219: * Returns the response associated with the specified frame target.
220: * Throws a runtime exception if no matching frame is defined.
221: **/
222: WebResponse getFrameContents(FrameSelector targetFrame) {
223: return _frameContents.getFrameContents(targetFrame);
224: }
225:
226: WebResponse getSubframeContents(FrameSelector frame,
227: String subFrameName) {
228: return _frameContents.getSubframeContents(frame, subFrameName);
229: }
230:
231: WebResponse getParentFrameContents(FrameSelector frame) {
232: return _frameContents.getParentFrameContents(frame);
233: }
234:
235: /**
236: * Returns the response representing the main page in this window.
237: */
238: public WebResponse getCurrentPage() {
239: return getFrameContents(WebRequest.TOP_FRAME);
240: }
241:
242: WebWindow(WebClient client) {
243: _client = client;
244: _frameContents = new FrameHolder(this );
245: _name = NO_NAME + _client.getOpenWindows().length;
246: }
247:
248: WebWindow(WebClient client, WebResponse opener) {
249: this (client);
250: _opener = opener;
251: }
252:
253: void updateFrameContents(WebResponse response,
254: RequestContext requestContext) throws IOException,
255: SAXException {
256: response.setWindow(this );
257: _frameContents.updateFrames(response, response.getFrame(),
258: requestContext);
259: }
260:
261: void setName(String name) {
262: _name = name;
263: }
264:
265: /**
266: * Delays the specified amount of time.
267: **/
268: private void delay(int numMilliseconds) {
269: if (numMilliseconds == 0)
270: return;
271: try {
272: Thread.sleep(numMilliseconds);
273: } catch (InterruptedException e) {
274: // ignore the exception
275: }
276: }
277:
278: private boolean shouldFollowRedirect(WebResponse response) {
279: return getClient().getClientProperties().isAutoRedirect()
280: && response.getResponseCode() >= HttpURLConnection.HTTP_MOVED_PERM
281: && response.getResponseCode() <= HttpURLConnection.HTTP_MOVED_TEMP
282: && response.getHeaderField("Location") != null;
283: }
284:
285: FrameSelector getTopFrame() {
286: return _frameContents.getTopFrame();
287: }
288:
289: FrameSelector getFrame(String target) {
290: return _frameContents.getFrame(target);
291: }
292:
293: }
|