Compare commits

..

7 Commits

Author SHA1 Message Date
Ariya Hidayat e2295f805c Lavender.
http://code.google.com/p/phantomjs/issues/detail?id=598
2012-06-20 23:28:04 -07:00
Ariya Hidayat ffc594e264 Fix Twitter-related examples to work with the new site.
http://code.google.com/p/phantomjs/issues/detail?id=609
2012-06-20 22:54:20 -07:00
Ariya Hidayat 78b1e271d6 Merge branch 'master' into 1.6 2012-06-20 21:30:30 -07:00
Ariya Hidayat e524157c81 Merge branch 'master' into 1.6 2012-06-20 09:06:25 -07:00
Ariya Hidayat 7944013141 Merge branch 'master' into 1.6 2012-06-19 17:26:04 -07:00
Ariya Hidayat 1f685ec241 Set version to 1.6.0.
http://code.google.com/p/phantomjs/issues/detail?id=598
2012-06-19 06:58:19 -07:00
Ariya Hidayat c68082e3b1 Undocument the callback mechanism.
This is private for now, see issue 133 for details.

http://code.google.com/p/phantomjs/issues/detail?id=133
2012-06-18 22:35:53 -07:00
13545 changed files with 2937710 additions and 93867 deletions

50
.gitignore vendored
View File

@ -2,6 +2,7 @@
*.pro.user* *.pro.user*
*.xcodeproj *.xcodeproj
Makefile* Makefile*
bin/phantomjs
*~ *~
*.moc *.moc
moc_* moc_*
@ -10,52 +11,15 @@ qrc_*
*.swp *.swp
*.pyc *.pyc
*.a *.a
/debian/*.debhelper debian/*.debhelper
/debian/files debian/files
/debian/*.log debian/*.log
/debian/*.substvars debian/*.substvars
/debian/*/ debian/*/
/deploy/qt-*.tar.gz /deploy/qt-*.tar.gz
/deploy/Qt-* /deploy/Qt-*
/symbols /symbols
/src/qt/qtc-debugging-helper
/src/phantomjs_plugin_import.cpp
# ignore ctags # ignore ctags
/tags /tags
/tools/dump_syms.app/ tools/dump_syms.app/
# Ignore Visual Studio temporary files, build results, etc
*.suo
*.user
*.sln.docstates
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.log
*.sdf
*.vcxproj
*.vcxproj.filters
*.lib
*.prl
*.intermediate.manifest
# Build results
[Dd]ebug*/
[Rr]elease/
bin/
*.class
build/
.gradle/

View File

@ -1,29 +0,0 @@
language: cpp
compiler:
- gcc
cache: apt
before_install:
- sudo apt-get -yqq update #< Suggested by the Travis CI doc
- sudo apt-get -fyq install #< Fixes inconsistency of packages installed previously
install:
- sudo apt-get -yq install build-essential chrpath libssl-dev libfontconfig1-dev sqlite3 libsqlite3-dev ruby gperf bison flex libicu48 libicu-dev #< Build Dependencies
before_script:
- chmod +x ./build.sh
- chmod +x ./test/run-tests.sh
- chmod +x ./test/run-tests-ghostdriver.sh
script:
- ./build.sh --confirm --silent #< Build
- ./test/run-tests.sh #< Test (PhantomJS)
- ./test/run-tests-ghostdriver.sh #< Test (GhostDriver / PhantomJSDriver)
notifications:
irc:
channels:
- "irc.freenode.org#phantomjs"
on_success: always
on_failure: always
use_notice: true

View File

@ -1,88 +0,0 @@
# Contribution Guide
This page describes how to contribute changes to PhantomJS.
Please do **not** create a pull request without reading this guide first. Failure to do so may result in the **rejection** of the pull request.
## For The Impatients
**Work on a feature branch**.
If your changes need to be modified due to some reviews, it is less clutter to tweak an isolated feature branch and push it again.
**Create a ticket in the issue tracker**.
This serves as a placeholder for important feedback, review, or any future updates.
In the commit message:
* **Keep the first line < 72 characters**. Write additional paragraphs
if necessary.
* **Put the link to the issue** (see above). This is important for cross-referencing purposes.
## Communicate
*Second opinion is always important.*
**Bug fixing**. If you have a fix for a bug, please attach your patch in the corresponding issue in the [issue tracker](https://github.com/ariya/phantomjs/issues). If there is no entry for the bug yet, then please create a new one. If you are confident working with Git, see the Get Ready section below on how to submit your change.
**Improvement and feature request**. If you have an improvement idea, please send an email to the [mailing list](http://groups.google.com/group/phantomjs) (rather than contacting the developers directly) so that other people can give their insights and opinions. This is also important to avoid duplicate work.
**Task management**. Once the feature idea is agreed upon and translated into concrete actions and tasks, please use the [issue tracker](https://github.com/ariya/phantomjs/issues) to create an issue for each individual task. Further technical discussion about the task and the implementation details should be carried out in the issue tracker.
**Extending with new API**. Whenever you want to introduce a new API, please send an email to the mailing list along with the link to the issue. Consider good API name for the object or function, read the [API Design Principle](http://developer.qt.nokia.com/wiki/API_Design_Principles) article. It may require few iterations to agree on the final API and hence it is important to engage all interested parties as early as possible.
## Get Ready
For your proposed change, you need to have:
* **an issue** (in the issue tracker) which describe your bug or feature
* **a feature branch** in your git fork
### Refer the Issue
The commit message needs to link to the issue. This cross-reference is [very important](http://ariya.ofilabs.com/2012/01/small-scale-software-craftsmanship.html) for the following reasons.
First, the commit log is frozen and can not be changed. If it contains a mistake or outdated information, the log can not be amended. However, further updates can be still posted to the linked issue, which can be followed from the commit log itself.
Second, it provides a placeholder for code review and other feedback.
An example of a bad commit log:
Fix Mountain Lion
The above log is too short and useless in the long run. A better version (and note the issue link):
Better support for OS X Mountain Lion.
require('system').os.version should give "10.8 (Mountain Lion)".
https://github.com/ariya/phantomjs/issues/10688
### Use Feature Branch
To isolate your change, please avoid working on the master branch. Instead, work on a *feature branch* (often also known as *topic branch*). You can create a new branch (example here crash-fix) off the master branch by using:
git checkout -b crash-fix master
Refer to your favorite Git tutorial/book for further detailed help.
Some good practices for the feature branch:
* Give it a meaningful name instead of, e.g. `prevent-zero-divide` instead of just `fix`
* Make *granular* and *atomic* commits, e.g. do not mix a typo fix with some major refactoring
* Keep one branch for one specific issue. If you need to work on other unrelated issues, create another branch.
## Review and Merge
When your branch is ready, send the pull request.
While it is not always the case, often it is necessary to improve parts of your code in the branch. This is the actual review process.
Here is a check list for the review:
* It does not break the test suite
* There is no typo
* The coding style follows the existing one
* There is a reasonable amount of comment
* The license header is intact
* All examples are still working

173
ChangeLog
View File

@ -1,181 +1,12 @@
Please see also http://phantomjs.org/releases.html. Please see also http://code.google.com/p/phantomjs/wiki/ReleaseNotes.
2015-01-23: Version 2.0.0
New features
* Switched to Qt 5 and updated WebKit (issue 10448)
* Implemented clearing of memory cache (issue 10357)
* Added support for HTTP header change for every request (issue 11299)
Improvements
* Fixed rendering of CJK text by always linking the codecs (issue 10249)
* Ensured onResourceReceived is still fired on an error (issue 11163)
* Fixed possible crash in handling network requests (issue 11252)
* Removed hardcoded GhostDriver launching message (issue 12681)
* Allowed disk cache more than 2 GB (issue 12303)
Examples
* Netsniff example should exit when fails to load (issue 11333)
2014-01-25: Version 1.9.7
* Reverted to GhostDriver 1.1.0 instead of 1.1.1 (issue 11915)
* Fixed another warning of obsolete userSpaceScaleFactor on OS X 10.9 (issue 11612)
2014-01-20: Version 1.9.6
* Updated GhostDriver to version 1.1.1 (issue 11877, 11893)
2014-01-19: Version 1.9.3
* Fixed CoreText performance note on OS X 10.9 (issue 11418)
* Fixed warning of obsolete userSpaceScaleFactor on OS X 10.9 (issue 11612)
2013-09-06: Version 1.9.2
* Fixed graphical artifacts with transparent background on Windows (issue 11276, 11007, 11366)
* Updated GhostDriver to version 1.0.4 (issue 11452)
2013-06-04: Version 1.9.1
Critical bug fixes:
* Fixed problems with specifying proxy server (issue 10811, 11117)
* Fixed UTF-8 encoding with system.stdout and system.stderr (issue 11162)
* Ensured that onResourceReceived will be always invoked (issue 11163)
* Fixed module loading from an absolute path on Windows (issue 11165)
* Fixed typo in the command-line option for setting the cache size (11219)
* Fixed possible crash when handling network requests (issue 11252, 11338)
2013-03-20: Version 1.9.0 "Sakura"
New features
* Added spawn and execFile to execute external programs (issue 10219)
* Added the ability to abort network requests (issue 10230)
* Added system access to stdin, stdout, and stderr (issue 10333)
* Added support for custom CA certificates location (issue 10916)
* Added seek function to the File stream (issue 10937)
* Implemented file read for a specified number of bytes (issue 10938)
* Added a callback to handle network error (issue 10954, 10997)
* Added custom encoding support when opening a page (issue 11043)
* Implemented require.stub() support for a factory function (issue 11044)
* Added page loading indicator and progress (issue 11091)
* Added a timeout option for network requests (issue 11129)
Improvements
* Fixed the build on FreeBSD (issue 10597)
* Ensured a consistent 72 dpi for Linux headless rendering (issue 10659)
* Fixed possible PDF error due to invalid CreationDate field (issue 10663)
* Fixed crash when uploading non existing files (issue 10941)
* Improved the autocomplete internal of the interactive/REPL mode (issue 10943)
* Fixed possible crash when accessing inline frames (issue 10947)
* Changed Linux binary package setup to be built on CentOS 5 (issue 10963)
* Extended SSL ignore setting to synchronous XHR (issue 10985)
* Added convenient constants for modifier keys (issue 11056)
* Fixed incorrect date handling in the cookies (issue 11068)
* Updated GhostDriver to version 1.0.3 (issue 11146)
Examples
* Fixed invalid data URI in the netsniff example (issue 10740)
* Implemented a new weather example (issue 10794)
* Fixed rendering issues in render_multi_url (issue 11021)
* Fixed proper event sequence in page_events example (issue 11028)
* Miscellanous tweaks (issue 11082)
2013-03-02: Version 1.8.2
Critical bug fixes:
* Fixed possible PDF error due to invalid CreationDate field (issue 663)
* Fixed crash when uploading non existing files (issue 941)
* Fixed possible crash when accessing inline frames (issue 947)
* Extended SSL ignore setting to synchronous XHR (issue 985)
* Fixed incorrect date handling in the cookies (issue 1068)
2013-01-06: Version 1.8.1
Critical bug fix:
* Mac OS X: Fix possible crash when using some TrueType fonts (issue 690)
2012-12-21: Version 1.8.0 "Blue Winter Rose"
New features
* Integrated GhostDriver as the WebDriver implementation (issue 49)
* Added an option to specify the SSL protocol (issue 174)
* Added encoding support for WebServer's response (issue 505)
* Added process ID (PID) to the System module (issue 769)
* Added properties to obtain page and frame title (issue 799)
* Added page navigation methods (issue 808)
* Added support for modifier keys in keyboard events (issue 835)
* Added onFilePicker callback for more generic file upload API (issue 843)
* Added the ability to set the page content and location (issue 909)
Improvements
* Fixed date parsing in ISO8601 format (issue 187, 267)
* Fixed window.location (issue 530, 632)
* Deregistered multiple callback handler (issue 807)
* Fixed sending of double-click events (issue 848)
* Increases maximum number of redirects (issue 849)
* Fixed keycodes sent for lowercase characters (issue 852)
* Fixed a regression in table row page break (issue 880)
* Completed the CoffeeScript version of the examples (issue 907)
* Updated Qt to version 4.8.4 (issue 918)
* Fixed potential hang in some example scripts (issue 922)
2012-09-22: Version 1.7.0 "Blazing Star"
New features
* Added a module system modelled after CommonJS/Node.js (issue 47)
* Added support for window pop-up (issue 151)
* Static build on Linux (issue 413)
* Added run-time detection of SSL support (issue 484)
* Added more events support (issue 492, 712)
* Added support for disabling automatic proxy detection (issue 580)
* Provided page closing callback (issue 678)
* Added methods to access URL, frames URL, frame Content (issue 758)
* Added more cookies-related API (issue 761)
Improvements
* Refactored command-line options handling (issue 55)
* Improved the workflow for producing release builds (issue 599)
* Improved cookies API and implementation (issue 603, 761)
* Improved frame switching API (issue 654)
* Fixed iframe handling regression (issue 683)
* Fixed OS version number with Windows 8 and Mountain Lion (issue 684, 688)
* Fixed HAR navigation info in the netsniff example (issue 733)
* Fixed compile warnings with Visual Studio (issue 744)
* Removed hacks for static linking on Windows (issue 753)
* Added ICO image handling on Windows (issue 779)
* Fixed font antialiasing on Windows (issue 785)
* Improved Jasmine test runner for Jasmine 1.2 (issue 792)
2012-07-22: Version 1.6.1
Bug fixes
* Don't build the deploy in debug mode (issue 599)
* Fixed building on Windows (issue 424)
* Fixed remote inspector when building statically (issue 430)
2012-06-20: Version 1.6.0 "Lavender" 2012-06-20: Version 1.6.0 "Lavender"
New features New features
* Added support for passing arguments to WebPage's evaluate (issue 132) * Added support for passing arguments to WebPage's evaluate (issue 132)
* Added callbacks for JavaScript onConfirm and onPrompt (issue 133) * Added callbacks for JavaScript onConfirm and onAlert (issue 133)
* Added stack trace when error occurs (issue 166) * Added stack trace when error occurs (issue 166)
* Added support for local storage path and quota (issue 300)
* Added initial support for cookies handling (issue 354) * Added initial support for cookies handling (issue 354)
* Added support for header footer when printing the page (issue 410, 512) * Added support for header footer when printing the page (issue 410, 512)
* Added headers support in the loading request (issue 452) * Added headers support in the loading request (issue 452)

View File

@ -1,24 +1,27 @@
# [PhantomJS](http://phantomjs.org) - Scriptable Headless WebKit # [PhantomJS](http://phantomjs.org) - Scriptable Headless WebKit
PhantomJS ([www.phantomjs.org](http://phantomjs.org)) is a headless WebKit scriptable with JavaScript. It is used by hundreds of [developers](http://phantomjs.org/buzz.html) and dozens of [organizations](http://phantomjs.org/users.html) for web-related development workflow. PhantomJS ([www.phantomjs.org](http://phantomjs.org)) is a headless WebKit scriptable with JavaScript or CoffeeScript. It is used by hundreds of [developers](http://code.google.com/p/phantomjs/wiki/ExternalArticles) and dozens of [organizations](http://code.google.com/p/phantomjs/wiki/WhoUsesPhantomJS) for web-related development workflow.
The latest [stable release](http://phantomjs.org/release-2.0.html) is version 2.0. The latest [stable release](http://code.google.com/p/phantomjs/wiki/ReleaseNotes) is version 1.6 (codenamed "Lavender"). Follow the official Twitter stream [@HeadlessPhantom](http://twitter.com/HeadlessPhantom) to get the frequent development updates.
**Note**: Please **do not** create a GitHub pull request **without** reading the [Contribution Guide](https://github.com/ariya/phantomjs/blob/master/CONTRIBUTING.md) first. Failure to do so may result in the rejection of the pull request. PhantomJS is created and maintained by [Ariya Hidayat](http://ariya.ofilabs.com/about) (Twitter: [@ariyahidayat](http://twitter.com/ariyahidayat)), with the help of [many contributors](https://github.com/ariya/phantomjs/contributors).
## Use Cases ## Use Cases
- **Headless web testing**. Lightning-fast testing without the browser is now possible! Various [test frameworks](http://phantomjs.org/headless-testing.html) such as Jasmine, Capybara, QUnit, Mocha, WebDriver, YUI Test, BusterJS, FuncUnit, Robot Framework, and many others are supported. - **Headless web testing**. Lightning-fast testing without the browser is now possible! Various [test frameworks](http://code.google.com/p/phantomjs/wiki/TestFrameworkIntegration) such as Jasmine, Capybara, QUnit, WebDriver, YUI Test, BusterJS, FuncUnit, Robot Framework, and many others are supported.
- **Page automation**. [Access and manipulate](http://phantomjs.org/page-automation.html) web pages with the standard DOM API, or with usual libraries like jQuery.
- **Screen capture**. Programmatically [capture web contents](http://phantomjs.org/screen-capture.html), including CSS, SVG and Canvas. Build server-side web graphics apps, from a screenshot service to a vector chart rasterizer. - **Site scraping**. [Access and manipulate](http://code.google.com/p/phantomjs/wiki/QuickStart#DOM_Manipulation) webpages with the standard DOM API, or with usual libraries like jQuery.
- **Network monitoring**. Automate performance analysis, track [page loading](http://phantomjs.org/network-monitoring.html) and export as standard HAR format.
- **Page rendering**. [Capture](http://code.google.com/p/phantomjs/wiki/QuickStart#Rendering) the full contents, even with SVG and Canvas, to an image. Build server-side web graphics apps, from a screenshot service to a vector chart rasterizer.
- **Network monitoring**. [Monitor](http://code.google.com/p/phantomjs/wiki/QuickStart#Network_traffic) network activity, track resource loading, perform load-balancing tests, verify contents optimization, and many others.
## Features ## Features
- **Multiplatform**, available on major operating systems: Windows, Mac OS X, Linux, and other Unices. - **Multiplatform**, available on major operating systems: Windows, Mac OS X, Linux, other Unices.
- **Fast and native implementation** of web standards: DOM, CSS, JavaScript, Canvas, and SVG. No emulation! - **Fast and native implementation** of web standards: DOM, CSS, JavaScript, Canvas, SVG. No emulation!
- **Pure headless (no X11) on Linux**, ideal for continuous integration systems. Also runs on Amazon EC2, Heroku, and Iron.io. - **Pure headless (X11) on Linux**, ideal for continuous integration systems. Also runs on Amazon EC2.
- **Easy to install**: [Download](http://phantomjs.org/download.html), unpack, and start having fun in just 5 minutes. - **Easy to install**: [Download](http://code.google.com/p/phantomjs/wiki/Installation), unpack, and start having fun in just 5 minutes.
## Ecosystem ## Ecosystem
@ -29,18 +32,11 @@ PhantomJS needs not be used only as a stand-alone tool. Check also some excellen
- [Guard::Jasmine](https://github.com/netzpirat/guard-jasmine) automatically tests Jasmine specs on Rails when files are modified. - [Guard::Jasmine](https://github.com/netzpirat/guard-jasmine) automatically tests Jasmine specs on Rails when files are modified.
- [GhostDriver](http://github.com/detro/ghostdriver/) complements Selenium tests with a PhantomJS WebDriver implementation. - [GhostDriver](http://github.com/detro/ghostdriver/) complements Selenium tests with a PhantomJS WebDriver implementation.
- [PhantomRobot](https://github.com/datakurre/phantomrobot) runs Robot Framework acceptance tests in the background via PhantomJS. - [PhantomRobot](https://github.com/datakurre/phantomrobot) runs Robot Framework acceptance tests in the background via PhantomJS.
- [Mocha-PhantomJS](https://github.com/metaskills/mocha-phantomjs) run Mocha tests using PhantomJS.
and many others [related projects](http://phantomjs.org/related-projects.html). and many others [companion projects](http://code.google.com/p/phantomjs/wiki/WhoUsesPhantomJS).
## Questions? ## Questions?
- Explore the complete [documentation](http://phantomjs.org/documentation/). - Explore the complete [documentation](http://code.google.com/p/phantomjs/wiki/PhantomJS?tm=6).
- Read tons of [user articles](http://phantomjs.org/buzz.html) on using PhantomJS. - Read tons of [user articles](http://code.google.com/p/phantomjs/wiki/ExternalArticles) on using PhantomJS.
- Join the [mailing-list](http://groups.google.com/group/phantomjs) and discuss with other PhantomJS fans. - Join the [mailing-list](http://groups.google.com/group/phantomjs) and discuss with other PhantomJS fans.
PhantomJS is free software/open source, and is distributed under the [BSD license](http://opensource.org/licenses/BSD-3-Clause). It contains third-party code, see the included `third-party.txt` file for the license information on third-party code.
PhantomJS is created and maintained by [Ariya Hidayat](http://ariya.ofilabs.com/about) (Twitter: [@ariyahidayat](http://twitter.com/ariyahidayat)), with the help of [many contributors](https://github.com/ariya/phantomjs/contributors). Follow the official Twitter stream [@PhantomJS](http://twitter.com/PhantomJS) to get the frequent development updates.

128
build.cmd
View File

@ -1,128 +0,0 @@
@echo off
SETLOCAL EnableExtensions EnableDelayedExpansion
set BUILD_TYPE=release
if /i "%1" == "debug" (
SET BUILD_TYPE=debug
)
set ROOT_DIR=%CD%
set 3RD_PARTY_LIBRARIES_REPO_URL=https://github.com/Vitallium/phantomjs-3rdparty-win
set 3RD_PARTY_LIBRARIES_REPO_BRANCH=msvc2013
if /i BUILD_TYPE == "debug" (
set 3RD_PARTY_LIBRARIES_REPO_BRANCH=msvc2013_debug
)
set BUILD_DATESTAMP=%date:~-4,4%%date:~-7,2%%date:~-10,2%
set BUILD_TIMESTAMP=%time:~-11,2%%time:~-8,2%
:: replace leading space with 0
set BUILD_TIMESTAMP=%BUILD_TIMESTAMP: =0%
set QT_LOG_FILE=!ROOT_DIR!\build_qt_!BUILD_DATESTAMP!-!BUILD_TIMESTAMP!.log
set WEBKIT_LOG_FILE=!ROOT_DIR!\build_webkit_!BUILD_DATESTAMP!_!BUILD_TIMESTAMP!.log
set PHANTOMJS_LOG_FILE=!ROOT_DIR!\build_phantomjs_!BUILD_DATESTAMP!_!BUILD_TIMESTAMP!.log
set MAKE_TOOL=nmake
echo:
echo Build type: !BUILD_TYPE!
call :build
ENDLOCAL
@exit /B 0
rem ========================================================================================================
:build
SETLOCAL EnableExtensions EnableDelayedExpansion
set _3RDPARTY=!ROOT_DIR!\src\qt\3rdparty
for %%X in (git.exe) do (set GIT_FOUND=%%~$PATH:X)
if defined GIT_FOUND (
echo.
echo GIT found. Getting 3rd party libraries.
if not exist !_3RDPARTY! call git clone -b !3RD_PARTY_LIBRARIES_REPO_BRANCH! !3RD_PARTY_LIBRARIES_REPO_URL! !_3RDPARTY!
) else (
ECHO.
CALL :exitB "Git is missing! Can't proceed. Please install Git."
GOTO :eof
)
:: prepare 3rdparty libraries
:: setup INCLUDE and LIB environment variables
:: OpenSSL
set OPENSSL_DIR=!_3RDPARTY!\openssl
set OPENSSL_LIB=!OPENSSL_DIR!\lib
set OPENSSL_INCLUDE=!OPENSSL_DIR!\include
:: ICU
set ICU_DIR=!_3RDPARTY!\libicu
set ICU_LIB=!ICU_DIR!\lib
set ICU_INCLUDE=!ICU_DIR!\include
:: libxml
set LIBXML_DIR=!_3RDPARTY!\libxml
set LIBXML_LIB=!LIBXML_DIR!\lib
set LIBXML_INCLUDE=!LIBXML_DIR!\include\libxml2
:: sqlite
set SQLITE3SRCDIR=!ROOT_DIR!\src\qt\qtbase\src\3rdparty\sqlite
set LIB=!OPENSSL_LIB!;!ICU_LIB!;!LIBXML_LIB!;%LIB%
set INCLUDE=!OPENSSL_INCLUDE!;!ICU_INCLUDE!;!LIBXML_INCLUDE!;%INCLUDE%
set PATH=!_3RDPARTY!\gnuwin32\bin;%PATH%
echo LIB: %LIB%
echo INCLUDE: %INCLUDE%
for %%X in (jom.exe) do (set JOMFOUND=%%~$PATH:X)
if defined JOMFOUND (
set MAKE_TOOL=jom
) else (
set MAKE_TOOL=nmake
)
pushd !ROOT_DIR!\src\qt
call preconfig.cmd !BUILD_TYPE! 2>&1 >> !QT_LOG_FILE!
popd
set PATH=!ROOT_DIR!\src\qt\qtbase\bin;%PATH%
for %%X in (qmake.exe) do (set QMAKE_FOUND=%%~$PATH:X)
if defined QMAKE_FOUND (
echo.
echo qmake found. Building QtWebkit
) else (
ECHO.
CALL :exitB "qmake.exe is missing! Can't proceed."
GOTO :eof
)
pushd !ROOT_DIR!\src\qt\qtwebkit
call qmake.exe
%MAKE_TOOL% %BUILD_TYPE% 2>&1 >> !WEBKIT_LOG_FILE!
popd
pushd !ROOT_DIR!\src
call qmake.exe
%MAKE_TOOL% %BUILD_TYPE% 2>&1 >> !PHANTOMJS_LOG_FILE!
popd
if EXIST !ROOT_DIR!\bin\phantomjs.exe (
echo.
echo Build has finished
echo.
) else (
echo:
echo Unable to find phantomjs.exe. Please, check log files:
echo Qt: !QT_LOG_FILE!
echo Webkit: !WEBKIT_LOG_FILE!
echo PhantomJS: !PHANTOMJS_LOG_FILE!
echo:
)
EXIT /b
rem ========================================================================================================
:: %1 an error message
:exitB
echo:
echo Error: %1
echo:
echo Contact vitaliy.slobodin@gmail.com
@exit /B 0

View File

@ -1,7 +1,9 @@
#!/usr/bin/env bash #!/bin/bash
set -e set -e
QT_CFG=''
COMPILE_JOBS=1 COMPILE_JOBS=1
MAKEFLAGS_JOBS='' MAKEFLAGS_JOBS=''
@ -16,67 +18,45 @@ elif [[ $OSTYPE = darwin* ]]; then
# We only support modern Mac machines, they are at least using # We only support modern Mac machines, they are at least using
# hyperthreaded dual-core CPU. # hyperthreaded dual-core CPU.
COMPILE_JOBS=4 COMPILE_JOBS=4
elif [[ $OSTYPE == freebsd* ]]; then
COMPILE_JOBS=`sysctl -n hw.ncpu`
else else
CPU_CORES=`grep -c ^processor /proc/cpuinfo` CPU_CORES=`grep -c ^processor /proc/cpuinfo`
if [[ "$CPU_CORES" -gt 1 ]]; then if [[ "$CPU_CORES" -gt 1 ]]; then
COMPILE_JOBS=$CPU_CORES COMPILE_JOBS=$CPU_CORES
if [[ "$COMPILE_JOBS" -gt 8 ]]; then
# Safety net.
COMPILE_JOBS=8
fi
fi fi
fi fi
if [[ "$COMPILE_JOBS" -gt 8 ]]; then until [ -z "$1" ]; do
# Safety net.
COMPILE_JOBS=8
fi
SILENT=
until [[ -z "$1" ]]; do
case $1 in case $1 in
(--qmake-args) "--qt-config")
shift
QT_CFG=" $1"
shift;;
"--qmake-args")
shift shift
QMAKE_ARGS=$1 QMAKE_ARGS=$1
shift;; shift;;
(--jobs) "--jobs")
shift shift
COMPILE_JOBS=$1 COMPILE_JOBS=$1
shift;; shift;;
(--silent)
SILENT=silent
shift;;
"--help") "--help")
cat <<EOF echo "Usage: $0 [--qt-config CONFIG] [--jobs NUM]"
Usage: $0 [--jobs NUM] echo
echo " --qt-config CONFIG Specify extra config options to be used when configuring Qt"
--silent Produce less verbose output. echo " --jobs NUM How many parallel compile jobs to use. Defaults to 4."
--jobs NUM How many parallel compile jobs to use. echo
Defaults to the number of CPU cores you have,
with a maximum of 8.
EOF
exit 0 exit 0
;; ;;
*) *)
echo "Unrecognised option: $1" >&2 echo "Unrecognised option: $1"
exit 1;; exit 1;;
esac esac
done done
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown cd src/qt && ./preconfig.sh --jobs $COMPILE_JOBS --qt-config "$QT_CFG" && cd ../..
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown src/qt/bin/qmake $QMAKE_ARGS
UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
echo "System architecture... ($UNAME_SYSTEM $UNAME_RELEASE $UNAME_MACHINE)"
export QMAKE=qmake
# some Linux distros (e.g. Debian) allow you to parallel-install
# Qt4 and Qt5, using this environment variable to declare which
# one you want
export QT_SELECT=qt5
echo
echo "Building main PhantomJS application..."
echo
$QMAKE $QMAKE_ARGS
make -j$COMPILE_JOBS make -j$COMPILE_JOBS

5
debian/changelog vendored Normal file
View File

@ -0,0 +1,5 @@
phantomjs (1.3-1~git20110703) unstable; urgency=low
* Initial release.
-- Dennis Kaarsemaker <dennis@kaarsemaker.net> Sun, 03 Jul 2011 20:45:52 +0200

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
7

24
debian/control vendored Normal file
View File

@ -0,0 +1,24 @@
Source: phantomjs
Section: python
Priority: extra
Maintainer: Dennis Kaarsemaker <dennis@kaarsemaker.net>
Build-Depends: debhelper (>= 7), libqt4-dev (>= 4.6), libqtwebkit-dev, qt4-qmake, python-support (>= 0.6.4), python-all-dev (>= 2.5), python-qt4-dev, python-qt4
Standards-Version: 3.8.4
XS-Python-Version: >= 2.6
Vcs-Git: http://github.com/ariya/phantomjs
Package: phantomjs
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: phantomjs - minimalistic headless WebKit-based JavaScript-driven tool
PhantomJS is a minimalistic, headless, WebKit-based, JavaScript-driven tool.
PhantomJs has native support for different web technologies: DOM handling,
CSS selector, JSON, Canvas, SVG, and of course JavaScript.
Package: python-pyphantomjs
Architecture: all
Depends: ${python:Depends}, ${shlibs:Depends}, ${misc:Depends}, python-qt4, python-argparse
Description: phantomjs - minimalistic headless WebKit-based JavaScript-driven tool
PhantomJS is a minimalistic, headless, WebKit-based, JavaScript-driven tool.
PhantomJs has native support for different web technologies: DOM handling,
CSS selector, JSON, Canvas, SVG, and of course JavaScript.

35
debian/copyright vendored Normal file
View File

@ -0,0 +1,35 @@
Format-Specification: http://anonscm.debian.org/viewvc/dep/web/deps/dep5.mdwn?revision=174&view=co&pathrev=174
Name: phantomjs
Maintainer: Dennis Kaarsemaker <dennis@kaarsemaker.net>
Source: http://github.com/ariya/phantomjs
Copyright: 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
License: BSD-3-clause
Files: debian/*
Copyright: 2011 Dennis Kaarsemaker <dennis@kaarsemaker.net>
License: BSD-3-clause
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
.
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

2
debian/phantomjs.docs vendored Normal file
View File

@ -0,0 +1,2 @@
README.md
examples

1
debian/phantomjs.install vendored Normal file
View File

@ -0,0 +1 @@
debian/tmp/usr/bin/phantomjs

1
debian/python-pyphantomjs.docs vendored Normal file
View File

@ -0,0 +1 @@
python/README.md

2
debian/python-pyphantomjs.install vendored Normal file
View File

@ -0,0 +1,2 @@
debian/tmp/usr/bin/pyphantomjs
debian/tmp/usr/lib

19
debian/rules vendored Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/make -f
%:
dh $@
override_dh_auto_build:
qmake-qt4
$(MAKE)
cd python && python setup.py build
override_dh_auto_install:
$(MAKE) install
mkdir -p debian/tmp/usr/bin
cp bin/* debian/tmp/usr/bin
cd python && python setup.py install --root=../debian/tmp --install-layout=deb
override_dh_auto_clean:
if [ -f Makefile ]; then $(MAKE) distclean; fi
cd python && python setup.py clean

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
1.0

16
deploy/Vagrantfile vendored
View File

@ -9,23 +9,11 @@ end
Vagrant::Config.run do |config| Vagrant::Config.run do |config|
config.vm.define :i686 do |c| config.vm.define :i686 do |c|
c.vm.box_url = "https://opscode-vm.s3.amazonaws.com/vagrant/boxes/opscode-centos-5.8-i386.box"
c.vm.box = "centos_58_32bit"
c.vm.customize ["modifyvm", :id, "--ostype", "RedHat"]
end
config.vm.define :x86_64 do |c|
c.vm.box_url = "https://opscode-vm.s3.amazonaws.com/vagrant/boxes/opscode-centos-5.8.box"
c.vm.box = "centos_58_64bit"
c.vm.customize ["modifyvm", :id, "--ostype", "RedHat_64"]
end
config.vm.define :lucid32 do |c|
c.vm.box_url = "http://files.vagrantup.com/lucid32.box" c.vm.box_url = "http://files.vagrantup.com/lucid32.box"
c.vm.box = "lucid32" c.vm.box = "lucid32"
end end
config.vm.define :lucid64 do |c| config.vm.define :x86_64 do |c|
c.vm.box_url = "http://files.vagrantup.com/lucid64.box" c.vm.box_url = "http://files.vagrantup.com/lucid64.box"
c.vm.box = "lucid64" c.vm.box = "lucid64"
end end
@ -37,6 +25,6 @@ Vagrant::Config.run do |config|
# You may wish to tweak these, but be aware that you need quite a lot of # You may wish to tweak these, but be aware that you need quite a lot of
# memory for the linking stage. # memory for the linking stage.
config.vm.customize ["modifyvm", :id, "--memory", 3072] config.vm.customize ["modifyvm", :id, "--memory", 2048]
config.vm.customize ["modifyvm", :id, "--cpus", 2] config.vm.customize ["modifyvm", :id, "--cpus", 2]
end end

View File

@ -2,16 +2,14 @@
cd `dirname $0`/.. cd `dirname $0`/..
echo "Building Qt and PhantomJS with debugging symbols. If you have previously" \ echo "Building Qt and PhantomJS in debug mode. If you have previously" \
"built without debugging symbols, you should run:" "built in release mode, you should run:"
echo echo
echo " $ git clean -xdff" echo " $ make clean && cd src/qt && make clean && cd ../.."
echo echo
# This incantation will cause Qt and WebKit and PhantomJS to all build in "release" # Build the project
# mode, with compiler optimisations, but also with debug symbols. (We will strip the ./build.sh --qt-config "-debug -webkit-debug" --qmake-args "CONFIG-=release CONFIG+=debug" || exit 1
# symbols in package.sh.)
CFLAGS=-g CXXFLAGS=-g ./build.sh --confirm --qt-config '-webkit-debug' --qmake-args "QMAKE_CFLAGS=-g QMAKE_CXXFLAGS=-g" || exit 1
# Package the release tarball # Package the release tarball
rm deploy/*.tar.bz2 2>/dev/null rm deploy/*.tar.bz2 2>/dev/null
@ -20,7 +18,7 @@ rm deploy/*.tar.bz2 2>/dev/null
# Build the dump_syms program for dumping breakpad debugging symbols # Build the dump_syms program for dumping breakpad debugging symbols
if [[ $OSTYPE = darwin* ]]; then if [[ $OSTYPE = darwin* ]]; then
pushd tools pushd tools
../src/qt/bin/qmake dump-syms-mac.pro && make ../src/qt/bin/qmake dump-syms-mac.pro
popd popd
else else
pushd src/breakpad pushd src/breakpad
@ -32,22 +30,13 @@ fi
./tools/dump-symbols.sh ./tools/dump-symbols.sh
version=$(bin/phantomjs --version | sed 's/ /-/' | sed 's/[()]//g')
if [[ $OSTYPE = darwin* ]]; then
symbols="phantomjs-$version-macosx-symbols"
else
symbols="phantomjs-$version-linux-$(uname -m)-symbols"
fi
cp -r symbols/ $symbols
# The minidump_stackwalk program is architecture-specific, so copy the # The minidump_stackwalk program is architecture-specific, so copy the
# binary for later use. This means that e.g. a developer on x86_64 can # binary for later use. This means that e.g. a developer on x86_64 can
# analyse a crash dump produced by a i686 user. # analyse a crash dump produced by a i686 user.
# #
# We don't yet have a process for building minidump_stackwalk on OS X # We don't yet have a process for building minidump_stackwalk on OS X
if [[ $OSTYPE != darwin* ]]; then if [[ $OSTYPE != darwin* ]]; then
cp src/breakpad/src/processor/minidump_stackwalk $symbols cp src/breakpad/src/processor/minidump_stackwalk symbols/
read -r -d '' README <<EOT read -r -d '' README <<EOT
These are symbols files that can be used to analyse a crash dump These are symbols files that can be used to analyse a crash dump
@ -57,11 +46,10 @@ run:
./minidump_stackwalk /path/to/crash.dmp . ./minidump_stackwalk /path/to/crash.dmp .
EOT EOT
echo "$README" > $symbols/README echo "$README" > symbols/README
fi fi
tar -cjf deploy/$symbols.tar.bz2 $symbols tar -cjf $(ls deploy/*.bz2 | sed 's/\.tar\.bz2/-symbols.tar.bz2/') symbols/
rm -r $symbols
echo "PhantomJS built and packaged:" echo "PhantomJS built and packaged:"
echo echo

View File

@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
# #
# usage: just run this script (after having run build.sh) # usage: just run this script (after having run build.sh)
@ -18,39 +18,14 @@ if [[ ! -f ../bin/phantomjs ]]; then
exit 1 exit 1
fi fi
if [[ "$1" = "--bundle-libs" ]]; then
bundle_libs=1
else
bundle_libs=0
fi
version=$(../bin/phantomjs --version | sed 's/ /-/' | sed 's/[()]//g') version=$(../bin/phantomjs --version | sed 's/ /-/' | sed 's/[()]//g')
src=.. src=..
echo "packaging phantomjs $version" echo "packaging phantomjs $version"
if [[ $OSTYPE = darwin* ]]; then if [[ $OSTYPE = darwin* ]]; then
dest="phantomjs-$version-macosx" dest="phantomjs-$version-macosx-static"
else else
dest="phantomjs-$version-linux-$(uname -m)"
fi
rm -Rf $dest{.tar.bz2,} &> /dev/null
mkdir -p $dest/bin
echo
echo -n "copying files..."
cp $src/bin/phantomjs $dest/bin
cp -r $src/{ChangeLog,examples,LICENSE.BSD,third-party.txt,README.md} $dest/
echo "done"
echo
phantomjs=$dest/bin/phantomjs
if [[ "$bundle_libs" = "1" ]]; then
mkdir -p $dest/lib
if [[ ! -f brandelf ]]; then if [[ ! -f brandelf ]]; then
echo echo
echo "brandelf executable not found in current dir" echo "brandelf executable not found in current dir"
@ -59,15 +34,36 @@ if [[ "$bundle_libs" = "1" ]]; then
echo "done" echo "done"
fi fi
libs=$(ldd $phantomjs | egrep -o "/[^ ]+ ") dest="phantomjs-$version-linux-$(uname -m)-dynamic"
fi
rm -Rf $dest{.tar.bz2,} &> /dev/null
mkdir -p $dest/bin
if [[ $OSTYPE != darwin* ]]; then
mkdir -p $dest/lib
fi
echo
echo -n "copying files..."
cp $src/bin/phantomjs $dest/bin
cp -r $src/{ChangeLog,examples,LICENSE.BSD,README.md} $dest/
echo "done"
echo
if [[ $OSTYPE == darwin* ]]; then
echo -n "compressing binary..."
[ ! -z upx ] && upx -qqq -9 $dest/bin/phantomjs
echo "done"
echo
else
echo -n "copying shared libs..." echo -n "copying shared libs..."
libld= libld=
for l in $libs; do for l in $(ldd $dest/bin/phantomjs | egrep -o "/[^ ]+ "); do
ll=$(basename $l) if [[ "$l" != "" ]]; then
cp $l $dest/lib/$ll ll=$(basename $l)
cp $l $dest/lib/$ll
if [[ "$bundle_libs" = "1" ]]; then
# ensure OS ABI compatibility # ensure OS ABI compatibility
./brandelf -t SVR4 $dest/lib/$ll ./brandelf -t SVR4 $dest/lib/$ll
if [[ "$l" == *"ld-linux"* ]]; then if [[ "$l" == *"ld-linux"* ]]; then
@ -77,44 +73,37 @@ if [[ "$bundle_libs" = "1" ]]; then
done done
echo "done" echo "done"
echo echo
fi
# strip to reduce file size
echo -n "stripping binary and libs..."
if [[ $OSTYPE = darwin* ]]; then
strip -x $dest/bin/*
else
strip -s $dest/lib/* $dest/bin/*
fi
echo "done"
echo
if [[ $OSTYPE != darwin* ]]; then
echo -n "writing run script..." echo -n "writing run script..."
mv $phantomjs $phantomjs.bin # write run scripts
phantomjs=$phantomjs.bin mv $dest/bin/phantomjs $dest/bin/phantomjs.bin
run=$dest/bin/phantomjs run=$dest/bin/phantomjs
echo '#!/bin/sh' >> $run echo '#!/bin/sh' >> $run
echo 'path=$(dirname $(dirname $(readlink -f $0)))' >> $run echo 'path=$(dirname $(dirname $(readlink -f $0)))' >> $run
echo 'export LD_LIBRARY_PATH=$path/lib' >> $run echo 'export LD_LIBRARY_PATH=$path/lib' >> $run
echo 'exec $path/lib/'$libld' $phantomjs $@' >> $run echo 'exec $path/lib/'$libld' $path/bin/phantomjs.bin $@' >> $run
chmod +x $run chmod +x $run
echo "done" echo "done"
echo echo
fi fi
echo -n "stripping binary and libs..." echo -n "creating tarball..."
if [[ $OSTYPE = darwin* ]]; then tar -cjf $dest{.tar.bz2,}
strip -x $phantomjs
else
strip -s $phantomjs
[[ -d $dest/lib ]] && strip -s $dest/lib/*
fi
echo "done" echo "done"
echo echo
echo -n "compressing binary..." echo "you can now deploy $dest or $dest.tar.bz2"
if type upx >/dev/null 2>&1; then
upx -qqq -9 $phantomjs
echo "done"
else
echo "upx not found"
fi
echo
echo -n "creating archive..."
if [[ $OSTYPE = darwin* ]]; then
zip -r $dest.zip $dest
else
tar -cjf $dest{.tar.bz2,}
fi
echo "done"
echo

View File

@ -1,29 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
export PATH=$HOME/git/bin:$PATH apt-get update
apt-get install -y build-essential git-core libssl-dev libfontconfig1-dev gdb binutils-gold
if type apt-get >/dev/null 2>&1; then
apt-get update
apt-get install -y build-essential git-core libssl-dev libfontconfig1-dev gdb binutils-gold
fi
if type yum >/dev/null 2>&1; then
yum -y update
yum -y install gcc gcc-c++ make openssl-devel freetype-devel fontconfig-devel
if type git >/dev/null 2>&1; then
echo "Git is already available."
else
yum -y install cpio expat-devel gettext-devel zlib-devel
echo "Downloading and building git..."
rm -rf git-*
wget -nv https://git-core.googlecode.com/files/git-1.8.0.3.tar.gz
tar -xzvf git-1.8.0.3.tar.gz
cd git-1.8.0.3
./configure --prefix=$HOME/git && make -j2 && make install
cd ..
sleep 3
fi
fi
if [[ ! -d phantomjs ]]; then if [[ ! -d phantomjs ]]; then
git clone git://github.com/ariya/phantomjs.git git clone git://github.com/ariya/phantomjs.git

View File

@ -0,0 +1,7 @@
system = require 'system'
if system.args.length is 1
console.log 'Try to pass some args when invoking this script!'
else
for arg, i in system.args
console.log i + ': ' + arg
phantom.exit()

View File

@ -1,27 +0,0 @@
var spawn = require("child_process").spawn
var execFile = require("child_process").execFile
var child = spawn("ls", ["-lF", "/rooot"])
child.stdout.on("data", function (data) {
console.log("spawnSTDOUT:", JSON.stringify(data))
})
child.stderr.on("data", function (data) {
console.log("spawnSTDERR:", JSON.stringify(data))
})
child.on("exit", function (code) {
console.log("spawnEXIT:", code)
})
//child.kill("SIGKILL")
execFile("ls", ["-lF", "/usr"], null, function (err, stdout, stderr) {
console.log("execFileSTDOUT:", JSON.stringify(stdout))
console.log("execFileSTDERR:", JSON.stringify(stderr))
})
setTimeout(function () {
phantom.exit(0)
}, 2000)

View File

@ -0,0 +1,46 @@
page = require('webpage').create()
page.viewportSize = { width: 400, height : 400 }
page.content = '<html><body><canvas id="surface"></canvas></body></html>'
page.evaluate ->
el = document.getElementById 'surface'
context = el.getContext '2d'
width = window.innerWidth
height = window.innerHeight
cx = width / 2
cy = height / 2
radius = width / 2.3
i = 0
el.width = width
el.height = height
imageData = context.createImageData(width, height)
pixels = imageData.data
for y in [0...height]
for x in [0...width]
i = i + 4
rx = x - cx
ry = y - cy
d = rx * rx + ry * ry
if d < radius * radius
hue = 6 * (Math.atan2(ry, rx) + Math.PI) / (2 * Math.PI)
sat = Math.sqrt(d) / radius
g = Math.floor(hue)
f = hue - g
u = 255 * (1 - sat)
v = 255 * (1 - sat * f)
w = 255 * (1 - sat * (1 - f))
pixels[i] = [255, v, u, u, w, 255, 255][g]
pixels[i + 1] = [w, 255, 255, v, u, u, w][g]
pixels[i + 2] = [u, u, w, 255, 255, v, u][g]
pixels[i + 3] = 255
context.putImageData imageData, 0, 0
document.body.style.backgroundColor = 'white'
document.body.style.margin = '0px'
page.render('colorwheel.png')
phantom.exit()

View File

@ -0,0 +1,8 @@
t = 10
interval = setInterval ->
if t > 0
console.log t--
else
console.log 'BLAST OFF!'
phantom.exit()
, 1000

View File

@ -0,0 +1,41 @@
page = require('webpage').create()
system = require 'system'
page.onInitialized = ->
page.evaluate ->
userAgent = window.navigator.userAgent
platform = window.navigator.platform
window.navigator =
appCodeName: 'Mozilla'
appName: 'Netscape'
cookieEnabled: false
sniffed: false
window.navigator.__defineGetter__ 'userAgent', ->
window.navigator.sniffed = true
userAgent
window.navigator.__defineGetter__ 'platform', ->
window.navigator.sniffed = true
platform
if system.args.length is 1
console.log 'Usage: unsniff.coffee <some URL>'
phantom.exit 1
else
address = system.args[1]
console.log 'Checking ' + address + '...'
page.open address, (status) ->
if status isnt 'success'
console.log 'FAIL to load the address'
else
window.setTimeout ->
sniffed = page.evaluate(->
navigator.sniffed
)
if sniffed
console.log 'The page tried to sniff the user agent.'
else
console.log 'The page did not try to sniff the user agent.'
phantom.exit()
, 1500

View File

@ -33,7 +33,7 @@ page.onInitialized = function () {
}; };
if (system.args.length === 1) { if (system.args.length === 1) {
console.log('Usage: detectsniff.js <some URL>'); console.log('Usage: unsniff.js <some URL>');
phantom.exit(1); phantom.exit(1);
} else { } else {
address = system.args[1]; address = system.args[1];
@ -41,7 +41,6 @@ if (system.args.length === 1) {
page.open(address, function (status) { page.open(address, function (status) {
if (status !== 'success') { if (status !== 'success') {
console.log('FAIL to load the address'); console.log('FAIL to load the address');
phantom.exit();
} else { } else {
window.setTimeout(function () { window.setTimeout(function () {
sniffed = page.evaluate(function () { sniffed = page.evaluate(function () {

30
examples/direction.coffee Normal file
View File

@ -0,0 +1,30 @@
# Get driving direction using Google Directions API.
page = require('webpage').create()
system = require 'system'
if system.args.length < 3
console.log 'Usage: direction.coffee origin destination'
console.log 'Example: direction.coffee "San Diego" "Palo Alto"'
phantom.exit 1
else
origin = system.args[1]
dest = system.args[2]
page.open encodeURI('http://maps.googleapis.com/maps/api/directions/xml?origin=' + origin +
'&destination=' + dest + '&units=imperial&mode=driving&sensor=false'),
(status) ->
if status isnt 'success'
console.log 'Unable to access network'
else
steps = page.content.match(/<html_instructions>(.*)<\/html_instructions>/ig)
if not steps
console.log 'No data available for ' + origin + ' to ' + dest
else
for ins in steps
ins = ins.replace(/\&lt;/ig, '<').replace(/\&gt;/ig, '>')
ins = ins.replace(/\<div/ig, '\n<div')
ins = ins.replace(/<.*?>/g, '')
console.log(ins)
console.log ''
console.log page.content.match(/<copyrights>.*<\/copyrights>/ig).join('').replace(/<.*?>/g, '')
phantom.exit()

View File

@ -0,0 +1,20 @@
# echoToFile.coffee - Write in a given file all the parameters passed on the CLI
fs = require 'fs'
system = require 'system'
if system.args.length < 3
console.log "Usage: echoToFile.coffee DESTINATION_FILE <arguments to echo...>"
phantom.exit 1
else
content = ""
f = null
i = 2
while i < system.args.length
content += system.args[i] + (if i == system.args.length - 1 then "" else " ")
++i
try
f = fs.open(system.args[1], "w")
f.writeLine content
catch e
console.log e
phantom.exit()

View File

@ -7,15 +7,16 @@ if (system.args.length < 3) {
phantom.exit(1); phantom.exit(1);
} else { } else {
var content = '', var content = '',
f = null, f = null;
i;
for ( i= 2; i < system.args.length; ++i ) { for ( i= 2; i < system.args.length; ++i ) {
content += system.args[i] + (i === system.args.length-1 ? '' : ' '); content += system.args[i] + (i === system.args.length-1 ? '' : ' ');
} }
try { try {
fs.write(system.args[1], content, 'w'); f = fs.open(system.args[1], "w");
} catch(e) { f.writeLine(content);
f.close();
} catch (e) {
console.log(e); console.log(e);
} }

8
examples/fibo.coffee Normal file
View File

@ -0,0 +1,8 @@
fibs = [0, 1]
f = ->
console.log fibs[fibs.length - 1]
fibs.push fibs[fibs.length - 1] + fibs[fibs.length - 2]
if fibs.length > 10
window.clearInterval ticker
phantom.exit()
ticker = window.setInterval(f, 300)

29
examples/follow.coffee Normal file
View File

@ -0,0 +1,29 @@
# List following and followers from several accounts
users= [
'ariyahidayat'
'detronizator'
'KDABQt'
'lfranchi'
'jonleighton'
]
follow = (user, callback) ->
page = require('webpage').create()
page.open 'http://mobile.twitter.com/' + user, (status) ->
if status is 'fail'
console.log user + ': ?'
else
data = page.evaluate -> document.querySelector('div.profile td.stat.stat-last div.statnum').innerText;
console.log user + ': ' + data
callback.apply()
process = () ->
if (users.length > 0)
user = users[0]
users.splice(0, 1)
follow(user, process)
else
phantom.exit()
process()

View File

@ -1,13 +1,10 @@
// List following and followers from several accounts // List following and followers from several accounts
var users = ['PhantomJS', var users = ['ariyahidayat',
'ariyahidayat',
'detronizator', 'detronizator',
'KDABQt', 'KDABQt',
'lfranchi', 'lfranchi',
'jonleighton', 'jonleighton'];
'_jamesmgreene',
'Vitalliumm'];
function follow(user, callback) { function follow(user, callback) {
var page = require('webpage').create(); var page = require('webpage').create();
@ -20,7 +17,6 @@ function follow(user, callback) {
}); });
console.log(user + ': ' + data); console.log(user + ': ' + data);
} }
page.close();
callback.apply(); callback.apply();
}); });
} }

2
examples/hello.coffee Normal file
View File

@ -0,0 +1,2 @@
console.log 'Hello, world!'
phantom.exit()

20
examples/imagebin.coffee Normal file
View File

@ -0,0 +1,20 @@
# Upload an image to imagebin.org
page = require('webpage').create()
system = require 'system'
if system.args.length isnt 2
console.log 'Usage: imagebin.coffee filename'
phantom.exit 1
else
fname = system.args[1]
page.open 'http://imagebin.org/index.php?page=add', ->
page.uploadFile 'input[name=image]', fname
page.evaluate ->
document.querySelector('input[name=nickname]').value = 'phantom'
document.querySelector('input[name=disclaimer_agree]').click()
document.querySelector('form').submit()
window.setTimeout ->
phantom.exit()
, 3000

View File

@ -2,7 +2,7 @@
var page = require('webpage').create(), var page = require('webpage').create(),
system = require('system'), system = require('system'),
fname; fname;
if (system.args.length !== 2) { if (system.args.length !== 2) {
console.log('Usage: imagebin.js filename'); console.log('Usage: imagebin.js filename');

23
examples/injectme.coffee Normal file
View File

@ -0,0 +1,23 @@
# Use 'page.injectJs()' to load the script itself in the Page context
if phantom?
page = require('webpage').create()
# Route "console.log()" calls from within the Page context to the main
# Phantom context (i.e. current "this")
page.onConsoleMessage = (msg) -> console.log(msg)
page.onAlert = (msg) -> console.log(msg)
console.log "* Script running in the Phantom context."
console.log "* Script will 'inject' itself in a page..."
page.open "about:blank", (status) ->
if status is "success"
if page.injectJs("injectme.coffee")
console.log "... done injecting itself!"
else
console.log "... fail! Check the $PWD?!"
phantom.exit()
else
alert "* Script running in the Page context."

13
examples/ipgeocode.coffee Normal file
View File

@ -0,0 +1,13 @@
# Give the estimated location based on the IP address.
window.cb = (data) ->
loc = data.city
if data.region_name.length > 0
loc = loc + ', ' + data.region_name
console.log 'IP address: ' + data.ip
console.log 'Estimated location: ' + loc
phantom.exit()
el = document.createElement 'script'
el.src = 'http://freegeoip.net/json/?callback=window.cb'
document.body.appendChild el

18
examples/loadspeed.coffee Normal file
View File

@ -0,0 +1,18 @@
page = require('webpage').create()
system = require 'system'
if system.args.length is 1
console.log 'Usage: loadspeed.coffee <some URL>'
phantom.exit 1
else
t = Date.now()
address = system.args[1]
page.open address, (status) ->
if status isnt 'success'
console.log('FAIL to load the address')
else
t = Date.now() - t
console.log('Page title is ' + page.evaluate( (-> document.title) ))
console.log('Loading time ' + t + ' msec')
phantom.exit()

View File

@ -1,25 +0,0 @@
var page = require('webpage').create(),
system = require('system');
if (system.args.length < 2) {
console.log('Usage: loadurlwithoutcss.js URL');
phantom.exit();
}
var address = system.args[1];
page.onResourceRequested = function(requestData, request) {
if ((/http:\/\/.+?\.css/gi).test(requestData['url']) || requestData.headers['Content-Type'] == 'text/css') {
console.log('The url of the request is matching. Aborting: ' + requestData['url']);
request.abort();
}
};
page.open(address, function(status) {
if (status === 'success') {
phantom.exit();
} else {
console.log('Unable to load the address!');
phantom.exit();
}
});

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +0,0 @@
var universe = require('./universe');
universe.start();
console.log('The answer is' + universe.answer);
phantom.exit();

13
examples/movies.coffee Normal file
View File

@ -0,0 +1,13 @@
# List movies from kids-in-mind.com
window.cbfunc = (data) ->
globaldata = data
list = data.query.results.movie
for item in list
console.log item.title + ' [' + item.rating.MPAA.content + ']'
phantom.exit()
el = document.createElement 'script'
el.src =
"http://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20movies.kids-in-mind&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=window.cbfunc"
document.body.appendChild el

18
examples/netlog.coffee Normal file
View File

@ -0,0 +1,18 @@
page = require('webpage').create()
system = require 'system'
if system.args.length is 1
console.log 'Usage: netlog.coffee <some URL>'
phantom.exit 1
else
address = system.args[1]
page.onResourceRequested = (req) ->
console.log 'requested ' + JSON.stringify(req, undefined, 4)
page.onResourceReceived = (res) ->
console.log 'received ' + JSON.stringify(res, undefined, 4)
page.open address, (status) ->
if status isnt 'success'
console.log 'FAIL to load the address'
phantom.exit()

110
examples/netsniff.coffee Normal file
View File

@ -0,0 +1,110 @@
if not Date::toISOString
Date::toISOString = ->
pad = (n) ->
if n < 10 then '0' + n else n
ms = (n) ->
if n < 10 then '00' + n else (if n < 100 then '0' + n else n)
@getFullYear() + '-' +
pad(@getMonth() + 1) + '-' +
pad(@getDate()) + 'T' +
pad(@getHours()) + ':' +
pad(@getMinutes()) + ':' +
pad(@getSeconds()) + '.' +
ms(@getMilliseconds()) + 'Z'
createHAR = (address, title, startTime, resources) ->
entries = []
resources.forEach (resource) ->
request = resource.request
startReply = resource.startReply
endReply = resource.endReply
if not request or not startReply or not endReply
return
entries.push
startedDateTime: request.time.toISOString()
time: endReply.time - request.time
request:
method: request.method
url: request.url
httpVersion: 'HTTP/1.1'
cookies: []
headers: request.headers
queryString: []
headersSize: -1
bodySize: -1
response:
status: endReply.status
statusText: endReply.statusText
httpVersion: 'HTTP/1.1'
cookies: []
headers: endReply.headers
redirectURL: ''
headersSize: -1
bodySize: startReply.bodySize
content:
size: startReply.bodySize
mimeType: endReply.contentType
cache: {}
timings:
blocked: 0
dns: -1
connect: -1
send: 0
wait: startReply.time - request.time
receive: endReply.time - startReply.time
ssl: -1
log:
version: '1.2'
creator:
name: 'PhantomJS'
version: phantom.version.major + '.' + phantom.version.minor + '.' + phantom.version.patch
pages: [
startedDateTime: startTime.toISOString()
id: address
title: title
pageTimings: {}
]
entries: entries
page = require('webpage').create()
system = require 'system'
if system.args.length is 1
console.log 'Usage: netsniff.coffee <some URL>'
phantom.exit 1
else
page.address = system.args[1]
page.resources = []
page.onLoadStarted = ->
page.startTime = new Date()
page.onResourceRequested = (req) ->
page.resources[req.id] =
request: req
startReply: null
endReply: null
page.onResourceReceived = (res) ->
if res.stage is 'start'
page.resources[res.id].startReply = res
if res.stage is 'end'
page.resources[res.id].endReply = res
page.open page.address, (status) ->
if status isnt 'success'
console.log 'FAIL to load the address'
else
page.title = page.evaluate ->
document.title
har = createHAR page.address, page.title, page.startTime, page.resources
console.log JSON.stringify har, undefined, 4
phantom.exit()

View File

@ -25,12 +25,6 @@ function createHAR(address, title, startTime, resources)
return; return;
} }
// Exclude Data URI from HAR file because
// they aren't included in specification
if (request.url.match(/(^data:image\/.*)/i)) {
return;
}
entries.push({ entries.push({
startedDateTime: request.time.toISOString(), startedDateTime: request.time.toISOString(),
time: endReply.time - request.time, time: endReply.time - request.time,
@ -67,8 +61,7 @@ function createHAR(address, title, startTime, resources)
wait: startReply.time - request.time, wait: startReply.time - request.time,
receive: endReply.time - startReply.time, receive: endReply.time - startReply.time,
ssl: -1 ssl: -1
}, }
pageref: address
}); });
}); });
@ -84,9 +77,7 @@ function createHAR(address, title, startTime, resources)
startedDateTime: startTime.toISOString(), startedDateTime: startTime.toISOString(),
id: address, id: address,
title: title, title: title,
pageTimings: { pageTimings: {}
onLoad: page.endTime - page.startTime
}
}], }],
entries: entries entries: entries
} }
@ -97,7 +88,7 @@ var page = require('webpage').create(),
system = require('system'); system = require('system');
if (system.args.length === 1) { if (system.args.length === 1) {
console.log('Usage: netsniff.js <some URL>'); console.log('Usage: netsniff.coffee <some URL>');
phantom.exit(1); phantom.exit(1);
} else { } else {
@ -129,15 +120,13 @@ if (system.args.length === 1) {
var har; var har;
if (status !== 'success') { if (status !== 'success') {
console.log('FAIL to load the address'); console.log('FAIL to load the address');
phantom.exit(1);
} else { } else {
page.endTime = new Date();
page.title = page.evaluate(function () { page.title = page.evaluate(function () {
return document.title; return document.title;
}); });
har = createHAR(page.address, page.title, page.startTime, page.resources); har = createHAR(page.address, page.title, page.startTime, page.resources);
console.log(JSON.stringify(har, undefined, 4)); console.log(JSON.stringify(har, undefined, 4));
phantom.exit();
} }
phantom.exit();
}); });
} }

View File

@ -1,24 +0,0 @@
var page = require('webpage').create(),
system = require('system'),
host, port, address;
if (system.args.length < 4) {
console.log('Usage: openurlwithproxy.js <proxyHost> <proxyPort> <URL>');
phantom.exit(1);
} else {
host = system.args[1];
port = system.args[2];
address = system.args[3];
phantom.setProxy(host, port, 'manual', '', '');
page.open(address, function (status) {
if (status !== 'success') {
console.log('FAIL to load the address "' +
address + '" using proxy "' + host + ':' + port + '"');
} else {
console.log('Page title is ' + page.evaluate(function () {
return document.title;
}));
}
phantom.exit();
});
}

View File

@ -0,0 +1,12 @@
helloWorld = () -> console.log phantom.outputEncoding + ": こんにちは、世界!"
console.log "Using default encoding..."
helloWorld()
console.log "\nUsing other encodings..."
for enc in ["euc-jp", "sjis", "utf8", "System"]
do (enc) ->
phantom.outputEncoding = enc
helloWorld()
phantom.exit()

View File

@ -1,146 +0,0 @@
// The purpose of this is to show how and when events fire, considering 5 steps
// happening as follows:
//
// 1. Load URL
// 2. Load same URL, but adding an internal FRAGMENT to it
// 3. Click on an internal Link, that points to another internal FRAGMENT
// 4. Click on an external Link, that will send the page somewhere else
// 5. Close page
//
// Take particular care when going through the output, to understand when
// things happen (and in which order). Particularly, notice what DOESN'T
// happen during step 3.
//
// If invoked with "-v" it will print out the Page Resources as they are
// Requested and Received.
//
// NOTE.1: The "onConsoleMessage/onAlert/onPrompt/onConfirm" events are
// registered but not used here. This is left for you to have fun with.
// NOTE.2: This script is not here to teach you ANY JavaScript. It's aweful!
// NOTE.3: Main audience for this are people new to PhantomJS.
var sys = require("system"),
page = require("webpage").create(),
logResources = false,
step1url = "http://en.wikipedia.org/wiki/DOM_events",
step2url = "http://en.wikipedia.org/wiki/DOM_events#Event_flow";
if (sys.args.length > 1 && sys.args[1] === "-v") {
logResources = true;
}
function printArgs() {
var i, ilen;
for (i = 0, ilen = arguments.length; i < ilen; ++i) {
console.log(" arguments[" + i + "] = " + JSON.stringify(arguments[i]));
}
console.log("");
}
////////////////////////////////////////////////////////////////////////////////
page.onInitialized = function() {
console.log("page.onInitialized");
printArgs.apply(this, arguments);
};
page.onLoadStarted = function() {
console.log("page.onLoadStarted");
printArgs.apply(this, arguments);
};
page.onLoadFinished = function() {
console.log("page.onLoadFinished");
printArgs.apply(this, arguments);
};
page.onUrlChanged = function() {
console.log("page.onUrlChanged");
printArgs.apply(this, arguments);
};
page.onNavigationRequested = function() {
console.log("page.onNavigationRequested");
printArgs.apply(this, arguments);
};
page.onRepaintRequested = function() {
console.log("page.onRepaintRequested");
printArgs.apply(this, arguments);
};
if (logResources === true) {
page.onResourceRequested = function() {
console.log("page.onResourceRequested");
printArgs.apply(this, arguments);
};
page.onResourceReceived = function() {
console.log("page.onResourceReceived");
printArgs.apply(this, arguments);
};
}
page.onClosing = function() {
console.log("page.onClosing");
printArgs.apply(this, arguments);
};
// window.console.log(msg);
page.onConsoleMessage = function() {
console.log("page.onConsoleMessage");
printArgs.apply(this, arguments);
};
// window.alert(msg);
page.onAlert = function() {
console.log("page.onAlert");
printArgs.apply(this, arguments);
};
// var confirmed = window.confirm(msg);
page.onConfirm = function() {
console.log("page.onConfirm");
printArgs.apply(this, arguments);
};
// var user_value = window.prompt(msg, default_value);
page.onPrompt = function() {
console.log("page.onPrompt");
printArgs.apply(this, arguments);
};
////////////////////////////////////////////////////////////////////////////////
setTimeout(function() {
console.log("");
console.log("### STEP 1: Load '" + step1url + "'");
page.open(step1url);
}, 0);
setTimeout(function() {
console.log("");
console.log("### STEP 2: Load '" + step2url + "' (load same URL plus FRAGMENT)");
page.open(step2url);
}, 5000);
setTimeout(function() {
console.log("");
console.log("### STEP 3: Click on page internal link (aka FRAGMENT)");
page.evaluate(function() {
var ev = document.createEvent("MouseEvents");
ev.initEvent("click", true, true);
document.querySelector("a[href='#Event_object']").dispatchEvent(ev);
});
}, 10000);
setTimeout(function() {
console.log("");
console.log("### STEP 4: Click on page external link");
page.evaluate(function() {
var ev = document.createEvent("MouseEvents");
ev.initEvent("click", true, true);
document.querySelector("a[title='JavaScript']").dispatchEvent(ev);
});
}, 15000);
setTimeout(function() {
console.log("");
console.log("### STEP 5: Close page and shutdown (with a delay)");
page.close();
setTimeout(function(){
phantom.exit();
}, 100);
}, 20000);

View File

@ -1,17 +0,0 @@
var p = require("webpage").create();
p.onConsoleMessage = function(msg) { console.log(msg); };
// Calls to "callPhantom" within the page 'p' arrive here
p.onCallback = function(msg) {
console.log("Received by the 'phantom' main context: "+msg);
return "Hello there, I'm coming to you from the 'phantom' context instead";
};
p.evaluate(function() {
// Return-value of the "onCallback" handler arrive here
var callbackResponse = window.callPhantom("Hello, I'm coming to you from the 'page' context");
console.log("Received by the 'page' context: "+callbackResponse);
});
phantom.exit();

View File

@ -0,0 +1,13 @@
# Read the Phantom webpage '#intro' element text using jQuery and "includeJs"
page = require('webpage').create()
page.onConsoleMessage = (msg) -> console.log msg
page.open "http://www.phantomjs.org", (status) ->
if status is "success"
page.includeJs "http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", ->
page.evaluate ->
console.log "$(\"#intro\").text() -> " + $("#intro").text()
phantom.exit()

View File

@ -10,7 +10,7 @@ page.open("http://www.phantomjs.org", function(status) {
if ( status === "success" ) { if ( status === "success" ) {
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() { page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
page.evaluate(function() { page.evaluate(function() {
console.log("$(\".explanation\").text() -> " + $(".explanation").text()); console.log("$(\"#intro\").text() -> " + $("#intro").text());
}); });
phantom.exit(); phantom.exit();
}); });

18
examples/pizza.coffee Normal file
View File

@ -0,0 +1,18 @@
# Find pizza in Mountain View using Yelp
page = require('webpage').create()
url = 'http://lite.yelp.com/search?find_desc=pizza&find_loc=94040&find_submit=Search'
page.open url,
(status) ->
if status isnt 'success'
console.log 'Unable to access network'
else
results = page.evaluate ->
pizza = []
list = document.querySelectorAll 'span.address'
for item in list
pizza.push(item.innerText)
return pizza
console.log results.join('\n')
phantom.exit()

View File

@ -8,7 +8,7 @@ page.open(url, function (status) {
console.log('Unable to access network'); console.log('Unable to access network');
} else { } else {
var results = page.evaluate(function() { var results = page.evaluate(function() {
var list = document.querySelectorAll('address'), pizza = [], i; var list = document.querySelectorAll('span.address'), pizza = [], i;
for (i = 0; i < list.length; i++) { for (i = 0; i < list.length; i++) {
pizza.push(list[i].innerText); pizza.push(list[i].innerText);
} }

12
examples/post.coffee Normal file
View File

@ -0,0 +1,12 @@
# Example using HTTP POST operation
page = require('webpage').create()
server = 'http://posttestserver.com/post.php?dump'
data = 'universe=expanding&answer=42'
page.open server, 'post', data, (status) ->
if status isnt 'success'
console.log 'Unable to post!'
else
console.log page.content
phantom.exit()

View File

@ -1,18 +0,0 @@
// Example using HTTP POST operation
var page = require('webpage').create(),
server = 'http://posttestserver.com/post.php?dump',
data = '{"universe": "expanding", "answer": 42}';
var headers = {
"Content-Type": "application/json"
}
page.open(server, 'post', data, headers, function (status) {
if (status !== 'success') {
console.log('Unable to post!');
} else {
console.log(page.content);
}
phantom.exit();
});

View File

@ -2,7 +2,7 @@ var page = require('webpage').create(),
system = require('system'); system = require('system');
function someCallback(pageNum, numPages) { function someCallback(pageNum, numPages) {
return "<h1> someCallback: " + pageNum + " / " + numPages + "</h1>"; return "<h1> somCallback: " + pageNum + " / " + numPages + "</h1>";
} }
if (system.args.length < 3) { if (system.args.length < 3) {

23
examples/rasterize.coffee Normal file
View File

@ -0,0 +1,23 @@
page = require('webpage').create()
system = require 'system'
if system.args.length < 3 or system.args.length > 4
console.log 'Usage: rasterize.coffee URL filename [paperwidth*paperheight|paperformat]'
console.log ' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"'
phantom.exit 1
else
address = system.args[1]
output = system.args[2]
page.viewportSize = { width: 600, height: 600 }
if system.args.length is 4 and system.args[2].substr(-4) is ".pdf"
size = system.args[3].split '*'
if size.length is 2
page.paperSize = { width: size[0], height: size[1], border: '0px' }
else
page.paperSize = { format: system.args[3], orientation: 'portrait', border: '1cm' }
page.open address, (status) ->
if status isnt 'success'
console.log 'Unable to load the address!'
phantom.exit()
else
window.setTimeout (-> page.render output; phantom.exit()), 200

View File

@ -5,8 +5,6 @@ var page = require('webpage').create(),
if (system.args.length < 3 || system.args.length > 5) { if (system.args.length < 3 || system.args.length > 5) {
console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]'); console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]');
console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"'); console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
console.log(' image (png/jpg output) examples: "1920px" entire page, window width 1920px');
console.log(' "800px*600px" window, clipped to 800x600');
phantom.exit(1); phantom.exit(1);
} else { } else {
address = system.args[1]; address = system.args[1];
@ -16,20 +14,6 @@ if (system.args.length < 3 || system.args.length > 5) {
size = system.args[3].split('*'); size = system.args[3].split('*');
page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
: { format: system.args[3], orientation: 'portrait', margin: '1cm' }; : { format: system.args[3], orientation: 'portrait', margin: '1cm' };
} else if (system.args.length > 3 && system.args[3].substr(-2) === "px") {
size = system.args[3].split('*');
if (size.length === 2) {
pageWidth = parseInt(size[0], 10);
pageHeight = parseInt(size[1], 10);
page.viewportSize = { width: pageWidth, height: pageHeight };
page.clipRect = { top: 0, left: 0, width: pageWidth, height: pageHeight };
} else {
console.log("size:", system.args[3]);
pageWidth = parseInt(system.args[3], 10);
pageHeight = parseInt(pageWidth * 3/4, 10); // it's as good an assumption as any
console.log ("pageHeight:",pageHeight);
page.viewportSize = { width: pageWidth, height: pageHeight };
}
} }
if (system.args.length > 4) { if (system.args.length > 4) {
page.zoomFactor = system.args[4]; page.zoomFactor = system.args[4];
@ -37,7 +21,6 @@ if (system.args.length < 3 || system.args.length > 5) {
page.open(address, function (status) { page.open(address, function (status) {
if (status !== 'success') { if (status !== 'success') {
console.log('Unable to load the address!'); console.log('Unable to load the address!');
phantom.exit(1);
} else { } else {
window.setTimeout(function () { window.setTimeout(function () {
page.render(output); page.render(output);

View File

@ -0,0 +1,50 @@
# Render Multiple URLs to file
# FIXME: For now it is fine with pure domain names: don't think it would work with paths and stuff like that
system = require 'system'
# Extend the Array Prototype with a 'foreach'
Array.prototype.forEach = (action) ->
for i, j in this
action j, i, _len
# Render a given url to a given file
# @param url URL to render
# @param file File to render to
# @param callback Callback function
renderUrlToFile = (url, file, callback) ->
page = require('webpage').create()
page.viewportSize = { width: 800, height : 600 }
page.settings.userAgent = 'Phantom.js bot'
page.open url, (status) ->
if status isnt 'success'
console.log "Unable to render '#{url}'"
else
page.render file
delete page
callback url, file
# Read the passed args
if system.args.length > 1
arrayOfUrls = Array.prototype.slice.call system.args, 1
else
# Default (no args passed)
console.log 'Usage: phantomjs render_multi_url.coffee [domain.name1, domain.name2, ...]'
arrayOfUrls = [
'www.google.com',
'www.bbc.co.uk',
'www.phantomjs.org'
]
# For each URL
arrayOfUrls.forEach (pos, url, total) ->
file_name = "./#{url}.png"
# Render to a file
renderUrlToFile "http://#{url}", file_name, (url, file) ->
console.log "Rendered '#{url}' at '#{file}'"
if pos is total - 1
# Close Phantom if it's the last URL
phantom.exit()

View File

@ -1,73 +1,62 @@
// Render Multiple URLs to file // Render Multiple URLs to file
// FIXME: For now it is fine with pure domain names: don't think it would work with paths and stuff like that
var RenderUrlsToFile, arrayOfUrls, system; var system = require('system');
system = require("system"); // Extend the Array Prototype with a 'foreach'
Array.prototype.forEach = function (action) {
/* var i, len;
Render given urls for ( i = 0, len = this.length; i < len; ++i ) {
@param array of URLs to render action(i, this[i], len);
@param callbackPerUrl Function called after finishing each URL, including the last URL }
@param callbackFinal Function called after finishing everything
*/
RenderUrlsToFile = function(urls, callbackPerUrl, callbackFinal) {
var getFilename, next, page, retrieve, urlIndex, webpage;
urlIndex = 0;
webpage = require("webpage");
page = null;
getFilename = function() {
return "rendermulti-" + urlIndex + ".png";
};
next = function(status, url, file) {
page.close();
callbackPerUrl(status, url, file);
return retrieve();
};
retrieve = function() {
var url;
if (urls.length > 0) {
url = urls.shift();
urlIndex++;
page = webpage.create();
page.viewportSize = {
width: 800,
height: 600
};
page.settings.userAgent = "Phantom.js bot";
return page.open("http://" + url, function(status) {
var file;
file = getFilename();
if (status === "success") {
return window.setTimeout((function() {
page.render(file);
return next(status, url, file);
}), 200);
} else {
return next(status, url, file);
}
});
} else {
return callbackFinal();
}
};
return retrieve();
}; };
arrayOfUrls = null; /**
* Render a given url to a given file
* @param url URL to render
* @param file File to render to
* @param callback Callback function
*/
function renderUrlToFile(url, file, callback) {
var page = require('webpage').create();
page.viewportSize = { width: 800, height : 600 };
page.settings.userAgent = "Phantom.js bot";
if (system.args.length > 1) { page.open(url, function(status){
arrayOfUrls = Array.prototype.slice.call(system.args, 1); if ( status !== "success") {
} else { console.log("Unable to render '"+url+"'");
console.log("Usage: phantomjs render_multi_url.js [domain.name1, domain.name2, ...]"); } else {
arrayOfUrls = ["www.google.com", "www.bbc.co.uk", "www.phantomjs.org"]; page.render(file);
}
delete page;
callback(url, file);
});
} }
RenderUrlsToFile(arrayOfUrls, (function(status, url, file) { // Read the passed args
if (status !== "success") { var arrayOfUrls;
return console.log("Unable to render '" + url + "'"); if ( system.args.length > 1 ) {
} else { arrayOfUrls = Array.prototype.slice.call(system.args, 1);
return console.log("Rendered '" + url + "' at '" + file + "'"); } else {
} // Default (no args passed)
}), function() { console.log("Usage: phantomjs render_multi_url.js [domain.name1, domain.name2, ...]");
return phantom.exit(); arrayOfUrls = [
'www.google.com',
'www.bbc.co.uk',
'www.phantomjs.org'
];
}
// For each URL
arrayOfUrls.forEach(function(pos, url, total){
var file_name = "./" + url + ".png";
// Render to a file
renderUrlToFile("http://"+url, file_name, function(url, file){
console.log("Rendered '"+url+"' at '"+file+"'");
if ( pos === total-1 ) {
// Close Phantom if it's the last URL
phantom.exit();
}
});
}); });

View File

@ -0,0 +1,61 @@
system = require 'system'
##
# Wait until the test condition is true or a timeout occurs. Useful for waiting
# on a server response or for a ui change (fadeIn, etc.) to occur.
#
# @param testFx javascript condition that evaluates to a boolean,
# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
# as a callback function.
# @param onReady what to do when testFx condition is fulfilled,
# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
# as a callback function.
# @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
##
waitFor = (testFx, onReady, timeOutMillis=3000) ->
start = new Date().getTime()
condition = false
f = ->
if (new Date().getTime() - start < timeOutMillis) and not condition
# If not time-out yet and condition not yet fulfilled
condition = (if typeof testFx is 'string' then eval testFx else testFx()) #< defensive code
else
if not condition
# If condition still not fulfilled (timeout but condition is 'false')
console.log "'waitFor()' timeout"
phantom.exit 1
else
# Condition fulfilled (timeout and/or condition is 'true')
console.log "'waitFor()' finished in #{new Date().getTime() - start}ms."
if typeof onReady is 'string' then eval onReady else onReady() #< Do what it's supposed to do once the condition is fulfilled
clearInterval interval #< Stop this interval
interval = setInterval f, 100 #< repeat check every 100ms
if system.args.length isnt 2
console.log 'Usage: run-jasmine.coffee URL'
phantom.exit 1
page = require('webpage').create()
# Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = (msg) ->
console.log msg
page.open system.args[1], (status) ->
if status isnt 'success'
console.log 'Unable to access network'
phantom.exit()
else
waitFor ->
page.evaluate ->
if document.body.querySelector '.finished-at'
return true
return false
, ->
page.evaluate ->
console.log document.body.querySelector('.description').innerText
list = document.body.querySelectorAll('.failed > .description, .failed > .messages > .resultMessage')
for el in list
console.log el.innerText
phantom.exit()

View File

@ -50,42 +50,30 @@ page.onConsoleMessage = function(msg) {
page.open(system.args[1], function(status){ page.open(system.args[1], function(status){
if (status !== "success") { if (status !== "success") {
console.log("Unable to open " + system.args[1]); console.log("Unable to access network");
phantom.exit(1); phantom.exit();
} else { } else {
waitFor(function(){ waitFor(function(){
return page.evaluate(function(){ return page.evaluate(function(){
return document.body.querySelector('.symbolSummary .pending') === null if (document.body.querySelector('.runner .description')) {
return true;
}
return false;
}); });
}, function(){ }, function(){
var exitCode = page.evaluate(function(){ page.evaluate(function(){
try { console.log(document.body.querySelector('.description').innerText);
list = document.body.querySelectorAll('div.jasmine_reporter > div.suite.failed');
for (i = 0; i < list.length; ++i) {
el = list[i];
desc = el.querySelectorAll('.description');
console.log(''); console.log('');
console.log(document.body.querySelector('.description').innerText); for (j = 0; j < desc.length; ++j) {
var list = document.body.querySelectorAll('.results > #details > .specDetail.failed'); console.log(desc[j].innerText);
if (list && list.length > 0) {
console.log('');
console.log(list.length + ' test(s) FAILED:');
for (i = 0; i < list.length; ++i) {
var el = list[i],
desc = el.querySelector('.description'),
msg = el.querySelector('.resultMessage.fail');
console.log('');
console.log(desc.innerText);
console.log(msg.innerText);
console.log('');
}
return 1;
} else {
console.log(document.body.querySelector('.alert > .passingAlert.bar').innerText);
return 0;
} }
} catch (ex) {
console.log(ex);
return 1;
} }
}); });
phantom.exit(exitCode); phantom.exit();
}); });
} }
}); });

View File

@ -1,92 +0,0 @@
var system = require('system');
/**
* Wait until the test condition is true or a timeout occurs. Useful for waiting
* on a server response or for a ui change (fadeIn, etc.) to occur.
*
* @param testFx javascript condition that evaluates to a boolean,
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
* as a callback function.
* @param onReady what to do when testFx condition is fulfilled,
* it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
* as a callback function.
* @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
*/
function waitFor(testFx, onReady, timeOutMillis) {
var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timeout is 3s
start = new Date().getTime(),
condition = false,
interval = setInterval(function() {
if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
// If not time-out yet and condition not yet fulfilled
condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
} else {
if(!condition) {
// If condition still not fulfilled (timeout but condition is 'false')
console.log("'waitFor()' timeout");
phantom.exit(1);
} else {
// Condition fulfilled (timeout and/or condition is 'true')
console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
clearInterval(interval); //< Stop this interval
}
}
}, 100); //< repeat check every 100ms
};
if (system.args.length !== 2) {
console.log('Usage: run-jasmine.js URL');
phantom.exit(1);
}
var page = require('webpage').create();
// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = function(msg) {
console.log(msg);
};
page.open(system.args[1], function(status){
if (status !== "success") {
console.log("Unable to access network");
phantom.exit();
} else {
waitFor(function(){
return page.evaluate(function(){
return document.body.querySelector('.symbolSummary .pending') === null
});
}, function(){
var exitCode = page.evaluate(function(){
console.log('');
var el = document.body.querySelector('.banner');
var banner = el.querySelector('.title').innerText + " " +
el.querySelector('.version').innerText + " " +
el.querySelector('.duration').innerText;
console.log(banner);
var list = document.body.querySelectorAll('.results > .failures > .spec-detail.failed');
if (list && list.length > 0) {
console.log('');
console.log(list.length + ' test(s) FAILED:');
for (i = 0; i < list.length; ++i) {
var el = list[i],
desc = el.querySelector('.description'),
msg = el.querySelector('.messages > .result-message');
console.log('');
console.log(desc.innerText);
console.log(msg.innerText);
console.log('');
}
return 1;
} else {
console.log(document.body.querySelector('.alert > .bar.passed').innerText);
return 0;
}
});
phantom.exit(exitCode);
});
}
});

64
examples/run-qunit.coffee Normal file
View File

@ -0,0 +1,64 @@
system = require 'system'
##
# Wait until the test condition is true or a timeout occurs. Useful for waiting
# on a server response or for a ui change (fadeIn, etc.) to occur.
#
# @param testFx javascript condition that evaluates to a boolean,
# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
# as a callback function.
# @param onReady what to do when testFx condition is fulfilled,
# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
# as a callback function.
# @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
##
waitFor = (testFx, onReady, timeOutMillis=3000) ->
start = new Date().getTime()
condition = false
f = ->
if (new Date().getTime() - start < timeOutMillis) and not condition
# If not time-out yet and condition not yet fulfilled
condition = (if typeof testFx is 'string' then eval testFx else testFx()) #< defensive code
else
if not condition
# If condition still not fulfilled (timeout but condition is 'false')
console.log "'waitFor()' timeout"
phantom.exit 1
else
# Condition fulfilled (timeout and/or condition is 'true')
console.log "'waitFor()' finished in #{new Date().getTime() - start}ms."
if typeof onReady is 'string' then eval onReady else onReady() #< Do what it's supposed to do once the condition is fulfilled
clearInterval interval #< Stop this interval
interval = setInterval f, 100 #< repeat check every 100ms
if system.args.length isnt 2
console.log 'Usage: run-qunit.coffee URL'
phantom.exit 1
page = require('webpage').create()
# Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = (msg) ->
console.log msg
page.open system.args[1], (status) ->
if status isnt 'success'
console.log 'Unable to access network'
phantom.exit 1
else
waitFor ->
page.evaluate ->
el = document.getElementById 'qunit-testresult'
if el and el.innerText.match 'completed'
return true
return false
, ->
failedNum = page.evaluate ->
el = document.getElementById 'qunit-testresult'
console.log el.innerText
try
return el.getElementsByClassName('failed')[0].innerHTML
catch e
return 10000
phantom.exit if parseInt(failedNum, 10) > 0 then 1 else 0

16
examples/scandir.coffee Normal file
View File

@ -0,0 +1,16 @@
# List all the files in a Tree of Directories
system = require 'system'
if system.args.length != 2
console.log "Usage: phantomjs scandir.coffee DIRECTORY_TO_SCAN"
phantom.exit 1
scanDirectory = (path) ->
fs = require 'fs'
if fs.exists(path) and fs.isFile(path)
console.log path
else if fs.isDirectory(path)
fs.list(path).forEach (e) ->
scanDirectory path + "/" + e if e != "." and e != ".."
scanDirectory system.args[1]
phantom.exit()

View File

@ -0,0 +1,17 @@
# Show BBC seasonal food list.
window.cbfunc = (data) ->
list = data.query.results.results.result
names = ['January', 'February', 'March',
'April', 'May', 'June',
'July', 'August', 'September',
'October', 'November', 'December']
for item in list
console.log [item.name.replace(/\s/ig, ' '), ':',
names[item.atItsBestUntil], 'to',
names[item.atItsBestFrom]].join(' ')
phantom.exit()
el = document.createElement 'script'
el.src = 'http://query.yahooapis.com/v1/public/yql?q=SELECT%20*%20FROM%20bbc.goodfood.seasonal%3B&format=json&diagnostics=true&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys&callback=window.cbfunc'
document.body.appendChild el

View File

@ -0,0 +1,38 @@
system = require 'system'
if system.args.length is 1
console.log "Usage: simpleserver.coffee <portnumber>"
phantom.exit 1
else
port = system.args[1]
server = require("webserver").create()
service = server.listen(port, (request, response) ->
console.log "Request at " + new Date()
console.log JSON.stringify(request, null, 4)
response.statusCode = 200
response.headers =
Cache: "no-cache"
"Content-Type": "text/html"
response.write "<html>"
response.write "<head>"
response.write "<title>Hello, world!</title>"
response.write "</head>"
response.write "<body>"
response.write "<p>This is from PhantomJS web server.</p>"
response.write "<p>Request data:</p>"
response.write "<pre>"
response.write JSON.stringify(request, null, 4)
response.write "</pre>"
response.write "</body>"
response.write "</html>"
response.close()
)
if service
console.log "Web server running on port " + port
else
console.log "Error: Could not create web server listening on port " + port
phantom.exit()

20
examples/sleepsort.coffee Normal file
View File

@ -0,0 +1,20 @@
###
Sort integers from the command line in a very ridiculous way: leveraging timeouts :P
###
system = require 'system'
if system.args.length < 2
console.log "Usage: phantomjs sleepsort.coffee PUT YOUR INTEGERS HERE SEPARATED BY SPACES"
phantom.exit 1
else
sortedCount = 0
args = Array.prototype.slice.call(system.args, 1)
for int in args
setTimeout (do (int) ->
->
console.log int
++sortedCount
phantom.exit() if sortedCount is args.length),
int

View File

@ -15,11 +15,11 @@ function sleepSort(array, callback) {
} }
} }
if ( system.args.length < 2 ) { if ( system.args < 2 ) {
console.log("Usage: phantomjs sleepsort.js PUT YOUR INTEGERS HERE SEPARATED BY SPACES"); console.log("Usage: phantomjs sleepsort.js PUT YOUR INTEGERS HERE SEPARATED BY SPACES");
phantom.exit(1); phantom.exit(1);
} else { } else {
sleepSort(system.args.slice(1), function() { sleepSort(Array.prototype.slice.call(system.args, 1), function() {
phantom.exit(); phantom.exit();
}); });
} }

View File

@ -1,18 +0,0 @@
var system = require('system');
system.stdout.write('Hello, system.stdout.write!');
system.stdout.writeLine('\nHello, system.stdout.writeLine!');
system.stderr.write('Hello, system.stderr.write!');
system.stderr.writeLine('\nHello, system.stderr.writeLine!');
system.stdout.writeLine('system.stdin.readLine(): ');
var line = system.stdin.readLine();
system.stdout.writeLine(JSON.stringify(line));
// This is essentially a `readAll`
system.stdout.writeLine('system.stdin.read(5): (ctrl+D to end)');
var input = system.stdin.read(5);
system.stdout.writeLine(JSON.stringify(input));
phantom.exit(0);

17
examples/technews.coffee Normal file
View File

@ -0,0 +1,17 @@
page = require('webpage').create()
page.viewportSize = { width: 320, height: 480 }
page.open 'http://news.google.com/news/i/section?&topic=t',
(status) ->
if status isnt 'success'
console.log 'Unable to access the network!'
else
page.evaluate ->
body = document.body
body.style.backgroundColor = '#fff'
body.querySelector('div#title-block').style.display = 'none'
body.querySelector('form#edition-picker-form')
.parentElement.parentElement.style.display = 'none'
page.render 'technews.png'
phantom.exit()

31
examples/tweets.coffee Normal file
View File

@ -0,0 +1,31 @@
# Get twitter status for given account (or for the default one, "HeadlessPhantom")
page = require('webpage').create()
system = require 'system'
twitterId = 'HeadlessPhantom' #< default value
# Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = (msg) ->
console.log msg
# Print usage message, if no twitter ID is passed
if system.args.length < 2
console.log 'Usage: tweets.coffee [twitter ID]'
else
twitterId = system.args[1]
# Heading
console.log "*** Latest tweets from @#{twitterId} ***\n"
# Open Twitter Mobile and, onPageLoad, do...
page.open encodeURI("http://mobile.twitter.com/#{twitterId}"), (status) ->
# Check for page load success
if status isnt 'success'
console.log 'Unable to access network'
else
# Execute some DOM inspection within the page context
page.evaluate ->
list = document.querySelectorAll 'div.tweet-text'
for i, j in list
console.log "#{j + 1}: #{i.innerText}"
phantom.exit()

View File

@ -1,8 +1,8 @@
// Get twitter status for given account (or for the default one, "PhantomJS") // Get twitter status for given account (or for the default one, "HeadlessPhantom")
var page = require('webpage').create(), var page = require('webpage').create(),
system = require('system'), system = require('system'),
twitterId = "PhantomJS"; //< default value twitterId = "HeadlessPhantom"; //< default value
// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = function(msg) { page.onConsoleMessage = function(msg) {

View File

@ -1,10 +0,0 @@
// This is to be used by "module.js" (and "module.coffee") example(s).
// There should NOT be a "universe.coffee" as only 1 of the 2 would
// ever be loaded unless the file extension was specified.
exports.answer = 42;
exports.start = function () {
console.log('Starting the universe....');
}

View File

@ -0,0 +1,18 @@
# Modify global object at the page initialization.
# In this example, effectively Math.random() always returns 0.42.
page = require('webpage').create()
page.onInitialized = ->
page.evaluate ->
Math.random = ->
42 / 100
page.open "http://ariya.github.com/js/random/", (status) ->
if status != "success"
console.log "Network error."
else
console.log page.evaluate(->
document.getElementById("numbers").textContent
)
phantom.exit()

11
examples/useragent.coffee Normal file
View File

@ -0,0 +1,11 @@
page = require('webpage').create()
console.log 'The default user agent is ' + page.settings.userAgent
page.settings.userAgent = 'SpecialAgent'
page.open 'http://www.httpuseragent.org', (status) ->
if status isnt 'success'
console.log 'Unable to access network'
else
console.log page.evaluate -> document.getElementById('myagent').innerText
phantom.exit()

5
examples/version.coffee Normal file
View File

@ -0,0 +1,5 @@
console.log 'using PhantomJS version ' +
phantom.version.major + '.' +
phantom.version.minor + '.' +
phantom.version.patch
phantom.exit()

48
examples/waitfor.coffee Normal file
View File

@ -0,0 +1,48 @@
##
# Wait until the test condition is true or a timeout occurs. Useful for waiting
# on a server response or for a ui change (fadeIn, etc.) to occur.
#
# @param testFx javascript condition that evaluates to a boolean,
# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
# as a callback function.
# @param onReady what to do when testFx condition is fulfilled,
# it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
# as a callback function.
# @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
##
waitFor = (testFx, onReady, timeOutMillis=3000) ->
start = new Date().getTime()
condition = false
f = ->
if (new Date().getTime() - start < timeOutMillis) and not condition
# If not time-out yet and condition not yet fulfilled
condition = (if typeof testFx is 'string' then eval testFx else testFx()) #< defensive code
else
if not condition
# If condition still not fulfilled (timeout but condition is 'false')
console.log "'waitFor()' timeout"
phantom.exit 1
else
# Condition fulfilled (timeout and/or condition is 'true')
console.log "'waitFor()' finished in #{new Date().getTime() - start}ms."
if typeof onReady is 'string' then eval onReady else onReady() #< Do what it's supposed to do once the condition is fulfilled
clearInterval interval #< Stop this interval
interval = setInterval f, 250 #< repeat check every 250ms
page = require('webpage').create()
# Open Twitter on 'sencha' profile and, onPageLoad, do...
page.open 'http://twitter.com/#!/sencha', (status) ->
# Check for page load success
if status isnt 'success'
console.log 'Unable to access network'
else
# Wait for 'signin-dropdown' to be visible
waitFor ->
# Check in the page if a specific element is now visible
page.evaluate ->
$('#signin-dropdown').is ':visible'
, ->
console.log 'The sign-in dialog should be visible now.'
phantom.exit()

View File

@ -0,0 +1,66 @@
pageTitle = (page) ->
page.evaluate ->
window.document.title
setPageTitle = (page, newTitle) ->
page.evaluate ((newTitle) ->
window.document.title = newTitle
), newTitle
p = require("webpage").create()
p.open "../test/webpage-spec-frames/index.html", (status) ->
console.log "pageTitle(): " + pageTitle(p)
console.log "currentFrameName(): " + p.currentFrameName()
console.log "childFramesCount(): " + p.childFramesCount()
console.log "childFramesName(): " + p.childFramesName()
console.log "setPageTitle(CURRENT TITLE+'-visited')"
setPageTitle p, pageTitle(p) + "-visited"
console.log ""
console.log "p.switchToChildFrame(\"frame1\"): " + p.switchToChildFrame("frame1")
console.log "pageTitle(): " + pageTitle(p)
console.log "currentFrameName(): " + p.currentFrameName()
console.log "childFramesCount(): " + p.childFramesCount()
console.log "childFramesName(): " + p.childFramesName()
console.log "setPageTitle(CURRENT TITLE+'-visited')"
setPageTitle p, pageTitle(p) + "-visited"
console.log ""
console.log "p.switchToChildFrame(\"frame1-2\"): " + p.switchToChildFrame("frame1-2")
console.log "pageTitle(): " + pageTitle(p)
console.log "currentFrameName(): " + p.currentFrameName()
console.log "childFramesCount(): " + p.childFramesCount()
console.log "childFramesName(): " + p.childFramesName()
console.log "setPageTitle(CURRENT TITLE+'-visited')"
setPageTitle p, pageTitle(p) + "-visited"
console.log ""
console.log "p.switchToParentFrame(): " + p.switchToParentFrame()
console.log "pageTitle(): " + pageTitle(p)
console.log "currentFrameName(): " + p.currentFrameName()
console.log "childFramesCount(): " + p.childFramesCount()
console.log "childFramesName(): " + p.childFramesName()
console.log "setPageTitle(CURRENT TITLE+'-visited')"
setPageTitle p, pageTitle(p) + "-visited"
console.log ""
console.log "p.switchToChildFrame(0): " + p.switchToChildFrame(0)
console.log "pageTitle(): " + pageTitle(p)
console.log "currentFrameName(): " + p.currentFrameName()
console.log "childFramesCount(): " + p.childFramesCount()
console.log "childFramesName(): " + p.childFramesName()
console.log "setPageTitle(CURRENT TITLE+'-visited')"
setPageTitle p, pageTitle(p) + "-visited"
console.log ""
console.log "p.switchToMainFrame()"
p.switchToMainFrame()
console.log "pageTitle(): " + pageTitle(p)
console.log "currentFrameName(): " + p.currentFrameName()
console.log "childFramesCount(): " + p.childFramesCount()
console.log "childFramesName(): " + p.childFramesName()
console.log "setPageTitle(CURRENT TITLE+'-visited')"
setPageTitle p, pageTitle(p) + "-visited"
console.log ""
console.log "p.switchToChildFrame(\"frame2\"): " + p.switchToChildFrame("frame2")
console.log "pageTitle(): " + pageTitle(p)
console.log "currentFrameName(): " + p.currentFrameName()
console.log "childFramesCount(): " + p.childFramesCount()
console.log "childFramesName(): " + p.childFramesName()
console.log "setPageTitle(CURRENT TITLE+'-visited')"
setPageTitle p, pageTitle(p) + "-visited"
console.log ""
phantom.exit()

49
examples/weather.coffee Normal file
View File

@ -0,0 +1,49 @@
# Get weather info for given address (or for the default one, "Mountain View")
page = require('webpage').create()
system = require 'system'
address = 'Mountain View' #< default value
# Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = (msg) ->
console.log msg
# Print usage message, if no address is passed
if system.args.length < 2
console.log 'Usage: weather.coffee [address]'
else
address = Array.prototype.slice.call(system.args, 1).join(' ')
# Heading
console.log "*** Loading weather information for '#{address}' ***\n"
# Open Google "secret" Weather API and, onPageLoad, do...
page.open encodeURI("http://www.google.com/ig/api?weather=#{address}"), (status) ->
# Check for page load success
if status isnt 'success'
console.log 'Unable to access network'
else
# Execute some DOM inspection within the page context
page.evaluate ->
if document.querySelectorAll('problem_cause').length > 0
console.log "No data available for #{address}"
else
data = (s, e) ->
e = e or document
el = e.querySelector s
if el then el.attributes.data.value else undefined
console.log """City: #{data 'weather > forecast_information > city'}
Current condition: #{data 'weather > current_conditions > condition'}
Temperature: #{data 'weather > current_conditions > temp_f'} F
#{data 'weather > current_conditions > humidity'}
#{data 'weather > current_conditions > wind_condition'}\n
"""
forecasts = document.querySelectorAll 'weather > forecast_conditions'
for i in forecasts
console.log "#{ data 'day_of_week', i }: " +
"#{ data 'low', i }-" +
"#{ data 'high', i } F " +
"#{ data 'condition', i }"
phantom.exit()

View File

@ -1,37 +1,58 @@
// Get weather info for given address (or for the default one, "Mountain View")
var page = require('webpage').create(), var page = require('webpage').create(),
system = require('system'), system = require('system'),
city, address = "Mountain View"; //< default value
url;
city = 'Mountain View, California'; // default // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
if (system.args.length > 1) { page.onConsoleMessage = function(msg) {
city = Array.prototype.slice.call(system.args, 1).join(' '); console.log(msg);
};
// Print usage message, if no address is passed
if (system.args.length < 2) {
console.log("Usage: weather.js [address]");
} else {
address = Array.prototype.slice.call(system.args, 1).join(' ');
} }
url = encodeURI('http://api.openweathermap.org/data/2.1/find/name?q=' + city);
console.log('Checking weather condition for', city, '...'); // Heading
console.log("*** Loading weather information for '" + address + "' ***\n");
page.open(url, function(status) { // Open Google "secret" Weather API and, onPageLoad, do...
var result, data; page.open(encodeURI('http://www.google.com/ig/api?weather=' + address), function (status) {
if (status !== 'success') { // Check for page load success
console.log('Error: Unable to access network!'); if (status !== "success") {
console.log("Unable to access network");
} else { } else {
result = page.evaluate(function () { // Execute some DOM inspection within the page context
return document.body.innerText; page.evaluate(function() {
if (document.querySelectorAll('problem_cause').length > 0) {
console.log('No data available for ' + address);
} else {
function data (s, e) {
var el;
e = e || document;
el = e.querySelector(s);
return el ? el.attributes.data.value : undefined;
};
console.log('City: ' + data('weather > forecast_information > city'));
console.log('Current condition: ' + data('weather > current_conditions > condition'));
console.log('Temperature: ' + data('weather > current_conditions > temp_f') + ' F');
console.log(data('weather > current_conditions > humidity'));
console.log(data('weather > current_conditions > wind_condition'));
console.log('');
var forecasts = document.querySelectorAll('weather > forecast_conditions');
for (var i = 0; i < forecasts.length; ++i) {
var f = forecasts[i];
console.log(data('day_of_week', f) + ': ' +
data('low', f) + '-' + data('high', f) + ' F ' +
data('condition', f));
}
}
}); });
try {
data = JSON.parse(result);
data = data.list[0];
console.log('');
console.log('City:', data.name);
console.log('Condition:', data.weather.map(function(entry) {
return entry.main;
}).join(', '));
console.log('Temperature:', Math.round(data.main.temp - 273.15), 'C');
console.log('Humidity:', Math.round(data.main.humidity), '%');
} catch (e) {
console.log('Error:', e.toString());
}
} }
phantom.exit(); phantom.exit();
}); });

View File

@ -1,3 +1,3 @@
TEMPLATE = subdirs TEMPLATE = subdirs
CONFIG += ordered CONFIG += ordered
SUBDIRS += src/qphantom/phantom.pro src/phantomjs.pro SUBDIRS += src/phantomjs.pro

View File

@ -1,24 +0,0 @@
#!/bin/sh
#
# A silly little helper script to build the RPM.
set -e
name=${1:?"Usage: build <toolname>"}
name=${name%.spec}
topdir=$(mktemp -d)
version=$(awk '/define version/ { print $NF }' ${name}.spec)
builddir=${TMPDIR:-/tmp}/${name}-${version}
sourcedir="${topdir}/SOURCES"
buildroot="${topdir}/BUILD/${name}-${version}-root"
mkdir -p ${topdir}/RPMS ${topdir}/SRPMS ${topdir}/SOURCES ${topdir}/BUILD
mkdir -p ${buildroot} ${builddir}
echo "=> Copying sources..."
( cd .. && tar cf - ./[A-Z]* ./bin ./examples | tar xf - -C ${builddir} )
echo "=> Creating source tarball under ${sourcedir}..."
( cd ${builddir}/.. && tar zcf ${sourcedir}/${name}-${version}.tar.gz ${name}-${version} )
echo "=> Building RPM..."
rpm=$(rpmbuild --define "_topdir ${topdir}" --buildroot ${buildroot} --clean -bb ${name}.spec 2>/dev/null | \
awk '/\/RPMS\// { print $2; }')
cp ${rpm} ${TMPDIR:-/tmp}/
rm -fr ${topdir}
echo ${TMPDIR:-/tmp}/${rpm##*/}

View File

@ -1,163 +0,0 @@
%define name phantomjs
%define version 1.9
%define release 1
%define prefix /usr
%define mybuilddir %{_builddir}/%{name}-%{version}-root
Summary: a headless WebKit with JavaScript API
Name: %{name}
Version: %{version}
License: BSD
Release: %{release}
Packager: Matthew Barr <mbarr@snap-interactive.com>
Group: Utilities/Misc
Source: %{name}-%{version}.tar.gz
BuildRoot: /tmp/%{name}-%{version}-root
%description
PhantomJS is a headless WebKit with JavaScript API. It has fast and native
support for various web standards: DOM handling, CSS selector, JSON,
Canvas, and SVG. PhantomJS is created by Ariya Hidayat.
%prep
%setup -q
%install
mkdir -p %{mybuilddir}%{prefix}/bin
mkdir -p %{mybuilddir}%{prefix}/share/%{name}/examples
cp bin/%{name} %{mybuilddir}%{prefix}/bin/%{name}
cp examples/* %{mybuilddir}%{prefix}/share/%{name}/examples/
cp CONTRIBUTING.md %{mybuilddir}%{prefix}/share/%{name}/
cp ChangeLog %{mybuilddir}%{prefix}/share/%{name}/
cp LICENSE.BSD %{mybuilddir}%{prefix}/share/%{name}/
cp README.md %{mybuilddir}%{prefix}/share/%{name}/
%files
%defattr(0444,root,root)
%attr(0555,root,root)%{prefix}/bin/%{name}
%{prefix}/share/%{name}/ChangeLog
%{prefix}/share/%{name}/CONTRIBUTING.md
%{prefix}/share/%{name}/examples/arguments.coffee
%{prefix}/share/%{name}/examples/arguments.js
%{prefix}/share/%{name}/examples/child_process-examples.coffee
%{prefix}/share/%{name}/examples/child_process-examples.js
%{prefix}/share/%{name}/examples/colorwheel.coffee
%{prefix}/share/%{name}/examples/colorwheel.js
%{prefix}/share/%{name}/examples/countdown.coffee
%{prefix}/share/%{name}/examples/countdown.js
%{prefix}/share/%{name}/examples/detectsniff.coffee
%{prefix}/share/%{name}/examples/detectsniff.js
%{prefix}/share/%{name}/examples/direction.coffee
%{prefix}/share/%{name}/examples/direction.js
%{prefix}/share/%{name}/examples/echoToFile.coffee
%{prefix}/share/%{name}/examples/echoToFile.js
%{prefix}/share/%{name}/examples/features.coffee
%{prefix}/share/%{name}/examples/features.js
%{prefix}/share/%{name}/examples/fibo.coffee
%{prefix}/share/%{name}/examples/fibo.js
%{prefix}/share/%{name}/examples/follow.coffee
%{prefix}/share/%{name}/examples/follow.js
%{prefix}/share/%{name}/examples/hello.coffee
%{prefix}/share/%{name}/examples/hello.js
%{prefix}/share/%{name}/examples/imagebin.coffee
%{prefix}/share/%{name}/examples/imagebin.js
%{prefix}/share/%{name}/examples/injectme.coffee
%{prefix}/share/%{name}/examples/injectme.js
%{prefix}/share/%{name}/examples/ipgeocode.coffee
%{prefix}/share/%{name}/examples/ipgeocode.js
%{prefix}/share/%{name}/examples/loadspeed.coffee
%{prefix}/share/%{name}/examples/loadspeed.js
%{prefix}/share/%{name}/examples/loadurlwithoutcss.coffee
%{prefix}/share/%{name}/examples/loadurlwithoutcss.js
%{prefix}/share/%{name}/examples/modernizr.js
%{prefix}/share/%{name}/examples/module.coffee
%{prefix}/share/%{name}/examples/module.js
%{prefix}/share/%{name}/examples/movies.coffee
%{prefix}/share/%{name}/examples/movies.js
%{prefix}/share/%{name}/examples/netlog.coffee
%{prefix}/share/%{name}/examples/netlog.js
%{prefix}/share/%{name}/examples/netsniff.coffee
%{prefix}/share/%{name}/examples/netsniff.js
%{prefix}/share/%{name}/examples/openurlwithproxy.coffee
%{prefix}/share/%{name}/examples/openurlwithproxy.js
%{prefix}/share/%{name}/examples/outputEncoding.coffee
%{prefix}/share/%{name}/examples/outputEncoding.js
%{prefix}/share/%{name}/examples/page_events.coffee
%{prefix}/share/%{name}/examples/page_events.js
%{prefix}/share/%{name}/examples/pagecallback.coffee
%{prefix}/share/%{name}/examples/pagecallback.js
%{prefix}/share/%{name}/examples/phantomwebintro.coffee
%{prefix}/share/%{name}/examples/phantomwebintro.js
%{prefix}/share/%{name}/examples/pizza.coffee
%{prefix}/share/%{name}/examples/pizza.js
%{prefix}/share/%{name}/examples/post.coffee
%{prefix}/share/%{name}/examples/post.js
%{prefix}/share/%{name}/examples/postjson.coffee
%{prefix}/share/%{name}/examples/postjson.js
%{prefix}/share/%{name}/examples/postserver.coffee
%{prefix}/share/%{name}/examples/postserver.js
%{prefix}/share/%{name}/examples/printenv.coffee
%{prefix}/share/%{name}/examples/printenv.js
%{prefix}/share/%{name}/examples/printheaderfooter.coffee
%{prefix}/share/%{name}/examples/printheaderfooter.js
%{prefix}/share/%{name}/examples/printmargins.coffee
%{prefix}/share/%{name}/examples/printmargins.js
%{prefix}/share/%{name}/examples/rasterize.coffee
%{prefix}/share/%{name}/examples/rasterize.js
%{prefix}/share/%{name}/examples/render_multi_url.coffee
%{prefix}/share/%{name}/examples/render_multi_url.js
%{prefix}/share/%{name}/examples/run-jasmine.coffee
%{prefix}/share/%{name}/examples/run-jasmine.js
%{prefix}/share/%{name}/examples/run-jasmine2.js
%{prefix}/share/%{name}/examples/run-qunit.coffee
%{prefix}/share/%{name}/examples/run-qunit.js
%{prefix}/share/%{name}/examples/scandir.coffee
%{prefix}/share/%{name}/examples/scandir.js
%{prefix}/share/%{name}/examples/seasonfood.coffee
%{prefix}/share/%{name}/examples/seasonfood.js
%{prefix}/share/%{name}/examples/server.coffee
%{prefix}/share/%{name}/examples/server.js
%{prefix}/share/%{name}/examples/serverkeepalive.coffee
%{prefix}/share/%{name}/examples/serverkeepalive.js
%{prefix}/share/%{name}/examples/simpleserver.coffee
%{prefix}/share/%{name}/examples/simpleserver.js
%{prefix}/share/%{name}/examples/sleepsort.coffee
%{prefix}/share/%{name}/examples/sleepsort.js
%{prefix}/share/%{name}/examples/stdin-stdout-stderr.coffee
%{prefix}/share/%{name}/examples/stdin-stdout-stderr.js
%{prefix}/share/%{name}/examples/technews.coffee
%{prefix}/share/%{name}/examples/technews.js
%{prefix}/share/%{name}/examples/tweets.coffee
%{prefix}/share/%{name}/examples/tweets.js
%{prefix}/share/%{name}/examples/universe.js
%{prefix}/share/%{name}/examples/unrandomize.coffee
%{prefix}/share/%{name}/examples/unrandomize.js
%{prefix}/share/%{name}/examples/useragent.coffee
%{prefix}/share/%{name}/examples/useragent.js
%{prefix}/share/%{name}/examples/version.coffee
%{prefix}/share/%{name}/examples/version.js
%{prefix}/share/%{name}/examples/waitfor.coffee
%{prefix}/share/%{name}/examples/waitfor.js
%{prefix}/share/%{name}/examples/walk_through_frames.coffee
%{prefix}/share/%{name}/examples/walk_through_frames.js
%{prefix}/share/%{name}/examples/weather.coffee
%{prefix}/share/%{name}/examples/weather.js
%{prefix}/share/%{name}/LICENSE.BSD
%{prefix}/share/%{name}/README.md
%changelog
* Fri Apr 18 2014 Eric Heydenberk <heydenberk@gmail.com>
- add missing filenames for examples to files section
* Tue Apr 30 2013 Eric Heydenberk <heydenberk@gmail.com>
- add missing filenames for examples to files section
* Wed Apr 24 2013 Robin Helgelin <lobbin@gmail.com>
- updated to version 1.9
* Thu Jan 24 2013 Matthew Barr <mbarr@snap-interactive.com>
- updated to version 1.8
* Thu Nov 15 2012 Jan Schaumann <jschauma@etsy.com>
- first rpm version

View File

@ -8,7 +8,6 @@
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com> Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
Copyright (C) 2011 James Roe <roejames12@hotmail.com> Copyright (C) 2011 James Roe <roejames12@hotmail.com>
Copyright (C) 2011 execjosh, http://execjosh.blogspot.com Copyright (C) 2011 execjosh, http://execjosh.blogspot.com
Copyright (C) 2012 James M. Greene <james.m.greene@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
@ -34,53 +33,53 @@
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
phantom.__defineErrorSignalHandler__ = function(obj, page, handlers) { function require(name) {
var handlerName = 'onError'; var code, func, exports;
Object.defineProperty(obj, handlerName, { if (name === 'webpage' || name === 'fs' || name === 'webserver' || name === 'system') {
set: function (f) { code = phantom.loadModuleSource(name);
// Disconnect previous handler (if any) func = new Function("exports", "window", code);
var handlerObj = handlers[handlerName]; exports = {};
if (!!handlerObj && typeof handlerObj.callback === "function" && typeof handlerObj.connector === "function") { if (name === 'fs') {
try { page.javaScriptErrorSent.disconnect(handlerObj.connector); } exports = phantom.createFilesystem();
catch (e) { } } else if (name === 'system') {
} exports = phantom.createSystem();
}
func.call({}, exports, {});
return exports;
}
// Delete the previous handler if (typeof exports === 'undefined') {
delete handlers[handlerName]; throw 'Unknown module ' + name + ' for require()';
}
}
if (typeof f === 'function') { phantom.__defineErrorSetter__ = function(obj, page) {
var connector = function (message, lineNumber, source, stack) { var handler;
var revisedStack = JSON.parse(stack).map(function (item) { var signal = page.javaScriptErrorSent;
return { file: item.url, line: item.lineNumber, function: item.functionName };
});
if (revisedStack.length == 0)
revisedStack = [{ file: source, line: lineNumber }];
f(message, revisedStack); obj.__defineSetter__('onError', function(f) {
}; if (handler && typeof handler === 'function') {
// Store the new handler for reference try { signal.disconnect(handler); }
handlers[handlerName] = { catch (e) {}
callback: f, }
connector: connector
};
page.javaScriptErrorSent.connect(connector); if (typeof f === 'function') {
} handler = function(message, stack) {
}, stack = JSON.parse(stack).map(function(item) {
get: function () { return { file: item.url, line: item.lineNumber, function: item.functionName }
var handlerObj = handlers[handlerName]; });
return (!!handlerObj && typeof handlerObj.callback === "function" && typeof handlerObj.connector === "function") ?
handlers[handlerName].callback : f(message, stack);
undefined; };
signal.connect(handler);
} else {
handler = null;
} }
}); });
}; };
(function() { phantom.__defineErrorSetter__(phantom, phantom.page);
var handlers = {};
phantom.__defineErrorSignalHandler__(phantom, phantom.page, handlers);
})();
// TODO: Make this output to STDERR // TODO: Make this output to STDERR
phantom.defaultErrorHandler = function(message, stack) { phantom.defaultErrorHandler = function(message, stack) {
@ -88,14 +87,12 @@ phantom.defaultErrorHandler = function(message, stack) {
stack.forEach(function(item) { stack.forEach(function(item) {
var message = item.file + ":" + item.line; var message = item.file + ":" + item.line;
if (item["function"]) if (item.function)
message += " in " + item["function"]; message += " in " + item.function;
console.log(" " + message); console.log(" " + message);
}); });
}; };
phantom.onError = phantom.defaultErrorHandler;
phantom.callback = function(callback) { phantom.callback = function(callback) {
var ret = phantom.createCallback(); var ret = phantom.createCallback();
ret.called.connect(function(args) { ret.called.connect(function(args) {
@ -105,223 +102,7 @@ phantom.callback = function(callback) {
return ret; return ret;
}; };
(function() { phantom.onError = phantom.defaultErrorHandler;
// CommonJS module implementation follows
window.global = window;
// fs is loaded at the end, when everything is ready
var fs;
var cache = {};
var paths = [];
// use getters to initialize lazily
// (for future, now both fs and system are loaded anyway)
var nativeExports = {
get fs() { return phantom.createFilesystem(); },
get child_process() { return phantom._createChildProcess(); },
get system() { return phantom.createSystem(); }
};
var extensions = {
'.js': function(module, filename) {
var code = fs.read(filename);
module._compile(code);
},
'.json': function(module, filename) {
module.exports = JSON.parse(fs.read(filename));
}
};
function loadFs() {
var file, code, module, filename = ':/modules/fs.js';
module = new Module(filename);
cache[filename] = module;
module.exports = nativeExports.fs;
file = module.exports._open(filename, { mode: 'r' })
code = file.read();
file.close();
module._compile(code);
return module.exports;
}
function dirname(path) {
var replaced = path.replace(/\/[^\/]*\/?$/, '');
if (replaced == path) {
replaced = '';
}
return replaced;
}
function basename(path) {
return path.replace(/.*\//, '');
}
function joinPath() {
// It should be okay to hard-code a slash here.
// The FileSystem module returns a platform-specific
// separator, but the JavaScript engine only expects
// the slash.
var args = Array.prototype.slice.call(arguments);
return args.join('/');
}
function tryFile(path) {
if (fs.isFile(path)) return path;
return null;
}
function tryExtensions(path) {
var filename, exts = Object.keys(extensions);
for (var i=0; i<exts.length; ++i) {
filename = tryFile(path + exts[i]);
if (filename) return filename;
}
return null;
}
function tryPackage(path) {
var filename, package, packageFile = joinPath(path, 'package.json');
if (fs.isFile(packageFile)) {
package = JSON.parse(fs.read(packageFile));
if (!package || !package.main) return null;
filename = fs.absolute(joinPath(path, package.main));
return tryFile(filename) || tryExtensions(filename) ||
tryExtensions(joinPath(filename, 'index'));
}
return null;
}
function Module(filename, stubs) {
if (filename) this._setFilename(filename);
this.exports = {};
this.stubs = {};
for (var name in stubs) {
this.stubs[name] = stubs[name];
}
}
Module.prototype._setFilename = function(filename) {
this.id = this.filename = filename;
this.dirname = dirname(filename);
};
Module.prototype._isNative = function() {
return this.filename && this.filename[0] === ':';
};
Module.prototype._getPaths = function(request) {
var _paths = [], dir;
if (request[0] === '.') {
_paths.push(fs.absolute(joinPath(phantom.webdriverMode ? ":/ghostdriver" : this.dirname, request)));
} else if (fs.isAbsolute(request)) {
_paths.push(fs.absolute(request));
} else {
// first look in PhantomJS modules
_paths.push(joinPath(':/modules', request));
// then look in node_modules directories
if (!this._isNative()) {
dir = this.dirname;
while (dir) {
_paths.push(joinPath(dir, 'node_modules', request));
dir = dirname(dir);
}
}
}
for (var i=0; i<paths.length; ++i) {
if(fs.isAbsolute(paths[i])) {
_paths.push(fs.absolute(joinPath(paths[i], request)));
} else {
_paths.push(fs.absolute(joinPath(this.dirname, paths[i], request)));
}
}
return _paths;
};
Module.prototype._getFilename = function(request) {
var path, filename = null, _paths = this._getPaths(request);
for (var i=0; i<_paths.length && !filename; ++i) {
path = _paths[i];
filename = tryFile(path) || tryExtensions(path) || tryPackage(path) ||
tryExtensions(joinPath(path, 'index'));
}
return filename;
};
Module.prototype._getRequire = function() {
var self = this;
function require(request) {
return self.require(request);
}
require.cache = cache;
require.extensions = extensions;
require.paths = paths;
require.stub = function(request, exports) {
self.stubs[request] = { exports: exports };
};
return require;
};
Module.prototype._load = function() {
var ext = this.filename.match(/\.[^.]+$/)[0];
if (!ext) ext = '.js';
extensions[ext](this, this.filename);
};
Module.prototype._compile = function(code) {
phantom.loadModule(code, this.filename);
};
Module.prototype.require = function(request) {
var filename, module;
// first see if there are any stubs for the request
if (this.stubs.hasOwnProperty(request)) {
if (this.stubs[request].exports instanceof Function) {
this.stubs[request].exports = this.stubs[request].exports();
}
return this.stubs[request].exports;
}
// else look for a file
filename = this._getFilename(request);
if (!filename) {
throw new Error("Cannot find module '" + request + "'");
}
if (cache.hasOwnProperty(filename)) {
return cache[filename].exports;
}
module = new Module(filename, this.stubs);
if (module._isNative()) {
module.exports = nativeExports[request] || {};
}
cache[filename] = module;
module._load();
return module.exports;
};
(function() {
var cwd, mainFilename, mainModule = new Module();
window.require = mainModule._getRequire();
fs = loadFs();
cwd = fs.absolute(phantom.libraryPath);
mainFilename = joinPath(cwd, basename(require('system').args[0]) || 'repl');
mainModule._setFilename(mainFilename);
}());
}());
// Legacy way to use WebPage // Legacy way to use WebPage
window.WebPage = require('webpage').create; window.WebPage = require('webpage').create;

View File

@ -1,18 +0,0 @@
/src/client/linux/linux_dumper_unittest_helper
/src/processor/minidump_dump
/src/processor/minidump_stackwalk
/src/tools/linux/core2md/core2md
/src/tools/linux/dump_syms/dump_syms
/src/tools/linux/md2core/minidump-2-core
/src/tools/linux/symupload/minidump_upload
/src/tools/linux/symupload/sym_upload
/src/config.h
/src/stamp-h1
/config.log
/config.status
/autom4te.cache
!Makefile.am
!Makefile.in
.dirstamp
.deps

1
src/breakpad/.gitignore vendored Symbolic link
View File

@ -0,0 +1 @@
../.gitignore-breakpad

View File

@ -0,0 +1 @@
/usr/share/automake-1.11/compile

View File

@ -29,8 +29,6 @@
#include "callback.h" #include "callback.h"
#include <QDebug>
Callback::Callback(QObject* parent) Callback::Callback(QObject* parent)
: QObject(parent) : QObject(parent)
{ {
@ -40,7 +38,6 @@ QVariant Callback::call(const QVariantList& arguments)
{ {
emit called(arguments); emit called(arguments);
qDebug() << "Callback - call result:" << m_returnValue;
return m_returnValue; return m_returnValue;
} }

View File

@ -1,136 +0,0 @@
/*
This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2012 execjosh, http://execjosh.blogspot.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "childprocess.h"
//
// ChildProcessContext
//
ChildProcessContext::ChildProcessContext(QObject *parent)
: QObject(parent)
, m_proc(this)
{
connect(&m_proc, SIGNAL(readyReadStandardOutput()), this, SLOT(_readyReadStandardOutput()));
connect(&m_proc, SIGNAL(readyReadStandardError()), this, SLOT(_readyReadStandardError()));
connect(&m_proc, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(_finished(int, QProcess::ExitStatus)));
connect(&m_proc, SIGNAL(error(QProcess::ProcessError)), this, SLOT(_error(QProcess::ProcessError)));
}
ChildProcessContext::~ChildProcessContext()
{
}
// public:
qint64 ChildProcessContext::pid() const
{
Q_PID pid = m_proc.pid();
#if !defined(Q_OS_WIN32) && !defined(Q_OS_WINCE)
return pid;
#else
return pid->dwProcessId;
#endif
}
void ChildProcessContext::kill(const QString &signal)
{
// TODO: it would be nice to be able to handle more signals
if ("SIGKILL" == signal) {
m_proc.kill();
} else {
// Default to "SIGTERM"
m_proc.terminate();
}
}
void ChildProcessContext::_setEncoding(const QString &encoding)
{
m_encoding.setEncoding(encoding);
}
// This is affected by [QTBUG-5990](https://bugreports.qt-project.org/browse/QTBUG-5990).
// `QProcess` doesn't properly handle the situations of `cmd` not existing or
// failing to start...
bool ChildProcessContext::_start(const QString &cmd, const QStringList &args)
{
m_proc.start(cmd, args);
// TODO: Is there a better way to do this???
return m_proc.waitForStarted(1000);
}
// private slots:
void ChildProcessContext::_readyReadStandardOutput()
{
QByteArray bytes = m_proc.readAllStandardOutput();
emit stdoutData(m_encoding.decode(bytes));
}
void ChildProcessContext::_readyReadStandardError()
{
QByteArray bytes = m_proc.readAllStandardError();
emit stderrData(m_encoding.decode(bytes));
}
void ChildProcessContext::_finished(const int exitCode, const QProcess::ExitStatus exitStatus)
{
Q_UNUSED(exitStatus)
emit exit(exitCode);
}
void ChildProcessContext::_error(const QProcess::ProcessError error)
{
Q_UNUSED(error)
emit exit(m_proc.exitCode());
}
//
// ChildProcess
//
ChildProcess::ChildProcess(QObject *parent)
: QObject(parent)
{
}
ChildProcess::~ChildProcess()
{
}
// public:
QObject *ChildProcess::_createChildProcessContext()
{
return new ChildProcessContext(this);
}

View File

@ -1,97 +0,0 @@
/*
This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2012 execjosh, http://execjosh.blogspot.com
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CHILDPROCESS_H
#define CHILDPROCESS_H
#include <QObject>
#include <QProcess>
#ifdef Q_OS_WIN32
#include <QtCore/qt_windows.h>
#endif
#include "encoding.h"
/**
* This class wraps a QProcess and facilitates emulation of node.js's ChildProcess
*/
class ChildProcessContext : public QObject
{
Q_OBJECT
Q_PROPERTY(qint64 pid READ pid)
public:
explicit ChildProcessContext(QObject *parent = 0);
virtual ~ChildProcessContext();
qint64 pid() const;
Q_INVOKABLE void kill(const QString &signal = "SIGTERM");
Q_INVOKABLE void _setEncoding(const QString &encoding);
Q_INVOKABLE bool _start(const QString &cmd, const QStringList &args);
signals:
void exit(const int code) const;
/**
* For emulating `child.stdout.on("data", function (data) {})`
*/
void stdoutData(const QString &data) const;
/**
* For emulating `child.stderr.on("data", function (data) {})`
*/
void stderrData(const QString &data) const;
private slots:
void _readyReadStandardOutput();
void _readyReadStandardError();
void _error(const QProcess::ProcessError error);
void _finished(const int exitCode, const QProcess::ExitStatus exitStatus);
private:
QProcess m_proc;
Encoding m_encoding;
};
/**
* Helper class for child_process module
*/
class ChildProcess : public QObject
{
Q_OBJECT
public:
explicit ChildProcess(QObject *parent = 0);
virtual ~ChildProcess();
Q_INVOKABLE QObject *_createChildProcessContext();
};
#endif // CHILDPROCESS_H

8
src/coffee-script.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,8 @@
/* /*
This file is part of the PhantomJS project from Ofi Labs. This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com> Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2011 execjosh, http://execjosh.blogspot.com Copyright (C) 2011 execjosh, http://execjosh.blogspot.com
Copyright (C) 2013 James M. Greene <james.m.greene@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
@ -33,65 +31,17 @@
#include "config.h" #include "config.h"
#include <QDir> #include <QDir>
#include <QFileInfo> #include <QWebPage>
#include <QtWebKitWidgets/QWebPage> #include <QWebFrame>
#include <QtWebKitWidgets/QWebFrame>
#include <QNetworkProxy> #include <QNetworkProxy>
#include "terminal.h" #include "terminal.h"
#include "qcommandline.h"
#include "utils.h" #include "utils.h"
#include "consts.h"
#include <iostream>
static const struct QCommandLineConfigEntry flags[] =
{
{ QCommandLine::Option, '\0', "cookies-file", "Sets the file name to store the persistent cookies", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "config", "Specifies JSON-formatted configuration file", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "debug", "Prints additional warning and debug message: 'true' or 'false' (default)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "disk-cache", "Enables disk cache: 'true' or 'false' (default)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ignore-ssl-errors", "Ignores SSL errors (expired/self-signed certificate errors): 'true' or 'false' (default)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "load-images", "Loads all inlined images: 'true' (default) or 'false'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "local-storage-path", "Specifies the location for offline local storage", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "local-storage-quota", "Sets the maximum size of the offline local storage (in KB)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "local-url-access", "Allows use of 'file:///' URLs: 'true' (default) or 'false'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "local-to-remote-url-access", "Allows local content to access remote URL: 'true' or 'false' (default)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "max-disk-cache-size", "Limits the size of the disk cache (in KB)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "output-encoding", "Sets the encoding for the terminal output, default is 'utf8'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "remote-debugger-port", "Starts the script in a debug harness and listens on the specified port", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "remote-debugger-autorun", "Runs the script in the debugger immediately: 'true' or 'false' (default)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "proxy", "Sets the proxy server, e.g. '--proxy=http://proxy.company.com:8080'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "proxy-auth", "Provides authentication information for the proxy, e.g. ''-proxy-auth=username:password'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "proxy-type", "Specifies the proxy type, 'http' (default), 'none' (disable completely), or 'socks5'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "script-encoding", "Sets the encoding used for the starting script, default is 'utf8'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "script-language", "Sets the script language instead of detecting it: 'javascript'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "web-security", "Enables web security, 'true' (default) or 'false'", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-protocol", "Selects a specific SSL protocol version to offer. Values (case insensitive): TLSv1.2, TLSv1.1, TLSv1.0, TLSv1 (same as v1.0), SSLv3, or ANY. Default is to offer all that Qt thinks are secure (SSLv3 and up). Not all values may be supported, depending on the system OpenSSL library.", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-ciphers", "Sets supported TLS/SSL ciphers. Argument is a colon-separated list of OpenSSL cipher names (macros like ALL, kRSA, etc. may not be used). Default matches modern browsers.", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "ssl-certificates-path", "Sets the location for custom CA certificates (if none set, uses system default)", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver", "Starts in 'Remote WebDriver mode' (embedded GhostDriver): '[[<IP>:]<PORT>]' (default '127.0.0.1:8910') ", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver-logfile", "File where to write the WebDriver's Log (default 'none') (NOTE: needs '--webdriver') ", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver-loglevel", "WebDriver Logging Level: (supported: 'ERROR', 'WARN', 'INFO', 'DEBUG') (default 'INFO') (NOTE: needs '--webdriver') ", QCommandLine::Optional },
{ QCommandLine::Option, '\0', "webdriver-selenium-grid-hub", "URL to the Selenium Grid HUB: 'URL_TO_HUB' (default 'none') (NOTE: needs '--webdriver') ", QCommandLine::Optional },
{ QCommandLine::Param, '\0', "script", "Script", QCommandLine::Flags(QCommandLine::Optional|QCommandLine::ParameterFence)},
{ QCommandLine::Param, '\0', "argument", "Script argument", QCommandLine::OptionalMultiple },
{ QCommandLine::Switch, 'w', "wd", "Equivalent to '--webdriver' option above", QCommandLine::Optional },
{ QCommandLine::Switch, 'h', "help", "Shows this message and quits", QCommandLine::Optional },
{ QCommandLine::Switch, 'v', "version", "Prints out PhantomJS version", QCommandLine::Optional },
QCOMMANDLINE_CONFIG_ENTRY_END
};
// public:
Config::Config(QObject *parent) Config::Config(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
m_cmdLine = new QCommandLine(this);
// We will handle --help and --version ourselves in phantom.cpp
m_cmdLine->enableHelp(false);
m_cmdLine->enableVersion(false);
resetToDefaults(); resetToDefaults();
} }
@ -99,46 +49,138 @@ void Config::init(const QStringList *const args)
{ {
resetToDefaults(); resetToDefaults();
QByteArray envSslCertDir = qgetenv("SSL_CERT_DIR");
if (!envSslCertDir.isEmpty())
setSslCertificatesPath(envSslCertDir);
processArgs(*args); processArgs(*args);
} }
void Config::processArgs(const QStringList &args) void Config::processArgs(const QStringList &args)
{ {
connect(m_cmdLine, SIGNAL(switchFound(const QString &)), this, SLOT(handleSwitch(const QString &))); QStringListIterator it(args);
connect(m_cmdLine, SIGNAL(optionFound(const QString &, const QVariant &)), this, SLOT(handleOption(const QString &, const QVariant &))); while (it.hasNext()) {
connect(m_cmdLine, SIGNAL(paramFound(const QString &, const QVariant &)), this, SLOT(handleParam(const QString &, const QVariant &))); const QString &arg = it.next();
connect(m_cmdLine, SIGNAL(parseError(const QString &)), this, SLOT(handleError(const QString &)));
m_cmdLine->setArguments(args); if (arg == "--help" || arg == "-h") {
m_cmdLine->setConfig(flags); setHelpFlag(true);
m_cmdLine->parse(); return;
}
// Inject command line parameters to be picked up by GhostDriver if (arg == "--version" || arg == "-v") {
if (isWebdriverMode()) { setVersionFlag(true);
QStringList argsForGhostDriver; return;
}
m_scriptFile = "main.js"; //< launch script if (arg == "--load-images=yes") {
setAutoLoadImages(true);
argsForGhostDriver << QString("--ip=%1").arg(m_webdriverIp); //< "--ip=IP" continue;
argsForGhostDriver << QString("--port=%1").arg(m_webdriverPort); //< "--port=PORT" }
if (arg == "--load-images=no") {
if (!m_webdriverSeleniumGridHub.isEmpty()) { setAutoLoadImages(false);
argsForGhostDriver << QString("--hub=%1").arg(m_webdriverSeleniumGridHub); //< "--hub=SELENIUM_GRID_HUB_URL" continue;
}
if (arg == "--disk-cache=yes") {
setDiskCacheEnabled(true);
continue;
}
if (arg == "--disk-cache=no") {
setDiskCacheEnabled(false);
continue;
}
if (arg.startsWith("--max-disk-cache-size=")) {
setMaxDiskCacheSize(arg.mid(arg.indexOf("=") + 1).trimmed().toInt());
continue;
}
if (arg == "--ignore-ssl-errors=yes") {
setIgnoreSslErrors(true);
continue;
}
if (arg == "--ignore-ssl-errors=no") {
setIgnoreSslErrors(false);
continue;
}
if (arg == "--local-to-remote-url-access=no") {
setLocalToRemoteUrlAccessEnabled(false);
continue;
}
if (arg == "--local-to-remote-url-access=yes") {
setLocalToRemoteUrlAccessEnabled(true);
continue;
}
if (arg.startsWith("--proxy-type=")) {
setProxyType(arg.mid(13).trimmed());
continue;
}
if (arg.startsWith("--proxy-auth=")){
setProxyAuth(arg.mid(13).trimmed());
continue;
}
if (arg.startsWith("--proxy=")) {
setProxy(arg.mid(8).trimmed());
continue;
}
if (arg.startsWith("--cookies-file=")) {
setCookiesFile(arg.mid(15).trimmed());
continue;
}
if (arg.startsWith("--local-storage-path=")) {
setOfflineStoragePath(arg.mid(21).trimmed());
continue;
}
if (arg.startsWith("--local-storage-quota=")) {
setOfflineStorageDefaultQuota(arg.mid(arg.indexOf("=") + 1).trimmed().toInt());
continue;
}
if (arg.startsWith("--output-encoding=")) {
setOutputEncoding(arg.mid(18).trimmed());
continue;
}
if (arg.startsWith("--script-encoding=")) {
setScriptEncoding(arg.mid(18).trimmed());
continue;
}
if (arg.startsWith("--config=")) {
QString configPath = arg.mid(9).trimmed();
loadJsonFile(configPath);
continue;
}
if (arg.startsWith("--remote-debugger-port=")) {
setDebug(true);
setRemoteDebugPort(arg.mid(23).trimmed().toInt());
continue;
}
if (arg == "--remote-debugger-autorun=yes") {
setRemoteDebugAutorun(true);
continue;
}
if (arg == "--remote-debugger-autorun=no") {
setRemoteDebugAutorun(false);
continue;
}
if (arg == "--web-security=yes") {
setWebSecurityEnabled(true);
continue;
}
if (arg == "--web-security=no") {
setWebSecurityEnabled(false);
continue;
}
if (arg == "--debug=yes") {
setPrintDebugMessages(true);
continue;
}
if (arg == "--debug=no") {
setPrintDebugMessages(false);
continue;
}
if (arg.startsWith("--")) {
setUnknownOption(QString("Unknown option '%1'").arg(arg));
return;
} }
if (!m_webdriverLogFile.isEmpty()) { // There are no more options at this point.
argsForGhostDriver << QString("--logFile=%1").arg(m_webdriverLogFile); //< "--logFile=LOG_FILE" // The remaining arguments are available for the script.
argsForGhostDriver << "--logColor=false"; //< Force no-color-output in Log File
m_scriptFile = arg;
while (it.hasNext()) {
m_scriptArgs += it.next();
} }
argsForGhostDriver << QString("--logLevel=%1").arg(m_webdriverLogLevel); //< "--logLevel=LOG_LEVEL"
// Clear current args and override with those
setScriptArgs(argsForGhostDriver);
} }
} }
@ -171,12 +213,7 @@ void Config::loadJsonFile(const QString &filePath)
// Add this object to the global scope // Add this object to the global scope
webPage.mainFrame()->addToJavaScriptWindowObject("config", this); webPage.mainFrame()->addToJavaScriptWindowObject("config", this);
// Apply the JSON config settings to this very object // Apply the JSON config settings to this very object
webPage.mainFrame()->evaluateJavaScript(configurator.arg(jsonConfig)); webPage.mainFrame()->evaluateJavaScript(configurator.arg(jsonConfig), QString());
}
QString Config::helpText() const
{
return m_cmdLine->help();
} }
bool Config::autoLoadImages() const bool Config::autoLoadImages() const
@ -250,16 +287,6 @@ void Config::setIgnoreSslErrors(const bool value)
m_ignoreSslErrors = value; m_ignoreSslErrors = value;
} }
bool Config::localUrlAccessEnabled() const
{
return m_localUrlAccessEnabled;
}
void Config::setLocalUrlAccessEnabled(const bool value)
{
m_localUrlAccessEnabled = value;
}
bool Config::localToRemoteUrlAccessEnabled() const bool Config::localToRemoteUrlAccessEnabled() const
{ {
return m_localToRemoteUrlAccessEnabled; return m_localToRemoteUrlAccessEnabled;
@ -296,17 +323,25 @@ void Config::setProxyType(const QString value)
QString Config::proxy() const QString Config::proxy() const
{ {
return m_proxyHost + ":" + QString::number(m_proxyPort); return proxyHost() + ":" + proxyPort();
} }
void Config::setProxy(const QString &value) void Config::setProxy(const QString &value)
{ {
QUrl proxyUrl = QUrl::fromUserInput(value); QString proxyHost = value;
int proxyPort = 1080;
if (proxyUrl.isValid()) { if (proxyHost.lastIndexOf(':') > 0) {
setProxyHost(proxyUrl.host()); bool ok = true;
setProxyPort(proxyUrl.port(1080)); int port = proxyHost.mid(proxyHost.lastIndexOf(':') + 1).toInt(&ok);
if (ok) {
proxyHost = proxyHost.left(proxyHost.lastIndexOf(':')).trimmed();
proxyPort = port;
}
} }
setProxyHost(proxyHost);
setProxyPort(proxyPort);
} }
void Config::setProxyAuth(const QString &value) void Config::setProxyAuth(const QString &value)
@ -377,20 +412,6 @@ void Config::setScriptEncoding(const QString &value)
m_scriptEncoding = value; m_scriptEncoding = value;
} }
QString Config::scriptLanguage() const
{
return m_scriptLanguage;
}
void Config::setScriptLanguage(const QString &value)
{
if (value.isEmpty()) {
return;
}
m_scriptLanguage = value;
}
QString Config::scriptFile() const QString Config::scriptFile() const
{ {
return m_scriptFile; return m_scriptFile;
@ -461,81 +482,6 @@ void Config::setWebSecurityEnabled(const bool value)
m_webSecurityEnabled = value; m_webSecurityEnabled = value;
} }
void Config::setJavascriptCanOpenWindows(const bool value)
{
m_javascriptCanOpenWindows = value;
}
bool Config::javascriptCanOpenWindows() const
{
return m_javascriptCanOpenWindows;
}
void Config::setJavascriptCanCloseWindows(const bool value)
{
m_javascriptCanCloseWindows = value;
}
bool Config::javascriptCanCloseWindows() const
{
return m_javascriptCanCloseWindows;
}
void Config::setWebdriver(const QString &webdriverConfig)
{
// Parse and validate the configuration
bool isValidPort;
QStringList wdCfg = webdriverConfig.split(':');
if (wdCfg.length() == 1 && wdCfg[0].toInt(&isValidPort) && isValidPort) {
// Only a PORT was provided
m_webdriverPort = wdCfg[0];
} else if(wdCfg.length() == 2 && !wdCfg[0].isEmpty() && wdCfg[1].toInt(&isValidPort) && isValidPort) {
// Both IP and PORT provided
m_webdriverIp = wdCfg[0];
m_webdriverPort = wdCfg[1];
}
}
QString Config::webdriver() const
{
return QString("%1:%2").arg(m_webdriverIp).arg(m_webdriverPort);
}
bool Config::isWebdriverMode() const
{
return !m_webdriverPort.isEmpty();
}
void Config::setWebdriverLogFile(const QString& webdriverLogFile)
{
m_webdriverLogFile = webdriverLogFile;
}
QString Config::webdriverLogFile() const
{
return m_webdriverLogFile;
}
void Config::setWebdriverLogLevel(const QString& webdriverLogLevel)
{
m_webdriverLogLevel = webdriverLogLevel;
}
QString Config::webdriverLogLevel() const
{
return m_webdriverLogLevel;
}
void Config::setWebdriverSeleniumGridHub(const QString &hubUrl)
{
m_webdriverSeleniumGridHub = hubUrl;
}
QString Config::webdriverSeleniumGridHub() const
{
return m_webdriverSeleniumGridHub;
}
// private: // private:
void Config::resetToDefaults() void Config::resetToDefaults()
{ {
@ -546,7 +492,6 @@ void Config::resetToDefaults()
m_diskCacheEnabled = false; m_diskCacheEnabled = false;
m_maxDiskCacheSize = -1; m_maxDiskCacheSize = -1;
m_ignoreSslErrors = false; m_ignoreSslErrors = false;
m_localUrlAccessEnabled = true;
m_localToRemoteUrlAccessEnabled = false; m_localToRemoteUrlAccessEnabled = false;
m_outputEncoding = "UTF-8"; m_outputEncoding = "UTF-8";
m_proxyType = "http"; m_proxyType = "http";
@ -556,7 +501,6 @@ void Config::resetToDefaults()
m_proxyAuthPass.clear(); m_proxyAuthPass.clear();
m_scriptArgs.clear(); m_scriptArgs.clear();
m_scriptEncoding = "UTF-8"; m_scriptEncoding = "UTF-8";
m_scriptLanguage.clear();
m_scriptFile.clear(); m_scriptFile.clear();
m_unknownOption.clear(); m_unknownOption.clear();
m_versionFlag = false; m_versionFlag = false;
@ -564,36 +508,8 @@ void Config::resetToDefaults()
m_remoteDebugPort = -1; m_remoteDebugPort = -1;
m_remoteDebugAutorun = false; m_remoteDebugAutorun = false;
m_webSecurityEnabled = true; m_webSecurityEnabled = true;
m_javascriptCanOpenWindows = true;
m_javascriptCanCloseWindows = true;
m_helpFlag = false; m_helpFlag = false;
m_printDebugMessages = false; m_printDebugMessages = false;
m_sslProtocol = "default";
// Default taken from Chromium 35.0.1916.153
m_sslCiphers = ("ECDHE-ECDSA-AES128-GCM-SHA256"
":ECDHE-RSA-AES128-GCM-SHA256"
":DHE-RSA-AES128-GCM-SHA256"
":ECDHE-ECDSA-AES256-SHA"
":ECDHE-ECDSA-AES128-SHA"
":ECDHE-RSA-AES128-SHA"
":ECDHE-RSA-AES256-SHA"
":ECDHE-ECDSA-RC4-SHA"
":ECDHE-RSA-RC4-SHA"
":DHE-RSA-AES128-SHA"
":DHE-DSS-AES128-SHA"
":DHE-RSA-AES256-SHA"
":AES128-GCM-SHA256"
":AES128-SHA"
":AES256-SHA"
":DES-CBC3-SHA"
":RC4-SHA"
":RC4-MD5");
m_sslCertificatesPath.clear();
m_webdriverIp = QString();
m_webdriverPort = QString();
m_webdriverLogFile = QString();
m_webdriverLogLevel = "INFO";
m_webdriverSeleniumGridHub = QString();
} }
void Config::setProxyAuthPass(const QString &value) void Config::setProxyAuthPass(const QString &value)
@ -635,191 +551,3 @@ void Config::setPrintDebugMessages(const bool value)
{ {
m_printDebugMessages = value; m_printDebugMessages = value;
} }
void Config::handleSwitch(const QString &sw)
{
setHelpFlag(sw == "help");
setVersionFlag(sw == "version");
if (sw == "wd") {
setWebdriver(DEFAULT_WEBDRIVER_CONFIG);
}
}
void Config::handleOption(const QString &option, const QVariant &value)
{
bool boolValue = false;
QStringList booleanFlags;
booleanFlags << "debug";
booleanFlags << "disk-cache";
booleanFlags << "ignore-ssl-errors";
booleanFlags << "load-images";
booleanFlags << "local-url-access";
booleanFlags << "local-to-remote-url-access";
booleanFlags << "remote-debugger-autorun";
booleanFlags << "web-security";
if (booleanFlags.contains(option)) {
if ((value != "true") && (value != "yes") && (value != "false") && (value != "no")) {
setUnknownOption(QString("Invalid values for '%1' option.").arg(option));
return;
}
boolValue = (value == "true") || (value == "yes");
}
if (option == "cookies-file") {
setCookiesFile(value.toString());
}
if (option == "config") {
loadJsonFile(value.toString());
}
if (option == "debug") {
setPrintDebugMessages(boolValue);
}
if (option == "disk-cache") {
setDiskCacheEnabled(boolValue);
}
if (option == "ignore-ssl-errors") {
setIgnoreSslErrors(boolValue);
}
if (option == "load-images") {
setAutoLoadImages(boolValue);
}
if (option == "local-storage-path") {
setOfflineStoragePath(value.toString());
}
if (option == "local-storage-quota") {
setOfflineStorageDefaultQuota(value.toInt());
}
if (option == "local-url-access") {
setLocalUrlAccessEnabled(boolValue);
}
if (option == "local-to-remote-url-access") {
setLocalToRemoteUrlAccessEnabled(boolValue);
}
if (option == "max-disk-cache-size") {
setMaxDiskCacheSize(value.toInt());
}
if (option == "output-encoding") {
setOutputEncoding(value.toString());
}
if (option == "remote-debugger-autorun") {
setRemoteDebugAutorun(boolValue);
}
if (option == "remote-debugger-port") {
setDebug(true);
setRemoteDebugPort(value.toInt());
}
if (option == "proxy") {
setProxy(value.toString());
}
if (option == "proxy-type") {
setProxyType(value.toString());
}
if (option == "proxy-auth") {
setProxyAuth(value.toString());
}
if (option == "script-encoding") {
setScriptEncoding(value.toString());
}
if (option == "script-language") {
setScriptLanguage(value.toString());
}
if (option == "web-security") {
setWebSecurityEnabled(boolValue);
}
if (option == "ssl-protocol") {
setSslProtocol(value.toString());
}
if (option == "ssl-ciphers") {
setSslCiphers(value.toString());
}
if (option == "ssl-certificates-path") {
setSslCertificatesPath(value.toString());
}
if (option == "webdriver") {
setWebdriver(value.toString().length() > 0 ? value.toString() : DEFAULT_WEBDRIVER_CONFIG);
}
if (option == "webdriver-logfile") {
setWebdriverLogFile(value.toString());
}
if (option == "webdriver-loglevel") {
setWebdriverLogLevel(value.toString());
}
if (option == "webdriver-selenium-grid-hub") {
setWebdriverSeleniumGridHub(value.toString());
}
}
void Config::handleParam(const QString& param, const QVariant &value)
{
Q_UNUSED(param);
if (m_scriptFile.isEmpty())
m_scriptFile = value.toString();
else
m_scriptArgs += value.toString();
}
void Config::handleError(const QString &error)
{
setUnknownOption(QString("Error: %1").arg(error));
}
QString Config::sslProtocol() const
{
return m_sslProtocol;
}
void Config::setSslProtocol(const QString& sslProtocolName)
{
m_sslProtocol = sslProtocolName.toLower();
}
QString Config::sslCiphers() const
{
return m_sslCiphers;
}
void Config::setSslCiphers(const QString& sslCiphersName)
{
// OpenSSL cipher strings are case sensitive.
m_sslCiphers = sslCiphersName;
}
QString Config::sslCertificatesPath() const
{
return m_sslCertificatesPath;
}
void Config::setSslCertificatesPath(const QString& sslCertificatesPath)
{
QFileInfo sslPathInfo = QFileInfo(sslCertificatesPath);
if (sslPathInfo.isDir()) {
if (sslCertificatesPath.endsWith('/'))
m_sslCertificatesPath = sslCertificatesPath + "*";
else
m_sslCertificatesPath = sslCertificatesPath + "/*";
} else {
m_sslCertificatesPath = sslCertificatesPath;
}
}

View File

@ -34,18 +34,14 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QNetworkProxy> #include <QNetworkProxy>
#include <QVariant>
class QCommandLine; class Config: QObject
class Config: public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QString cookiesFile READ cookiesFile WRITE setCookiesFile) Q_PROPERTY(QString cookiesFile READ cookiesFile WRITE setCookiesFile)
Q_PROPERTY(bool diskCacheEnabled READ diskCacheEnabled WRITE setDiskCacheEnabled) Q_PROPERTY(bool diskCacheEnabled READ diskCacheEnabled WRITE setDiskCacheEnabled)
Q_PROPERTY(int maxDiskCacheSize READ maxDiskCacheSize WRITE setMaxDiskCacheSize) Q_PROPERTY(int maxDiskCacheSize READ maxDiskCacheSize WRITE setMaxDiskCacheSize)
Q_PROPERTY(bool ignoreSslErrors READ ignoreSslErrors WRITE setIgnoreSslErrors) Q_PROPERTY(bool ignoreSslErrors READ ignoreSslErrors WRITE setIgnoreSslErrors)
Q_PROPERTY(bool localUrlAccessEnabled READ localUrlAccessEnabled WRITE setLocalUrlAccessEnabled)
Q_PROPERTY(bool localToRemoteUrlAccessEnabled READ localToRemoteUrlAccessEnabled WRITE setLocalToRemoteUrlAccessEnabled) Q_PROPERTY(bool localToRemoteUrlAccessEnabled READ localToRemoteUrlAccessEnabled WRITE setLocalToRemoteUrlAccessEnabled)
Q_PROPERTY(QString outputEncoding READ outputEncoding WRITE setOutputEncoding) Q_PROPERTY(QString outputEncoding READ outputEncoding WRITE setOutputEncoding)
Q_PROPERTY(QString proxyType READ proxyType WRITE setProxyType) Q_PROPERTY(QString proxyType READ proxyType WRITE setProxyType)
@ -56,15 +52,6 @@ class Config: public QObject
Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath) Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath)
Q_PROPERTY(int offlineStorageDefaultQuota READ offlineStorageDefaultQuota WRITE setOfflineStorageDefaultQuota) Q_PROPERTY(int offlineStorageDefaultQuota READ offlineStorageDefaultQuota WRITE setOfflineStorageDefaultQuota)
Q_PROPERTY(bool printDebugMessages READ printDebugMessages WRITE setPrintDebugMessages) Q_PROPERTY(bool printDebugMessages READ printDebugMessages WRITE setPrintDebugMessages)
Q_PROPERTY(bool javascriptCanOpenWindows READ javascriptCanOpenWindows WRITE setJavascriptCanOpenWindows)
Q_PROPERTY(bool javascriptCanCloseWindows READ javascriptCanCloseWindows WRITE setJavascriptCanCloseWindows)
Q_PROPERTY(QString sslProtocol READ sslProtocol WRITE setSslProtocol)
Q_PROPERTY(QString sslCiphers READ sslCiphers WRITE setSslCiphers)
Q_PROPERTY(QString sslCertificatesPath READ sslCertificatesPath WRITE setSslCertificatesPath)
Q_PROPERTY(QString webdriver READ webdriver WRITE setWebdriver)
Q_PROPERTY(QString webdriverLogFile READ webdriverLogFile WRITE setWebdriverLogFile)
Q_PROPERTY(QString webdriverLogLevel READ webdriverLogLevel WRITE setWebdriverLogLevel)
Q_PROPERTY(QString webdriverSeleniumGridHub READ webdriverSeleniumGridHub WRITE setWebdriverSeleniumGridHub)
public: public:
Config(QObject *parent = 0); Config(QObject *parent = 0);
@ -73,8 +60,6 @@ public:
void processArgs(const QStringList &args); void processArgs(const QStringList &args);
void loadJsonFile(const QString &filePath); void loadJsonFile(const QString &filePath);
QString helpText() const;
bool autoLoadImages() const; bool autoLoadImages() const;
void setAutoLoadImages(const bool value); void setAutoLoadImages(const bool value);
@ -96,9 +81,6 @@ public:
bool ignoreSslErrors() const; bool ignoreSslErrors() const;
void setIgnoreSslErrors(const bool value); void setIgnoreSslErrors(const bool value);
bool localUrlAccessEnabled() const;
void setLocalUrlAccessEnabled(const bool value);
bool localToRemoteUrlAccessEnabled() const; bool localToRemoteUrlAccessEnabled() const;
void setLocalToRemoteUrlAccessEnabled(const bool value); void setLocalToRemoteUrlAccessEnabled(const bool value);
@ -126,9 +108,6 @@ public:
QString scriptEncoding() const; QString scriptEncoding() const;
void setScriptEncoding(const QString &value); void setScriptEncoding(const QString &value);
QString scriptLanguage() const;
void setScriptLanguage(const QString &value);
QString scriptFile() const; QString scriptFile() const;
void setScriptFile(const QString &value); void setScriptFile(const QString &value);
@ -156,40 +135,6 @@ public:
void setPrintDebugMessages(const bool value); void setPrintDebugMessages(const bool value);
bool printDebugMessages() const; bool printDebugMessages() const;
void setJavascriptCanOpenWindows(const bool value);
bool javascriptCanOpenWindows() const;
void setJavascriptCanCloseWindows(const bool value);
bool javascriptCanCloseWindows() const;
void setSslProtocol(const QString& sslProtocolName);
QString sslProtocol() const;
void setSslCiphers(const QString& sslCiphersName);
QString sslCiphers() const;
void setSslCertificatesPath(const QString& sslCertificatesPath);
QString sslCertificatesPath() const;
void setWebdriver(const QString& webdriverConfig);
QString webdriver() const;
bool isWebdriverMode() const;
void setWebdriverLogFile(const QString& webdriverLogFile);
QString webdriverLogFile() const;
void setWebdriverLogLevel(const QString& webdriverLogLevel);
QString webdriverLogLevel() const;
void setWebdriverSeleniumGridHub(const QString& hubUrl);
QString webdriverSeleniumGridHub() const;
public slots:
void handleSwitch(const QString &sw);
void handleOption(const QString &option, const QVariant &value);
void handleParam(const QString& param, const QVariant &value);
void handleError(const QString &error);
private: private:
void resetToDefaults(); void resetToDefaults();
void setProxyHost(const QString &value); void setProxyHost(const QString &value);
@ -197,7 +142,6 @@ private:
void setAuthUser(const QString &value); void setAuthUser(const QString &value);
void setAuthPass(const QString &value); void setAuthPass(const QString &value);
QCommandLine *m_cmdLine;
bool m_autoLoadImages; bool m_autoLoadImages;
QString m_cookiesFile; QString m_cookiesFile;
QString m_offlineStoragePath; QString m_offlineStoragePath;
@ -205,7 +149,6 @@ private:
bool m_diskCacheEnabled; bool m_diskCacheEnabled;
int m_maxDiskCacheSize; int m_maxDiskCacheSize;
bool m_ignoreSslErrors; bool m_ignoreSslErrors;
bool m_localUrlAccessEnabled;
bool m_localToRemoteUrlAccessEnabled; bool m_localToRemoteUrlAccessEnabled;
QString m_outputEncoding; QString m_outputEncoding;
QString m_proxyType; QString m_proxyType;
@ -215,7 +158,6 @@ private:
QString m_proxyAuthPass; QString m_proxyAuthPass;
QStringList m_scriptArgs; QStringList m_scriptArgs;
QString m_scriptEncoding; QString m_scriptEncoding;
QString m_scriptLanguage;
QString m_scriptFile; QString m_scriptFile;
QString m_unknownOption; QString m_unknownOption;
bool m_versionFlag; bool m_versionFlag;
@ -227,16 +169,6 @@ private:
bool m_webSecurityEnabled; bool m_webSecurityEnabled;
bool m_helpFlag; bool m_helpFlag;
bool m_printDebugMessages; bool m_printDebugMessages;
bool m_javascriptCanOpenWindows;
bool m_javascriptCanCloseWindows;
QString m_sslProtocol;
QString m_sslCiphers;
QString m_sslCertificatesPath;
QString m_webdriverIp;
QString m_webdriverPort;
QString m_webdriverLogFile;
QString m_webdriverLogLevel;
QString m_webdriverSeleniumGridHub;
}; };
#endif // CONFIG_H #endif // CONFIG_H

View File

@ -32,13 +32,13 @@
#ifndef CONSTS_H #ifndef CONSTS_H
#define CONSTS_H #define CONSTS_H
#define PHANTOMJS_VERSION_MAJOR 2 // Current Version: 1.6.0
#define PHANTOMJS_VERSION_MINOR 0 #define PHANTOMJS_VERSION_MAJOR 1
#define PHANTOMJS_VERSION_MINOR 6
#define PHANTOMJS_VERSION_PATCH 0 #define PHANTOMJS_VERSION_PATCH 0
#define PHANTOMJS_VERSION_STRING "2.0.0" #define PHANTOMJS_VERSION_STRING "1.6.0"
#define HTTP_HEADER_CONTENT_LENGTH "content-length" #define COFFEE_SCRIPT_EXTENSION ".coffee"
#define HTTP_HEADER_CONTENT_TYPE "content-type"
#define JS_ELEMENT_CLICK "(function (el) { " \ #define JS_ELEMENT_CLICK "(function (el) { " \
"var ev = document.createEvent('MouseEvents');" \ "var ev = document.createEvent('MouseEvents');" \
@ -58,12 +58,6 @@
#define PAGE_SETTINGS_LOCAL_ACCESS_REMOTE "localToRemoteUrlAccessEnabled" #define PAGE_SETTINGS_LOCAL_ACCESS_REMOTE "localToRemoteUrlAccessEnabled"
#define PAGE_SETTINGS_USERNAME "userName" #define PAGE_SETTINGS_USERNAME "userName"
#define PAGE_SETTINGS_PASSWORD "password" #define PAGE_SETTINGS_PASSWORD "password"
#define PAGE_SETTINGS_MAX_AUTH_ATTEMPTS "maxAuthAttempts"
#define PAGE_SETTINGS_RESOURCE_TIMEOUT "resourceTimeout"
#define PAGE_SETTINGS_WEB_SECURITY_ENABLED "webSecurityEnabled" #define PAGE_SETTINGS_WEB_SECURITY_ENABLED "webSecurityEnabled"
#define PAGE_SETTINGS_JS_CAN_OPEN_WINDOWS "javascriptCanOpenWindows"
#define PAGE_SETTINGS_JS_CAN_CLOSE_WINDOWS "javascriptCanCloseWindows"
#define DEFAULT_WEBDRIVER_CONFIG "127.0.0.1:8910"
#endif // CONSTS_H #endif // CONSTS_H

View File

@ -2,7 +2,6 @@
This file is part of the PhantomJS project from Ofi Labs. This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com> Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2012 Ivan De Marino <ivan.de.marino@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
@ -28,480 +27,44 @@
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include "phantom.h"
#include "config.h"
#include "cookiejar.h" #include "cookiejar.h"
#include <QDateTime>
#include <QSettings> #include <QSettings>
#include <QTimer> #include <QStringList>
#define COOKIE_JAR_VERSION 1 CookieJar::CookieJar(QString cookiesFile)
: QNetworkCookieJar()
// Operators needed for Cookie Serialization
QT_BEGIN_NAMESPACE
QDataStream &operator<<(QDataStream &stream, const QList<QNetworkCookie> &list)
{ {
stream << COOKIE_JAR_VERSION; m_cookiesFile = cookiesFile;
stream << quint32(list.size());
for (int i = 0; i < list.size(); ++i)
stream << list.at(i).toRawForm();
return stream;
} }
QDataStream &operator>>(QDataStream &stream, QList<QNetworkCookie> &list) bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> & cookieList, const QUrl & url)
{ {
list.clear(); QSettings settings(m_cookiesFile, QSettings::IniFormat);
quint32 version; settings.beginGroup(url.host());
stream >> version;
if (version != COOKIE_JAR_VERSION) for (QList<QNetworkCookie>::const_iterator i = cookieList.begin(); i != cookieList.end(); i++) {
return stream; settings.setValue((*i).name(), QString((*i).value()));
quint32 count;
stream >> count;
for(quint32 i = 0; i < count; ++i)
{
QByteArray value;
stream >> value;
QList<QNetworkCookie> newCookies = QNetworkCookie::parseCookies(value);
if (newCookies.count() == 0 && value.length() != 0) {
qWarning() << "CookieJar: Unable to parse saved cookie:" << value;
}
for (int j = 0; j < newCookies.count(); ++j)
list.append(newCookies.at(j));
if (stream.atEnd())
break;
}
return stream;
}
QT_END_NAMESPACE
// public:
CookieJar::CookieJar(QString cookiesFile, QObject *parent)
: QNetworkCookieJar(parent)
, m_enabled(true)
{
if (cookiesFile == "") {
m_cookieStorage = 0;
qDebug() << "CookieJar - Created but will not store cookies (use option '--cookies-file=<filename>' to enable persistent cookie storage)";
} else {
m_cookieStorage = new QSettings(cookiesFile, QSettings::IniFormat, this);
load();
qDebug() << "CookieJar - Created and will store cookies in:" << cookiesFile;
}
}
// private:
CookieJar::~CookieJar()
{
// On destruction, before saving, clear all the session cookies
purgeSessionCookies();
save();
}
bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> & cookieList, const QUrl &url)
{
// Update cookies in memory
if (isEnabled()) {
QNetworkCookieJar::setCookiesFromUrl(cookieList, url);
save();
}
// No changes occurred
return false;
}
QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl &url) const
{
if (isEnabled()) {
return QNetworkCookieJar::cookiesForUrl(url);
}
// The CookieJar is disabled: don't return any cookie
return QList<QNetworkCookie>();
}
bool CookieJar::addCookie(const QNetworkCookie &cookie, const QString &url)
{
if (isEnabled() && (!url.isEmpty() || !cookie.domain().isEmpty())) {
// Save a single cookie
setCookiesFromUrl(
QList<QNetworkCookie>() << cookie, //< unfortunately, "setCookiesFromUrl" requires a list
!url.isEmpty() ?
url : //< use given URL
QString( //< mock-up a URL
(cookie.isSecure() ? "https://" : "http://") + //< URL protocol
QString(cookie.domain().startsWith('.') ? "www" : "") + cookie.domain() + //< URL domain
(cookie.path().isEmpty() ? "/" : cookie.path()))); //< URL path
// Return "true" if the cookie was really set
if (contains(cookie)) {
return true;
}
qDebug() << "CookieJar - Rejected Cookie" << cookie.toRawForm();
return false;
}
return false;
}
void CookieJar::addCookie(const QVariantMap &cookie)
{
addCookieFromMap(cookie);
}
bool CookieJar::addCookieFromMap(const QVariantMap &cookie, const QString &url)
{
QNetworkCookie newCookie;
// The cookie must have a non-empty "name" and a "value"
if (!cookie["name"].isNull() && !cookie["name"].toString().isEmpty() && !cookie["value"].isNull()) {
// Name & Value
newCookie.setName(cookie["name"].toByteArray());
newCookie.setValue(cookie["value"].toByteArray());
// Domain, if provided
if (!cookie["domain"].isNull() && !cookie["domain"].toString().isEmpty()) {
newCookie.setDomain(cookie["domain"].toString());
}
// Path, if provided
if (!cookie["path"].isNull() || !cookie["path"].toString().isEmpty()) {
newCookie.setPath(cookie["path"].toString());
}
// HttpOnly, false by default
newCookie.setHttpOnly(cookie["httponly"].isNull() ? false : cookie["httponly"].toBool());
// Secure, false by default
newCookie.setSecure(cookie["secure"].isNull() ? false : cookie["secure"].toBool());
// Expiration Date, if provided, giving priority to "expires" over "expiry"
QVariant expiresVar;
if (!cookie["expires"].isNull()) {
expiresVar = cookie["expires"];
} else if (!cookie["expiry"].isNull()) {
expiresVar = cookie["expiry"];
}
if (expiresVar.isValid()) {
QDateTime expirationDate;
if (expiresVar.type() == QVariant::String) {
// Set cookie expire date via "classic" string format
QString datetime = expiresVar.toString().replace(" GMT", "");
expirationDate = QDateTime::fromString(datetime, "ddd, dd MMM yyyy hh:mm:ss");
} else if (expiresVar.type() == QVariant::Double){
// Set cookie expire date via "number of seconds since epoch"
// NOTE: Every JS number is a Double.
// @see http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
expirationDate = QDateTime::fromMSecsSinceEpoch(expiresVar.toLongLong());
}
if (expirationDate.isValid()) {
newCookie.setExpirationDate(expirationDate);
}
}
return addCookie(newCookie, url);
}
return false;
}
bool CookieJar::addCookies(const QList<QNetworkCookie> &cookiesList, const QString &url)
{
bool added = false;
for (int i = cookiesList.length() -1; i >=0; --i) {
if (addCookie(cookiesList.at(i), url)) {
// change it to "true" if at least 1 cookie was set
added = true;
}
}
return added;
}
bool CookieJar::addCookiesFromMap(const QVariantList &cookiesList, const QString &url)
{
bool added = false;
for (int i = cookiesList.length() -1; i >= 0; --i) {
if (addCookieFromMap(cookiesList.at(i).toMap(), url)) {
// change it to "true" if at least 1 cookie was set
added = true;
}
}
return added;
}
QList<QNetworkCookie> CookieJar::cookies(const QString &url) const
{
if (url.isEmpty()) {
// No url provided: return all the cookies in this CookieJar
return allCookies();
} else {
// Return ONLY the cookies that match this URL
return cookiesForUrl(url);
}
}
QVariantList CookieJar::cookiesToMap(const QString &url) const
{
QVariantList result;
QNetworkCookie c;
QVariantMap cookie;
QList<QNetworkCookie> cookiesList = cookies(url);
for (int i = cookiesList.length() -1; i >= 0; --i) {
c = cookiesList.at(i);
cookie["domain"] = QVariant(c.domain());
cookie["name"] = QVariant(QString(c.name()));
cookie["value"] = QVariant(QString(c.value()));
cookie["path"] = (c.path().isNull() || c.path().isEmpty()) ? QVariant("/") : QVariant(c.path());
cookie["httponly"] = QVariant(c.isHttpOnly());
cookie["secure"] = QVariant(c.isSecure());
if (c.expirationDate().isValid()) {
cookie["expires"] = QVariant(QString(c.expirationDate().toString("ddd, dd MMM yyyy hh:mm:ss")).append(" GMT"));
cookie["expiry"] = QVariant(c.expirationDate().toMSecsSinceEpoch() / 1000);
}
result.append(cookie);
} }
return result; settings.sync();
return true;
} }
QNetworkCookie CookieJar::cookie(const QString &name, const QString &url) const QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl & url) const
{ {
QList<QNetworkCookie> cookiesList = cookies(url); QSettings settings(m_cookiesFile, QSettings::IniFormat);
for (int i = cookiesList.length() -1; i >= 0; --i) { QList<QNetworkCookie> cookieList;
if (cookiesList.at(i).name() == name) {
return cookiesList.at(i);
}
}
return QNetworkCookie();
}
QVariantMap CookieJar::cookieToMap(const QString &name, const QString &url) const settings.beginGroup(url.host());
{
QVariantMap cookie;
QVariantList cookiesList = cookiesToMap(url); QStringList keys = settings.childKeys();
for (int i = cookiesList.length() -1; i >= 0; --i) {
cookie = cookiesList.at(i).toMap();
if (cookie["name"].toString() == name) {
return cookie;
}
}
return QVariantMap();
}
bool CookieJar::deleteCookie(const QString &name, const QString &url) for (QStringList::iterator i = keys.begin(); i != keys.end(); i++) {
{ cookieList.push_back(QNetworkCookie((*i).toLocal8Bit(), settings.value(*i).toByteArray()));
bool deleted = false;
if (isEnabled()) {
// NOTE: This code has been written in an "extended form" to make it
// easy to understand. Surely this could be "shrinked", but it
// would probably look uglier.
QList<QNetworkCookie> cookiesListAll;
if (url.isEmpty()) {
if (name.isEmpty()) { //< Neither "name" or "url" provided
// This method has been used wrong:
// "redirecting" to the right method for the job
clearCookies();
} else { //< Only "name" provided
// Delete all cookies with the given name from the CookieJar
cookiesListAll = allCookies();
for (int i = cookiesListAll.length() -1; i >= 0; --i) {
if (cookiesListAll.at(i).name() == name) {
// Remove this cookie
qDebug() << "CookieJar - Deleted" << cookiesListAll.at(i).toRawForm();
cookiesListAll.removeAt(i);
deleted = true;
}
}
}
} else {
// Delete cookie(s) from the ones visible to the given "url".
// Use the "name" to delete only the right one, otherwise all of them.
QList<QNetworkCookie> cookiesListUrl = cookies(url);
cookiesListAll = allCookies();
for (int i = cookiesListAll.length() -1; i >= 0; --i) {
if (cookiesListUrl.contains(cookiesListAll.at(i)) && //< if it part of the set of cookies visible at URL
(cookiesListAll.at(i).name() == name || name.isEmpty())) { //< and if the name matches, or no name provided
// Remove this cookie
qDebug() << "CookieJar - Deleted" << cookiesListAll.at(i).toRawForm();
cookiesListAll.removeAt(i);
deleted = true;
if (!name.isEmpty()) {
// Only one cookie was supposed to be deleted: we are done here!
break;
}
}
}
}
// Put back the remaining cookies
setAllCookies(cookiesListAll);
}
return deleted;
}
bool CookieJar::deleteCookies(const QString &url)
{
if (isEnabled()) {
if (url.isEmpty()) {
// No URL provided: delete ALL the cookies in the CookieJar
clearCookies();
return true;
}
// No cookie name provided: delete all the cookies visible by this URL
qDebug() << "Delete all cookies for URL:" << url;
return deleteCookie("", url);
}
return false;
}
void CookieJar::clearCookies()
{
if (isEnabled()) {
setAllCookies(QList<QNetworkCookie>());
}
}
void CookieJar::enable()
{
m_enabled = true;
}
void CookieJar::disable()
{
m_enabled = false;
}
bool CookieJar::isEnabled() const
{
return m_enabled;
}
void CookieJar::close()
{
deleteLater();
}
// private:
bool CookieJar::purgeExpiredCookies()
{
QList<QNetworkCookie> cookiesList = allCookies();
// If empty, there is nothing to purge
if (cookiesList.isEmpty()) {
return false;
} }
// Check if any cookie has expired return cookieList;
int prePurgeCookiesCount = cookiesList.count();
QDateTime now = QDateTime::currentDateTime();
for (int i = cookiesList.count() - 1; i >= 0; --i) {
if (!cookiesList.at(i).isSessionCookie() && cookiesList.at(i).expirationDate() < now) {
qDebug() << "CookieJar - Purged (expired)" << cookiesList.at(i).toRawForm();
cookiesList.removeAt(i);
}
}
// Set cookies and returns "true" if at least 1 cookie expired and has been removed
if (prePurgeCookiesCount != cookiesList.count()) {
setAllCookies(cookiesList);
return true;
}
return false;
}
bool CookieJar::purgeSessionCookies()
{
QList<QNetworkCookie> cookiesList = allCookies();
// If empty, there is nothing to purge
if (cookiesList.isEmpty()) {
return false;
}
// Check if any cookie has expired
int prePurgeCookiesCount = cookiesList.count();
for (int i = cookiesList.count() - 1; i >= 0; --i) {
if (cookiesList.at(i).isSessionCookie() || !cookiesList.at(i).expirationDate().isValid() || cookiesList.at(i).expirationDate().isNull()) {
qDebug() << "CookieJar - Purged (session)" << cookiesList.at(i).toRawForm();
cookiesList.removeAt(i);
}
}
// Set cookies and returns "true" if at least 1 session cookie was found and removed
if (prePurgeCookiesCount != cookiesList.count()) {
setAllCookies(cookiesList);
return true;
}
return false;
}
void CookieJar::save()
{
if (isEnabled()) {
// Get rid of all the Cookies that have expired
purgeExpiredCookies();
#ifndef QT_NO_DEBUG_OUTPUT
foreach (QNetworkCookie cookie, allCookies()) {
qDebug() << "CookieJar - Saved" << cookie.toRawForm();
}
#endif
// Store cookies
if (m_cookieStorage) {
m_cookieStorage->setValue(QLatin1String("cookies"), QVariant::fromValue<QList<QNetworkCookie> >(allCookies()));
}
}
}
void CookieJar::load()
{
if (isEnabled()) {
// Register a "StreamOperator" for this Meta Type, so we can easily serialize/deserialize the cookies
qRegisterMetaTypeStreamOperators<QList<QNetworkCookie> >("QList<QNetworkCookie>");
// Load all the cookies
if (m_cookieStorage) {
setAllCookies(qvariant_cast<QList<QNetworkCookie> >(m_cookieStorage->value(QLatin1String("cookies"))));
}
// If any cookie has expired since last execution, purge and save before going any further
if (purgeExpiredCookies()) {
save();
}
#ifndef QT_NO_DEBUG_OUTPUT
foreach (QNetworkCookie cookie, allCookies()) {
qDebug() << "CookieJar - Loaded" << cookie.toRawForm();
}
#endif
}
}
bool CookieJar::contains(const QNetworkCookie &cookie) const
{
QList<QNetworkCookie> cookiesList = allCookies();
for (int i = cookiesList.length() -1; i >= 0; --i) {
if (cookie.name() == cookiesList.at(i).name() &&
cookie.value() == cookiesList.at(i).value() &&
(cookie.domain().isEmpty() || cookiesList.at(i).domain().prepend('.').endsWith(cookie.domain())) &&
(cookie.path().isEmpty() || cookiesList.at(i).path() == cookie.path()) &&
cookie.isSecure() == cookiesList.at(i).isSecure() &&
cookie.isHttpOnly() == cookiesList.at(i).isHttpOnly() &&
cookie.expirationDate().toMSecsSinceEpoch() == cookiesList.at(i).expirationDate().toMSecsSinceEpoch()
) {
return true;
}
}
return false;
} }

View File

@ -2,7 +2,6 @@
This file is part of the PhantomJS project from Ofi Labs. This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com> Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2012 Ivan De Marino <ivan.de.marino@gmail.com>
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
@ -31,61 +30,18 @@
#ifndef COOKIEJAR_H #ifndef COOKIEJAR_H
#define COOKIEJAR_H #define COOKIEJAR_H
#include <QSettings>
#include <QNetworkCookie>
#include <QNetworkCookieJar> #include <QNetworkCookieJar>
#include <QVariantList>
#include <QVariantMap>
class CookieJar: public QNetworkCookieJar class CookieJar: public QNetworkCookieJar
{ {
Q_OBJECT private:
QString m_cookiesFile;
Q_PROPERTY(QVariantList cookies READ cookiesToMap WRITE addCookiesFromMap)
public: public:
CookieJar(QString cookiesFile, QObject *parent = NULL); CookieJar(QString cookiesFile);
virtual ~CookieJar();
bool setCookiesFromUrl(const QList<QNetworkCookie> &cookieList, const QUrl & url); bool setCookiesFromUrl(const QList<QNetworkCookie> & cookieList, const QUrl & url);
QList<QNetworkCookie> cookiesForUrl (const QUrl & url) const; QList<QNetworkCookie> cookiesForUrl (const QUrl & url) const;
bool addCookie(const QNetworkCookie &cookie, const QString &url = QString());
bool addCookies(const QList<QNetworkCookie> &cookiesList, const QString &url = QString());
QList<QNetworkCookie> cookies(const QString &url = QString()) const;
QNetworkCookie cookie(const QString &name, const QString &url = QString()) const;
using QNetworkCookieJar::deleteCookie;
bool deleteCookies(const QString &url = QString());
void enable();
void disable();
bool isEnabled() const;
public slots:
void addCookie(const QVariantMap &cookie);
bool addCookieFromMap(const QVariantMap &cookie, const QString &url = QString());
bool addCookiesFromMap(const QVariantList &cookiesList, const QString &url = QString());
QVariantList cookiesToMap(const QString &url = QString()) const;
QVariantMap cookieToMap(const QString &name, const QString &url = QString()) const;
bool deleteCookie(const QString &name, const QString &url = QString());
void clearCookies();
void close();
private slots:
bool purgeExpiredCookies();
bool purgeSessionCookies();
void save();
void load();
private:
bool contains(const QNetworkCookie &cookie) const;
private:
QSettings *m_cookieStorage;
bool m_enabled;
}; };
#endif // COOKIEJAR_H #endif // COOKIEJAR_H

View File

@ -1,199 +0,0 @@
/*
This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "crashdump.h"
#include <QtGlobal>
#include <QString>
#include <QByteArray>
#include <exception>
#include <cstdlib>
#ifdef Q_OS_LINUX
#include "client/linux/handler/exception_handler.h"
#define HAVE_BREAKPAD
#define EHC_EXTRA_ARGS true
#define MDC_PATH_ARG const char*
#define MDC_EXTRA_ARGS void*
#endif
#ifdef Q_OS_MAC
#include "client/mac/handler/exception_handler.h"
#define HAVE_BREAKPAD
#define EHC_EXTRA_ARGS true, NULL
#define MDC_PATH_ARG const char*
#define MDC_EXTRA_ARGS void*
#endif
#ifdef Q_OS_WIN32
#include "client/windows/handler/exception_handler.h"
#define HAVE_BREAKPAD
#define EHC_EXTRA_ARGS BreakpadEH::HANDLER_ALL
#define MDC_PATH_ARG const wchar_t*
#define MDC_EXTRA_ARGS void*, EXCEPTION_POINTERS*, MDRawAssertionInfo*
#endif
#ifdef HAVE_BREAKPAD
// This is not just 'using google_breakpad::ExceptionHandler' because
// one of the headers included by exception_handler.h on MacOS defines
// a typedef name 'ExceptionHandler' in the global namespace, and
// (apparently) a using-directive doesn't completely mask that.
typedef google_breakpad::ExceptionHandler BreakpadEH;
#ifdef Q_OS_WIN32
// qgetenv doesn't handle environment variables containing Unicode
// characters very well. Breakpad-for-Windows works exclusively with
// Unicode paths anyway, so we use the native API to retrieve %TEMP%
// as Unicode. This code based upon qglobal.cpp::qgetenv.
static QString
q_wgetenv(const wchar_t *varName)
{
size_t requiredSize;
_wgetenv_s(&requiredSize, 0, 0, varName);
if (requiredSize == 0 || requiredSize > size_t(INT_MAX / sizeof(wchar_t)))
return QString();
// Unfortunately it does not appear to be safe to pass QString::data()
// to a Windows API that expects wchar_t*. QChar is too different.
// We have to employ a scratch buffer. Limiting the length to
// INT_MAX / sizeof(wchar_t), above, ensures that the multiplication
// here cannot overflow.
wchar_t *buffer = (wchar_t *)malloc(requiredSize * sizeof(wchar_t));
if (!buffer)
return QString();
// requiredSize includes the terminating null, which we don't want.
// The range-check above also ensures that the conversion to int here
// does not overflow.
_wgetenv_s(&requiredSize, buffer, requiredSize, varName);
Q_ASSERT(buffer[requiredSize-1] == L'\0');
QString ret = QString::fromWCharArray(buffer, int(requiredSize - 1));
free(buffer);
return ret;
}
#endif
//
// Crash messages.
//
#ifdef Q_OS_WIN32
#define CRASH_DUMP_FMT "%ls\\%ls.dmp"
#define CRASH_DIR_FMT "%%TEMP%% (%ls)"
#else
#define CRASH_DUMP_FMT "%s/%s.dmp"
#define CRASH_DIR_FMT "$TMPDIR (%s)"
#endif
#define CRASH_MESSAGE_BASE \
"PhantomJS has crashed. Please read the crash reporting guide at\n" \
"<http://phantomjs.org/crash-reporting.html> and file a bug report at\n" \
"<https://github.com/ariya/phantomjs/issues/new>.\n"
#define CRASH_MESSAGE_HAVE_DUMP \
CRASH_MESSAGE_BASE \
"Please attach the crash dump file:\n " CRASH_DUMP_FMT "\n"
#define CRASH_MESSAGE_NO_DUMP \
CRASH_MESSAGE_BASE \
"Unfortunately, no crash dump is available.\n" \
"(Is " CRASH_DIR_FMT " a directory you cannot write?)\n"
static bool minidumpCallback(MDC_PATH_ARG dump_path,
MDC_PATH_ARG minidump_id,
MDC_EXTRA_ARGS,
bool succeeded)
{
if (succeeded)
fprintf(stderr, CRASH_MESSAGE_HAVE_DUMP, dump_path, minidump_id);
else
fprintf(stderr, CRASH_MESSAGE_NO_DUMP, dump_path);
return succeeded;
}
static BreakpadEH *initBreakpad()
{
// On all platforms, Breakpad can be disabled by setting the
// environment variable PHANTOMJS_DISABLE_CRASH_DUMPS to any
// non-empty value. This is not a command line argument because
// we want to initialize Breakpad before parsing the command line.
if (!qEnvironmentVariableIsEmpty("PHANTOMJS_DISABLE_CRASH_DUMPS"))
return 0;
// Windows and Unix have different conventions for the environment
// variable naming the directory that should hold scratch files.
#ifdef Q_OS_WIN32
std::wstring dumpPath(L".");
QString varbuf = q_wgetenv(L"TEMP");
if (!varbuf.isEmpty())
dumpPath = varbuf.toStdWString();
#else
std::string dumpPath("/tmp");
QByteArray varbuf = qgetenv("TMPDIR");
if (!varbuf.isEmpty())
dumpPath = varbuf.constData();
#endif
return new BreakpadEH(dumpPath, NULL, minidumpCallback, NULL,
EHC_EXTRA_ARGS);
}
#else // no HAVE_BREAKPAD
#define initBreakpad() NULL
#endif
// Qt, QtWebkit, and PhantomJS mostly don't make use of C++ exceptions,
// so in the rare cases where an exception does get thrown, it tends
// to pass all the way up the stack and cause the C++ runtime to call
// std::terminate(). The default std::terminate() handler in some
// C++ runtimes tries to print details of the exception or maybe even
// a stack trace. Breakpad does a better job of this.
//
// Worse, if the exception is bad_alloc, thrown because we've run into
// a system-imposed hard upper limit on memory allocation, a clever
// terminate handler like that may itself perform more memory allocation,
// which will throw another bad_alloc, and cause a recursive call to
// terminate. In some cases this may happen several times before the
// process finally dies.
//
// Short-circuit all this mess by forcing the terminate handler to
// be plain old std::abort, which will invoke Breakpad if it's active
// and crash promptly if not.
CrashHandler::CrashHandler()
: old_terminate_handler(std::set_terminate(std::abort)),
eh(initBreakpad())
{}
CrashHandler::~CrashHandler()
{
delete eh;
std::set_terminate(old_terminate_handler);
}

View File

@ -1,49 +0,0 @@
/*
This file is part of the PhantomJS project from Ofi Labs.
Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CRASHDUMP_H
#define CRASHDUMP_H
namespace google_breakpad {
class ExceptionHandler;
}
class CrashHandler
{
public:
CrashHandler();
~CrashHandler();
private:
void (*old_terminate_handler)();
google_breakpad::ExceptionHandler *eh;
};
#endif // CRASHDUMP_H

Some files were not shown because too many files have changed in this diff Show More