0001: /*
0002: * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
0003: *
0004: * Redistribution and use in source and binary forms, with or without
0005: * modification, are permitted provided that the following conditions are met:
0006: *
0007: * 1. Redistributions of source code must retain the above copyright notice,
0008: * this list of conditions and the following disclaimer.
0009: * 2. Redistributions in binary form must reproduce the above copyright notice,
0010: * this list of conditions and the following disclaimer in the documentation
0011: * and/or other materials provided with the distribution.
0012: * 3. The end-user documentation included with the redistribution, if any, must
0013: * include the following acknowledgment:
0014: *
0015: * "This product includes software developed by Gargoyle Software Inc.
0016: * (http://www.GargoyleSoftware.com/)."
0017: *
0018: * Alternately, this acknowledgment may appear in the software itself, if
0019: * and wherever such third-party acknowledgments normally appear.
0020: * 4. The name "Gargoyle Software" must not be used to endorse or promote
0021: * products derived from this software without prior written permission.
0022: * For written permission, please contact info@GargoyleSoftware.com.
0023: * 5. Products derived from this software may not be called "HtmlUnit", nor may
0024: * "HtmlUnit" appear in their name, without prior written permission of
0025: * Gargoyle Software Inc.
0026: *
0027: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
0028: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
0029: * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
0030: * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
0031: * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0032: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
0033: * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0034: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0035: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0036: * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0037: */
0038: package com.gargoylesoftware.htmlunit.javascript.host;
0039:
0040: import java.io.IOException;
0041: import java.net.MalformedURLException;
0042: import java.net.URL;
0043: import java.util.Arrays;
0044: import java.util.HashMap;
0045: import java.util.List;
0046: import java.util.Map;
0047:
0048: import org.apache.commons.collections.Transformer;
0049: import org.apache.commons.lang.StringUtils;
0050: import org.jaxen.JaxenException;
0051: import org.jaxen.XPath;
0052: import org.mozilla.javascript.Context;
0053: import org.mozilla.javascript.Function;
0054: import org.mozilla.javascript.Scriptable;
0055:
0056: import com.gargoylesoftware.htmlunit.AlertHandler;
0057: import com.gargoylesoftware.htmlunit.Assert;
0058: import com.gargoylesoftware.htmlunit.ConfirmHandler;
0059: import com.gargoylesoftware.htmlunit.ElementNotFoundException;
0060: import com.gargoylesoftware.htmlunit.Page;
0061: import com.gargoylesoftware.htmlunit.PromptHandler;
0062: import com.gargoylesoftware.htmlunit.StatusHandler;
0063: import com.gargoylesoftware.htmlunit.TopLevelWindow;
0064: import com.gargoylesoftware.htmlunit.WebClient;
0065: import com.gargoylesoftware.htmlunit.WebWindow;
0066: import com.gargoylesoftware.htmlunit.WebWindowNotFoundException;
0067: import com.gargoylesoftware.htmlunit.html.BaseFrame;
0068: import com.gargoylesoftware.htmlunit.html.DomNode;
0069: import com.gargoylesoftware.htmlunit.html.FrameWindow;
0070: import com.gargoylesoftware.htmlunit.html.HtmlElement;
0071: import com.gargoylesoftware.htmlunit.html.HtmlPage;
0072: import com.gargoylesoftware.htmlunit.html.xpath.HtmlUnitXPath;
0073: import com.gargoylesoftware.htmlunit.javascript.HTMLCollection;
0074: import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
0075: import com.gargoylesoftware.htmlunit.javascript.ScriptableWithFallbackGetter;
0076: import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
0077:
0078: /**
0079: * A JavaScript object for a Window.
0080: *
0081: * @version $Revision: 2155 $
0082: * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
0083: * @author <a href="mailto:chen_jun@users.sourceforge.net">Chen Jun</a>
0084: * @author David K. Taylor
0085: * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
0086: * @author Darrell DeBoer
0087: * @author Marc Guillemot
0088: * @author Dierk Koenig
0089: * @author Daniel Gredler
0090: * @author David D. Kilzer
0091: * @author Chris Erskine
0092: * @author Ahmed Ashour
0093: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/objects/obj_window.asp">
0094: * MSDN documentation</a>
0095: */
0096: public class Window extends SimpleScriptable implements
0097: ScriptableWithFallbackGetter {
0098:
0099: private static final long serialVersionUID = -7730298149962810325L;
0100: private Document document_;
0101: private Navigator navigator_;
0102: private WebWindow webWindow_;
0103: private Screen screen_;
0104: private History history_;
0105: private Location location_;
0106: private Object event_;
0107: private String status_ = "";
0108: private HTMLCollection frames_; // has to be a member to have equality (==) working
0109: private Map prototypes_ = new HashMap();
0110: private final JavaScriptEngine scriptEngine_;
0111: private EventListenersContainer eventListenersContainer_;
0112:
0113: /**
0114: * Creates an instance.
0115: *
0116: * @param scriptEngine The JavaScript engine responsible for the new window instance.
0117: */
0118: public Window(final JavaScriptEngine scriptEngine) {
0119: scriptEngine_ = scriptEngine;
0120: }
0121:
0122: /**
0123: * Gets the Javascript Engine responsible for this object.
0124: * @return the javascript engine
0125: */
0126: public JavaScriptEngine getJavaScriptEngine() {
0127: return scriptEngine_;
0128: }
0129:
0130: /**
0131: * Returns the prototype object corresponding to the specified HtmlUnit class inside the window scope.
0132: * @param jsClass the class whose prototype is to be returned
0133: * @return the prototype object corresponding to the specified class inside the specified scope
0134: */
0135: public Scriptable getPrototype(final Class jsClass) {
0136: return (Scriptable) prototypes_.get(jsClass);
0137: }
0138:
0139: /**
0140: * Sets the prototypes for HtmlUnit host classes
0141: * @param map a Map of ({@link Class}, {@link Scriptable})
0142: */
0143: public void setPrototypes(final Map map) {
0144: prototypes_ = map;
0145: }
0146:
0147: /**
0148: * The javascript function "alert()"
0149: * @param message The message
0150: */
0151: public void jsxFunction_alert(final Object message) {
0152: // use Object as parameter and perform String conversion by ourself
0153: // this allows to place breakpoint here and "see" the message object and its properties
0154: final String stringMessage = Context.toString(message);
0155: final AlertHandler handler = getWebWindow().getWebClient()
0156: .getAlertHandler();
0157: if (handler == null) {
0158: getLog().warn(
0159: "window.alert(\"" + stringMessage
0160: + "\") no alert handler installed");
0161: } else {
0162: handler.handleAlert(document_.getHtmlPage(), stringMessage);
0163: }
0164: }
0165:
0166: /**
0167: * The javascript function "confirm()"
0168: * @param message The message
0169: * @return true if ok was pressed, false if cancel was pressed
0170: */
0171: public boolean jsxFunction_confirm(final String message) {
0172: final ConfirmHandler handler = getWebWindow().getWebClient()
0173: .getConfirmHandler();
0174: if (handler == null) {
0175: getLog()
0176: .warn(
0177: "window.confirm(\""
0178: + message
0179: + "\") no confirm handler installed, simulating the OK button");
0180: return true;
0181: } else {
0182: return handler.handleConfirm(document_.getHtmlPage(),
0183: message);
0184: }
0185: }
0186:
0187: /**
0188: * The javascript function "prompt()"
0189: * @param message The message
0190: * @return true if ok was pressed, false if cancel was pressed
0191: */
0192: public String jsxFunction_prompt(final String message) {
0193: final PromptHandler handler = getWebWindow().getWebClient()
0194: .getPromptHandler();
0195: if (handler == null) {
0196: getLog().warn(
0197: "window.prompt(\"" + message
0198: + "\") no prompt handler installed");
0199: return null;
0200: } else {
0201: return handler.handlePrompt(document_.getHtmlPage(),
0202: message);
0203: }
0204: }
0205:
0206: /**
0207: * Return the javascript property "document"
0208: * @return The document
0209: */
0210: public Document jsxGet_document() {
0211: return document_;
0212: }
0213:
0214: /**
0215: * Return the current event
0216: * @return <code>null</code> if no event is currently available
0217: */
0218: public Object jsxGet_event() {
0219: return event_;
0220: }
0221:
0222: /**
0223: * Sets the current event
0224: * @param event the event
0225: */
0226: public void setEvent(final Object event) {
0227: event_ = event;
0228: }
0229:
0230: /**
0231: * Opens a new window.
0232: *
0233: * @param context The javascript Context
0234: * @param scriptable The object that the function was called on.
0235: * @param args The arguments passed to the function.
0236: * @param function The function object that was invoked.
0237: * @return the newly opened window, or <tt>null</tt> if popup windows have been disabled
0238: * @see WebClient#isPopupBlockerEnabled()
0239: */
0240: public static Object jsxFunction_open(final Context context,
0241: final Scriptable scriptable, final Object[] args,
0242: final Function function) {
0243:
0244: final String url = getStringArg(0, args, null);
0245: final String windowName = getStringArg(1, args, "");
0246: final String features = getStringArg(2, args, null);
0247: final boolean replaceCurrentEntryInBrowsingHistory = getBooleanArg(
0248: 3, args, false);
0249: final Window this Window = (Window) scriptable;
0250: final WebClient webClient = this Window.webWindow_
0251: .getWebClient();
0252:
0253: if (webClient.isPopupBlockerEnabled()) {
0254: this Window
0255: .getLog()
0256: .debug(
0257: "Ignoring window.open() invocation because popups are blocked.");
0258: return null;
0259: }
0260:
0261: if (features != null || replaceCurrentEntryInBrowsingHistory) {
0262: this Window.getLog().debug(
0263: "Window.open: features and replaceCurrentEntryInBrowsingHistory "
0264: + "not implemented: url=[" + url
0265: + "] windowName=[" + windowName
0266: + "] features=[" + features
0267: + "] replaceCurrentEntry=["
0268: + replaceCurrentEntryInBrowsingHistory
0269: + "]");
0270: }
0271:
0272: // if specified name is the name of an existing window, then hold it
0273: if (StringUtils.isEmpty(url) && !"".equals(windowName)) {
0274: final WebWindow webWindow;
0275: try {
0276: webWindow = webClient.getWebWindowByName(windowName);
0277: return webWindow.getScriptObject();
0278: } catch (final WebWindowNotFoundException e) {
0279: // nothing
0280: }
0281: }
0282: final URL newUrl = this Window.makeUrlForOpenWindow(url);
0283: final WebWindow newWebWindow = webClient.openWindow(newUrl,
0284: windowName, this Window.webWindow_);
0285: return newWebWindow.getScriptObject();
0286: }
0287:
0288: /**
0289: * Creates a popup window Open a new window
0290: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/createpopup.asp">
0291: * MSDN documentation</a>
0292: * @param context The javascript Context
0293: * @param scriptable The object that the function was called on.
0294: * @param args The arguments passed to the function.
0295: * @param function The function object that was invoked.
0296: * @return The created popup
0297: */
0298: public static Popup jsxFunction_createPopup(final Context context,
0299: final Scriptable scriptable, final Object[] args,
0300: final Function function) {
0301:
0302: final Window this Window = (Window) scriptable;
0303:
0304: final Popup popup = new Popup();
0305: popup.setParentScope(this Window);
0306: popup.setPrototype(this Window.getPrototype(Popup.class));
0307: popup.init(this Window);
0308:
0309: return popup;
0310: }
0311:
0312: private URL makeUrlForOpenWindow(final String urlString) {
0313: if (urlString.length() == 0) {
0314: // IE handles "" as "about:blank" in window.open
0315: if (getWebWindow().getWebClient().getBrowserVersion()
0316: .isIE()) {
0317: return WebClient.URL_ABOUT_BLANK;
0318: } else {
0319: return null;
0320: }
0321: }
0322:
0323: try {
0324: final Page page = webWindow_.getEnclosedPage();
0325: if (page != null && page instanceof HtmlPage) {
0326: return ((HtmlPage) page)
0327: .getFullyQualifiedUrl(urlString);
0328: } else {
0329: return new URL(urlString);
0330: }
0331: } catch (final MalformedURLException e) {
0332: getLog().error(
0333: "Unable to create url for openWindow: relativeUrl=["
0334: + urlString + "]", e);
0335: return null;
0336: }
0337: }
0338:
0339: /**
0340: * Makes the job object for setTimeout and setInterval
0341: *
0342: * @param codeToExec either a Function or a String of the javascript code
0343: * @param timeout time to wait
0344: * @param thisWindow the window to associate the thread with
0345: * @param loopForever if the thread should keep looping (setTimeout vs setInterval)
0346: * @return the job
0347: */
0348: private static JavaScriptBackgroundJob createJavaScriptBackgroundJob(
0349: final Object codeToExec, final int timeout,
0350: final Window this Window, final boolean loopForever,
0351: final String label) {
0352: if (codeToExec == null) {
0353: throw Context.reportRuntimeError("Function not provided");
0354: } else if (codeToExec instanceof String) {
0355: final String scriptString = (String) codeToExec;
0356: return new JavaScriptBackgroundJob(this Window, timeout,
0357: scriptString, loopForever, label);
0358: } else if (codeToExec instanceof Function) {
0359: final Function scriptFunction = (Function) codeToExec;
0360: return new JavaScriptBackgroundJob(this Window, timeout,
0361: scriptFunction, loopForever, label);
0362: } else {
0363: throw Context
0364: .reportRuntimeError("Unknown type for function");
0365: }
0366: }
0367:
0368: /**
0369: * Set a chunk of javascript to be invoked at some specified time later.
0370: * The invocation occurs only if the window is opened after the delay
0371: * and does not contain an other page than the one that originated the setTimeout.
0372: *
0373: * JavaScript param 1: The code to execute, either a String or a Function.
0374: * JavaScript param 2: the delay in milliseconds to wait before executing the code.
0375: *
0376: * @param context The javascript Context
0377: * @param scriptable The object that the function was called on.
0378: * @param args The arguments passed to the function.
0379: * @param function The function object that was invoked.
0380: * @return the id of the created timer
0381: */
0382: public static int jsxFunction_setTimeout(final Context context,
0383: final Scriptable scriptable, final Object[] args,
0384: final Function function) {
0385: final Window this Window = (Window) scriptable;
0386: final Object codeToExec = getObjectArg(0, args, null);
0387: final int timeout = getIntArg(1, args, 0);
0388:
0389: this Window.getLog().debug(
0390: "setTimeout(" + codeToExec + ", " + timeout + ")");
0391: final Runnable job = createJavaScriptBackgroundJob(codeToExec,
0392: timeout, this Window, false, "setTimeout");
0393: final int id = this Window.getWebWindow().getThreadManager()
0394: .startThread(job, "window.setTimeout");
0395: return id;
0396: }
0397:
0398: /**
0399: * Cancels a time-out previously set with the <tt>setTimeout</tt> method.
0400: *
0401: * @param timeoutId identifier for the timeout to clear (returned by <tt>setTimeout</tt>)
0402: */
0403: public void jsxFunction_clearTimeout(final int timeoutId) {
0404: getWebWindow().getThreadManager().stopThread(timeoutId);
0405: }
0406:
0407: /**
0408: * Return the javascript property "navigator"
0409: * @return The document
0410: */
0411: public Navigator jsxGet_navigator() {
0412: return navigator_;
0413: }
0414:
0415: /**
0416: * Return the window property. This is a synonym for "self"
0417: * @return A reference to this
0418: */
0419: public Window jsxGet_window() {
0420: return this ;
0421: }
0422:
0423: /**
0424: * Return the "self" property
0425: * @return this
0426: */
0427: public Window jsxGet_self() {
0428: return this ;
0429: }
0430:
0431: /**
0432: * Return the location property
0433: * @return The location property
0434: */
0435: public Location jsxGet_location() {
0436: return location_;
0437: }
0438:
0439: /**
0440: * Set the location property. This will cause a reload of the window.
0441: * @param newLocation The url of the new content.
0442: * @throws IOException when location loading fails
0443: */
0444: public void jsxSet_location(final String newLocation)
0445: throws IOException {
0446: location_.jsxSet_href(newLocation);
0447: }
0448:
0449: /**
0450: * Return the "screen" property
0451: * @return the screen property
0452: */
0453: public Screen jsxGet_screen() {
0454: return screen_;
0455: }
0456:
0457: /**
0458: * Return the "history" property
0459: * @return the "history" property
0460: */
0461: public History jsxGet_history() {
0462: return history_;
0463: }
0464:
0465: /**
0466: * Initialize the object.
0467: * @param webWindow The web window containing the javascript.
0468: * @exception Exception If an error occurs.
0469: */
0470: public void initialize(final WebWindow webWindow) throws Exception {
0471: webWindow_ = webWindow;
0472: webWindow_.setScriptObject(this );
0473:
0474: document_ = new Document();
0475: document_.setParentScope(this );
0476: document_.setPrototype(getPrototype(Document.class));
0477: document_.setWindow(this );
0478: if (webWindow.getEnclosedPage() instanceof HtmlPage) {
0479: document_.setDomNode((DomNode) webWindow.getEnclosedPage());
0480: }
0481:
0482: navigator_ = new Navigator();
0483: navigator_.setParentScope(this );
0484: navigator_.setPrototype(getPrototype(Navigator.class));
0485:
0486: screen_ = new Screen();
0487: screen_.setParentScope(this );
0488: screen_.setPrototype(getPrototype(Screen.class));
0489:
0490: history_ = new History();
0491: history_.setParentScope(this );
0492: history_.setPrototype(getPrototype(History.class));
0493:
0494: location_ = new Location();
0495: location_.setParentScope(this );
0496: location_.setPrototype(getPrototype(Location.class));
0497: location_.initialize(this );
0498: }
0499:
0500: /**
0501: * Initialize the object.
0502: * @param enclosedPage The page containing the javascript.
0503: */
0504: public void initialize(final Page enclosedPage) {
0505: if (enclosedPage instanceof HtmlPage) {
0506: final HtmlPage htmlPage = (HtmlPage) enclosedPage;
0507:
0508: // Windows don't have corresponding DomNodes so set the domNode
0509: // variable to be the page. If this isn't set then SimpleScriptable.get()
0510: // won't work properly
0511: setDomNode(htmlPage);
0512:
0513: Assert.notNull("document_", document_);
0514: document_.setDomNode(htmlPage);
0515: }
0516: }
0517:
0518: /**
0519: * Initialize the object. Only call for Windows with no contents.
0520: */
0521: public void initialize() {
0522: }
0523:
0524: /**
0525: * Return the value of the top property
0526: * @return The value of "top"
0527: */
0528: public SimpleScriptable jsxGet_top() {
0529: final WebWindow topWebWindow = webWindow_.getTopWindow();
0530: return (SimpleScriptable) topWebWindow.getScriptObject();
0531: }
0532:
0533: /**
0534: * Return the value of the parent property
0535: * @return the value of window.parent
0536: */
0537: public SimpleScriptable jsxGet_parent() {
0538: final WebWindow parentWebWindow = webWindow_.getParentWindow();
0539: return (SimpleScriptable) parentWebWindow.getScriptObject();
0540: }
0541:
0542: /**
0543: * Return the value of the opener property.
0544: * @return the value of window.opener, <code>null</code> for a top level window
0545: */
0546: public Object jsxGet_opener() {
0547: if (webWindow_ instanceof TopLevelWindow) {
0548: final WebWindow opener = ((TopLevelWindow) webWindow_)
0549: .getOpener();
0550: if (opener != null) {
0551: return opener.getScriptObject();
0552: }
0553: }
0554:
0555: return null;
0556: }
0557:
0558: /**
0559: * Return the (i)frame in which the window is contained.
0560: * @return <code>null</code> for a top level window
0561: */
0562: public Object jsxGet_frameElement() {
0563: final WebWindow window = getWebWindow();
0564: if (window instanceof FrameWindow) {
0565: return ((FrameWindow) window).getFrameElement()
0566: .getScriptObject();
0567: } else {
0568: return null;
0569: }
0570: }
0571:
0572: /**
0573: * Return the value of the frames property.
0574: * @return The live collection of frames
0575: */
0576: public HTMLCollection jsxGet_frames() {
0577: if (frames_ == null) {
0578: final XPath xpath;
0579: try {
0580: xpath = new HtmlUnitXPath(
0581: "//*[(name() = 'frame' or name() = 'iframe')]");
0582: } catch (final JaxenException e) {
0583: // should never occur
0584: throw Context
0585: .reportRuntimeError("Failed initializing frame collections: "
0586: + e.getMessage());
0587: }
0588: final HtmlPage page = (HtmlPage) getWebWindow()
0589: .getEnclosedPage();
0590: frames_ = new HTMLCollection(this );
0591: final Transformer toEnclosedWindow = new Transformer() {
0592: public Object transform(final Object obj) {
0593: if (obj instanceof BaseFrame) {
0594: return ((BaseFrame) obj).getEnclosedWindow();
0595: } else {
0596: return ((FrameWindow) obj).getFrameElement()
0597: .getEnclosedWindow();
0598: }
0599: }
0600: };
0601: frames_.init(page, xpath, toEnclosedWindow);
0602: }
0603:
0604: return frames_;
0605: }
0606:
0607: /**
0608: * Return the WebWindow associated with this Window
0609: * @return The WebWindow
0610: */
0611: public WebWindow getWebWindow() {
0612: return webWindow_;
0613: }
0614:
0615: /**
0616: * Set the focus to this element.
0617: */
0618: public void jsxFunction_focus() {
0619: webWindow_.getWebClient().setCurrentWindow(webWindow_);
0620: }
0621:
0622: /**
0623: * Remove focus from this element
0624: */
0625: public void jsxFunction_blur() {
0626: getLog().debug("Window.blur() not implemented");
0627: }
0628:
0629: /**
0630: * Close this window
0631: */
0632: public void jsxFunction_close() {
0633: getWebWindow().getWebClient().deregisterWebWindow(
0634: getWebWindow());
0635: }
0636:
0637: /**
0638: * Indicates if this window is closed
0639: * @return <code>true</code> if this window is closed
0640: */
0641: public boolean jsxGet_closed() {
0642: return !getWebWindow().getWebClient().getWebWindows().contains(
0643: getWebWindow());
0644: }
0645:
0646: /**
0647: * Does nothing.
0648: * @param x The horizontal position
0649: * @param y The vertical position
0650: */
0651: public void jsxFunction_moveTo(final int x, final int y) {
0652: getLog().debug("Window.moveTo() not implemented");
0653: }
0654:
0655: /**
0656: * Does nothing.
0657: * @param x The horizontal position
0658: * @param y The vertical position
0659: */
0660: public void jsxFunction_moveBy(final int x, final int y) {
0661: getLog().debug("Window.moveBy() not implemented");
0662: }
0663:
0664: /**
0665: * Does nothing.
0666: * @param width The width offset
0667: * @param height The height offset.
0668: */
0669: public void jsxFunction_resizeBy(final int width, final int height) {
0670: getLog().debug("Window.resizeBy() not implemented");
0671: }
0672:
0673: /**
0674: * Does nothing.
0675: * @param width The width of the Window in pixel after resize.
0676: * @param height The height of the Window in pixel after resize.
0677: */
0678: public void jsxFunction_resizeTo(final int width, final int height) {
0679: getLog().debug("Window.resizeTo() not implemented");
0680: }
0681:
0682: /**
0683: * Does nothing.
0684: * @param x The horizontal position to scroll to
0685: * @param y The vertical position to scroll to
0686: */
0687: public void jsxFunction_scroll(final int x, final int y) {
0688: getLog().debug("Window.scroll() not implemented");
0689: }
0690:
0691: /**
0692: * Does nothing.
0693: * @param x The horizontal distance to scroll by
0694: * @param y The vertical distance to scroll by
0695: */
0696: public void jsxFunction_scrollBy(final int x, final int y) {
0697: getLog().debug("Window.scrollBy() not implemented");
0698: }
0699:
0700: /**
0701: * Does nothing.
0702: * @param lines The number of lines to scroll down
0703: */
0704: public void jsxFunction_scrollByLines(final int lines) {
0705: getLog().debug("Window.scrollByLines() not implemented");
0706: }
0707:
0708: /**
0709: * Does nothing.
0710: * @param pages The number of pages to scroll down
0711: */
0712: public void jsxFunction_scrollByPages(final int pages) {
0713: getLog().debug("Window.scrollByPages() not implemented");
0714: }
0715:
0716: /**
0717: * Does nothing.
0718: * @param x The horizontal position to scroll to
0719: * @param y The vertical position to scroll to
0720: */
0721: public void jsxFunction_scrollTo(final int x, final int y) {
0722: getLog().debug("Window.scrollTo() not implemented");
0723: }
0724:
0725: /**
0726: * Set the value of the onload event handler.
0727: * @param newOnload The new handler
0728: */
0729: public void jsxSet_onload(final Object newOnload) {
0730: getEventListenersContainer().setEventHandlerProp("load",
0731: newOnload);
0732: }
0733:
0734: /**
0735: * Set the value of the onclick event handler.
0736: * @param newOnload The new handler
0737: */
0738: public void jsxSet_onclick(final Object newOnload) {
0739: getEventListenersContainer().setEventHandlerProp("click",
0740: newOnload);
0741: }
0742:
0743: /**
0744: * Return the onclick property (caution this is not necessary a function if something else has
0745: * been set)
0746: * @return the onclick property
0747: */
0748: public Object jsxGet_onclick() {
0749: return getEventListenersContainer()
0750: .getEventHandlerProp("click");
0751: }
0752:
0753: /**
0754: * Set the value of the ondblclick event handler.
0755: * @param newHandler The new handler
0756: */
0757: public void jsxSet_ondblclick(final Object newHandler) {
0758: getEventListenersContainer().setEventHandlerProp("dblclick",
0759: newHandler);
0760: }
0761:
0762: /**
0763: * Return the ondblclick property (caution this is not necessary a function if something else has
0764: * been set)
0765: * @return the ondblclick property
0766: */
0767: public Object jsxGet_ondblclick() {
0768: return getEventListenersContainer().getEventHandlerProp(
0769: "dblclick");
0770: }
0771:
0772: /**
0773: * Return the onload property (caution this is not necessary a function if something else has
0774: * been set)
0775: * @return the onload property
0776: */
0777: public Object jsxGet_onload() {
0778: final Object onload = getEventListenersContainer()
0779: .getEventHandlerProp("load");
0780: if (onload == null) {
0781: // NB: for IE, the onload of window is the one of the body element but not for Mozilla.
0782: final HtmlPage page = (HtmlPage) webWindow_
0783: .getEnclosedPage();
0784: final List listTagNames = Arrays.asList(new String[] {
0785: "body", "frameset" });
0786: final List listElements = page.getDocumentHtmlElement()
0787: .getHtmlElementsByTagNames(listTagNames);
0788: if (!listElements.isEmpty()) {
0789: return ((HtmlElement) listElements.get(0))
0790: .getEventHandler("onload");
0791: } else {
0792: return null;
0793: }
0794: } else {
0795: return onload;
0796: }
0797: }
0798:
0799: /**
0800: * Gets the container for event listeners
0801: * @return the container (newly created if needed)
0802: */
0803: EventListenersContainer getEventListenersContainer() {
0804: if (eventListenersContainer_ == null) {
0805: eventListenersContainer_ = new EventListenersContainer(this );
0806: }
0807: return eventListenersContainer_;
0808: }
0809:
0810: /**
0811: * Allows the registration of event listeners on the event target
0812: * @param type the event type to listen for (like "load")
0813: * @param listener the event listener
0814: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/attachevent.asp">
0815: * MSDN documentation</a>
0816: * @return <code>true</code> if the listener has been added
0817: */
0818: public boolean jsxFunction_attachEvent(final String type,
0819: final Function listener) {
0820: return getEventListenersContainer().addEventListener(
0821: StringUtils.substring(type, 2), listener, false);
0822: }
0823:
0824: /**
0825: * Allows the registration of event listeners on the event target
0826: * @param type the event type to listen for (like "onload")
0827: * @param listener the event listener
0828: * @param useCapture If <code>true</code>, indicates that the user wishes to initiate capture (not yet implemented)
0829: * @see <a href="http://developer.mozilla.org/en/docs/DOM:element.addEventListener">Mozilla documentation</a>
0830: */
0831: public void jsxFunction_addEventListener(final String type,
0832: final Function listener, final boolean useCapture) {
0833: getEventListenersContainer().addEventListener(type, listener,
0834: useCapture);
0835: }
0836:
0837: /**
0838: * Allows the removal of event listeners on the event target
0839: * @param type the event type to listen for (like "onload")
0840: * @param listener the event listener
0841: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/detachevent.asp">
0842: * MSDN documentation</a>
0843: */
0844: public void jsxFunction_detachEvent(final String type,
0845: final Function listener) {
0846: getEventListenersContainer().removeEventListener(
0847: StringUtils.substring(type, 2), listener, false);
0848: }
0849:
0850: /**
0851: * Allows the removal of event listeners on the event target
0852: * @param type the event type to listen for (like "load")
0853: * @param listener the event listener
0854: * @param useCapture If <code>true</code>, indicates that the user wishes to initiate capture (not yet implemented)
0855: * @see <a href="http://developer.mozilla.org/en/docs/DOM:element.removeEventListener">Mozilla documentation</a>
0856: */
0857: public void jsxFunction_removeEventListener(final String type,
0858: final Function listener, final boolean useCapture) {
0859: getEventListenersContainer().removeEventListener(type,
0860: listener, useCapture);
0861: }
0862:
0863: /**
0864: * Return the value of the name property
0865: * @return The window name
0866: */
0867: public String jsxGet_name() {
0868: return webWindow_.getName();
0869: }
0870:
0871: /**
0872: * Set the value of the newName property
0873: * @param newName The new window name
0874: */
0875: public void jsxSet_name(final String newName) {
0876: webWindow_.setName(newName);
0877: }
0878:
0879: /**
0880: * Return the value of the onerror property
0881: * @return The value
0882: */
0883: public String jsxGet_onerror() {
0884: getLog().debug("Window.onerror not implemented");
0885: return "";
0886: }
0887:
0888: /**
0889: * Set the value of the onerror property
0890: * @param newValue The value
0891: */
0892: public void jsxSet_onerror(final String newValue) {
0893: getLog().debug("Window.onerror not implemented");
0894: }
0895:
0896: /**
0897: * Looks at attributes with the given name
0898: * {@inheritDoc}
0899: */
0900: public Object getWithFallback(final String name) {
0901: Object result = NOT_FOUND;
0902: final DomNode domNode = getDomNodeOrNull();
0903: if (domNode != null) {
0904: result = getFrameByName(domNode.getPage(), name);
0905: }
0906:
0907: // See if it is an attempt to access an element directly by name or id if we are emulating IE.
0908: if (result == NOT_FOUND) {
0909: // this tests are quite silly and should be removed when custom JS objects have a clean
0910: // way to get the WebClient they are running in.
0911: if (domNode != null
0912: && domNode.getPage().getWebClient()
0913: .getBrowserVersion().isIE()) {
0914: final HTMLCollection array = (HTMLCollection) document_
0915: .jsxFunction_getElementsByName(name);
0916: final int length = array.jsxGet_length();
0917: if (length == 1) {
0918: result = array.get(0, array);
0919: } else if (length > 1) {
0920: result = array;
0921: } else {
0922: result = document_.jsxFunction_getElementById(name);
0923: if (result == null) {
0924: result = NOT_FOUND;
0925: }
0926: }
0927: }
0928: }
0929:
0930: return result;
0931: }
0932:
0933: /**
0934: * {@inheritDoc}
0935: */
0936: public Object get(final String name, final Scriptable start) {
0937: // Hack to make eval work in other window scope when needed.
0938: // See unit test testEvalScopeOtherWindow().
0939: // TODO: Find a cleaner way to handle this.
0940: if ("eval".equals(name)) {
0941: final Window w = (Window) getTopScope(getStartingScope());
0942: if (w != this ) {
0943: return getAssociatedValue("custom_eval");
0944: }
0945: }
0946: return super .get(name, start);
0947: }
0948:
0949: private Scriptable getTopScope(final Scriptable s) {
0950: Scriptable top = s;
0951: while (top != null && top.getParentScope() != null) {
0952: top = top.getParentScope();
0953: }
0954: return top;
0955: }
0956:
0957: private Object getFrameByName(final HtmlPage page, final String name) {
0958: try {
0959: return page.getFrameByName(name).getScriptObject();
0960: } catch (final ElementNotFoundException e) {
0961: return NOT_FOUND;
0962: }
0963: }
0964:
0965: /**
0966: * Executes the specified script code as long as the laguage is JavaScript or JScript. Does
0967: * nothing if the language specified is VBScript.
0968: * @param script the script code to execute
0969: * @param language the language of the specified code ("JavaScript", "JScript" or "VBScript")
0970: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/execscript.asp">
0971: * MSDN documentation</a>
0972: * @return this method always returns <code>null</code>, like Internet Explorer
0973: */
0974: public Object jsxFunction_execScript(final String script,
0975: final String language) {
0976: if ("javascript".equalsIgnoreCase(language)
0977: || "jscript".equalsIgnoreCase(language)) {
0978: custom_eval(script);
0979: return null;
0980: } else if ("vbscript".equalsIgnoreCase(language)) {
0981: getLog().warn(
0982: "VBScript not supported in Window.execScript().");
0983: } else {
0984: // Unrecognized language: use the IE error message ("Invalid class string").
0985: throw Context.reportRuntimeError("Invalid class string");
0986: }
0987: return null;
0988: }
0989:
0990: /**
0991: * Executes the specified script code in the scope of this window.
0992: * This is used only when eval() is called on a Window other than the starting scope
0993: * @param scriptCode some javascript code
0994: * @return the evaluation result
0995: */
0996: public Object custom_eval(final String scriptCode) {
0997: final Context context = Context.getCurrentContext();
0998: final org.mozilla.javascript.Script script = context
0999: .compileString(scriptCode, "eval body", 0, null);
1000: return script.exec(context, this );
1001: }
1002:
1003: /**
1004: * Return the text from the status line.
1005: * @return the status line text
1006: */
1007: public String jsxGet_status() {
1008: return status_;
1009: }
1010:
1011: /**
1012: * Set the text from the status line.
1013: * @param message the status line text
1014: */
1015: public void jsxSet_status(final String message) {
1016: status_ = message;
1017:
1018: final StatusHandler statusHandler = webWindow_.getWebClient()
1019: .getStatusHandler();
1020: if (statusHandler != null) {
1021: statusHandler.statusMessageChanged(webWindow_
1022: .getEnclosedPage(), message);
1023: }
1024: }
1025:
1026: /**
1027: * Set a chunk of javascript to be invoked each time a specified number of milliseconds has elapsed
1028: * Current implementation does nothing.
1029: *
1030: * JavaScript param 1: The code to execute, either a String or a Function.
1031: * JavaScript param 2: the delay in milliseconds to wait before executing the code.
1032: *
1033: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/setinterval.asp">
1034: * MSDN documentation</a>
1035: *
1036: * @param context The javascript Context
1037: * @param scriptable The object that the function was called on.
1038: * @param args The arguments passed to the function.
1039: * @param function The function object that was invoked.
1040: * @return the id of the created interval
1041: */
1042: public static int jsxFunction_setInterval(final Context context,
1043: final Scriptable scriptable, final Object[] args,
1044: final Function function) {
1045: final Window this Window = (Window) scriptable;
1046: final Object codeToExec = getObjectArg(0, args, null);
1047: final int timeout = getIntArg(1, args, 0);
1048:
1049: this Window.getLog().debug(
1050: "setInterval(" + codeToExec + ", " + timeout + ")");
1051: final Runnable job = createJavaScriptBackgroundJob(codeToExec,
1052: timeout, this Window, true, "setInterval");
1053: final int id = this Window.getWebWindow().getThreadManager()
1054: .startThread(job, "window.setInterval");
1055: return id;
1056: }
1057:
1058: /**
1059: * Cancels the interval previously started using the setInterval method.
1060: * Current implementation does nothing.
1061: * @param intervalID specifies the interval to cancel as returned by the setInterval method.
1062: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/clearinterval.asp">
1063: * MSDN documentation</a>
1064: */
1065: public void jsxFunction_clearInterval(final int intervalID) {
1066: getWebWindow().getThreadManager().stopThread(intervalID);
1067: }
1068:
1069: /**
1070: * Return the innerWidth.
1071: * @return a dummy value
1072: * @see <a href="http://www.mozilla.org/docs/dom/domref/dom_window_ref28.html">Mozilla doc</a>
1073: */
1074: public int jsxGet_innerWidth() {
1075: return 1276; // why this value? this is the current value of my Mozilla
1076: }
1077:
1078: /**
1079: * Return the outerWidth.
1080: * @return a dummy value
1081: * @see <a href="http://www.mozilla.org/docs/dom/domref/dom_window_ref79.html">Mozilla doc</a>
1082: */
1083: public int jsxGet_outerWidth() {
1084: return 1276; // why this value? this is the current value of my Mozilla
1085: }
1086:
1087: /**
1088: * Return the innerHeight.
1089: * @return a dummy value
1090: * @see <a href="http://www.mozilla.org/docs/dom/domref/dom_window_ref27.html">Mozilla doc</a>
1091: */
1092: public int jsxGet_innerHeight() {
1093: return 778; // why this value? this is the current value of my Mozilla
1094: }
1095:
1096: /**
1097: * Return the outer height.
1098: * @return a dummy value
1099: * @see <a href="http://www.mozilla.org/docs/dom/domref/dom_window_ref78.html">Mozilla doc</a>
1100: */
1101: public int jsxGet_outerHeight() {
1102: return 936; // why this value? this is the current value of my Mozilla
1103: }
1104:
1105: /**
1106: * Prints the current page. The current implementation does nothing.
1107: * @see <a href="http://www.mozilla.org/docs/dom/domref/dom_window_ref85.html">
1108: * Mozilla documentation</a>
1109: * @see <a href="http://msdn.microsoft.com/workshop/author/dhtml/reference/methods/print.asp">
1110: * MSDN documentation</a>
1111: */
1112: public void jsxFunction_print() {
1113: getLog().debug("window.print() not implemented");
1114: }
1115:
1116: /**
1117: * Does nothing special anymore... just like FF.
1118: * @param type the type of events
1119: */
1120: public void jsxFunction_captureEvents(final String type) {
1121: // nothing
1122: }
1123:
1124: /**
1125: * Returns computed style of the element. Computed style represents the final computed values
1126: * of all CSS properties for the element. This method's return value is of the same type as
1127: * that of <tt>element.style</tt>, but the value returned by this method is read-only.
1128: *
1129: * @param element the element
1130: * @param pseudoElt is a string specifying the pseudo-element to match, can be null.
1131: * @return the computed style.
1132: */
1133: public Object jsxFunction_getComputedStyle(final Node element,
1134: final String pseudoElt) {
1135:
1136: final HTMLElement e = (HTMLElement) element;
1137: final Style original = (Style) e.jsxGet_style();
1138: final Style style = original.createClone();
1139: style.setWriteMode(Style.WRITE_MODE_DO_NOT_UPDATE_ELEMENT);
1140:
1141: final StyleSheetList sheets = (StyleSheetList) document_
1142: .jsxGet_styleSheets();
1143: for (int i = 0; i < sheets.jsxGet_length(); i++) {
1144: final Stylesheet sheet = sheets.jsxFunction_item(i);
1145: sheet.modifyIfNecessary(style, e);
1146: }
1147:
1148: style.setWriteMode(Style.WRITE_MODE_NOT_WRITEABLE);
1149: return style;
1150: }
1151:
1152: }
|