Browse files

Add support for IFRAMEs.

  • Loading branch information...
1 parent 39b5a06 commit 0382f5309ae6be26cd7619bc6cff7249cee478d7 @djanowski djanowski committed Feb 1, 2011
Showing with 111 additions and 40 deletions.
  1. +5 −0 CHANGELOG.md
  2. +31 −0 spec/browser-spec.coffee
  3. +34 −23 src/zombie/browser.coffee
  4. +17 −8 src/zombie/history.coffee
  5. +24 −9 src/zombie/jsdom_patches.coffee
View
5 CHANGELOG.md
@@ -2,6 +2,11 @@ zombie.js-changelog(7) -- Changelog
===================================
+### Version 0.8.13 Pending
+
+Add support for IFRAMEs (Damian Janowski).
+
+
### Version 0.8.12 2011-02-01
Tested with Node 0.3.7 in preparation for Node 0.4.0.
View
31 spec/browser-spec.coffee
@@ -123,6 +123,17 @@ brains.get "/screen", (req, res)-> res.send """
</script>
"""
+brains.get "/iframe", (req, res)-> res.send """
+ <html>
+ <head>
+ <script src="/jquery.js"></script>
+ </head>
+ <body>
+ <iframe src="/static" />
+ </body>
+ </html>
+ """
+
vows.describe("Browser").addBatch(
"open page":
zombie.wants "http://localhost:3003/scripted"
@@ -133,6 +144,7 @@ vows.describe("Browser").addBatch(
assert.typeOf jQuery.ajax, "function"
"should run jQuery.onready": (browser)-> assert.equal browser.document.title, "Awesome"
"should return status code of last request": (browser)-> assert.equal browser.statusCode, 200
+ "should have a parent": (browser)-> assert.ok browser.window.parent == browser.window
"visit":
"successful":
@@ -360,4 +372,23 @@ vows.describe("Browser").addBatch(
forked.window.history.back()
assert.equal "http://localhost:3003/living", forked.location.href
+ "iframes":
+ zombie.wants "http://localhost:3003/iframe"
+ "should load": (browser)->
+ assert.equal "Whatever", browser.querySelector("iframe").window.document.title
+ assert.match browser.querySelector("iframe").window.document.querySelector("body").innerHTML, /Hello World/
+ assert.equal "http://localhost:3003/static", browser.querySelector("iframe").window.location
+ "should reference the parent": (browser)->
+ assert.ok browser.window == browser.querySelector("iframe").window.parent
+ "should not alter the parent": (browser)->
+ assert.equal "http://localhost:3003/iframe", browser.window.location
+ "after a refresh":
+ topic: (browser)->
+ callback = @callback
+ browser.querySelector("iframe").window.location.reload(true)
+ browser.wait -> callback null, browser
+ "should still reference the parent": (browser)->
+ assert.ok browser.window == browser.querySelector("iframe").window.parent
+
+
).export(module)
View
57 src/zombie/browser.coffee
@@ -4,6 +4,7 @@ vm = process.binding("evals")
require "./jsdom_patches"
require "./forms"
require "./xpath"
+History = require("./history").History
@@ -16,7 +17,6 @@ class Browser extends require("events").EventEmitter
cookies = require("./cookies").use(this)
storage = require("./storage").use(this)
eventloop = require("./eventloop").use(this)
- history = require("./history").use(this)
interact = require("./interact").use(this)
xhr = require("./xhr").use(cache)
resources = require("./resources")
@@ -82,26 +82,37 @@ class Browser extends require("events").EventEmitter
# ### browser.open() => Window
#
# Open new browser window.
- this.open = ->
- window = jsdom.createWindow(html)
- window.__defineGetter__ "browser", => this
- window.__defineGetter__ "title", => @window?.document?.title
- window.__defineSetter__ "title", (title)=> @window?.document?.title = title
- window.navigator.userAgent = @userAgent
- resources.extend window
- cookies.extend window
- storage.extend window
- eventloop.extend window
- history.extend window
- interact.extend window
- xhr.extend window
- window.screen = new Screen()
- window.JSON = JSON
+ this.open = (features = {})->
+ features.interactive ?= true
+
+ history = features.history || new History
+
+ newWindow = jsdom.createWindow(html)
+
+ # Switch to the newly created window if it's interactive.
+ # Examples of non-interactive windows are frames.
+ window = newWindow if features.interactive
+
+ newWindow.parent = newWindow
+ newWindow.__defineGetter__ "browser", => this
+ newWindow.__defineGetter__ "title", => @window?.document?.title
+ newWindow.__defineSetter__ "title", (title)=> @window?.document?.title = title
+ newWindow.navigator.userAgent = @userAgent
+ resources.extend newWindow
+ cookies.extend newWindow
+ storage.extend newWindow
+ eventloop.extend newWindow
+ history.extend newWindow
+ interact.extend newWindow
+ xhr.extend newWindow
+ newWindow.screen = new Screen()
+ newWindow.JSON = JSON
# Default onerror handler.
- window.onerror = (event)=> @emit "error", event.error || new Error("Error loading script")
+ newWindow.onerror = (event)=> @emit "error", event.error || new Error("Error loading script")
# TODO: Fix
- window.Image = ->
- return window
+ newWindow.Image = ->
+
+ return newWindow
@@ -305,7 +316,7 @@ class Browser extends require("events").EventEmitter
if typeof options is "function"
[callback, options] = [options, null]
@withOptions options, (reset)=>
- history._assign url
+ window.history._assign url
@wait (error, browser)->
reset()
if callback && error
@@ -354,12 +365,12 @@ class Browser extends require("events").EventEmitter
#
# Save history to a text string. You can use this to load the data
# later on using `browser.loadHistory`.
- this.saveHistory = -> history.save()
+ this.saveHistory = -> window.history.save()
# ### browser.loadHistory(String)
#
# Load history from a text string (e.g. previously created using
# `browser.saveHistory`.
- this.loadHistory = (serialized)-> history.load serialized
+ this.loadHistory = (serialized)-> window.history.load serialized
# Forms
@@ -756,7 +767,7 @@ class Browser extends require("events").EventEmitter
indent = (lines)-> lines.map((l) -> " #{l}\n").join("")
console.log "Zombie: #{exports.version}\n"
console.log "URL: #{@window.location.href}"
- console.log "History:\n#{indent history.dump()}"
+ console.log "History:\n#{indent window.history.dump()}"
console.log "Cookies:\n#{indent cookies.dump()}"
console.log "Storage:\n#{indent storage.dump()}"
console.log "Eventloop:\n#{indent eventloop.dump()}"
View
25 src/zombie/history.coffee
@@ -29,11 +29,14 @@ class Entry
#
# Represents window.history.
class History
- constructor: (browser)->
+ constructor: ->
# History is a stack of Entry objects.
stack = []
index = -1
+ window = null
+ browser = null
+
# Called when we switch to a new page with the URL of the old page.
pageChanged = (was)=>
url = stack[index]?.url
@@ -44,7 +47,7 @@ class History
# Hash changed. Do not reload page, but do send hashchange
evt = browser.document.createEvent("HTMLEvents")
evt.initEvent "hashchange", true, false
- browser.window.dispatchEvent evt
+ window.dispatchEvent evt
else
# Load new page for now (but later on use caching).
resource url
@@ -54,11 +57,14 @@ class History
resource = (url, method, data, headers)=>
method = (method || "GET").toUpperCase()
throw new Error("Cannot load resource: #{URL.format(url)}") unless url.protocol && url.hostname
+
# If the browser has a new window, use it. If a document was already
# loaded into that window it would have state information we don't want
# (e.g. window.$) so open a new window.
- window = browser.window
- window = browser.open() if browser.window.document
+ if window.document
+ browser.open
+ history: this
+ interactive: window.parent == window
# Create new DOM Level 3 document, add features (load external
# resources, etc) and associate it with current document. From this
@@ -75,6 +81,7 @@ class History
if browser.runScripts
options.features.ProcessExternalResources.push "script"
options.features.FetchExternalResources.push "script"
+ options.features.FetchExternalResources.push "iframe"
document = jsdom.jsdom(false, jsdom.level3, options)
document.fixQueue()
window.document = document
@@ -171,7 +178,10 @@ class History
resource stack[index].url, method, data, headers
# Add Location/History to window.
- this.extend = (window)->
+ this.extend = (new_window)->
+ window = new_window
+ browser = window.browser
+
window.__defineGetter__ "history", => this
window.__defineGetter__ "location", => stack[index]?.location || new Location(this, {})
window.__defineSetter__ "location", (url)=>
@@ -202,7 +212,7 @@ class History
[url, state] = line.split(/\s/)
options = state && { state: JSON.parse(state), title: null, pop: true }
stack[++index] = new Entry(this, url, state)
-
+
# ## window.location
#
@@ -236,5 +246,4 @@ class Location
html.HTMLDocument.prototype.__defineGetter__ "location", -> @parentWindow.location
-exports.use = (browser)->
- return new History(browser)
+exports.History = History
View
33 src/zombie/jsdom_patches.coffee
@@ -27,15 +27,22 @@ core.resourceLoader.load = (element, href, callback)->
document = element.ownerDocument
window = document.parentWindow
ownerImplementation = document.implementation
- if ownerImplementation.hasFeature('FetchExternalResources', element.tagName.toLowerCase())
- url = URL.parse(@resolve(document, href))
- loaded = (response, filename)->
- callback.call this, response.body, URL.parse(response.url).pathname
- if url.hostname
- window.resources.get url, @enqueue(element, loaded, url.pathname)
- else
- file = @resolve(document, url.pathname)
- @readFile file, @enqueue(element, loaded, file)
+ tagName = element.tagName.toLowerCase()
+
+ if ownerImplementation.hasFeature('FetchExternalResources', tagName)
+ switch tagName
+ when "iframe"
+ element.window.location = URL.resolve(element.window.parent.location, href)
+
+ else
+ url = URL.parse(@resolve(document, href))
+ loaded = (response, filename)->
+ callback.call this, response.body, URL.parse(response.url).pathname
+ if url.hostname
+ window.resources.get url, @enqueue(element, loaded, url.pathname)
+ else
+ file = @resolve(document, url.pathname)
+ @readFile file, @enqueue(element, loaded, file)
# Scripts
@@ -84,6 +91,14 @@ core.Document.prototype._elementBuilders["script"] = (doc, s)->
core.resourceLoader.enqueue(this, loaded, filename)(null, code)
return script
+core.Document.prototype._elementBuilders["iframe"] = (doc, s)->
+ window = doc.parentWindow
+
+ iframe = new core.HTMLIFrameElement(doc, s)
+ iframe.window = window.browser.open(interactive: false)
+ iframe.window.parent = window
+
+ return iframe
# Queue
# -----

0 comments on commit 0382f53

Please sign in to comment.