mirror of https://github.com/vitalif/phantomjs
Compare commits
504 Commits
Author | SHA1 | Date |
---|---|---|
Vitaliy Filippov | 2c0316f07d | |
Vitaliy Filippov | cef6c84e00 | |
Vitaliy Filippov | f37c7e29e0 | |
Ariya Hidayat | d3cd0599c8 | |
Ariya Hidayat | a2912c216d | |
Ariya Hidayat | 0352ddb502 | |
Paul Millar | 05d7e3e77d | |
Andrey Plotnikov | cf246eb853 | |
Jacek Migdal | 806da21e2b | |
Jacek Migdal | 32d20b4aa7 | |
Jacek Migdal | 0bf14a3d0c | |
Jacek Migdal | 3b8282cb2e | |
Zack Weinberg | 50ae50e871 | |
Vitaly Slobodin | a65a487d99 | |
Vitaly Slobodin | 12870d90ad | |
Ariya Hidayat | eddb0db1d2 | |
Ariya Hidayat | 619225b1da | |
Ariya Hidayat | ce66f290c3 | |
Ariya Hidayat | 2cbb695490 | |
Ariya Hidayat | bad1fee468 | |
Ariya Hidayat | b68d9467a7 | |
Ariya Hidayat | a5f0ff7433 | |
Ariya Hidayat | 7e8986cd3e | |
Ariya Hidayat | 7bcb9a8725 | |
Ariya Hidayat | 5768b705a0 | |
Ariya Hidayat | 105bcb32a9 | |
Ariya Hidayat | 51d5835942 | |
Ariya Hidayat | 72c53b5476 | |
Ariya Hidayat | 639e09bd3a | |
Ariya Hidayat | 7c61f8857d | |
Ariya Hidayat | b36da277e9 | |
Ariya Hidayat | 9c7753189f | |
Zack Weinberg | 4d4aa42c94 | |
Zack Weinberg | 3d4f89b41e | |
Ariya Hidayat | 2e8f0e8f68 | |
Zack Weinberg | 767494dc35 | |
Zack Weinberg | 4d60e9450d | |
Zack Weinberg | 2b2d52e343 | |
Zack Weinberg | 1adbbe4830 | |
Ariya Hidayat | 83f4baf8c6 | |
Ariya Hidayat | f85238322e | |
Ariya Hidayat | 3b972fbfbb | |
Ariya Hidayat | dca6f77a36 | |
Ariya Hidayat | d52df7a1cf | |
Ariya Hidayat | f95ef19fcb | |
Ariya Hidayat | f29b76bb7f | |
Ariya Hidayat | 804f61bcf5 | |
Ariya Hidayat | f20835735c | |
Ariya Hidayat | 2c10f6095b | |
Ariya Hidayat | 784a06c3a7 | |
Vitaly Slobodin | 235d072db5 | |
Zack Weinberg | ab2e4788c6 | |
Ariya Hidayat | e8598a849e | |
Ariya Hidayat | 729f12c7d8 | |
Ariya Hidayat | c883a80caf | |
Ariya Hidayat | 3274001e07 | |
Ariya Hidayat | 6c6059fd91 | |
Zack Weinberg | f4eb3645f2 | |
Zack Weinberg | e8cddbfe7b | |
Ariya Hidayat | 488a130799 | |
Zack Weinberg | 9afcf3c23d | |
Zack Weinberg | c64b1c078c | |
Zack Weinberg | 0693711b0a | |
Ariya Hidayat | a680ed41b2 | |
Zack Weinberg | 858972e7ef | |
Zack Weinberg | 2ab57b875e | |
Ariya Hidayat | 2c0364b082 | |
Ariya Hidayat | 7317724723 | |
Ariya Hidayat | 4bc91b1bf0 | |
Ariya Hidayat | 3a9fc49679 | |
Ariya Hidayat | 0cd703085d | |
Ariya Hidayat | 5ec80ff09c | |
Ariya Hidayat | 8cabfed4b3 | |
Ariya Hidayat | 51c8c9cc62 | |
Milian Wolff | 9e832a1569 | |
Vitaliy Slobodin | 22e74549a3 | |
Marcel Duran | 3be7005a17 | |
Zack Weinberg | 1e7829db97 | |
Ariya Hidayat | b5626bff97 | |
Milian Wolff | a9809996b1 | |
Ariya Hidayat | 77c47044cf | |
Artem | 5e018bd650 | |
Ariya Hidayat | a6f6130767 | |
Zack Weinberg | 26934f32a9 | |
Zack Weinberg | b524d53d36 | |
Zack Weinberg | 6fb347d296 | |
Zack Weinberg | e6b67bddc5 | |
Zack Weinberg | 7102e87bf4 | |
Zack Weinberg | 406f736e14 | |
Craig Teegarden | f245d3ed22 | |
Zack Weinberg | 9bbff95a57 | |
Ariya Hidayat | 2681756efd | |
Zack Weinberg | 30a4a01bca | |
Zack Weinberg | 8d23afb782 | |
Zack Weinberg | 4246ed0533 | |
Zack Weinberg | e9e6f30e69 | |
Zack Weinberg | 9254f6855e | |
Zack Weinberg | 615f33c9c4 | |
Zack Weinberg | 5b5e63af23 | |
Zack Weinberg | b81d7aec0a | |
Vitaliy Slobodin | 0c8eb88c15 | |
John Gozde | de2a19da9c | |
Petteri Räty | a86ae1948c | |
Ariya Hidayat | fbe8eefc29 | |
Ariya Hidayat | 25ddf566e7 | |
Mike McQuaid | 0a8b13403f | |
Ariya Hidayat | f532b73cb2 | |
Ariya Hidayat | 3c9cf90d12 | |
Ariya Hidayat | eca1081b6e | |
Milian Wolff | aa0300607c | |
Milian Wolff | 4bf75c50b0 | |
Ariya Hidayat | 3136898976 | |
Ariya Hidayat | bbdaa9e884 | |
Vitaly Slobodin | 28045aaf6b | |
Vitaly Slobodin | 2ebe5fd8a8 | |
Milian Wolff | 0c55c0a9e0 | |
Milian Wolff | 514972db80 | |
Ariya Hidayat | 48fabe0646 | |
Ariya Hidayat | 64b6fd2f4b | |
Ariya Hidayat | 20a73a6d40 | |
Ariya Hidayat | dc8d3c7052 | |
Ariya Hidayat | 13e788f2c1 | |
Ariya Hidayat | 53edf23e8f | |
Ariya Hidayat | e2682edf48 | |
Ariya Hidayat | 1bf54d8776 | |
Ariya Hidayat | 125c8d3d49 | |
Vitaliy Slobodin | cf12fc4a23 | |
Vitaly Slobodin | d10b8dc583 | |
Vitaly Slobodin | 4393627a80 | |
Vitaliy Slobodin | c166662c0d | |
Vitaly Slobodin | 05277b2d84 | |
Artem Koshelev | 00afabc993 | |
Zack Weinberg | d65601fe90 | |
Sven Eckelmann | 13ad8a134e | |
Sven Eckelmann | b4e295cc97 | |
Sven Eckelmann | 800fbe8452 | |
Ivan De Marino | 62fbad4814 | |
Eric Heydenberk | ccdd86f47f | |
Artem Koshelev | 3c5302d567 | |
Thomas Schlage | 9da842df2b | |
Dmitry Mazuro | 4d2fedf243 | |
Richard Harris | 7d3f3f0819 | |
Ivan De Marino | e9c77252ee | |
petercoles | d831dea8c8 | |
Ivan De Marino | e9a47eddee | |
Ivan De Marino | 8f685818aa | |
Ivan De Marino | 77b5de1362 | |
Ivan De Marino | ba5998f74e | |
Ivan De Marino | 1797c146f1 | |
Adrian Chung | 300f28cc5d | |
Rikke Simonsen | bda8838698 | |
paulo alem | 15d0636b70 | |
Ivan De Marino | 9940e8b25e | |
Ivan De Marino | 0765f9bdd2 | |
Ivan De Marino | 06672a03aa | |
Ivan De Marino | 056aa50c19 | |
Trevor North | 18b8a4d444 | |
Ariya Hidayat | 42b3a86bcd | |
Joseph Rollinson | 244cf251cd | |
Vasyl Vavrychuk | 3ae9d38d40 | |
Ashish Kulkarni | 3ed2f68909 | |
Ariya Hidayat | 6b45113cfe | |
Ariya Hidayat | 266ef0da59 | |
Ariya Hidayat | 46eb122ba3 | |
Ariya Hidayat | e533587107 | |
Ariya Hidayat | 23f31391af | |
Ariya Hidayat | e897ab8bb2 | |
Ariya Hidayat | aa388a05ef | |
James McParlane | dca15d7ff6 | |
Ivan De Marino | 9bfe22b428 | |
Ivan De Marino | a9a219e74b | |
Ariya Hidayat | b70ff8929c | |
Ariya Hidayat | b67866b612 | |
Ivan De Marino | 9b0132712b | |
Brad Daily | 7659f2551c | |
Ben Cox | 18b342d3e7 | |
Ivan De Marino | e1ae72a866 | |
Brad Daily | e40ebb93d7 | |
Bojan Markovic | 54c1611801 | |
Aaron Stone | 3d80670e22 | |
Aaron Stone | 176d435901 | |
Oleg Plakhotniuk | f4128d7ede | |
Aaron Stone | 4ca640c5e7 | |
Aaron Stone | 394e2f8699 | |
Francisco de Borja Lopez Río | d5eaf41063 | |
Martin Popelak | 8f8de58752 | |
Vitaliy Slobodin | efcc6c7861 | |
Vasyl Vavrychuk | c8e4215097 | |
Vitaliy Slobodin | 6a01a8dece | |
Max Edmands | 94e63bfa04 | |
Vitaliy Slobodin | 23df8811a1 | |
Mike McQuaid | fe6a967bad | |
Ariya Hidayat | 2691540711 | |
hexid | b1e181176e | |
Morgan Roderick | 7431cbf229 | |
Ivan De Marino | 73bb560840 | |
Dmitry Parshin | 8114d44a28 | |
Richard Harris | 4989445e71 | |
Vitaliy Slobodin | 1a25383307 | |
Vitaliy Slobodin | b1cb3a00bd | |
Vitaliy Slobodin | fdec25ac4c | |
Ivan De Marino | 6ba33cbcab | |
Ivan De Marino | c466d8aeef | |
Ariya Hidayat | edf2d90a11 | |
Ariya Hidayat | 644f379588 | |
Ariya Hidayat | b4c4429e86 | |
Ariya Hidayat | 01587211cb | |
Ariya Hidayat | 44c3d6080d | |
Ariya Hidayat | 9f1f56bc3b | |
Ariya Hidayat | f57fa468ab | |
Bryan Bishop | fb8edb7c72 | |
Vitaliy Slobodin | 4d916971b3 | |
Vitaliy Slobodin | 639e8c85b2 | |
Ariya Hidayat | fa238856f9 | |
Ariya Hidayat | 9aa0705d3c | |
Alex Alvarez | 3bd7a3dfa6 | |
Alex Alvarez | 2f851086e4 | |
Jan Minar | 320608662d | |
Vitaliy Slobodin | 0726a8e2bb | |
Ariya Hidayat | 706e928e78 | |
Eric Heydenberk | f1472b54d1 | |
Eric Heydenberk | dff8ce2526 | |
Vitaliy Slobodin | 5528d75c9d | |
Vitaliy Slobodin | f8e79fb8c6 | |
Ivan De Marino | 23515550d5 | |
Andrew Galloni | 3ae632e704 | |
Robin Helgelin | f72f2962d1 | |
execjosh | 39bec1ce17 | |
execjosh | b159144a48 | |
execjosh | 3d874d9e0d | |
Ariya Hidayat | c07a2efa33 | |
execjosh | f8a905c8b0 | |
execjosh | d925a510d9 | |
Vitaliy Slobodin | 3edcabef9f | |
Vitaliy Slobodin | 78242e5d6c | |
execjosh | 36ab7194e4 | |
execjosh | 8042f3b92c | |
Ariya Hidayat | 24078b56c7 | |
Ariya Hidayat | 0bc2eb418e | |
Pavel | fcdd274f2e | |
Vitaliy Slobodin | 47dc82681d | |
Vitaliy Slobodin | 9ca45ed62e | |
Ariya Hidayat | da71c5fbdd | |
Ariya Hidayat | bb1a407e8d | |
Ariya Hidayat | bda3355060 | |
Ariya Hidayat | 08bbd324d1 | |
Ariya Hidayat | 9ef69005d4 | |
DjinnS | b5345bc133 | |
Jonathan Wilkins | 9f9053ec44 | |
Tom Aizenberg | 52883ced68 | |
Laurent Jouanneau | b16a5348a9 | |
Ariya Hidayat | 63dc1e2c8f | |
Max Desyatov | 0147fcbc07 | |
Max Desyatov | 357dbf46d2 | |
Ivan De Marino | 6989188fa0 | |
Ivan De Marino | 78d90641df | |
Ivan De Marino | 005db037cf | |
Ivan De Marino | 9740990990 | |
Ariya Hidayat | 9af1f09f58 | |
Ariya Hidayat | 9df52bb752 | |
Ariya Hidayat | d42c4002c4 | |
Ariya Hidayat | 1c80f7306e | |
Alexander James Phillips | 5d6b8240f7 | |
Dody Suria Wijaya | 58566bbe76 | |
Vitaliy Slobodin | fed209c546 | |
Vitaliy Slobodin | 212e4b7cd9 | |
Capi Etheriel | ca27a8eab1 | |
Ivan De Marino | 04368c6af8 | |
Ariya Hidayat | fef171e14f | |
Juliusz Gonera | 2d42b52c67 | |
Julian Szulc | 1e5638678d | |
James M. Greene | 1604b4d6c1 | |
James M. Greene | e6ae4cca6d | |
Ariya Hidayat | 43ab20d4ae | |
Jeff Boulter | ec6b242a9f | |
Matthew Barr | fe78d4d9fe | |
bongole | 0734811514 | |
Vitaliy Slobodin | 6d81933941 | |
Ariya Hidayat | 797b47ae81 | |
Ivan De Marino | 7e7325c0f5 | |
Ivan De Marino | 12bb24f418 | |
Ivan De Marino | 4dec091ac2 | |
Vitaliy Slobodin | a7a3928af9 | |
James M. Greene | 1a487d2bbf | |
James M. Greene | 360dc3a30c | |
Ariya Hidayat | 34ed1169f3 | |
Ariya Hidayat | d58eae54d7 | |
Ariya Hidayat | aaca7774c9 | |
Oleg Pudeyev | 26e51f4b8a | |
Oleg Pudeyev | c3c17a5364 | |
Oleg Pudeyev | 52e67c5aa7 | |
Ariya Hidayat | 69bf72bca7 | |
Ariya Hidayat | aa907343b9 | |
Vitaliy Slobodin | 09e929d599 | |
Vitaliy Slobodin | fd700b0702 | |
execjosh | d906bc3819 | |
Ariya Hidayat | d0fe6864a9 | |
Ariya Hidayat | 2a2b6e455c | |
execjosh | f52044cd31 | |
Ariya Hidayat | 83e8152dd6 | |
Ariya Hidayat | 98ce8922c6 | |
execjosh | 836719f72e | |
execjosh | 6bc3a93118 | |
execjosh | f6c87221a7 | |
Ken Collins | e3517f108a | |
Ariya Hidayat | d7652abc11 | |
Ariya Hidayat | 3f42fb230f | |
Ariya Hidayat | fac15407e8 | |
execjosh | 3458d4d507 | |
execjosh | 03500e6b55 | |
Vitaliy Slobodin | fc7a5b7b9f | |
Ariya Hidayat | c33b916cfb | |
Ivan De Marino | d998c59358 | |
Ariya Hidayat | 8836398825 | |
Sebastian Krzyszkowiak | f70a6ab4ee | |
Sebastian Krzyszkowiak | 78e72312dc | |
Ivan De Marino | 302050e0ce | |
Ariya Hidayat | 9a8b84a293 | |
Ariya Hidayat | 9c0888d54d | |
David Burrows | 15fe514025 | |
qubird | f6a91a8813 | |
Ariya Hidayat | daae36c3ba | |
fastclemmy | 3b2a1c27a1 | |
fastclemmy | 8f14ef027e | |
Ivan De Marino | 027aa93b18 | |
Ivan De Marino | f5652e5110 | |
Vitaliy Slobodin | 04b74f99fd | |
Jon Leighton | eb3c9caa6e | |
Jon Leighton | 66ab9a1113 | |
Vitaliy Slobodin | 5eb0f64e6b | |
James M. Greene | 31dd714a22 | |
Jon Leighton | 487fbf3035 | |
James M. Greene | e8380e42d7 | |
James M. Greene | 4c285c419d | |
James M. Greene | f61635f2d4 | |
James M. Greene | c9f9b5a14b | |
Ariya Hidayat | a777797942 | |
Jan Schaumann | 94e1f40ad9 | |
Milian Wolff | fd653fe61f | |
Ivan De Marino | 4caa71a6b7 | |
Ivan De Marino | ffa9fab316 | |
Ivan De Marino | 2dcccc8968 | |
Vitaliy Slobodin | 63dd36205f | |
Milian Wolff | 2d778f687e | |
Milian Wolff | 5c87852c32 | |
Ivan De Marino | 9ba13ba989 | |
Vitaliy Slobodin | 40a14b72b1 | |
Ariya Hidayat | 559afcd4e8 | |
Ariya Hidayat | 59dbd77ef5 | |
Ariya Hidayat | 7b84e43a10 | |
James M. Greene | 280305797e | |
Ivan De Marino | eadb03a978 | |
Ivan De Marino | 1fa9c04845 | |
Ivan De Marino | 7c7d1f961c | |
Ivan De Marino | 4c5e96d17f | |
Ivan De Marino | b113993314 | |
Ivan De Marino | 3fe308bf8c | |
Ivan De Marino | 9cf6cbe818 | |
Ivan De Marino | d5eb657ecc | |
Ivan De Marino | e31528adfe | |
Ivan De Marino | 60ced2ccb6 | |
Ivan De Marino | 3f874067f5 | |
Jim Evans | 42bf8b36d8 | |
Jim Evans | 395af9cada | |
Jim Evans | c2df526110 | |
Ivan De Marino | f2628b32fe | |
Vitaliy Slobodin | f3d920908a | |
Vitaliy Slobodin | b834f2a590 | |
Jim Evans | 380c56e672 | |
Jim Evans | f402a8d975 | |
Johan Sköld | b7ca845327 | |
Vitallium | 35c1971595 | |
Ivan De Marino | e521dc16e7 | |
Ariya Hidayat | 4f17d94afd | |
Vitaliy Slobodin | 2403c00e59 | |
Ariya Hidayat | 8ba3c52d50 | |
Ariya Hidayat | 63e06cbcbf | |
Ariya Hidayat | f29827f201 | |
Ariya Hidayat | 9a0f8d0e3a | |
Ariya Hidayat | 703a4d14e4 | |
Ariya Hidayat | 5e91e5355d | |
Ariya Hidayat | b9e1ced36f | |
Ariya Hidayat | e5b040ed1a | |
Ariya Hidayat | 7921cb00e1 | |
Ariya Hidayat | 58f970a30d | |
Ivan De Marino | 32e23339bd | |
Ariya Hidayat | 8ab4209e0f | |
Ian Oxley | e85140e80f | |
Ian Oxley | 1dbd371540 | |
Ian Oxley | be767ee983 | |
Ian Oxley | 2662d5875b | |
Shawn Krisman | ecda224233 | |
Ariya Hidayat | 9f8056334e | |
Ariya Hidayat | 18ca114111 | |
Ariya Hidayat | cab2635e66 | |
Jon Leighton | e7c37d4a2b | |
Ariya Hidayat | 0d63e9c0a8 | |
Ariya Hidayat | 9a25f31715 | |
Ivan De Marino | 26583740c8 | |
Ivan De Marino | e5ebcf90df | |
Ivan De Marino | b1185cd22a | |
Ariya Hidayat | 64fa65edaf | |
Ariya Hidayat | 22c8e5610f | |
Ariya Hidayat | 2c4e95ad3e | |
Zhaolong | 62584ce7d4 | |
Ryan Cumming | 23fe144a39 | |
Harry Waye | 4f7df7073a | |
Ariya Hidayat | e33bfedb9a | |
Vitaliy Slobodin | af49821cf4 | |
Ariya Hidayat | 1558c07a76 | |
Jakub | 1ab5c66df8 | |
Ariya Hidayat | 795d067f4f | |
Ariya Hidayat | 27d6f908e4 | |
Ariya Hidayat | 3479beb894 | |
Ivan De Marino | bd21373732 | |
Ivan De Marino | 5cb68a1585 | |
Ivan De Marino | 6217d6b960 | |
Ivan De Marino | 5b25c6feea | |
Ivan De Marino | 70d1ff8392 | |
Ivan De Marino | a65be87642 | |
Ivan De Marino | 733d21042b | |
Ivan De Marino | 27dc699919 | |
Ariya Hidayat | 0a0fa917ff | |
Ariya Hidayat | 2e96c7c359 | |
Ariya Hidayat | 93686d8306 | |
Ariya Hidayat | 4d95cf2783 | |
Ivan De Marino | b3a0eeff87 | |
Ariya Hidayat | 3b161b914a | |
Ariya Hidayat | 08bd78f7b9 | |
Ariya Hidayat | 094d3381e8 | |
Ariya Hidayat | a5eb729c9d | |
Ariya Hidayat | 614259df0e | |
neraliu | 4e1735c899 | |
Jim Evans | 55a660f35a | |
Jim Evans | e7499d0b33 | |
Jim Evans | d2c3a078ab | |
Jim Evans | b8c28cd42a | |
Ilya Grigorik | fafa01d6ba | |
Jim Evans | 11e8bb1fcd | |
Ariya Hidayat | 0ea2aa0c35 | |
Ariya Hidayat | ae7f39b4ef | |
Ariya Hidayat | be5fe36b98 | |
Jon Leighton | 1d23a11bd7 | |
Alessandro Portale | f6a26033c9 | |
Ariya Hidayat | b3b3578a1f | |
Ariya Hidayat | c191a00277 | |
Ariya Hidayat | c4de69fcaa | |
Ariya Hidayat | 9ca88ea681 | |
Ariya Hidayat | 5f88a6b95f | |
Ariya Hidayat | c3b30ac136 | |
Ariya Hidayat | 5bafcd4f8c | |
Ariya Hidayat | d57c8cc0f7 | |
Ariya Hidayat | 89593bd63d | |
Ariya Hidayat | b11c6de9ab | |
Ariya Hidayat | 5f1a636583 | |
Ariya Hidayat | 1602394efb | |
Ariya Hidayat | 281d291cfb | |
Ariya Hidayat | 8d0a0b28ed | |
Ariya Hidayat | b2af7929fb | |
Jon Leighton | 31157fbb98 | |
Ivan De Marino | 0ce4dcd719 | |
Ivan De Marino | 8d2384fd29 | |
Ariya Hidayat | b54612bf68 | |
Milian Wolff | 1b2a84df9c | |
Milian Wolff | bf6fd70c88 | |
Nicolas Perriault | 11044a8fbd | |
Nicolas Perriault | 25c48d2ed5 | |
Ivan De Marino | 5f0ed88a90 | |
Ivan De Marino | ea83351da3 | |
Ivan De Marino | f869e8408d | |
Ivan De Marino | 0b04f0a672 | |
Ivan De Marino | 008019ae10 | |
Ariya Hidayat | 6ce27bbefa | |
Ariya Hidayat | 0632658afe | |
Juliusz Gonera | 0ce2974ff7 | |
Juliusz Gonera | a02cf63984 | |
Juliusz Gonera | 2555cb448c | |
Juliusz Gonera | 15204cb8ae | |
Juliusz Gonera | 5015dbec2a | |
Juliusz Gonera | 893108ad11 | |
Juliusz Gonera | ba3eabb29e | |
Juliusz Gonera | b22e995337 | |
Juliusz Gonera | fdd727e5da | |
Juliusz Gonera | 70925a873b | |
Juliusz Gonera | 2cfffa029a | |
Juliusz Gonera | 945a4c0f8c | |
Jon Leighton | 7892ff3b3e | |
Jon Leighton | 1bcacde12c | |
Ariya Hidayat | c0e8aa996f | |
Ariya Hidayat | 940e0089ba | |
Jon Leighton | 95186c653e | |
Ariya Hidayat | 13d929f3dc | |
Ariya Hidayat | 516c70b4fd | |
Ivan De Marino | 7409ec62d0 | |
Ariya Hidayat | df0f70f0b7 | |
Jon Leighton | e05c887c96 | |
Jon Leighton | 5f510967db | |
Jon Leighton | a714bab04b | |
Jon Leighton | 13c1c164d9 | |
Ariya Hidayat | 59f6bb9496 | |
Ariya Hidayat | ca7e8bb8f3 | |
Ivan De Marino | 5ad891c2f3 | |
Ariya Hidayat | 3da512370e | |
Ariya Hidayat | 91d31ffe9e |
|
@ -2,7 +2,6 @@
|
|||
*.pro.user*
|
||||
*.xcodeproj
|
||||
Makefile*
|
||||
bin/phantomjs
|
||||
*~
|
||||
*.moc
|
||||
moc_*
|
||||
|
@ -11,15 +10,52 @@ qrc_*
|
|||
*.swp
|
||||
*.pyc
|
||||
*.a
|
||||
debian/*.debhelper
|
||||
debian/files
|
||||
debian/*.log
|
||||
debian/*.substvars
|
||||
debian/*/
|
||||
/debian/*.debhelper
|
||||
/debian/files
|
||||
/debian/*.log
|
||||
/debian/*.substvars
|
||||
/debian/*/
|
||||
/deploy/qt-*.tar.gz
|
||||
/deploy/Qt-*
|
||||
/symbols
|
||||
/src/qt/qtc-debugging-helper
|
||||
/src/phantomjs_plugin_import.cpp
|
||||
|
||||
# ignore ctags
|
||||
/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/
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
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
|
|
@ -0,0 +1,88 @@
|
|||
# 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
|
||||
|
174
ChangeLog
174
ChangeLog
|
@ -1,12 +1,181 @@
|
|||
Please see also http://code.google.com/p/phantomjs/wiki/ReleaseNotes.
|
||||
Please see also http://phantomjs.org/releases.html.
|
||||
|
||||
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"
|
||||
|
||||
New features
|
||||
|
||||
* Added support for passing arguments to WebPage's evaluate (issue 132)
|
||||
* Added callbacks for JavaScript onConfirm and onAlert (issue 133)
|
||||
* Added callbacks for JavaScript onConfirm and onPrompt (issue 133)
|
||||
* 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 support for header footer when printing the page (issue 410, 512)
|
||||
* Added headers support in the loading request (issue 452)
|
||||
|
@ -32,6 +201,7 @@ Please see also http://code.google.com/p/phantomjs/wiki/ReleaseNotes.
|
|||
* Fixed example scripts to exit with the right exit code (issue 544)
|
||||
* Fixed build failure with glib 2.31.0+ (issue 559)
|
||||
* Fixed error handler failures in some cases (issue 589)
|
||||
* Fixed Twitter-related examples to work with the new site (issue 609)
|
||||
|
||||
2012-03-20: Version 1.5.0 "Ghost Flower"
|
||||
|
||||
|
|
38
README.md
38
README.md
|
@ -1,27 +1,24 @@
|
|||
# [PhantomJS](http://phantomjs.org) - Scriptable Headless WebKit
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The latest [stable release](http://code.google.com/p/phantomjs/wiki/ReleaseNotes) is version 1.5 (codenamed "Ghost Flower"). Follow the official Twitter stream [@HeadlessPhantom](http://twitter.com/HeadlessPhantom) to get the frequent development updates.
|
||||
The latest [stable release](http://phantomjs.org/release-2.0.html) is version 2.0.
|
||||
|
||||
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).
|
||||
**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.
|
||||
|
||||
## Use Cases
|
||||
|
||||
- **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.
|
||||
|
||||
- **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.
|
||||
|
||||
- **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.
|
||||
- **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.
|
||||
- **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.
|
||||
- **Network monitoring**. Automate performance analysis, track [page loading](http://phantomjs.org/network-monitoring.html) and export as standard HAR format.
|
||||
|
||||
## Features
|
||||
|
||||
- **Multiplatform**, available on major operating systems: Windows, Mac OS X, Linux, other Unices.
|
||||
- **Fast and native implementation** of web standards: DOM, CSS, JavaScript, Canvas, SVG. No emulation!
|
||||
- **Pure headless (X11) on Linux**, ideal for continuous integration systems. Also runs on Amazon EC2.
|
||||
- **Easy to install**: [Download](http://code.google.com/p/phantomjs/wiki/Installation), unpack, and start having fun in just 5 minutes.
|
||||
- **Multiplatform**, available on major operating systems: Windows, Mac OS X, Linux, and other Unices.
|
||||
- **Fast and native implementation** of web standards: DOM, CSS, JavaScript, Canvas, and SVG. No emulation!
|
||||
- **Pure headless (no X11) on Linux**, ideal for continuous integration systems. Also runs on Amazon EC2, Heroku, and Iron.io.
|
||||
- **Easy to install**: [Download](http://phantomjs.org/download.html), unpack, and start having fun in just 5 minutes.
|
||||
|
||||
## Ecosystem
|
||||
|
||||
|
@ -32,11 +29,18 @@ 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.
|
||||
- [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.
|
||||
- [Mocha-PhantomJS](https://github.com/metaskills/mocha-phantomjs) run Mocha tests using PhantomJS.
|
||||
|
||||
and many others [companion projects](http://code.google.com/p/phantomjs/wiki/WhoUsesPhantomJS).
|
||||
and many others [related projects](http://phantomjs.org/related-projects.html).
|
||||
|
||||
## Questions?
|
||||
|
||||
- Explore the complete [documentation](http://code.google.com/p/phantomjs/wiki/PhantomJS?tm=6).
|
||||
- Read tons of [user articles](http://code.google.com/p/phantomjs/wiki/ExternalArticles) on using PhantomJS.
|
||||
- Explore the complete [documentation](http://phantomjs.org/documentation/).
|
||||
- Read tons of [user articles](http://phantomjs.org/buzz.html) on using PhantomJS.
|
||||
- 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.
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
@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
|
64
build.sh
64
build.sh
|
@ -1,9 +1,7 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
|
||||
QT_CFG=''
|
||||
|
||||
COMPILE_JOBS=1
|
||||
MAKEFLAGS_JOBS=''
|
||||
|
||||
|
@ -18,45 +16,67 @@ elif [[ $OSTYPE = darwin* ]]; then
|
|||
# We only support modern Mac machines, they are at least using
|
||||
# hyperthreaded dual-core CPU.
|
||||
COMPILE_JOBS=4
|
||||
elif [[ $OSTYPE == freebsd* ]]; then
|
||||
COMPILE_JOBS=`sysctl -n hw.ncpu`
|
||||
else
|
||||
CPU_CORES=`grep -c ^processor /proc/cpuinfo`
|
||||
if [[ "$CPU_CORES" -gt 1 ]]; then
|
||||
COMPILE_JOBS=$CPU_CORES
|
||||
if [[ "$COMPILE_JOBS" -gt 8 ]]; then
|
||||
# Safety net.
|
||||
COMPILE_JOBS=8
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
until [ -z "$1" ]; do
|
||||
if [[ "$COMPILE_JOBS" -gt 8 ]]; then
|
||||
# Safety net.
|
||||
COMPILE_JOBS=8
|
||||
fi
|
||||
|
||||
SILENT=
|
||||
|
||||
until [[ -z "$1" ]]; do
|
||||
case $1 in
|
||||
"--qt-config")
|
||||
shift
|
||||
QT_CFG=" $1"
|
||||
shift;;
|
||||
"--qmake-args")
|
||||
(--qmake-args)
|
||||
shift
|
||||
QMAKE_ARGS=$1
|
||||
shift;;
|
||||
"--jobs")
|
||||
(--jobs)
|
||||
shift
|
||||
COMPILE_JOBS=$1
|
||||
shift;;
|
||||
(--silent)
|
||||
SILENT=silent
|
||||
shift;;
|
||||
|
||||
"--help")
|
||||
echo "Usage: $0 [--qt-config CONFIG] [--jobs NUM]"
|
||||
echo
|
||||
echo " --qt-config CONFIG Specify extra config options to be used when configuring Qt"
|
||||
echo " --jobs NUM How many parallel compile jobs to use. Defaults to 4."
|
||||
echo
|
||||
cat <<EOF
|
||||
Usage: $0 [--jobs NUM]
|
||||
|
||||
--silent Produce less verbose output.
|
||||
--jobs NUM How many parallel compile jobs to use.
|
||||
Defaults to the number of CPU cores you have,
|
||||
with a maximum of 8.
|
||||
EOF
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unrecognised option: $1"
|
||||
echo "Unrecognised option: $1" >&2
|
||||
exit 1;;
|
||||
esac
|
||||
done
|
||||
|
||||
cd src/qt && ./preconfig.sh --jobs $COMPILE_JOBS --qt-config "$QT_CFG" && cd ../..
|
||||
src/qt/bin/qmake $QMAKE_ARGS
|
||||
UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
|
||||
UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
|
||||
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
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
phantomjs (1.3-1~git20110703) unstable; urgency=low
|
||||
|
||||
* Initial release.
|
||||
|
||||
-- Dennis Kaarsemaker <dennis@kaarsemaker.net> Sun, 03 Jul 2011 20:45:52 +0200
|
|
@ -1 +0,0 @@
|
|||
7
|
|
@ -1,24 +0,0 @@
|
|||
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.
|
|
@ -1,35 +0,0 @@
|
|||
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.
|
|
@ -1,2 +0,0 @@
|
|||
README.md
|
||||
examples
|
|
@ -1 +0,0 @@
|
|||
debian/tmp/usr/bin/phantomjs
|
|
@ -1 +0,0 @@
|
|||
python/README.md
|
|
@ -1,2 +0,0 @@
|
|||
debian/tmp/usr/bin/pyphantomjs
|
||||
debian/tmp/usr/lib
|
|
@ -1,19 +0,0 @@
|
|||
#!/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 +0,0 @@
|
|||
1.0
|
|
@ -9,11 +9,23 @@ end
|
|||
|
||||
Vagrant::Config.run do |config|
|
||||
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 = "lucid32"
|
||||
end
|
||||
|
||||
config.vm.define :x86_64 do |c|
|
||||
config.vm.define :lucid64 do |c|
|
||||
c.vm.box_url = "http://files.vagrantup.com/lucid64.box"
|
||||
c.vm.box = "lucid64"
|
||||
end
|
||||
|
@ -25,6 +37,6 @@ Vagrant::Config.run do |config|
|
|||
|
||||
# You may wish to tweak these, but be aware that you need quite a lot of
|
||||
# memory for the linking stage.
|
||||
config.vm.customize ["modifyvm", :id, "--memory", 2048]
|
||||
config.vm.customize ["modifyvm", :id, "--memory", 3072]
|
||||
config.vm.customize ["modifyvm", :id, "--cpus", 2]
|
||||
end
|
||||
|
|
|
@ -2,14 +2,16 @@
|
|||
|
||||
cd `dirname $0`/..
|
||||
|
||||
echo "Building Qt and PhantomJS in debug mode. If you have previously" \
|
||||
"built in release mode, you should run:"
|
||||
echo "Building Qt and PhantomJS with debugging symbols. If you have previously" \
|
||||
"built without debugging symbols, you should run:"
|
||||
echo
|
||||
echo " $ make clean && cd src/qt && make clean && cd ../.."
|
||||
echo " $ git clean -xdff"
|
||||
echo
|
||||
|
||||
# Build the project
|
||||
./build.sh --qt-config "-debug -webkit-debug" --qmake-args "CONFIG-=release CONFIG+=debug" || exit 1
|
||||
# This incantation will cause Qt and WebKit and PhantomJS to all build in "release"
|
||||
# mode, with compiler optimisations, but also with debug symbols. (We will strip the
|
||||
# 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
|
||||
rm deploy/*.tar.bz2 2>/dev/null
|
||||
|
@ -18,7 +20,7 @@ rm deploy/*.tar.bz2 2>/dev/null
|
|||
# Build the dump_syms program for dumping breakpad debugging symbols
|
||||
if [[ $OSTYPE = darwin* ]]; then
|
||||
pushd tools
|
||||
../src/qt/bin/qmake dump-syms-mac.pro
|
||||
../src/qt/bin/qmake dump-syms-mac.pro && make
|
||||
popd
|
||||
else
|
||||
pushd src/breakpad
|
||||
|
@ -30,13 +32,22 @@ fi
|
|||
|
||||
./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
|
||||
# binary for later use. This means that e.g. a developer on x86_64 can
|
||||
# analyse a crash dump produced by a i686 user.
|
||||
#
|
||||
# We don't yet have a process for building minidump_stackwalk on OS X
|
||||
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
|
||||
These are symbols files that can be used to analyse a crash dump
|
||||
|
@ -46,10 +57,11 @@ run:
|
|||
./minidump_stackwalk /path/to/crash.dmp .
|
||||
EOT
|
||||
|
||||
echo "$README" > symbols/README
|
||||
echo "$README" > $symbols/README
|
||||
fi
|
||||
|
||||
tar -cjf $(ls deploy/*.bz2 | sed 's/\.tar\.bz2/-symbols.tar.bz2/') symbols/
|
||||
tar -cjf deploy/$symbols.tar.bz2 $symbols
|
||||
rm -r $symbols
|
||||
|
||||
echo "PhantomJS built and packaged:"
|
||||
echo
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# usage: just run this script (after having run build.sh)
|
||||
|
@ -18,14 +18,39 @@ if [[ ! -f ../bin/phantomjs ]]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$1" = "--bundle-libs" ]]; then
|
||||
bundle_libs=1
|
||||
else
|
||||
bundle_libs=0
|
||||
fi
|
||||
|
||||
version=$(../bin/phantomjs --version | sed 's/ /-/' | sed 's/[()]//g')
|
||||
src=..
|
||||
|
||||
echo "packaging phantomjs $version"
|
||||
|
||||
if [[ $OSTYPE = darwin* ]]; then
|
||||
dest="phantomjs-$version-macosx-static"
|
||||
dest="phantomjs-$version-macosx"
|
||||
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
|
||||
echo
|
||||
echo "brandelf executable not found in current dir"
|
||||
|
@ -34,36 +59,15 @@ else
|
|||
echo "done"
|
||||
fi
|
||||
|
||||
dest="phantomjs-$version-linux-$(uname -m)-dynamic"
|
||||
fi
|
||||
libs=$(ldd $phantomjs | egrep -o "/[^ ]+ ")
|
||||
|
||||
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..."
|
||||
libld=
|
||||
for l in $(ldd $dest/bin/phantomjs | egrep -o "/[^ ]+ "); do
|
||||
if [[ "$l" != "" ]]; then
|
||||
ll=$(basename $l)
|
||||
cp $l $dest/lib/$ll
|
||||
for l in $libs; do
|
||||
ll=$(basename $l)
|
||||
cp $l $dest/lib/$ll
|
||||
|
||||
if [[ "$bundle_libs" = "1" ]]; then
|
||||
# ensure OS ABI compatibility
|
||||
./brandelf -t SVR4 $dest/lib/$ll
|
||||
if [[ "$l" == *"ld-linux"* ]]; then
|
||||
|
@ -73,37 +77,44 @@ else
|
|||
done
|
||||
echo "done"
|
||||
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..."
|
||||
# write run scripts
|
||||
mv $dest/bin/phantomjs $dest/bin/phantomjs.bin
|
||||
mv $phantomjs $phantomjs.bin
|
||||
phantomjs=$phantomjs.bin
|
||||
run=$dest/bin/phantomjs
|
||||
echo '#!/bin/sh' >> $run
|
||||
echo 'path=$(dirname $(dirname $(readlink -f $0)))' >> $run
|
||||
echo 'export LD_LIBRARY_PATH=$path/lib' >> $run
|
||||
echo 'exec $path/lib/'$libld' $path/bin/phantomjs.bin $@' >> $run
|
||||
echo 'exec $path/lib/'$libld' $phantomjs $@' >> $run
|
||||
chmod +x $run
|
||||
echo "done"
|
||||
echo
|
||||
fi
|
||||
|
||||
echo -n "creating tarball..."
|
||||
tar -cjf $dest{.tar.bz2,}
|
||||
echo -n "stripping binary and libs..."
|
||||
if [[ $OSTYPE = darwin* ]]; then
|
||||
strip -x $phantomjs
|
||||
else
|
||||
strip -s $phantomjs
|
||||
[[ -d $dest/lib ]] && strip -s $dest/lib/*
|
||||
fi
|
||||
echo "done"
|
||||
echo
|
||||
|
||||
echo "you can now deploy $dest or $dest.tar.bz2"
|
||||
echo -n "compressing binary..."
|
||||
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
|
||||
|
|
|
@ -1,7 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
apt-get update
|
||||
apt-get install -y build-essential git-core libssl-dev libfontconfig1-dev gdb binutils-gold
|
||||
export PATH=$HOME/git/bin:$PATH
|
||||
|
||||
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
|
||||
git clone git://github.com/ariya/phantomjs.git
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
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()
|
|
@ -0,0 +1,27 @@
|
|||
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)
|
|
@ -1,46 +0,0 @@
|
|||
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()
|
|
@ -1,8 +0,0 @@
|
|||
t = 10
|
||||
interval = setInterval ->
|
||||
if t > 0
|
||||
console.log t--
|
||||
else
|
||||
console.log 'BLAST OFF!'
|
||||
phantom.exit()
|
||||
, 1000
|
|
@ -1,41 +0,0 @@
|
|||
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
|
|
@ -33,7 +33,7 @@ page.onInitialized = function () {
|
|||
};
|
||||
|
||||
if (system.args.length === 1) {
|
||||
console.log('Usage: unsniff.js <some URL>');
|
||||
console.log('Usage: detectsniff.js <some URL>');
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
address = system.args[1];
|
||||
|
@ -41,6 +41,7 @@ if (system.args.length === 1) {
|
|||
page.open(address, function (status) {
|
||||
if (status !== 'success') {
|
||||
console.log('FAIL to load the address');
|
||||
phantom.exit();
|
||||
} else {
|
||||
window.setTimeout(function () {
|
||||
sniffed = page.evaluate(function () {
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# 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(/\</ig, '<').replace(/\>/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()
|
|
@ -1,20 +0,0 @@
|
|||
# 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()
|
|
@ -7,16 +7,15 @@ if (system.args.length < 3) {
|
|||
phantom.exit(1);
|
||||
} else {
|
||||
var content = '',
|
||||
f = null;
|
||||
f = null,
|
||||
i;
|
||||
for ( i= 2; i < system.args.length; ++i ) {
|
||||
content += system.args[i] + (i === system.args.length-1 ? '' : ' ');
|
||||
}
|
||||
|
||||
try {
|
||||
f = fs.open(system.args[1], "w");
|
||||
f.writeLine(content);
|
||||
f.close();
|
||||
} catch (e) {
|
||||
fs.write(system.args[1], content, 'w');
|
||||
} catch(e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
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)
|
|
@ -1,29 +0,0 @@
|
|||
# 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.timeline-following').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()
|
|
@ -1,10 +1,13 @@
|
|||
// List following and followers from several accounts
|
||||
|
||||
var users = ['ariyahidayat',
|
||||
var users = ['PhantomJS',
|
||||
'ariyahidayat',
|
||||
'detronizator',
|
||||
'KDABQt',
|
||||
'lfranchi',
|
||||
'jonleighton'];
|
||||
'jonleighton',
|
||||
'_jamesmgreene',
|
||||
'Vitalliumm'];
|
||||
|
||||
function follow(user, callback) {
|
||||
var page = require('webpage').create();
|
||||
|
@ -13,10 +16,11 @@ function follow(user, callback) {
|
|||
console.log(user + ': ?');
|
||||
} else {
|
||||
var data = page.evaluate(function () {
|
||||
return document.querySelector('div.timeline-following').innerText;
|
||||
return document.querySelector('div.profile td.stat.stat-last div.statnum').innerText;
|
||||
});
|
||||
console.log(user + ': ' + data);
|
||||
}
|
||||
page.close();
|
||||
callback.apply();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
console.log 'Hello, world!'
|
||||
phantom.exit()
|
|
@ -1,20 +0,0 @@
|
|||
# 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
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
var page = require('webpage').create(),
|
||||
system = require('system'),
|
||||
fname;
|
||||
fname;
|
||||
|
||||
if (system.args.length !== 2) {
|
||||
console.log('Usage: imagebin.js filename');
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
# 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."
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
# 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
|
|
@ -1,18 +0,0 @@
|
|||
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()
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
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
|
@ -0,0 +1,4 @@
|
|||
var universe = require('./universe');
|
||||
universe.start();
|
||||
console.log('The answer is' + universe.answer);
|
||||
phantom.exit();
|
|
@ -1,13 +0,0 @@
|
|||
# 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
|
|
@ -1,18 +0,0 @@
|
|||
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()
|
|
@ -1,110 +0,0 @@
|
|||
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()
|
|
@ -25,6 +25,12 @@ function createHAR(address, title, startTime, resources)
|
|||
return;
|
||||
}
|
||||
|
||||
// Exclude Data URI from HAR file because
|
||||
// they aren't included in specification
|
||||
if (request.url.match(/(^data:image\/.*)/i)) {
|
||||
return;
|
||||
}
|
||||
|
||||
entries.push({
|
||||
startedDateTime: request.time.toISOString(),
|
||||
time: endReply.time - request.time,
|
||||
|
@ -61,7 +67,8 @@ function createHAR(address, title, startTime, resources)
|
|||
wait: startReply.time - request.time,
|
||||
receive: endReply.time - startReply.time,
|
||||
ssl: -1
|
||||
}
|
||||
},
|
||||
pageref: address
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -77,7 +84,9 @@ function createHAR(address, title, startTime, resources)
|
|||
startedDateTime: startTime.toISOString(),
|
||||
id: address,
|
||||
title: title,
|
||||
pageTimings: {}
|
||||
pageTimings: {
|
||||
onLoad: page.endTime - page.startTime
|
||||
}
|
||||
}],
|
||||
entries: entries
|
||||
}
|
||||
|
@ -88,7 +97,7 @@ var page = require('webpage').create(),
|
|||
system = require('system');
|
||||
|
||||
if (system.args.length === 1) {
|
||||
console.log('Usage: netsniff.coffee <some URL>');
|
||||
console.log('Usage: netsniff.js <some URL>');
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
|
||||
|
@ -120,13 +129,15 @@ if (system.args.length === 1) {
|
|||
var har;
|
||||
if (status !== 'success') {
|
||||
console.log('FAIL to load the address');
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
page.endTime = new Date();
|
||||
page.title = page.evaluate(function () {
|
||||
return document.title;
|
||||
});
|
||||
har = createHAR(page.address, page.title, page.startTime, page.resources);
|
||||
console.log(JSON.stringify(har, undefined, 4));
|
||||
phantom.exit();
|
||||
}
|
||||
phantom.exit();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
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();
|
||||
});
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
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()
|
|
@ -0,0 +1,146 @@
|
|||
// 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);
|
|
@ -1,13 +0,0 @@
|
|||
p = require("webpage").create()
|
||||
p.onConsoleMessage = (msg) ->
|
||||
console.log msg
|
||||
|
||||
p.onCallback = (msg) ->
|
||||
console.log "Received by the 'phantom' main context: " + msg
|
||||
"Hello there, I'm coming to you from the 'phantom' context instead"
|
||||
|
||||
p.evaluate ->
|
||||
callbackResponse = phantomCallback("Hello, I'm coming to you from the 'page' context")
|
||||
console.log "Received by the 'page' context: " + callbackResponse
|
||||
|
||||
phantom.exit()
|
|
@ -2,7 +2,7 @@ var p = require("webpage").create();
|
|||
|
||||
p.onConsoleMessage = function(msg) { console.log(msg); };
|
||||
|
||||
// Calls to "phantomCallback" within the page 'p' arrive here
|
||||
// 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";
|
||||
|
@ -10,7 +10,7 @@ p.onCallback = function(msg) {
|
|||
|
||||
p.evaluate(function() {
|
||||
// Return-value of the "onCallback" handler arrive here
|
||||
var callbackResponse = callPhantom("Hello, I'm coming to you from the 'page' context");
|
||||
var callbackResponse = window.callPhantom("Hello, I'm coming to you from the 'page' context");
|
||||
console.log("Received by the 'page' context: "+callbackResponse);
|
||||
});
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
# 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()
|
||||
|
|
@ -10,7 +10,7 @@ page.open("http://www.phantomjs.org", function(status) {
|
|||
if ( status === "success" ) {
|
||||
page.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js", function() {
|
||||
page.evaluate(function() {
|
||||
console.log("$(\"#intro\").text() -> " + $("#intro").text());
|
||||
console.log("$(\".explanation\").text() -> " + $(".explanation").text());
|
||||
});
|
||||
phantom.exit();
|
||||
});
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
# 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()
|
|
@ -8,7 +8,7 @@ page.open(url, function (status) {
|
|||
console.log('Unable to access network');
|
||||
} else {
|
||||
var results = page.evaluate(function() {
|
||||
var list = document.querySelectorAll('span.address'), pizza = [], i;
|
||||
var list = document.querySelectorAll('address'), pizza = [], i;
|
||||
for (i = 0; i < list.length; i++) {
|
||||
pizza.push(list[i].innerText);
|
||||
}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
# 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()
|
|
@ -0,0 +1,18 @@
|
|||
// 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();
|
||||
});
|
|
@ -2,7 +2,7 @@ var page = require('webpage').create(),
|
|||
system = require('system');
|
||||
|
||||
function someCallback(pageNum, numPages) {
|
||||
return "<h1> somCallback: " + pageNum + " / " + numPages + "</h1>";
|
||||
return "<h1> someCallback: " + pageNum + " / " + numPages + "</h1>";
|
||||
}
|
||||
|
||||
if (system.args.length < 3) {
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
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
|
|
@ -5,6 +5,8 @@ var page = require('webpage').create(),
|
|||
if (system.args.length < 3 || system.args.length > 5) {
|
||||
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(' image (png/jpg output) examples: "1920px" entire page, window width 1920px');
|
||||
console.log(' "800px*600px" window, clipped to 800x600');
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
address = system.args[1];
|
||||
|
@ -14,6 +16,20 @@ if (system.args.length < 3 || system.args.length > 5) {
|
|||
size = system.args[3].split('*');
|
||||
page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
|
||||
: { 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) {
|
||||
page.zoomFactor = system.args[4];
|
||||
|
@ -21,6 +37,7 @@ if (system.args.length < 3 || system.args.length > 5) {
|
|||
page.open(address, function (status) {
|
||||
if (status !== 'success') {
|
||||
console.log('Unable to load the address!');
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
window.setTimeout(function () {
|
||||
page.render(output);
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
# 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()
|
|
@ -1,62 +1,73 @@
|
|||
// 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 system = require('system');
|
||||
var RenderUrlsToFile, arrayOfUrls, system;
|
||||
|
||||
// Extend the Array Prototype with a 'foreach'
|
||||
Array.prototype.forEach = function (action) {
|
||||
var i, len;
|
||||
for ( i = 0, len = this.length; i < len; ++i ) {
|
||||
action(i, this[i], len);
|
||||
}
|
||||
system = require("system");
|
||||
|
||||
/*
|
||||
Render given urls
|
||||
@param array of URLs to render
|
||||
@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();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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";
|
||||
arrayOfUrls = null;
|
||||
|
||||
page.open(url, function(status){
|
||||
if ( status !== "success") {
|
||||
console.log("Unable to render '"+url+"'");
|
||||
} else {
|
||||
page.render(file);
|
||||
}
|
||||
delete page;
|
||||
callback(url, file);
|
||||
});
|
||||
}
|
||||
|
||||
// Read the passed args
|
||||
var arrayOfUrls;
|
||||
if ( system.args.length > 1 ) {
|
||||
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.js [domain.name1, domain.name2, ...]");
|
||||
arrayOfUrls = [
|
||||
'www.google.com',
|
||||
'www.bbc.co.uk',
|
||||
'www.phantomjs.org'
|
||||
];
|
||||
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();
|
||||
}
|
||||
});
|
||||
RenderUrlsToFile(arrayOfUrls, (function(status, url, file) {
|
||||
if (status !== "success") {
|
||||
return console.log("Unable to render '" + url + "'");
|
||||
} else {
|
||||
return console.log("Rendered '" + url + "' at '" + file + "'");
|
||||
}
|
||||
}), function() {
|
||||
return phantom.exit();
|
||||
});
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
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()
|
|
@ -50,30 +50,42 @@ page.onConsoleMessage = function(msg) {
|
|||
|
||||
page.open(system.args[1], function(status){
|
||||
if (status !== "success") {
|
||||
console.log("Unable to access network");
|
||||
phantom.exit();
|
||||
console.log("Unable to open " + system.args[1]);
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
waitFor(function(){
|
||||
return page.evaluate(function(){
|
||||
if (document.body.querySelector('.runner .description')) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return document.body.querySelector('.symbolSummary .pending') === null
|
||||
});
|
||||
}, function(){
|
||||
page.evaluate(function(){
|
||||
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');
|
||||
var exitCode = page.evaluate(function(){
|
||||
try {
|
||||
console.log('');
|
||||
for (j = 0; j < desc.length; ++j) {
|
||||
console.log(desc[j].innerText);
|
||||
console.log(document.body.querySelector('.description').innerText);
|
||||
var list = document.body.querySelectorAll('.results > #details > .specDetail.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('.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();
|
||||
phantom.exit(exitCode);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
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);
|
||||
});
|
||||
}
|
||||
});
|
|
@ -1,64 +0,0 @@
|
|||
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
|
|
@ -1,16 +0,0 @@
|
|||
# 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()
|
|
@ -1,17 +0,0 @@
|
|||
# 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
|
|
@ -1,38 +0,0 @@
|
|||
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()
|
|
@ -1,20 +0,0 @@
|
|||
###
|
||||
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
|
||||
|
|
@ -15,11 +15,11 @@ function sleepSort(array, callback) {
|
|||
}
|
||||
}
|
||||
|
||||
if ( system.args < 2 ) {
|
||||
if ( system.args.length < 2 ) {
|
||||
console.log("Usage: phantomjs sleepsort.js PUT YOUR INTEGERS HERE SEPARATED BY SPACES");
|
||||
phantom.exit(1);
|
||||
} else {
|
||||
sleepSort(Array.prototype.slice.call(system.args, 1), function() {
|
||||
sleepSort(system.args.slice(1), function() {
|
||||
phantom.exit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
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);
|
|
@ -1,17 +0,0 @@
|
|||
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()
|
|
@ -1,31 +0,0 @@
|
|||
# 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 'span.status'
|
||||
for i, j in list
|
||||
console.log "#{j + 1}: #{i.innerHTML.replace /<.*?>/g, ''}"
|
||||
phantom.exit()
|
|
@ -1,8 +1,8 @@
|
|||
// Get twitter status for given account (or for the default one, "HeadlessPhantom")
|
||||
// Get twitter status for given account (or for the default one, "PhantomJS")
|
||||
|
||||
var page = require('webpage').create(),
|
||||
system = require('system'),
|
||||
twitterId = "HeadlessPhantom"; //< default value
|
||||
twitterId = "PhantomJS"; //< default value
|
||||
|
||||
// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
|
||||
page.onConsoleMessage = function(msg) {
|
||||
|
@ -27,9 +27,9 @@ page.open(encodeURI("http://mobile.twitter.com/" + twitterId), function (status)
|
|||
} else {
|
||||
// Execute some DOM inspection within the page context
|
||||
page.evaluate(function() {
|
||||
var list = document.querySelectorAll('span.status');
|
||||
var list = document.querySelectorAll('div.tweet-text');
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
console.log((i + 1) + ": " + list[i].innerHTML.replace(/<.*?>/g, ''));
|
||||
console.log((i + 1) + ": " + list[i].innerText);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
// 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....');
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
# 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()
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
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()
|
|
@ -1,5 +0,0 @@
|
|||
console.log 'using PhantomJS version ' +
|
||||
phantom.version.major + '.' +
|
||||
phantom.version.minor + '.' +
|
||||
phantom.version.patch
|
||||
phantom.exit()
|
|
@ -1,48 +0,0 @@
|
|||
##
|
||||
# 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()
|
|
@ -1,66 +0,0 @@
|
|||
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()
|
|
@ -1,49 +0,0 @@
|
|||
# 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()
|
|
@ -1,58 +1,37 @@
|
|||
// Get weather info for given address (or for the default one, "Mountain View")
|
||||
|
||||
var page = require('webpage').create(),
|
||||
system = require('system'),
|
||||
address = "Mountain View"; //< default value
|
||||
city,
|
||||
url;
|
||||
|
||||
// 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);
|
||||
};
|
||||
|
||||
// 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(' ');
|
||||
city = 'Mountain View, California'; // default
|
||||
if (system.args.length > 1) {
|
||||
city = Array.prototype.slice.call(system.args, 1).join(' ');
|
||||
}
|
||||
url = encodeURI('http://api.openweathermap.org/data/2.1/find/name?q=' + city);
|
||||
|
||||
// Heading
|
||||
console.log("*** Loading weather information for '" + address + "' ***\n");
|
||||
console.log('Checking weather condition for', city, '...');
|
||||
|
||||
// Open Google "secret" Weather API and, onPageLoad, do...
|
||||
page.open(encodeURI('http://www.google.com/ig/api?weather=' + address), function (status) {
|
||||
// Check for page load success
|
||||
if (status !== "success") {
|
||||
console.log("Unable to access network");
|
||||
page.open(url, function(status) {
|
||||
var result, data;
|
||||
if (status !== 'success') {
|
||||
console.log('Error: Unable to access network!');
|
||||
} else {
|
||||
// Execute some DOM inspection within the page context
|
||||
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));
|
||||
}
|
||||
}
|
||||
result = page.evaluate(function () {
|
||||
return document.body.innerText;
|
||||
});
|
||||
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();
|
||||
});
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
TEMPLATE = subdirs
|
||||
CONFIG += ordered
|
||||
SUBDIRS += src/phantomjs.pro
|
||||
SUBDIRS += src/qphantom/phantom.pro src/phantomjs.pro
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
#!/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##*/}
|
|
@ -0,0 +1,163 @@
|
|||
%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
|
301
src/bootstrap.js
301
src/bootstrap.js
|
@ -8,6 +8,7 @@
|
|||
Copyright (C) 2011 Ivan De Marino <ivan.de.marino@gmail.com>
|
||||
Copyright (C) 2011 James Roe <roejames12@hotmail.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
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@ -33,53 +34,53 @@
|
|||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
function require(name) {
|
||||
var code, func, exports;
|
||||
phantom.__defineErrorSignalHandler__ = function(obj, page, handlers) {
|
||||
var handlerName = 'onError';
|
||||
|
||||
if (name === 'webpage' || name === 'fs' || name === 'webserver' || name === 'system') {
|
||||
code = phantom.loadModuleSource(name);
|
||||
func = new Function("exports", "window", code);
|
||||
exports = {};
|
||||
if (name === 'fs') {
|
||||
exports = phantom.createFilesystem();
|
||||
} else if (name === 'system') {
|
||||
exports = phantom.createSystem();
|
||||
}
|
||||
func.call({}, exports, {});
|
||||
return exports;
|
||||
}
|
||||
Object.defineProperty(obj, handlerName, {
|
||||
set: function (f) {
|
||||
// Disconnect previous handler (if any)
|
||||
var handlerObj = handlers[handlerName];
|
||||
if (!!handlerObj && typeof handlerObj.callback === "function" && typeof handlerObj.connector === "function") {
|
||||
try { page.javaScriptErrorSent.disconnect(handlerObj.connector); }
|
||||
catch (e) { }
|
||||
}
|
||||
|
||||
if (typeof exports === 'undefined') {
|
||||
throw 'Unknown module ' + name + ' for require()';
|
||||
}
|
||||
}
|
||||
// Delete the previous handler
|
||||
delete handlers[handlerName];
|
||||
|
||||
phantom.__defineErrorSetter__ = function(obj, page) {
|
||||
var handler;
|
||||
var signal = page.javaScriptErrorSent;
|
||||
if (typeof f === 'function') {
|
||||
var connector = function (message, lineNumber, source, stack) {
|
||||
var revisedStack = JSON.parse(stack).map(function (item) {
|
||||
return { file: item.url, line: item.lineNumber, function: item.functionName };
|
||||
});
|
||||
if (revisedStack.length == 0)
|
||||
revisedStack = [{ file: source, line: lineNumber }];
|
||||
|
||||
obj.__defineSetter__('onError', function(f) {
|
||||
if (handler && typeof handler === 'function') {
|
||||
try { signal.disconnect(handler); }
|
||||
catch (e) {}
|
||||
}
|
||||
f(message, revisedStack);
|
||||
};
|
||||
// Store the new handler for reference
|
||||
handlers[handlerName] = {
|
||||
callback: f,
|
||||
connector: connector
|
||||
};
|
||||
|
||||
if (typeof f === 'function') {
|
||||
handler = function(message, stack) {
|
||||
stack = JSON.parse(stack).map(function(item) {
|
||||
return { file: item.url, line: item.lineNumber, function: item.functionName }
|
||||
});
|
||||
|
||||
f(message, stack);
|
||||
};
|
||||
signal.connect(handler);
|
||||
} else {
|
||||
handler = null;
|
||||
page.javaScriptErrorSent.connect(connector);
|
||||
}
|
||||
},
|
||||
get: function () {
|
||||
var handlerObj = handlers[handlerName];
|
||||
return (!!handlerObj && typeof handlerObj.callback === "function" && typeof handlerObj.connector === "function") ?
|
||||
handlers[handlerName].callback :
|
||||
undefined;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
phantom.__defineErrorSetter__(phantom, phantom.page);
|
||||
(function() {
|
||||
var handlers = {};
|
||||
phantom.__defineErrorSignalHandler__(phantom, phantom.page, handlers);
|
||||
})();
|
||||
|
||||
// TODO: Make this output to STDERR
|
||||
phantom.defaultErrorHandler = function(message, stack) {
|
||||
|
@ -87,12 +88,14 @@ phantom.defaultErrorHandler = function(message, stack) {
|
|||
|
||||
stack.forEach(function(item) {
|
||||
var message = item.file + ":" + item.line;
|
||||
if (item.function)
|
||||
message += " in " + item.function;
|
||||
if (item["function"])
|
||||
message += " in " + item["function"];
|
||||
console.log(" " + message);
|
||||
});
|
||||
};
|
||||
|
||||
phantom.onError = phantom.defaultErrorHandler;
|
||||
|
||||
phantom.callback = function(callback) {
|
||||
var ret = phantom.createCallback();
|
||||
ret.called.connect(function(args) {
|
||||
|
@ -102,7 +105,223 @@ phantom.callback = function(callback) {
|
|||
return ret;
|
||||
};
|
||||
|
||||
phantom.onError = phantom.defaultErrorHandler;
|
||||
(function() {
|
||||
// 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
|
||||
window.WebPage = require('webpage').create;
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
../.gitignore-breakpad
|
|
@ -0,0 +1,18 @@
|
|||
/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 +0,0 @@
|
|||
/usr/share/automake-1.11/compile
|
|
@ -29,6 +29,8 @@
|
|||
|
||||
#include "callback.h"
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
Callback::Callback(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
|
@ -38,6 +40,7 @@ QVariant Callback::call(const QVariantList& arguments)
|
|||
{
|
||||
emit called(arguments);
|
||||
|
||||
qDebug() << "Callback - call result:" << m_returnValue;
|
||||
return m_returnValue;
|
||||
}
|
||||
|
||||
|
@ -49,4 +52,4 @@ QVariant Callback::returnValue() const
|
|||
void Callback::setReturnValue(const QVariant& returnValue)
|
||||
{
|
||||
m_returnValue = returnValue;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
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
|
File diff suppressed because one or more lines are too long
552
src/config.cpp
552
src/config.cpp
|
@ -1,8 +1,10 @@
|
|||
/*
|
||||
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 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
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@ -31,17 +33,65 @@
|
|||
#include "config.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QWebPage>
|
||||
#include <QWebFrame>
|
||||
#include <QFileInfo>
|
||||
#include <QtWebKitWidgets/QWebPage>
|
||||
#include <QtWebKitWidgets/QWebFrame>
|
||||
#include <QNetworkProxy>
|
||||
|
||||
#include "terminal.h"
|
||||
#include "qcommandline.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)
|
||||
: 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();
|
||||
}
|
||||
|
||||
|
@ -49,138 +99,46 @@ void Config::init(const QStringList *const args)
|
|||
{
|
||||
resetToDefaults();
|
||||
|
||||
QByteArray envSslCertDir = qgetenv("SSL_CERT_DIR");
|
||||
if (!envSslCertDir.isEmpty())
|
||||
setSslCertificatesPath(envSslCertDir);
|
||||
|
||||
processArgs(*args);
|
||||
}
|
||||
|
||||
void Config::processArgs(const QStringList &args)
|
||||
{
|
||||
QStringListIterator it(args);
|
||||
while (it.hasNext()) {
|
||||
const QString &arg = it.next();
|
||||
connect(m_cmdLine, SIGNAL(switchFound(const QString &)), this, SLOT(handleSwitch(const QString &)));
|
||||
connect(m_cmdLine, SIGNAL(optionFound(const QString &, const QVariant &)), this, SLOT(handleOption(const QString &, const QVariant &)));
|
||||
connect(m_cmdLine, SIGNAL(paramFound(const QString &, const QVariant &)), this, SLOT(handleParam(const QString &, const QVariant &)));
|
||||
connect(m_cmdLine, SIGNAL(parseError(const QString &)), this, SLOT(handleError(const QString &)));
|
||||
|
||||
if (arg == "--help" || arg == "-h") {
|
||||
setHelpFlag(true);
|
||||
return;
|
||||
}
|
||||
if (arg == "--version" || arg == "-v") {
|
||||
setVersionFlag(true);
|
||||
return;
|
||||
}
|
||||
if (arg == "--load-images=yes") {
|
||||
setAutoLoadImages(true);
|
||||
continue;
|
||||
}
|
||||
if (arg == "--load-images=no") {
|
||||
setAutoLoadImages(false);
|
||||
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;
|
||||
m_cmdLine->setArguments(args);
|
||||
m_cmdLine->setConfig(flags);
|
||||
m_cmdLine->parse();
|
||||
|
||||
// Inject command line parameters to be picked up by GhostDriver
|
||||
if (isWebdriverMode()) {
|
||||
QStringList argsForGhostDriver;
|
||||
|
||||
m_scriptFile = "main.js"; //< launch script
|
||||
|
||||
argsForGhostDriver << QString("--ip=%1").arg(m_webdriverIp); //< "--ip=IP"
|
||||
argsForGhostDriver << QString("--port=%1").arg(m_webdriverPort); //< "--port=PORT"
|
||||
|
||||
if (!m_webdriverSeleniumGridHub.isEmpty()) {
|
||||
argsForGhostDriver << QString("--hub=%1").arg(m_webdriverSeleniumGridHub); //< "--hub=SELENIUM_GRID_HUB_URL"
|
||||
}
|
||||
|
||||
// There are no more options at this point.
|
||||
// The remaining arguments are available for the script.
|
||||
|
||||
m_scriptFile = arg;
|
||||
|
||||
while (it.hasNext()) {
|
||||
m_scriptArgs += it.next();
|
||||
if (!m_webdriverLogFile.isEmpty()) {
|
||||
argsForGhostDriver << QString("--logFile=%1").arg(m_webdriverLogFile); //< "--logFile=LOG_FILE"
|
||||
argsForGhostDriver << "--logColor=false"; //< Force no-color-output in Log File
|
||||
}
|
||||
|
||||
argsForGhostDriver << QString("--logLevel=%1").arg(m_webdriverLogLevel); //< "--logLevel=LOG_LEVEL"
|
||||
|
||||
// Clear current args and override with those
|
||||
setScriptArgs(argsForGhostDriver);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +171,12 @@ void Config::loadJsonFile(const QString &filePath)
|
|||
// Add this object to the global scope
|
||||
webPage.mainFrame()->addToJavaScriptWindowObject("config", this);
|
||||
// Apply the JSON config settings to this very object
|
||||
webPage.mainFrame()->evaluateJavaScript(configurator.arg(jsonConfig), QString());
|
||||
webPage.mainFrame()->evaluateJavaScript(configurator.arg(jsonConfig));
|
||||
}
|
||||
|
||||
QString Config::helpText() const
|
||||
{
|
||||
return m_cmdLine->help();
|
||||
}
|
||||
|
||||
bool Config::autoLoadImages() const
|
||||
|
@ -287,6 +250,16 @@ void Config::setIgnoreSslErrors(const bool value)
|
|||
m_ignoreSslErrors = value;
|
||||
}
|
||||
|
||||
bool Config::localUrlAccessEnabled() const
|
||||
{
|
||||
return m_localUrlAccessEnabled;
|
||||
}
|
||||
|
||||
void Config::setLocalUrlAccessEnabled(const bool value)
|
||||
{
|
||||
m_localUrlAccessEnabled = value;
|
||||
}
|
||||
|
||||
bool Config::localToRemoteUrlAccessEnabled() const
|
||||
{
|
||||
return m_localToRemoteUrlAccessEnabled;
|
||||
|
@ -323,25 +296,17 @@ void Config::setProxyType(const QString value)
|
|||
|
||||
QString Config::proxy() const
|
||||
{
|
||||
return proxyHost() + ":" + proxyPort();
|
||||
return m_proxyHost + ":" + QString::number(m_proxyPort);
|
||||
}
|
||||
|
||||
void Config::setProxy(const QString &value)
|
||||
{
|
||||
QString proxyHost = value;
|
||||
int proxyPort = 1080;
|
||||
QUrl proxyUrl = QUrl::fromUserInput(value);
|
||||
|
||||
if (proxyHost.lastIndexOf(':') > 0) {
|
||||
bool ok = true;
|
||||
int port = proxyHost.mid(proxyHost.lastIndexOf(':') + 1).toInt(&ok);
|
||||
if (ok) {
|
||||
proxyHost = proxyHost.left(proxyHost.lastIndexOf(':')).trimmed();
|
||||
proxyPort = port;
|
||||
}
|
||||
if (proxyUrl.isValid()) {
|
||||
setProxyHost(proxyUrl.host());
|
||||
setProxyPort(proxyUrl.port(1080));
|
||||
}
|
||||
|
||||
setProxyHost(proxyHost);
|
||||
setProxyPort(proxyPort);
|
||||
}
|
||||
|
||||
void Config::setProxyAuth(const QString &value)
|
||||
|
@ -412,6 +377,20 @@ void Config::setScriptEncoding(const QString &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
|
||||
{
|
||||
return m_scriptFile;
|
||||
|
@ -482,6 +461,81 @@ void Config::setWebSecurityEnabled(const bool 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:
|
||||
void Config::resetToDefaults()
|
||||
{
|
||||
|
@ -492,6 +546,7 @@ void Config::resetToDefaults()
|
|||
m_diskCacheEnabled = false;
|
||||
m_maxDiskCacheSize = -1;
|
||||
m_ignoreSslErrors = false;
|
||||
m_localUrlAccessEnabled = true;
|
||||
m_localToRemoteUrlAccessEnabled = false;
|
||||
m_outputEncoding = "UTF-8";
|
||||
m_proxyType = "http";
|
||||
|
@ -501,6 +556,7 @@ void Config::resetToDefaults()
|
|||
m_proxyAuthPass.clear();
|
||||
m_scriptArgs.clear();
|
||||
m_scriptEncoding = "UTF-8";
|
||||
m_scriptLanguage.clear();
|
||||
m_scriptFile.clear();
|
||||
m_unknownOption.clear();
|
||||
m_versionFlag = false;
|
||||
|
@ -508,8 +564,36 @@ void Config::resetToDefaults()
|
|||
m_remoteDebugPort = -1;
|
||||
m_remoteDebugAutorun = false;
|
||||
m_webSecurityEnabled = true;
|
||||
m_javascriptCanOpenWindows = true;
|
||||
m_javascriptCanCloseWindows = true;
|
||||
m_helpFlag = 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)
|
||||
|
@ -551,3 +635,191 @@ void Config::setPrintDebugMessages(const bool 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;
|
||||
}
|
||||
}
|
||||
|
|
70
src/config.h
70
src/config.h
|
@ -34,14 +34,18 @@
|
|||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QNetworkProxy>
|
||||
#include <QVariant>
|
||||
|
||||
class Config: QObject
|
||||
class QCommandLine;
|
||||
|
||||
class Config: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QString cookiesFile READ cookiesFile WRITE setCookiesFile)
|
||||
Q_PROPERTY(bool diskCacheEnabled READ diskCacheEnabled WRITE setDiskCacheEnabled)
|
||||
Q_PROPERTY(int maxDiskCacheSize READ maxDiskCacheSize WRITE setMaxDiskCacheSize)
|
||||
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(QString outputEncoding READ outputEncoding WRITE setOutputEncoding)
|
||||
Q_PROPERTY(QString proxyType READ proxyType WRITE setProxyType)
|
||||
|
@ -52,6 +56,15 @@ class Config: QObject
|
|||
Q_PROPERTY(QString offlineStoragePath READ offlineStoragePath WRITE setOfflineStoragePath)
|
||||
Q_PROPERTY(int offlineStorageDefaultQuota READ offlineStorageDefaultQuota WRITE setOfflineStorageDefaultQuota)
|
||||
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:
|
||||
Config(QObject *parent = 0);
|
||||
|
@ -60,6 +73,8 @@ public:
|
|||
void processArgs(const QStringList &args);
|
||||
void loadJsonFile(const QString &filePath);
|
||||
|
||||
QString helpText() const;
|
||||
|
||||
bool autoLoadImages() const;
|
||||
void setAutoLoadImages(const bool value);
|
||||
|
||||
|
@ -81,6 +96,9 @@ public:
|
|||
bool ignoreSslErrors() const;
|
||||
void setIgnoreSslErrors(const bool value);
|
||||
|
||||
bool localUrlAccessEnabled() const;
|
||||
void setLocalUrlAccessEnabled(const bool value);
|
||||
|
||||
bool localToRemoteUrlAccessEnabled() const;
|
||||
void setLocalToRemoteUrlAccessEnabled(const bool value);
|
||||
|
||||
|
@ -108,6 +126,9 @@ public:
|
|||
QString scriptEncoding() const;
|
||||
void setScriptEncoding(const QString &value);
|
||||
|
||||
QString scriptLanguage() const;
|
||||
void setScriptLanguage(const QString &value);
|
||||
|
||||
QString scriptFile() const;
|
||||
void setScriptFile(const QString &value);
|
||||
|
||||
|
@ -135,6 +156,40 @@ public:
|
|||
void setPrintDebugMessages(const bool value);
|
||||
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:
|
||||
void resetToDefaults();
|
||||
void setProxyHost(const QString &value);
|
||||
|
@ -142,6 +197,7 @@ private:
|
|||
void setAuthUser(const QString &value);
|
||||
void setAuthPass(const QString &value);
|
||||
|
||||
QCommandLine *m_cmdLine;
|
||||
bool m_autoLoadImages;
|
||||
QString m_cookiesFile;
|
||||
QString m_offlineStoragePath;
|
||||
|
@ -149,6 +205,7 @@ private:
|
|||
bool m_diskCacheEnabled;
|
||||
int m_maxDiskCacheSize;
|
||||
bool m_ignoreSslErrors;
|
||||
bool m_localUrlAccessEnabled;
|
||||
bool m_localToRemoteUrlAccessEnabled;
|
||||
QString m_outputEncoding;
|
||||
QString m_proxyType;
|
||||
|
@ -158,6 +215,7 @@ private:
|
|||
QString m_proxyAuthPass;
|
||||
QStringList m_scriptArgs;
|
||||
QString m_scriptEncoding;
|
||||
QString m_scriptLanguage;
|
||||
QString m_scriptFile;
|
||||
QString m_unknownOption;
|
||||
bool m_versionFlag;
|
||||
|
@ -169,6 +227,16 @@ private:
|
|||
bool m_webSecurityEnabled;
|
||||
bool m_helpFlag;
|
||||
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
|
||||
|
|
16
src/consts.h
16
src/consts.h
|
@ -32,13 +32,13 @@
|
|||
#ifndef CONSTS_H
|
||||
#define CONSTS_H
|
||||
|
||||
// Current Version: 1.6.0
|
||||
#define PHANTOMJS_VERSION_MAJOR 1
|
||||
#define PHANTOMJS_VERSION_MINOR 6
|
||||
#define PHANTOMJS_VERSION_MAJOR 2
|
||||
#define PHANTOMJS_VERSION_MINOR 0
|
||||
#define PHANTOMJS_VERSION_PATCH 0
|
||||
#define PHANTOMJS_VERSION_STRING "1.6.0 (development)"
|
||||
#define PHANTOMJS_VERSION_STRING "2.0.0"
|
||||
|
||||
#define COFFEE_SCRIPT_EXTENSION ".coffee"
|
||||
#define HTTP_HEADER_CONTENT_LENGTH "content-length"
|
||||
#define HTTP_HEADER_CONTENT_TYPE "content-type"
|
||||
|
||||
#define JS_ELEMENT_CLICK "(function (el) { " \
|
||||
"var ev = document.createEvent('MouseEvents');" \
|
||||
|
@ -58,6 +58,12 @@
|
|||
#define PAGE_SETTINGS_LOCAL_ACCESS_REMOTE "localToRemoteUrlAccessEnabled"
|
||||
#define PAGE_SETTINGS_USERNAME "userName"
|
||||
#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_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
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
This file is part of the PhantomJS project from Ofi Labs.
|
||||
|
||||
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
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@ -27,44 +28,480 @@
|
|||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "phantom.h"
|
||||
#include "config.h"
|
||||
#include "cookiejar.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QSettings>
|
||||
#include <QStringList>
|
||||
#include <QTimer>
|
||||
|
||||
CookieJar::CookieJar(QString cookiesFile)
|
||||
: QNetworkCookieJar()
|
||||
#define COOKIE_JAR_VERSION 1
|
||||
|
||||
// Operators needed for Cookie Serialization
|
||||
QT_BEGIN_NAMESPACE
|
||||
QDataStream &operator<<(QDataStream &stream, const QList<QNetworkCookie> &list)
|
||||
{
|
||||
m_cookiesFile = cookiesFile;
|
||||
stream << COOKIE_JAR_VERSION;
|
||||
stream << quint32(list.size());
|
||||
for (int i = 0; i < list.size(); ++i)
|
||||
stream << list.at(i).toRawForm();
|
||||
return stream;
|
||||
}
|
||||
|
||||
bool CookieJar::setCookiesFromUrl(const QList<QNetworkCookie> & cookieList, const QUrl & url)
|
||||
QDataStream &operator>>(QDataStream &stream, QList<QNetworkCookie> &list)
|
||||
{
|
||||
QSettings settings(m_cookiesFile, QSettings::IniFormat);
|
||||
list.clear();
|
||||
|
||||
settings.beginGroup(url.host());
|
||||
|
||||
for (QList<QNetworkCookie>::const_iterator i = cookieList.begin(); i != cookieList.end(); i++) {
|
||||
settings.setValue((*i).name(), QString((*i).value()));
|
||||
quint32 version;
|
||||
stream >> version;
|
||||
|
||||
if (version != COOKIE_JAR_VERSION)
|
||||
return stream;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
settings.sync();
|
||||
|
||||
return true;
|
||||
return stream;
|
||||
}
|
||||
QT_END_NAMESPACE
|
||||
|
||||
QList<QNetworkCookie> CookieJar::cookiesForUrl(const QUrl & url) const
|
||||
// public:
|
||||
CookieJar::CookieJar(QString cookiesFile, QObject *parent)
|
||||
: QNetworkCookieJar(parent)
|
||||
, m_enabled(true)
|
||||
{
|
||||
QSettings settings(m_cookiesFile, QSettings::IniFormat);
|
||||
QList<QNetworkCookie> cookieList;
|
||||
|
||||
settings.beginGroup(url.host());
|
||||
|
||||
QStringList keys = settings.childKeys();
|
||||
|
||||
for (QStringList::iterator i = keys.begin(); i != keys.end(); i++) {
|
||||
cookieList.push_back(QNetworkCookie((*i).toLocal8Bit(), settings.value(*i).toByteArray()));
|
||||
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;
|
||||
}
|
||||
|
||||
return cookieList;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
QNetworkCookie CookieJar::cookie(const QString &name, const QString &url) const
|
||||
{
|
||||
QList<QNetworkCookie> cookiesList = cookies(url);
|
||||
for (int i = cookiesList.length() -1; i >= 0; --i) {
|
||||
if (cookiesList.at(i).name() == name) {
|
||||
return cookiesList.at(i);
|
||||
}
|
||||
}
|
||||
return QNetworkCookie();
|
||||
}
|
||||
|
||||
QVariantMap CookieJar::cookieToMap(const QString &name, const QString &url) const
|
||||
{
|
||||
QVariantMap cookie;
|
||||
|
||||
QVariantList cookiesList = cookiesToMap(url);
|
||||
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)
|
||||
{
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
This file is part of the PhantomJS project from Ofi Labs.
|
||||
|
||||
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
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
@ -30,18 +31,61 @@
|
|||
#ifndef COOKIEJAR_H
|
||||
#define COOKIEJAR_H
|
||||
|
||||
#include <QSettings>
|
||||
#include <QNetworkCookie>
|
||||
#include <QNetworkCookieJar>
|
||||
#include <QVariantList>
|
||||
#include <QVariantMap>
|
||||
|
||||
class CookieJar: public QNetworkCookieJar
|
||||
{
|
||||
private:
|
||||
QString m_cookiesFile;
|
||||
Q_OBJECT
|
||||
|
||||
Q_PROPERTY(QVariantList cookies READ cookiesToMap WRITE addCookiesFromMap)
|
||||
|
||||
public:
|
||||
CookieJar(QString cookiesFile);
|
||||
CookieJar(QString cookiesFile, QObject *parent = NULL);
|
||||
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;
|
||||
|
||||
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
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
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);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue