Adding property "page.ownsPages".

When set to "true", any page that gets created, is
owned by the "page" that control it's lifetime.
Also, the pages can be found in the "page.pages[]" array.

Default value is "true".

Addresses [Issue #151](http://code.google.com/p/phantomjs/issues/detail?id=151)
1.7
Ivan De Marino 2012-07-27 17:13:43 +01:00
parent 27dc699919
commit 733d21042b
5 changed files with 177 additions and 45 deletions

View File

@ -41,6 +41,7 @@
#include "system.h"
class WebPage;
class CustomPage;
class WebServer;
class Phantom: public REPLCompletable
@ -133,6 +134,8 @@ private:
QList<QPointer<WebServer> > m_servers;
Config m_config;
QVariantMap m_keyMap;
friend class CustomPage;
};
#endif // PHANTOM_H

View File

@ -181,9 +181,16 @@ protected:
QWebPage *createWindow (WebWindowType type) {
Q_UNUSED(type);
WebPage *newPage;
// Create a new "raw" WebPage object
WebPage *newPage = new WebPage(m_webPage);
if (m_webPage->ownsPages()) {
newPage = new WebPage(m_webPage);
} else {
newPage = new WebPage(Phantom::instance());
Phantom::instance()->m_pages.append(newPage);
}
// Apply default settings
newPage->applySettings(Phantom::instance()->defaultPageSettings());
@ -264,6 +271,7 @@ WebPage::WebPage(QObject *parent, const QUrl &baseUrl)
: REPLCompletable(parent)
, m_callbacks(NULL)
, m_navigationLocked(false)
, m_ownsPages(true)
{
setObjectName("WebPage");
m_customWebPage = new CustomPage(this);
@ -415,7 +423,6 @@ bool WebPage::navigationLocked()
return m_navigationLocked;
}
void WebPage::setViewportSize(const QVariantMap &size)
{
int w = size.value("width").toInt();
@ -1062,6 +1069,16 @@ QObject *WebPage::getPage(const QString &windowName) const
return NULL;
}
bool WebPage::ownsPages() const
{
return m_ownsPages;
}
void WebPage::setOwnsPages(const bool owns)
{
m_ownsPages = owns;
}
int WebPage::framesCount() const
{
return m_customWebPage->currentFrame()->childFrames().count();

View File

@ -64,6 +64,7 @@ class WebPage: public REPLCompletable, public QWebFrame::PrintCallback
Q_PROPERTY(QString windowName READ windowName)
Q_PROPERTY(QObjectList pages READ pages)
Q_PROPERTY(QStringList pagesWindowName READ pagesWindowName)
Q_PROPERTY(bool ownsPages READ ownsPages WRITE setOwnsPages)
Q_PROPERTY(QStringList framesName READ framesName)
Q_PROPERTY(QString frameName READ frameName)
Q_PROPERTY(int framesCount READ framesCount)
@ -136,6 +137,8 @@ public:
*
* NOTE: The ownership of this array is held by the Page: it's not adviced
* to have a "long running reference" to this array, as it might change.
* NOTE: If "ownsPages()" is "false", the page will create pages but not
* hold any ownership to it. Resource management is than left to the user.
*
* @brief pages
* @return List (JS Array) containing the Pages that this page
@ -148,6 +151,8 @@ public:
* NOTE: When a page is opened with <code>"window.open"</code>, a window
* <code>"name"</code> might be provided as second parameter.
* This provides a useful list of those.
* NOTE: If "ownsPages()" is "false", the page will create pages but not
* hold any ownership of it. Resource management is than left to the user.
*
* @brief pagesWindowName
* @return List (JS Array) containing the <code>'window.name'</code>(s) of
@ -155,6 +160,26 @@ public:
*/
QStringList pagesWindowName() const;
/**
* Returns "true" if it owns the pages it creates (and keeps them in "pages[]").
* Default value is "true". Can be changed using {@link setOwnsPages()}.
*
* @brief ownsPages()
* @return "true" if it owns the pages it creates in "pages[]", "false" otherwise.
*/
bool ownsPages() const;
/**
* Set if, from now on, it should own the pages it creates in "pages[]".
* Default value is "true".
*
* NOTE: When switching from "false" to "true", only the pages created
* from that point on will be owned. It's NOT retroactive.
*
* @brief setOwnsPages
* @param owns "true" to make it own the pages it creates in "pages[]", "false" otherwise.
*/
void setOwnsPages(const bool owns);
/**
* Returns the number of Child Frames inside the Current Frame.
* NOTE: The Current Frame changes when focus moves (via API or JS) to a specific child frame.
@ -338,6 +363,7 @@ private:
WebpageCallbacks *m_callbacks;
bool m_navigationLocked;
QPoint m_mousePos;
bool m_ownsPages;
friend class Phantom;
friend class CustomPage;

View File

@ -6,11 +6,11 @@ describe("Basic Files API (read, write, remove, ...)", function() {
FILENAME_ENC = FILENAME + ".enc",
FILENAME_BIN = FILENAME + ".bin",
ABSENT = "absent-01.test";
it("should be able to create and write a file", function() {
try{
var f = fs.open(FILENAME, "w");
f.write("hello");
f.writeLine("");
f.writeLine("world");
@ -25,31 +25,29 @@ describe("Basic Files API (read, write, remove, ...)", function() {
expect(fs.exists(FILENAME_EMPTY)).toBeTruthy();
expect(fs.size(FILENAME_EMPTY)).toEqual(0);
});
it("should be able to read content from a file", function() {
var content = "";
try{
var f = fs.open(FILENAME, "r");
content = f.read();
content = f.read();
f.close();
} catch (e) { }
expect(content).toEqual("hello\nworld\n");
});
it("should be able to read/write/append content from a file", function() {
var content = "";
try{
var f = fs.open(FILENAME, "rw+");
console.log(f.read().length);
f.writeLine("asdf");
content = f.read();
console.log(content.length);
f.close();
} catch (e) { }
expect(content).toEqual("hello\nworld\nasdf\n");
});
it("should be able to copy a file", function() {
expect(fs.exists(FILENAME_COPY)).toBeFalsy();
fs.copy(FILENAME, FILENAME_COPY);
@ -77,7 +75,7 @@ describe("Basic Files API (read, write, remove, ...)", function() {
fs.remove(FILENAME_COPY);
expect(fs.exists(FILENAME_COPY)).toBeFalsy();
});
it("should be able to remove an empty file", function() {
expect(fs.exists(FILENAME_EMPTY)).toBeTruthy();
fs.remove(FILENAME_EMPTY);
@ -139,4 +137,4 @@ describe("Basic Files API (read, write, remove, ...)", function() {
} catch (e) { }
expect(content).toEqual(output);
});
});
});

View File

@ -157,6 +157,7 @@ describe("WebPage object", function() {
expectHasFunction(page, 'loadStarted');
expectHasFunction(page, 'openUrl');
expectHasFunction(page, 'release');
expectHasFunction(page, 'close');
expectHasFunction(page, 'render');
expectHasFunction(page, 'resourceReceived');
expectHasFunction(page, 'resourceRequested');
@ -242,7 +243,7 @@ describe("WebPage object", function() {
return page.evaluate(function() {
return document.querySelector('input').value;
});
}
};
page.sendEvent('keypress', phantom.keys.A);
expect(getText()).toEqual("A");
page.sendEvent('keypress', phantom.keys.B);
@ -431,7 +432,7 @@ describe("WebPage object", function() {
runs(function() {
page.evaluate(function() {
setTimeout(function() { referenceError }, 0);
setTimeout(function() { referenceError(); }, 0);
});
});
@ -440,13 +441,13 @@ describe("WebPage object", function() {
runs(function() {
expect(lastError).toEqual("ReferenceError: Can't find variable: referenceError");
page.evaluate(function() { referenceError2 });
page.evaluate(function() { referenceError2(); });
expect(lastError).toEqual("ReferenceError: Can't find variable: referenceError2");
page.evaluate(function() { throw "foo" });
page.evaluate(function() { throw "foo"; });
expect(lastError).toEqual("foo");
page.evaluate(function() { throw Error("foo") });
page.evaluate(function() { throw Error("foo"); });
expect(lastError).toEqual("Error: foo");
});
});
@ -462,32 +463,32 @@ describe("WebPage object", function() {
caughtError = false;
try {
referenceError
referenceError();
} catch(e) {
caughtError = true;
}
});
expect(hadError).toEqual(false);
expect(page.evaluate(function() { return caughtError })).toEqual(true);
expect(page.evaluate(function() { return caughtError; })).toEqual(true);
});
})
});
it("reports the sourceURL and line of errors", function() {
runs(function() {
var e1, e2;
try {
referenceError
referenceError();
} catch (e) {
e1 = e
};
e1 = e;
}
try {
referenceError
referenceError();
} catch (e) {
e2 = e
};
e2 = e;
}
expect(e1.sourceURL).toMatch(/webpage-spec.js$/);
expect(e1.line).toBeGreaterThan(1);
@ -503,15 +504,15 @@ describe("WebPage object", function() {
runs(function() {
function test() {
ErrorHelper.foo()
};
ErrorHelper.foo();
}
var err;
try {
test()
test();
} catch (e) {
err = e
};
err = e;
}
var lines = err.stack.split("\n");
@ -522,8 +523,8 @@ describe("WebPage object", function() {
page.injectJs(helperFile);
page.onError = function(message, s) { stack = s };
page.evaluate(function() { setTimeout(function() { ErrorHelper.foo() }, 0) });
page.onError = function(message, s) { stack = s; };
page.evaluate(function() { setTimeout(function() { ErrorHelper.foo(); }, 0); });
});
waits(0);
@ -531,16 +532,16 @@ describe("WebPage object", function() {
runs(function() {
expect(stack[0].file).toEqual("./fixtures/error-helper.js");
expect(stack[0].line).toEqual(7);
expect(stack[0].function).toEqual("bar");
expect(stack[0]["function"]).toEqual("bar");
});
});
it("reports errors that occur in the main context", function() {
var error;
phantom.onError = function(e) { error = e };
phantom.onError = function(e) { error = e; };
runs(function() {
setTimeout(function() { zomg }, 0);
setTimeout(function() { zomg(); }, 0);
});
waits(0);
@ -881,7 +882,7 @@ describe("WebPage construction with options", function () {
decodedText = page.evaluate(function() {
return document.getElementsByTagName('pre')[0].innerText;
});
page.release();
page.close();
});
it("Should support text codec " + text.codec, function() {
expect(decodedText.match("^" + text.reference) == text.reference).toEqual(true);
@ -1092,7 +1093,7 @@ describe("WebPage opening and closing of windows/child-pages", function(){
var yahoo = p.getPage("yahoo");
expect(yahoo).not.toBe(null);
yahoo.release();
yahoo.close();
waitsFor(function(){
return p.pages.length === 1;
@ -1101,6 +1102,7 @@ describe("WebPage opening and closing of windows/child-pages", function(){
runs(function(){
expect(p.pages.length).toEqual(1);
expect(p.pagesWindowName).toEqual(["bing"]);
p.close();
});
});
});
@ -1117,9 +1119,15 @@ describe("WebPage closing notification/alerting", function(){
p.close();
expect(spy).toHaveBeenCalled(); //< called
expect(spy.calls.length).toEqual(1); //< only once
expect(spy).toHaveBeenCalledWith(p); //< called passing reference to the closing page 'p'
waitsFor(function() {
return spy.calls.length === 1;
}, "after 2sec 'onClosing' had still not been invoked", 2000);
runs(function() {
expect(spy).toHaveBeenCalled(); //< called
expect(spy.calls.length).toEqual(1); //< only once
expect(spy).toHaveBeenCalledWith(p); //< called passing reference to the closing page 'p'
});
});
it("should call 'onClosing' when a page closes on it's own", function(){
@ -1135,8 +1143,88 @@ describe("WebPage closing notification/alerting", function(){
window.close();
});
expect(spy).toHaveBeenCalled(); //< called
expect(spy.calls.length).toEqual(1); //< only once
expect(spy).toHaveBeenCalledWith(p); //< called passing reference to the closing page 'p'
waitsFor(function() {
return spy.calls.length === 1;
}, "after 2sec 'onClosing' had still not been invoked", 2000);
runs(function() {
expect(spy).toHaveBeenCalled(); //< called
expect(spy.calls.length).toEqual(1); //< only once
expect(spy).toHaveBeenCalledWith(p); //< called passing reference to the closing page 'p'
});
});
});
describe("WebPage closing notification/alerting: closing propagation control", function(){
it("should close all 4 pages if parent page is closed (default value for 'ownsPages')", function(){
var p = require("webpage").create(),
pages,
openPagesCount = 0;
p.onPageCreated = jasmine.createSpy("onPageCreated spy");
expect(p.ownsPages).toBeTruthy();
p.evaluate(function() {
// yeah, I know globals. YIKES!
window.w1 = window.open("http://www.google.com", "google");
window.w2 = window.open("http://www.yahoo.com", "yahoo");
window.w3 = window.open("http://www.bing.com", "bing");
});
pages = p.pages;
openPagesCount = p.pages.length + 1;
expect(p.onPageCreated).toHaveBeenCalled();
expect(p.onPageCreated.calls.length).toEqual(3);
expect(p.pages.length).toEqual(3);
p.onClosing = function() { --openPagesCount; };
pages[0].onClosing = function() { --openPagesCount; };
pages[1].onClosing = function() { --openPagesCount; };
pages[2].onClosing = function() { --openPagesCount; };
p.close();
waitsFor(function() {
return openPagesCount === 0;
}, "after 2sec pages were still open", 2000);
runs(function() {
expect(openPagesCount).toBe(0);
});
});
it("should NOT close all 4 pages if parent page is closed, just parent itself ('ownsPages' set to false)", function(){
var p = require("webpage").create(),
pages,
openPagesCount = 0;
p.ownsPages = false;
p.onPageCreated = jasmine.createSpy("onPageCreated spy");
expect(p.ownsPages).toBeFalsy();
p.evaluate(function() {
// yeah, I know globals. YIKES!
window.w1 = window.open("http://www.google.com", "google");
window.w2 = window.open("http://www.yahoo.com", "yahoo");
window.w3 = window.open("http://www.bing.com", "bing");
});
pages = p.pages;
openPagesCount = 1;
expect(p.onPageCreated).toHaveBeenCalled();
expect(p.onPageCreated.calls.length).toEqual(3);
expect(p.pages.length).toEqual(0);
p.onClosing = function() { --openPagesCount; };
p.close();
waitsFor(function() {
return openPagesCount === 0;
}, "after 2sec pages were still open", 2000);
runs(function() {
expect(openPagesCount).toBe(0);
});
});
});