diff --git a/LICENSES/FLOSS-exception.txt b/LICENSES/FLOSS-exception.txt new file mode 100644 index 000000000..483246685 --- /dev/null +++ b/LICENSES/FLOSS-exception.txt @@ -0,0 +1,114 @@ +Linden Research, Inc. ("Linden Lab") Viewer FLOSS License Exception v0.5 + +The Linden Lab Exception for Free/Libre and Open Source Software-only +Applications Using Linden Lab Viewer Software (the "FLOSS Exception"). + +Exception Intent + +Linden Lab is releasing the source code for certain software that +enables users to view or otherwise access the Second Life virtual +world environment (the "Viewer Software"), under version 2 of the GNU +General Public License (the "GPL"). The creation or distribution of +works based on the Program (as defined under the GPL) of the Viewer +Software may require the use of certain Free/Libre and Open Source +Software ("FLOSS") works that are subject to license agreements not +compatible with re-licensing under the GPL. Because we want to allow +the Viewer Software to be distributed with these FLOSS works, this +FLOSS Exception following exception applies subject to the terms and +conditions below. + +Legal Terms and Conditions + +As a special exception to the terms and conditions of version 2.0 of +the GPL: + +You are free to distribute a work based on the Program that is formed +entirely from the Viewer Software (and any modifications thereof) and +one or more works that are independent and separate works not derived +from the Viewer Software, and are licensed under one or more of the +licenses listed below in section 1 (each, a "FLOSS Work") , as long +as: + + A. You obey the GPL in all respects for the Viewer Software and any + work based on the Program, except for the FLOSS Works, for which + you must comply with B below, + + B. all FLOSS Works, + + i. are distributed subject to one of the FLOSS licenses + listed below, and + + ii. the object code or executable form of the FLOSS Works are + accompanied by the complete corresponding + machine-readable source code for those FLOSS Works on the + same medium and under the same FLOSS license as the + corresponding object code or executable forms thereof, + and + + C. any works that are aggregated with the Viewer Software or a work + based on the Program on a volume of a storage or distribution + medium in accordance with the GPL, and are not licensed under + the FLOSS licenses listed below, are independent and separate + works in themselves which are not derivatives of either the + Viewer Software, a work based on the Program or a FLOSS Work. + +If the above conditions are not met, then the Viewer Software may only +be copied, modified, distributed or used under the terms and +conditions of the GPL or another valid licensing option from Linden +Lab. + +1. FLOSS License List + +License name Version(s)/Copyright Date +Academic Free License 2.0 +Apache Software License 1.0/1.1/2.0 +Apple Public Source License 2.0 +Artistic license From Perl 5.8.0 +BSD license "July 22 1999" +Common Development and + Distribution License (CDDL) 1.0 +Common Public License 1.0 +GNU Library or "Lesser" General + Public License (LGPL) 2.0/2.1 +Jabber Open Source License 1.0 +MIT License (As listed in file MIT-License.txt) - +Mozilla Public License (MPL) 1.0/1.1 +Open Software License 2.0 +OpenSSL license (with + original SSLeay license) "2003" ("1998") +PHP License 3.0 +Python license (CNRI Python License) - +Python Software Foundation License 2.1.1 +Sleepycat License "1999" +W3C License "2001" +X11 License "2001" +Zlib/libpng License - +Zope Public License 2.0 + +Due to the many variants of some of the above licenses, we require +that any variant of the above licenses be identical in substance to +the form approved by the Open Source Initiative and follow the 2003 +version of the Free Software Foundation's Free Software Definition +(http://www.gnu.org/philosophy/free-sw.html) or version 1.9 of the +Open Source Definition by the Open Source Initiative +(http://www.opensource.org/docs/definition.php). + +2. Definitions + +Terms used, but not defined, herein shall have the meaning provided in +the GPL. + +3. Applicability + +This FLOSS Exception applies to all Viewer Software files that contain +a notice placed by Linden Lab saying that the Viewer Software may be +distributed under the terms of this FLOSS Exception. If you create or +distribute a work which is a work based on the Program for the Viewer +Software and any other work licensed under the GPL, then this FLOSS +Exception is not available for that work; thus, you must remove the +FLOSS Exception notice from that work and comply with the GPL in all +respects, including by retaining all GPL notices. You may choose to +redistribute a copy of the Viewer Software exclusively under the terms +of the GPL by removing the FLOSS Exception notice from that copy of +the Viewer Software, provided that the copy has never been modified by +you or any third party. diff --git a/LICENSES/GPL-license.txt b/LICENSES/GPL-license.txt new file mode 100644 index 000000000..d511905c1 --- /dev/null +++ b/LICENSES/GPL-license.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/LICENSES/LLQTWEBKIT_LICENSE.txt b/LICENSES/LLQTWEBKIT_LICENSE.txt new file mode 100644 index 000000000..405d89131 --- /dev/null +++ b/LICENSES/LLQTWEBKIT_LICENSE.txt @@ -0,0 +1,36 @@ +Source code +======== +The license for the source code in this distribution should be clearly +marked on each source file. Unless otherwise specified, the source +code in this distribution ("Source Code") is provided by Linden Lab to +you under the terms of the GNU General Public License, version 2.0 +("GPL"), unless you have obtained a separate licensing agreement +("Other License"), formally executed by you and Linden Lab. Terms of +the GPL can be found in GPL-license.txt in this distribution, or +online at http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + +There are special exceptions to the terms and conditions of the GPL as +it is applied to this Source Code. View the full text of the exception +in the file FLOSS-exception.txt in this software distribution, or +online at http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + +By copying, modifying or distributing this software, you acknowledge +that you have read and understood your obligations described above, +and agree to abide by those obligations. + +ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO +WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, +COMPLETENESS OR PERFORMANCE. + +Logos and trademarks +============== + +"Second Life" and "Linden Lab" are registered trademarks of Linden +Research, Inc. Other trademarks include (but are not limited to): the +names Linden and Linden Research, as well as the Linden Lab Hexagon +Design and the Second Life Hand Design logos. + +Use of logos and trademarks are subject to the Linden Lab trademark +policy, available at: + +http://secondlife.com/corporate/trademark/ diff --git a/indra/CMakeLists.txt b/indra/CMakeLists.txt index 08daa9681..d5f75fa89 100644 --- a/indra/CMakeLists.txt +++ b/indra/CMakeLists.txt @@ -62,6 +62,10 @@ add_subdirectory(${LIBS_OPEN_PREFIX}llvfs) add_subdirectory(${LIBS_OPEN_PREFIX}llwindow) add_subdirectory(${LIBS_OPEN_PREFIX}llxml) +if(STANDALONE) + add_subdirectory(${LIBS_OPEN_PREFIX}llqtwebkit) +endif(STANDALONE) + if (EXISTS ${LIBS_CLOSED_DIR}llkdu AND NOT STANDALONE) add_subdirectory(${LIBS_CLOSED_PREFIX}llkdu) endif (EXISTS ${LIBS_CLOSED_DIR}llkdu AND NOT STANDALONE) diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 21eee45ac..ce860a988 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -54,6 +54,7 @@ set(cmake_SOURCE_FILES LLMessage.cmake LLPlugin.cmake LLPrimitive.cmake + LLQtWebkit.cmake LLRender.cmake LLScene.cmake LLUI.cmake @@ -70,6 +71,7 @@ set(cmake_SOURCE_FILES PNG.cmake Python.cmake Prebuilt.cmake + Qt4.cmake RunBuildTest.cmake TemplateCheck.cmake Tut.cmake diff --git a/indra/cmake/GooglePerfTools.cmake b/indra/cmake/GooglePerfTools.cmake index c102542e0..c65012162 100644 --- a/indra/cmake/GooglePerfTools.cmake +++ b/indra/cmake/GooglePerfTools.cmake @@ -1,9 +1,9 @@ # -*- cmake -*- include(Prebuilt) -if(WORD_SIZE EQUAL 64) - set(DISABLE_TCMALLOC TRUE) -endif(WORD_SIZE EQUAL 64) +#if(WORD_SIZE EQUAL 64) +# set(DISABLE_TCMALLOC TRUE) +#endif(WORD_SIZE EQUAL 64) if (STANDALONE) include(FindGooglePerfTools) diff --git a/indra/cmake/LLQtWebkit.cmake b/indra/cmake/LLQtWebkit.cmake new file mode 100644 index 000000000..cec7e22e9 --- /dev/null +++ b/indra/cmake/LLQtWebkit.cmake @@ -0,0 +1,11 @@ +# -*- cmake -*- + +if (STANDALONE) + set(LLQTWEBKIT_INCLUDE_DIR + ${LIBS_OPEN_DIR}/llqtwebkit + ) + + set(LLQTWEBKIT_LIBRARY + llqtwebkit + ) +endif (STANDALONE) diff --git a/indra/cmake/Qt4.cmake b/indra/cmake/Qt4.cmake new file mode 100644 index 000000000..37d2799a2 --- /dev/null +++ b/indra/cmake/Qt4.cmake @@ -0,0 +1,12 @@ +# -*- cmake -*- +include(Prebuilt) + +if (STANDALONE) + set(Qt4_FIND_REQUIRED ON) + + include(FindQt4) + + find_package(Qt4 4.7.0 COMPONENTS QtCore QtGui QtNetwork QtOpenGL QtWebKit REQUIRED) + include(${QT_USE_FILE}) + add_definitions(${QT_DEFINITIONS}) +endif (STANDALONE) diff --git a/indra/cmake/WebKitLibPlugin.cmake b/indra/cmake/WebKitLibPlugin.cmake index 7ed824aea..5d1036c6f 100644 --- a/indra/cmake/WebKitLibPlugin.cmake +++ b/indra/cmake/WebKitLibPlugin.cmake @@ -1,31 +1,10 @@ # -*- cmake -*- include(Linking) include(Prebuilt) +include(LLQtWebkit) +include(Qt4) if (STANDALONE) - # The minimal version, 4.4.3, is rather arbitrary: it's the version in Debian/Lenny. - find_package(Qt4 4.4.3 COMPONENTS QtCore QtGui QtNetwork QtOpenGL QtWebKit REQUIRED) - include(${QT_USE_FILE}) - set(QTDIR $ENV{QTDIR}) - if (QTDIR AND NOT "${QT_BINARY_DIR}" STREQUAL "${QTDIR}/bin") - message(FATAL_ERROR "\"${QT_BINARY_DIR}\" is unequal \"${QTDIR}/bin\"; " - "Qt is found by looking for qmake in your PATH. " - "Please set your PATH such that 'qmake' is found in \$QTDIR/bin, " - "or unset QTDIR if the found Qt is correct.") - endif (QTDIR AND NOT "${QT_BINARY_DIR}" STREQUAL "${QTDIR}/bin") - find_package(LLQtWebkit REQUIRED QUIET) - # Add the plugins. - set(QT_PLUGIN_LIBRARIES) - foreach(qlibname qgif qjpeg) - find_library(QT_PLUGIN_${qlibname} ${qlibname} PATHS ${QT_PLUGINS_DIR}/imageformats NO_DEFAULT_PATH) - if (QT_PLUGIN_${qlibname}) - list(APPEND QT_PLUGIN_LIBRARIES ${QT_PLUGIN_${qlibname}}) - else (QT_PLUGIN_${qtlibname}) - message(FATAL_ERROR "Could not find the Qt plugin ${qlibname} in \"${QT_PLUGINS_DIR}/imageformats\"!") - endif (QT_PLUGIN_${qlibname}) - endforeach(qlibname) - # qjpeg depends on libjpeg - list(APPEND QT_PLUGIN_LIBRARIES jpeg) set(WEBKITLIBPLUGIN OFF CACHE BOOL "WEBKITLIBPLUGIN support for the llplugin/llmedia test apps.") else (STANDALONE) diff --git a/indra/llplugin/llpluginclassmedia.cpp b/indra/llplugin/llpluginclassmedia.cpp index c71515496..064b12da1 100644 --- a/indra/llplugin/llpluginclassmedia.cpp +++ b/indra/llplugin/llpluginclassmedia.cpp @@ -39,8 +39,6 @@ #include "llpluginclassmedia.h" #include "llpluginmessageclasses.h" -#include "llqtwebkit.h" - static int LOW_PRIORITY_TEXTURE_SIZE_DEFAULT = 256; static int nextPowerOf2( int value ) diff --git a/indra/llqtwebkit/CMakeLists.txt b/indra/llqtwebkit/CMakeLists.txt new file mode 100644 index 000000000..6fbf5e960 --- /dev/null +++ b/indra/llqtwebkit/CMakeLists.txt @@ -0,0 +1,70 @@ +# -*- cmake -*- + +project(llqtwebkit) + +include(00-Common) +include(Qt4) + +if(NOT WORD_SIZE EQUAL 32) + if(WINDOWS) + add_definitions(/FIXED:NO) + else(WINDOWS) + add_definitions(-fPIC) + endif(WINDOWS) +endif(NOT WORD_SIZE EQUAL 32) + +include_directories(${QT_INCLUDES}) + +add_subdirectory(qtwebkit_cookiejar) +include_directories(qtwebkit_cookiejar/src/) + +set(llqtwebkit_SOURCE_FILES + llembeddedbrowser.cpp + llembeddedbrowserwindow.cpp + lljsobject.cpp + llnetworkaccessmanager.cpp + llqtwebkit.cpp + llstyle.cpp + llwebpage.cpp + llwebpageopenshim.cpp + ) + +set(llqtwebkit_HEADER_FILES + llembeddedbrowser.h + llembeddedbrowser_p.h + llembeddedbrowserwindow.h + llembeddedbrowserwindow_p.h + lljsobject.h + llnetworkaccessmanager.h + llqtwebkit.h + llstyle.h + llwebpage.h + llwebpageopenshim.h + pstdint.h + ) + +set(llqtwebkit_UI_FILES + passworddialog.ui + ) + +set(llqtwebkit_LINK_LIBRARIES + networkcookiejar +) + +QT4_WRAP_UI(llqtwebkit_UI_MOC ${llqtwebkit_UI_FILES}) +QT4_WRAP_CPP(llqtwebkit_HEADERS_MOC ${llqtwebkit_HEADER_FILES}) +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +add_library(llqtwebkit + ${llqtwebkit_SOURCE_FILES} + ${llqtwebkit_HEADERS_MOC} + ${llqtwebkit_UI_MOC} +) + +add_dependencies(llqtwebkit prepare) + +target_link_libraries(llqtwebkit ${llqtwebkit_LINK_LIBRARIES}) + +add_dependencies(llqtwebkit + networkcookiejar +) diff --git a/indra/llqtwebkit/autotests/llembeddedbrowser/llembeddedbrowser.pro b/indra/llqtwebkit/autotests/llembeddedbrowser/llembeddedbrowser.pro new file mode 100644 index 000000000..02ecce5a2 --- /dev/null +++ b/indra/llqtwebkit/autotests/llembeddedbrowser/llembeddedbrowser.pro @@ -0,0 +1,14 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +CONFIG += qtestlib +QT += webkit opengl network + +include(../../llmozlib2.pri) +DEFINES += AUTOTEST + +# Input +SOURCES += tst_llembeddedbrowser.cpp + diff --git a/indra/llqtwebkit/autotests/llembeddedbrowser/tst_llembeddedbrowser.cpp b/indra/llqtwebkit/autotests/llembeddedbrowser/tst_llembeddedbrowser.cpp new file mode 100644 index 000000000..a59cc9e19 --- /dev/null +++ b/indra/llqtwebkit/autotests/llembeddedbrowser/tst_llembeddedbrowser.cpp @@ -0,0 +1,400 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include +#include + +class tst_LLEmbeddedBrowser : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void llembeddedbrowser_data(); + void llembeddedbrowser(); + + void clearAllCookies(); + void clearCache_data(); + void clearCache(); + void clearLastError_data(); + void clearLastError(); + void createBrowserWindow_data(); + void createBrowserWindow(); + void destroyBrowserWindow(); + void enableCookies_data(); + void enableCookies(); + void enablePlugins_data(); + void enablePlugins(); + void enableProxy_data(); + void enableProxy(); + void getGREVersion_data(); + void getGREVersion(); + void getInstance(); + void getLastError_data(); + void getLastError(); + void initBrowser_data(); + void initBrowser(); //change function init as initbrowser + void reset(); + void setBrowserAgentId_data(); + void setBrowserAgentId(); + void setLastError_data(); + void setLastError(); +}; + +// Subclass that exposes the protected functions. +class SubLLEmbeddedBrowser : public LLEmbeddedBrowser +{ +public: + +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_LLEmbeddedBrowser::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_LLEmbeddedBrowser::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_LLEmbeddedBrowser::init() +{ +} + +// This will be called after every test function. +void tst_LLEmbeddedBrowser::cleanup() +{ +} + +void tst_LLEmbeddedBrowser::llembeddedbrowser_data() +{ +} + +void tst_LLEmbeddedBrowser::llembeddedbrowser() +{ + SubLLEmbeddedBrowser browser; + QCOMPARE(browser.clearAllCookies(), false); + QCOMPARE(browser.clearCache(), false); + browser.clearLastError(); + QCOMPARE(browser.enableCookies(false), false); + QCOMPARE(browser.enablePlugins(false), true); + QCOMPARE(browser.enableProxy(false, std::string(""), -1), true); + QCOMPARE(browser.getGREVersion(), std::string(QT_VERSION_STR)); + QVERIFY(browser.getInstance() != NULL); + QCOMPARE(browser.getLastError(), 0); + browser.setBrowserAgentId("uBrowser"); + browser.setLastError(-1); + QCOMPARE(browser.reset(), true); + browser.destroyBrowserWindow(0); + browser.destroyBrowserWindow((LLEmbeddedBrowserWindow*)6); + QCOMPARE(browser.getWindowCount(), 0); + QCOMPARE(browser.init(std::string(""),std::string(""),std::string(""),0), true); +} + +// public bool clearAllCookies() +void tst_LLEmbeddedBrowser::clearAllCookies() +{ + SubLLEmbeddedBrowser browser; + + QCOMPARE(browser.clearAllCookies(), false); + browser.reset(); + QCOMPARE(browser.clearAllCookies(), true); +} + +void tst_LLEmbeddedBrowser::clearCache_data() +{ + QTest::addColumn("clearCache"); +#if QT_VERSION < 0x040500 + QTest::newRow("QTVersion < 4.5") << false; +#else + QTest::newRow("QTVersion > 4.5") << true; +#endif +} + +// public bool clearCache() +void tst_LLEmbeddedBrowser::clearCache() +{ + QFETCH(bool, clearCache); + + SubLLEmbeddedBrowser browser; + browser.reset(); + QCOMPARE(browser.clearCache(), clearCache); +} + +void tst_LLEmbeddedBrowser::clearLastError_data() +{ + QTest::addColumn("lastError"); + QTest::newRow("1") << 1; +} + +// public void clearLastError() +void tst_LLEmbeddedBrowser::clearLastError() +{ + SubLLEmbeddedBrowser browser; + QFETCH(int, lastError); + + browser.setLastError(lastError); + browser.clearLastError(); + QCOMPARE(browser.getLastError(), 0); +} + +void tst_LLEmbeddedBrowser::createBrowserWindow_data() +{ + QTest::addColumn("width"); + QTest::addColumn("height"); + QTest::newRow("0,0") << 0 << 0; + QTest::newRow("800,600") << 800 << 600; +} + +// public LLEmbeddedBrowserWindow* createBrowserWindow(int width, int height) +void tst_LLEmbeddedBrowser::createBrowserWindow() +{ + QFETCH(int, width); + QFETCH(int, height); + SubLLEmbeddedBrowser browser; + + LLEmbeddedBrowserWindow *window = browser.createBrowserWindow(width, height); + QVERIFY(window); + QCOMPARE(browser.getLastError(), 0); + QCOMPARE(browser.getWindowCount(), 1); + QCOMPARE(window->getBrowserWidth(), (int16_t)width); + QCOMPARE(window->getBrowserHeight(), (int16_t)height); +} + +// public bool destroyBrowserWindow(LLEmbeddedBrowserWindow* browser_window) +void tst_LLEmbeddedBrowser::destroyBrowserWindow() +{ + SubLLEmbeddedBrowser browser; + browser.reset(); + LLEmbeddedBrowserWindow* browser_window = browser.createBrowserWindow(200, 100); + if (browser_window) + { + QCOMPARE(browser.getWindowCount(), 1); + browser.destroyBrowserWindow(browser_window); + QCOMPARE(browser.getLastError(), 0); + QCOMPARE(browser.getWindowCount(), 0); + } + + browser_window = browser.createBrowserWindow(800, 600); + if (browser_window) + { + QCOMPARE(browser.getWindowCount(), 1); + browser.destroyBrowserWindow(browser_window); + QCOMPARE(browser.getLastError(), 0); + QCOMPARE(browser.getWindowCount(), 0); + } +} + +void tst_LLEmbeddedBrowser::enableCookies_data() +{ + QTest::addColumn("enabled"); + QTest::addColumn("enableCookies"); + QTest::newRow("disable") << false << false; + QTest::newRow("enable") << true << false; +} + +// public bool enableCookies(bool enabled) +void tst_LLEmbeddedBrowser::enableCookies() +{ + QFETCH(bool, enabled); + QFETCH(bool, enableCookies); + + SubLLEmbeddedBrowser browser; + browser.reset(); + QCOMPARE(browser.enableCookies(enabled), enableCookies); + // TODO check that cookies are not saved +} + +void tst_LLEmbeddedBrowser::enablePlugins_data() +{ + QTest::addColumn("enabled"); + QTest::addColumn("enablePlugins"); + QTest::newRow("disable") << false << true; + QTest::newRow("enable") << true << true; +} + +// public bool enablePlugins(bool enabled) +void tst_LLEmbeddedBrowser::enablePlugins() +{ + QFETCH(bool, enabled); + QFETCH(bool, enablePlugins); + + SubLLEmbeddedBrowser browser; + browser.reset(); + QCOMPARE(browser.enablePlugins(enabled), enablePlugins); + // TODO check that plugins work/do not work +} + +Q_DECLARE_METATYPE(std::string) +void tst_LLEmbeddedBrowser::enableProxy_data() +{ + QTest::addColumn("enabled"); + QTest::addColumn("host_name"); + QTest::addColumn("port"); + QTest::addColumn("enableProxy"); + QTest::newRow("null") << false << std::string() << 0 << true; + QTest::newRow("valid") << true << std::string("wtfsurf.com") << 80 << true; +} + +// public bool enableProxy(bool enabled, std::string host_name, int port) +void tst_LLEmbeddedBrowser::enableProxy() +{ + QFETCH(bool, enabled); + QFETCH(std::string, host_name); + QFETCH(int, port); + QFETCH(bool, enableProxy); + + SubLLEmbeddedBrowser browser; + browser.reset(); + QCOMPARE(browser.enableProxy(enabled, host_name, port), enableProxy); + // TODO need some proxy servers to test this +} + +void tst_LLEmbeddedBrowser::getGREVersion_data() +{ + QTest::addColumn("getGREVersion"); + QTest::newRow("valid") << std::string(QT_VERSION_STR); +} + +// public std::string getGREVersion() +void tst_LLEmbeddedBrowser::getGREVersion() +{ + QFETCH(std::string, getGREVersion); + + SubLLEmbeddedBrowser browser; + browser.reset(); + QCOMPARE(browser.getGREVersion(), getGREVersion); +} + +// public static LLEmbeddedBrowser* getInstance() +void tst_LLEmbeddedBrowser::getInstance() +{ + SubLLEmbeddedBrowser browser; + QVERIFY(browser.getInstance() != NULL); +} + +void tst_LLEmbeddedBrowser::getLastError_data() +{ + QTest::addColumn("error"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; + QTest::newRow("100") << 100; +} + +// public int getLastError() +void tst_LLEmbeddedBrowser::getLastError() +{ + QFETCH(int, error); + SubLLEmbeddedBrowser browser; + browser.setLastError(error); + QCOMPARE(browser.getLastError(), error); +} + +void tst_LLEmbeddedBrowser::initBrowser_data() +{ + QTest::addColumn("application_directory"); + QTest::addColumn("component_directory"); + QTest::addColumn("profile_directory"); + QTest::addColumn("native_window_handleCount"); + QTest::addColumn("init"); + QTest::newRow("null") << std::string() << std::string() << std::string() << (void *)0 << true; + QTest::newRow("valid") << std::string("/home/crystal/Settings/") << std::string() << std::string() << (void *)0 << true; +} +void tst_LLEmbeddedBrowser::initBrowser() +{ + QFETCH(std::string, application_directory); + QFETCH(std::string, component_directory); + QFETCH(std::string, profile_directory); + QFETCH(void *, native_window_handleCount); + SubLLEmbeddedBrowser browser; + browser.init(application_directory,component_directory,profile_directory,native_window_handleCount); + QCOMPARE(browser.getLastError(), 0); +} + +// public bool reset() +void tst_LLEmbeddedBrowser::reset() +{ + SubLLEmbeddedBrowser browser; + + browser.setLastError(100); + QCOMPARE(browser.getLastError(), 100); + QVERIFY(browser.reset()); + QCOMPARE(browser.getLastError(), 0); + // TODO what should reset really do? +} + +void tst_LLEmbeddedBrowser::setBrowserAgentId_data() +{ + QTest::addColumn("id"); + QTest::newRow("null") << std::string(); + QTest::newRow("valid") << std::string("uBrowser"); + +} + +// public void setBrowserAgentId(std::string id) +void tst_LLEmbeddedBrowser::setBrowserAgentId() +{ + QFETCH(std::string, id); + + SubLLEmbeddedBrowser browser; + browser.reset(); + browser.setBrowserAgentId(id); + LLEmbeddedBrowserWindow *window = browser.createBrowserWindow(0, 0); + Q_UNUSED(window); + // TODO confirm that the page is actually sending the agent ID +} + +void tst_LLEmbeddedBrowser::setLastError_data() +{ + QTest::addColumn("error_number"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; + QTest::newRow("100") << 100; +} + +// public void setLastError(int error_number) +void tst_LLEmbeddedBrowser::setLastError() +{ + QFETCH(int, error_number); + + SubLLEmbeddedBrowser browser; + + browser.setLastError(error_number); + QCOMPARE(browser.getLastError(), error_number); +} + +QTEST_MAIN(tst_LLEmbeddedBrowser) +#include "tst_llembeddedbrowser.moc" + diff --git a/indra/llqtwebkit/autotests/llembeddedbrowserwindow/llembeddedbrowserwindow.pro b/indra/llqtwebkit/autotests/llembeddedbrowserwindow/llembeddedbrowserwindow.pro new file mode 100644 index 000000000..a89f50065 --- /dev/null +++ b/indra/llqtwebkit/autotests/llembeddedbrowserwindow/llembeddedbrowserwindow.pro @@ -0,0 +1,14 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +CONFIG += qtestlib +QT += webkit opengl network + +include(../../llmozlib2.pri) +DEFINES += AUTOTEST + +# Input +SOURCES += tst_llembeddedbrowserwindow.cpp + diff --git a/indra/llqtwebkit/autotests/llembeddedbrowserwindow/tst_llembeddedbrowserwindow.cpp b/indra/llqtwebkit/autotests/llembeddedbrowserwindow/tst_llembeddedbrowserwindow.cpp new file mode 100644 index 000000000..4c365d371 --- /dev/null +++ b/indra/llqtwebkit/autotests/llembeddedbrowserwindow/tst_llembeddedbrowserwindow.cpp @@ -0,0 +1,1027 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include + +#include "llembeddedbrowserwindow.h" +#include "llembeddedbrowser.h" + +#ifndef QTRY_COMPARE + +#define __TRY_TIMEOUT__ 10000 +#define __TRY_STEP__ 50 + +#define __QTRY(__expression__, __functionToCall__) \ + do { \ + int __i = 0; \ + while (!(__expression__) && __i < __TRY_TIMEOUT__) { \ + QTest::qWait(__TRY_STEP__); \ + __i += __TRY_STEP__; \ + } \ + __functionToCall__; \ + } while(0) + +#define QTRY_COMPARE(__expression__, __expected__) \ + __QTRY((__expression__ == __expected__), QCOMPARE(__expression__, __expected__)); + +#define QTRY_VERIFY(__expression__) \ + __QTRY(__expression__, QVERIFY(__expression__)); + +#endif // QTRY_COMPARE + +class tst_LLEmbeddedBrowserWindow : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void llembeddedbrowserwindow_data(); + void llembeddedbrowserwindow(); + + void addObserver_data(); + void addObserver(); + void canNavigateBack_data(); + void canNavigateBack(); + void canNavigateForward_data(); + void canNavigateForward(); + void evaluateJavascript_data(); + void evaluateJavascript(); + void flipWindow_data(); + void flipWindow(); + void focusBrowser_data(); + void focusBrowser(); + void getBrowserDepth_data(); + void getBrowserDepth(); + void getBrowserHeight_data(); + void getBrowserHeight(); + void getBrowserRowSpan_data(); + void getBrowserRowSpan(); + void getBrowserWidth_data(); + void getBrowserWidth(); + void getClickLinkHref_data(); + void getClickLinkHref(); + void getClickLinkTarget_data(); + void getClickLinkTarget(); + void getCurrentUri_data(); + void getCurrentUri(); + void getNoFollowScheme_data(); + void getNoFollowScheme(); + void getPageBuffer_data(); + void getPageBuffer(); + void getPercentComplete_data(); + void getPercentComplete(); + void getStatusMsg_data(); + void getStatusMsg(); + void getWindowId_data(); + void getWindowId(); + void grabWindow_data(); + void grabWindow(); + void keyPress_data(); + void keyPress(); + void mouseDown_data(); + void mouseDown(); + void mouseLeftDoubleClick_data(); + void mouseLeftDoubleClick(); + void mouseMove_data(); + void mouseMove(); + void mouseUp_data(); + void mouseUp(); + void navigateBack_data(); + void navigateBack(); + void navigateForward_data(); + void navigateForward(); + void navigateReload_data(); + void navigateReload(); + void navigateStop_data(); + void navigateStop(); + void navigateTo_data(); + void navigateTo(); + void remObserver_data(); + void remObserver(); + void scrollByLines_data(); + void scrollByLines(); + void setBackgroundColor_data(); + void setBackgroundColor(); + void setCaretColor_data(); + void setCaretColor(); + void setEnabled_data(); + void setEnabled(); + void setNoFollowScheme_data(); + void setNoFollowScheme(); + void setParent_data(); + void setParent(); + void setSize_data(); + void setSize(); + void setWindowId_data(); + void setWindowId(); + void unicodeInput_data(); + void unicodeInput(); +}; + +// Subclass that exposes the protected functions. +class SubLLEmbeddedBrowserWindow : public LLEmbeddedBrowserWindow +{ +public: + +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_LLEmbeddedBrowserWindow::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_LLEmbeddedBrowserWindow::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_LLEmbeddedBrowserWindow::init() +{ +} + +// This will be called after every test function. +void tst_LLEmbeddedBrowserWindow::cleanup() +{ +} + +void tst_LLEmbeddedBrowserWindow::llembeddedbrowserwindow_data() +{ +} + +void tst_LLEmbeddedBrowserWindow::llembeddedbrowserwindow() +{ + SubLLEmbeddedBrowserWindow window; + QCOMPARE(window.addObserver((LLEmbeddedBrowserWindowObserver*)0), false); + QCOMPARE(window.canNavigateBack(), false); + QCOMPARE(window.canNavigateForward(), false); + QCOMPARE(window.evaluateJavascript(std::string()), std::string()); + QCOMPARE(window.flipWindow(false), true); + window.focusBrowser(false); + QCOMPARE(window.getBrowserDepth(), (int16_t)4); + QCOMPARE(window.getBrowserHeight(), (int16_t)0); + QCOMPARE(window.getBrowserRowSpan(), (int32_t)0); + QCOMPARE(window.getBrowserWidth(), (int16_t)0); + QCOMPARE(window.getClickLinkHref(), std::string()); + QCOMPARE(window.getClickLinkTarget(), std::string()); + QCOMPARE(window.getCurrentUri(), std::string()); + QCOMPARE(window.getNoFollowScheme(), std::string("secondlife")); + QCOMPARE(window.getPageBuffer(), (unsigned char*)0); + QCOMPARE(window.getPercentComplete(), (int16_t)0); + QCOMPARE(window.getStatusMsg(), std::string()); + QCOMPARE(window.getWindowId(), -1); + QCOMPARE(window.grabWindow(-1, -1, -1, -1), (unsigned char*)0); + window.keyPress(0); + window.mouseDown(0, 0); + window.mouseLeftDoubleClick(0, 0); + window.mouseMove(0, 0); + window.mouseUp(0, 0); + window.navigateBack(); + window.navigateForward(); + window.navigateReload(); + window.navigateStop(); + QCOMPARE(window.navigateTo(std::string()), true); + QCOMPARE(window.remObserver((LLEmbeddedBrowserWindowObserver*)0), false); + window.scrollByLines(0); + window.setBackgroundColor(0, 0, 0); + window.setCaretColor(0, 0, 0); + window.setEnabled(false); + window.setNoFollowScheme(std::string()); + window.setParent((LLEmbeddedBrowser*)0); + QCOMPARE(window.setSize(0, 0), true); + window.setWindowId(-1); + window.unicodeInput((uint32_t)0); +} + +void tst_LLEmbeddedBrowserWindow::addObserver_data() +{ +#if 0 + QTest::addColumn("observerCount"); + QTest::addColumn("addObserver"); + QTest::newRow("null") << 0 << false; +#endif +} + +// public bool addObserver(LLEmbeddedBrowserWindowObserver* observer) +void tst_LLEmbeddedBrowserWindow::addObserver() +{ +#if 0 + QFETCH(int, observerCount); + QFETCH(bool, addObserver); + + SubLLEmbeddedBrowserWindow window; + + QCOMPARE(window.addObserver(observer), addObserver); +#endif + QSKIP("Test is same with remObserver.", SkipAll); +} + +void tst_LLEmbeddedBrowserWindow::canNavigateBack_data() +{ +#if 0 + QTest::addColumn("canNavigateBack"); + QTest::newRow("true") << true; + QTest::newRow("false") << false; +#endif +} + +// public bool canNavigateBack() +void tst_LLEmbeddedBrowserWindow::canNavigateBack() +{ + //QFETCH(bool, canNavigateForward); + + SubLLEmbeddedBrowserWindow window; + window.setSize(800,600); + window.setParent(new LLEmbeddedBrowser()); + QCOMPARE(window.canNavigateForward(), false); + window.navigateTo(std::string("http://www.google.com")); + QTest::qWait(__TRY_TIMEOUT__); + QCOMPARE(window.canNavigateBack(), false); + window.navigateTo(std::string("http://www.cnn.com")); + QTest::qWait(__TRY_TIMEOUT__); + QCOMPARE(window.canNavigateBack(), true); + window.navigateBack(); + QTRY_COMPARE(window.canNavigateForward(), true); + window.navigateForward(); + QTRY_COMPARE(window.canNavigateBack(), true); +} + +void tst_LLEmbeddedBrowserWindow::canNavigateForward_data() +{ +#if 0 + QTest::addColumn("canNavigateForward"); + QTest::newRow("true") << true; + QTest::newRow("false") << false; +#endif +} + +// public bool canNavigateForward() +void tst_LLEmbeddedBrowserWindow::canNavigateForward() +{ + QSKIP("Test is same with canNavigateBack().", SkipAll); +} + +Q_DECLARE_METATYPE(std::string) +void tst_LLEmbeddedBrowserWindow::evaluateJavascript_data() +{ + QTest::addColumn("script"); + QTest::addColumn("evaluateJavascript"); + QTest::newRow("null") << std::string() << std::string(); + //QTest::newRow("valid") << std::string("alert(\"hey!\")") << std::string("alert(\"hey!\")"); +} + +// public std::string evaluateJavascript(std::string script) +void tst_LLEmbeddedBrowserWindow::evaluateJavascript() +{ + QFETCH(std::string, script); + QFETCH(std::string, evaluateJavascript); + + SubLLEmbeddedBrowserWindow window; + + window.evaluateJavascript(script); +} + +void tst_LLEmbeddedBrowserWindow::flipWindow_data() +{ + QTest::addColumn("flip"); + QTest::addColumn("flipWindow"); + QTest::newRow("false") << false << true; + QTest::newRow("true") << true << true; +} + +// public bool flipWindow(bool flip) +void tst_LLEmbeddedBrowserWindow::flipWindow() +{ + QFETCH(bool, flip); + QFETCH(bool, flipWindow); + + SubLLEmbeddedBrowserWindow window; + + QCOMPARE(window.flipWindow(flip), flipWindow); +} + +void tst_LLEmbeddedBrowserWindow::focusBrowser_data() +{ + QTest::addColumn("focus_browser"); + QTest::newRow("true") << true; + QTest::newRow("false") << false; +} + +// public void focusBrowser(bool focus_browser) +void tst_LLEmbeddedBrowserWindow::focusBrowser() +{ + QFETCH(bool, focus_browser); + + SubLLEmbeddedBrowserWindow window; + window.focusBrowser(focus_browser); +} + +Q_DECLARE_METATYPE(int16_t) +void tst_LLEmbeddedBrowserWindow::getBrowserDepth_data() +{ +#if 0 + QTest::addColumn("getBrowserDepth"); + QTest::newRow("null") << int16_t(); +#endif +} + +// public int16_t getBrowserDepth() +void tst_LLEmbeddedBrowserWindow::getBrowserDepth() +{ + //QFETCH(int16_t, getBrowserDepth); + + SubLLEmbeddedBrowserWindow window; + + QCOMPARE(window.getBrowserDepth(), int16_t(4)); +} + +void tst_LLEmbeddedBrowserWindow::getBrowserHeight_data() +{ +#if 0 + QTest::addColumn("getBrowserHeight"); + QTest::newRow("null") << int16_t(); +#endif +} + +// public int16_t getBrowserHeight() +void tst_LLEmbeddedBrowserWindow::getBrowserHeight() +{ +#if 0 + QFETCH(int16_t, getBrowserHeight); + + SubLLEmbeddedBrowserWindow window; + + QCOMPARE(window.getBrowserHeight(), getBrowserHeight); +#endif + QSKIP("Test is same with setSize().", SkipAll); +} + +Q_DECLARE_METATYPE(int32_t) +void tst_LLEmbeddedBrowserWindow::getBrowserRowSpan_data() +{ +#if 0 + QTest::addColumn("getBrowserRowSpan"); + QTest::newRow("null") << int32_t(); +#endif +} + +// public int32_t getBrowserRowSpan() +void tst_LLEmbeddedBrowserWindow::getBrowserRowSpan() +{ +#if 0 + SubLLEmbeddedBrowserWindow window; + window.setSize(0, 0); + + QCOMPARE(window.getBrowserWidth(), int16_t(0)); + QCOMPARE(window.getBrowserRowSpan(), int32_t(0)); + window.setSize(100, 100); + + QCOMPARE(window.getBrowserWidth(), int16_t(100)); + QCOMPARE(window.getBrowserRowSpan(), int32_t(400)); +#endif + QSKIP("Test is same with setSize().", SkipAll); +} + +void tst_LLEmbeddedBrowserWindow::getBrowserWidth_data() +{ +#if 0 + QTest::addColumn("getBrowserWidth"); + QTest::newRow("null") << int16_t(); +#endif +} + +// public int16_t getBrowserWidth() +void tst_LLEmbeddedBrowserWindow::getBrowserWidth() +{ +#if 0 + //QFETCH(int16_t, getBrowserWidth); + + SubLLEmbeddedBrowserWindow window; + window.setSize(0, 0); + + QCOMPARE(window.getBrowserWidth(), int16_t(0)); + QCOMPARE(window.getBrowserHeight(), int16_t(0)); + window.setSize(100, 100); + + QCOMPARE(window.getBrowserWidth(), int16_t(100)); + QCOMPARE(window.getBrowserHeight(), int16_t(100)); +#endif + QSKIP("Test is same with setSize().", SkipAll); +} + +//Q_DECLARE_METATYPE(std::string const) +void tst_LLEmbeddedBrowserWindow::getClickLinkHref_data() +{ +#if 0 + QTest::addColumn("getClickLinkHref"); + QTest::newRow("null") << std::string const(); +#endif +} + +// public std::string const getClickLinkHref() +void tst_LLEmbeddedBrowserWindow::getClickLinkHref() +{ + //QFETCH(std::string const, getClickLinkHref); + + SubLLEmbeddedBrowserWindow window; + + window.getClickLinkHref(); +} + +void tst_LLEmbeddedBrowserWindow::getClickLinkTarget_data() +{ +#if 0 + QTest::addColumn("getClickLinkTarget"); + QTest::newRow("null") << std::string const(); +#endif +} + +// public std::string const getClickLinkTarget() +void tst_LLEmbeddedBrowserWindow::getClickLinkTarget() +{ + //QFETCH(std::string const, getClickLinkTarget); + + SubLLEmbeddedBrowserWindow window; + + window.getClickLinkTarget(); +} + +void tst_LLEmbeddedBrowserWindow::getCurrentUri_data() +{ +#if 0 + QTest::addColumn("getCurrentUri"); + QTest::newRow("null") << std::string const(); +#endif +} + +// public std::string const getCurrentUri() +void tst_LLEmbeddedBrowserWindow::getCurrentUri() +{ + //QFETCH(std::string const, getCurrentUri); + + SubLLEmbeddedBrowserWindow window; + window.navigateTo(std::string("http://www.google.ca/")); + QTRY_COMPARE(QString::fromStdString(window.getCurrentUri()), QString::fromStdString(std::string("http://www.google.ca/"))); +} + +void tst_LLEmbeddedBrowserWindow::getNoFollowScheme_data() +{ +#if 0 + QTest::addColumn("getNoFollowScheme"); + QTest::newRow("FTP") << std::string("FTP"); +#endif +} + +// public std::string getNoFollowScheme() +void tst_LLEmbeddedBrowserWindow::getNoFollowScheme() +{ + //QFETCH(std::string, getNoFollowScheme); + + SubLLEmbeddedBrowserWindow window; + window.setNoFollowScheme("FTP://www.google.com"); + + QCOMPARE(window.getNoFollowScheme(), std::string("FTP")); +} + +//Q_DECLARE_METATYPE(unsigned char*) +void tst_LLEmbeddedBrowserWindow::getPageBuffer_data() +{ +#if 0 + QTest::addColumn("getPageBuffer"); + QTest::newRow("null") << unsigned char*(); +#endif +} + +// public unsigned char* getPageBuffer() +void tst_LLEmbeddedBrowserWindow::getPageBuffer() +{ + //QFETCH(unsigned char*, getPageBuffer); + + SubLLEmbeddedBrowserWindow window; + window.setSize(100,100); + window.grabWindow(0, 0, 100, 100); + + QVERIFY(window.getPageBuffer() != NULL); +} + +//Q_DECLARE_METATYPE(int16_t const) +void tst_LLEmbeddedBrowserWindow::getPercentComplete_data() +{ +#if 0 + QTest::addColumn("getPercentComplete"); + QTest::newRow("null") << int16_t const(); +#endif +} + +// public int16_t const getPercentComplete() +void tst_LLEmbeddedBrowserWindow::getPercentComplete() +{ + //QFETCH(int16_t const, getPercentComplete); + SubLLEmbeddedBrowserWindow window; + window.navigateTo(std::string("http://www.google.com")); + QTest::qWait(1000); + QVERIFY(window.getPercentComplete() > 0); +} + +void tst_LLEmbeddedBrowserWindow::getStatusMsg_data() +{ +#if 0 + QTest::addColumn("getStatusMsg"); + QTest::newRow("null") << std::string const(); +#endif +} + +// public std::string const getStatusMsg() +void tst_LLEmbeddedBrowserWindow::getStatusMsg() +{ + //QFETCH(std::string const, getStatusMsg); + + SubLLEmbeddedBrowserWindow window; + window.navigateTo(std::string("http://www.google.com")); + QTest::qWait(1000); + window.navigateStop(); + window.navigateTo(std::string("http://www.yahoo.com")); + // Seems status msg will always be null during navigating. + //QTRY_VERIFY(QString::fromStdString(window.getStatusMsg())!= NULL); + QSKIP("Status msg will always be null during navigating", SkipAll); +} + +void tst_LLEmbeddedBrowserWindow::getWindowId_data() +{ +#if 0 + QTest::addColumn("getWindowId"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; +#endif +} + +// public int getWindowId() +void tst_LLEmbeddedBrowserWindow::getWindowId() +{ + //QFETCH(int, getWindowId); + + SubLLEmbeddedBrowserWindow window; + window.setWindowId(0); + QCOMPARE(window.getWindowId(), 0); + window.setWindowId(100); + QCOMPARE(window.getWindowId(), 100); +} + +void tst_LLEmbeddedBrowserWindow::grabWindow_data() +{ +#if 0 + QTest::addColumn("x"); + QTest::addColumn("y"); + QTest::addColumn("width"); + QTest::addColumn("height"); + QTest::addColumn("grabWindow"); + QTest::newRow("null") << 0 << 0 << 0 << 0 << 0; +#endif +} + +// public unsigned char* grabWindow(int x, int y, int width, int height) +void tst_LLEmbeddedBrowserWindow::grabWindow() +{ + QSKIP("Test is same with getPageBuffer().", SkipAll); +} + +void tst_LLEmbeddedBrowserWindow::keyPress_data() +{ + QTest::addColumn("key_code"); + QTest::newRow("null") << int16_t(0); + QTest::newRow("valid") << int16_t(0x0E); +} + +// public void keyPress(int16_t key_code) +void tst_LLEmbeddedBrowserWindow::keyPress() +{ + QFETCH(int16_t, key_code); + + SubLLEmbeddedBrowserWindow window; + window.keyPress(key_code); +} + +void tst_LLEmbeddedBrowserWindow::mouseDown_data() +{ + QTest::addColumn("x"); + QTest::addColumn("y"); + QTest::newRow("0") << int16_t(0) << int16_t(0); + QTest::newRow("bignumber") << int16_t(100000) << int16_t(100000); + QTest::newRow("valid") << int16_t(100) << int16_t(100); +} + +// public void mouseDown(int16_t x, int16_t y) +void tst_LLEmbeddedBrowserWindow::mouseDown() +{ + QFETCH(int16_t, x); + QFETCH(int16_t, y); + + SubLLEmbeddedBrowserWindow window; + window.mouseDown(x, y); +} + +void tst_LLEmbeddedBrowserWindow::mouseLeftDoubleClick_data() +{ + QTest::addColumn("x"); + QTest::addColumn("y"); + QTest::newRow("0") << int16_t(0) << int16_t(0); + QTest::newRow("bignumber") << int16_t(100000) << int16_t(100000); + QTest::newRow("valid") << int16_t(100) << int16_t(100); +} + +// public void mouseLeftDoubleClick(int16_t x, int16_t y) +void tst_LLEmbeddedBrowserWindow::mouseLeftDoubleClick() +{ + QFETCH(int16_t, x); + QFETCH(int16_t, y); + + SubLLEmbeddedBrowserWindow window; + window.mouseLeftDoubleClick(x, y); +} + +void tst_LLEmbeddedBrowserWindow::mouseMove_data() +{ + QTest::addColumn("x"); + QTest::addColumn("y"); + QTest::newRow("0") << int16_t(0) << int16_t(0); + QTest::newRow("bignumber") << int16_t(100000) << int16_t(100000); + QTest::newRow("valid") << int16_t(100) << int16_t(100); +} + +// public void mouseMove(int16_t x, int16_t y) +void tst_LLEmbeddedBrowserWindow::mouseMove() +{ + QFETCH(int16_t, x); + QFETCH(int16_t, y); + + SubLLEmbeddedBrowserWindow window; + window.mouseMove(x, y); +} + +void tst_LLEmbeddedBrowserWindow::mouseUp_data() +{ + QTest::addColumn("x"); + QTest::addColumn("y"); + QTest::newRow("0") << int16_t(0) << int16_t(0); + QTest::newRow("bignumber") << int16_t(100000) << int16_t(100000); + QTest::newRow("valid") << int16_t(100) << int16_t(100); +} + +// public void mouseUp(int16_t x, int16_t y) +void tst_LLEmbeddedBrowserWindow::mouseUp() +{ + QFETCH(int16_t, x); + QFETCH(int16_t, y); + + SubLLEmbeddedBrowserWindow window; + window.mouseUp(x, y); +} + +void tst_LLEmbeddedBrowserWindow::navigateBack_data() +{ +#if 0 + QTest::addColumn("foo"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; +#endif +} + +// public void navigateBack() +void tst_LLEmbeddedBrowserWindow::navigateBack() +{ + //QFETCH(int, foo); + + SubLLEmbeddedBrowserWindow window; + window.navigateTo(std::string("http://www.google.ca/")); + QTest::qWait(__TRY_TIMEOUT__); + QCOMPARE(window.canNavigateForward(), false); + window.navigateTo(std::string("http://www.yahoo.com/")); + QTest::qWait(__TRY_TIMEOUT__); + QCOMPARE(window.canNavigateBack(), true); + window.navigateBack(); + QTRY_COMPARE(QString::fromStdString((window.getCurrentUri())), QString("http://www.google.ca/")); + window.navigateBack(); + QTRY_COMPARE(QString::fromStdString((window.getCurrentUri())), QString("http://www.google.ca/")); +} + +void tst_LLEmbeddedBrowserWindow::navigateForward_data() +{ +#if 0 + QTest::addColumn("foo"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; +#endif +} + +// public void navigateForward() +void tst_LLEmbeddedBrowserWindow::navigateForward() +{ + // QFETCH(int, foo); + SubLLEmbeddedBrowserWindow window; + window.navigateTo(std::string("http://www.google.ca/")); + QTest::qWait(__TRY_TIMEOUT__); + QCOMPARE(window.canNavigateForward(), false); + window.navigateTo(std::string("http://www.yahoo.ca/")); + QTest::qWait(__TRY_TIMEOUT__); + QCOMPARE(window.canNavigateBack(), true); + window.navigateBack(); + QTRY_COMPARE(QString::fromStdString((window.getCurrentUri())), QString("http://www.google.ca/")); + window.navigateForward(); + QTRY_COMPARE(QString::fromStdString((window.getCurrentUri())), QString("http://ca.yahoo.com/")); + window.navigateForward(); + QTRY_COMPARE(QString::fromStdString((window.getCurrentUri())), QString("http://ca.yahoo.com/")); +} + +void tst_LLEmbeddedBrowserWindow::navigateReload_data() +{ +#if 0 + QTest::addColumn("foo"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; +#endif +} + +// public void navigateReload() +void tst_LLEmbeddedBrowserWindow::navigateReload() +{ + SubLLEmbeddedBrowserWindow window; + + window.navigateTo(std::string("http://www.google.ca/")); + QTest::qWait(__TRY_TIMEOUT__); + window.navigateReload(); + QTRY_COMPARE(QString::fromStdString((window.getCurrentUri())), QString("http://www.google.ca/")); +} + +void tst_LLEmbeddedBrowserWindow::navigateStop_data() +{ +#if 0 + QTest::addColumn("foo"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; +#endif +} + +// public void navigateStop() +void tst_LLEmbeddedBrowserWindow::navigateStop() +{ + SubLLEmbeddedBrowserWindow window; + window.navigateTo("www.google.com"); + window.navigateStop(); +} + +void tst_LLEmbeddedBrowserWindow::navigateTo_data() +{ + QTest::addColumn("uri"); + QTest::addColumn("navigateTo"); + QTest::newRow("null") << std::string() << std::string(); + QTest::newRow("valid") << std::string("http://www.google.ca/") << std::string("http://www.google.ca/"); +} + +// public bool navigateTo(std::string const uri) +void tst_LLEmbeddedBrowserWindow::navigateTo() +{ + QSKIP("Test is same with navigateBack(), navigateForward().", SkipAll); +} + +void tst_LLEmbeddedBrowserWindow::remObserver_data() +{ +#if 0 + QTest::addColumn("observerCount"); + QTest::addColumn("remObserver"); + QTest::newRow("null") << 0 << false; +#endif +} + +// public bool remObserver(LLEmbeddedBrowserWindowObserver* observer) +void tst_LLEmbeddedBrowserWindow::remObserver() +{ +// QFETCH(int, observerCount); +// QFETCH(bool, remObserver); + + SubLLEmbeddedBrowserWindow window; + LLEmbeddedBrowserWindowObserver* observer = new LLEmbeddedBrowserWindowObserver(); + window.addObserver(observer); + QCOMPARE(window.getObserverNumber(), 1); + window.remObserver(observer); + QCOMPARE(window.getObserverNumber(), 0); +} + +void tst_LLEmbeddedBrowserWindow::scrollByLines_data() +{ + QTest::addColumn("lines"); + QTest::newRow("null") << int16_t(0); + QTest::addColumn("lines"); + QTest::newRow("100") << int16_t(100); +} + +// public void scrollByLines(int16_t lines) +void tst_LLEmbeddedBrowserWindow::scrollByLines() +{ + QFETCH(int16_t, lines); + + SubLLEmbeddedBrowserWindow window; + + window.scrollByLines(lines); +} + +Q_DECLARE_METATYPE(uint8_t) +void tst_LLEmbeddedBrowserWindow::setBackgroundColor_data() +{ + QTest::addColumn("red"); + QTest::addColumn("green"); + QTest::addColumn("blue"); + QTest::newRow("black") << uint8_t(0) << uint8_t(0) << uint8_t(0); + QTest::newRow("red") << uint8_t(255) << uint8_t(0) << uint8_t(0); + QTest::newRow("green") << uint8_t(0) << uint8_t(255) << uint8_t(0); + QTest::newRow("blue") << uint8_t(0) << uint8_t(0) << uint8_t(255); +} + +// public void setBackgroundColor(uint8_t const red, uint8_t const green, uint8_t const blue) +void tst_LLEmbeddedBrowserWindow::setBackgroundColor() +{ + QFETCH(uint8_t, red); + QFETCH(uint8_t, green); + QFETCH(uint8_t, blue); + + SubLLEmbeddedBrowserWindow window; + + window.setBackgroundColor(red, green, blue); +} + +void tst_LLEmbeddedBrowserWindow::setCaretColor_data() +{ + QTest::addColumn("red"); + QTest::addColumn("green"); + QTest::addColumn("blue"); + QTest::newRow("black") << uint8_t(0) << uint8_t(0) << uint8_t(0); + QTest::newRow("red") << uint8_t(255) << uint8_t(0) << uint8_t(0); + QTest::newRow("green") << uint8_t(0) << uint8_t(255) << uint8_t(0); + QTest::newRow("blue") << uint8_t(0) << uint8_t(0) << uint8_t(255); +} + +// public void setCaretColor(uint8_t const red, uint8_t const green, uint8_t const blue) +void tst_LLEmbeddedBrowserWindow::setCaretColor() +{ + QFETCH(uint8_t, red); + QFETCH(uint8_t, green); + QFETCH(uint8_t, blue); + + SubLLEmbeddedBrowserWindow window; + + window.setCaretColor(red, green, blue); +} + +void tst_LLEmbeddedBrowserWindow::setEnabled_data() +{ + QTest::addColumn("enabledIn"); + QTest::newRow("true") << true; + QTest::newRow("false") << false; +} + +// public void setEnabled(bool enabledIn) +void tst_LLEmbeddedBrowserWindow::setEnabled() +{ + QFETCH(bool, enabledIn); + + SubLLEmbeddedBrowserWindow window; + + window.setEnabled(enabledIn); +} + +void tst_LLEmbeddedBrowserWindow::setNoFollowScheme_data() +{ + QTest::addColumn("scheme"); + QTest::addColumn("result"); + QTest::newRow("null") << std::string() << std::string(); + QTest::newRow("valid") << std::string("ftp://www.google.com") << std::string("ftp");; +} + +// public void setNoFollowScheme(std::string scheme) +void tst_LLEmbeddedBrowserWindow::setNoFollowScheme() +{ + QFETCH(std::string, scheme); + QFETCH(std::string, result); + + SubLLEmbeddedBrowserWindow window; + + window.setNoFollowScheme(scheme); + QCOMPARE(window.getNoFollowScheme(), result); +} + +void tst_LLEmbeddedBrowserWindow::setParent_data() +{ +#if 0 + QTest::addColumn("parentCount"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; +#endif +} + +// public void setParent(LLEmbeddedBrowser* parent) +void tst_LLEmbeddedBrowserWindow::setParent() +{ +#if 0 + QFETCH(int, parentCount); + + SubLLEmbeddedBrowserWindow window; + LLEmbeddedBrowser* parent = new LLEmbeddedBrowser(); + + window.setParent(parent); +#endif + QSKIP("Has been tested before.", SkipAll); +} + +void tst_LLEmbeddedBrowserWindow::setSize_data() +{ + QTest::addColumn("width"); + QTest::addColumn("height"); + QTest::addColumn("setSize"); + QTest::newRow("null") << int16_t(0) << int16_t(0) << true; + QTest::newRow("valid") << int16_t(100) << int16_t(200) << true; +} + +// public bool setSize(int16_t width, int16_t height) +void tst_LLEmbeddedBrowserWindow::setSize() +{ + QFETCH(int16_t, width); + QFETCH(int16_t, height); + QFETCH(bool, setSize); + + SubLLEmbeddedBrowserWindow window; + QCOMPARE(window.setSize(width, height), setSize); + window.grabWindow(0, 0, 800, 600); + + QCOMPARE(window.getBrowserWidth(), width); + QCOMPARE(window.getBrowserHeight(), height); + QCOMPARE(window.getBrowserRowSpan(), (int32_t)width * 4); +} + +void tst_LLEmbeddedBrowserWindow::setWindowId_data() +{ + QTest::addColumn("window_id"); + QTest::newRow("0") << 0; + QTest::newRow("-1") << -1; + QTest::newRow("100") << 100; +} + +// public void setWindowId(int window_id) +void tst_LLEmbeddedBrowserWindow::setWindowId() +{ + QFETCH(int, window_id); + + SubLLEmbeddedBrowserWindow window; + + window.setWindowId(window_id); + QCOMPARE(window.getWindowId(), window_id); +} + +Q_DECLARE_METATYPE(uint32_t) +void tst_LLEmbeddedBrowserWindow::unicodeInput_data() +{ + QTest::addColumn("unicode_char"); + QTest::newRow("null") << uint32_t(); + QTest::newRow("valid") << uint32_t(54); +} + +// public void unicodeInput(uint32_t unicode_char) +void tst_LLEmbeddedBrowserWindow::unicodeInput() +{ + QFETCH(uint32_t, unicode_char); + + SubLLEmbeddedBrowserWindow window; + + window.unicodeInput(unicode_char); +} + +QTEST_MAIN(tst_LLEmbeddedBrowserWindow) +#include "tst_llembeddedbrowserwindow.moc" + diff --git a/indra/llqtwebkit/llembeddedbrowser.cpp b/indra/llqtwebkit/llembeddedbrowser.cpp new file mode 100644 index 000000000..f38019b9b --- /dev/null +++ b/indra/llqtwebkit/llembeddedbrowser.cpp @@ -0,0 +1,759 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "llembeddedbrowser.h" + +#include "llembeddedbrowser_p.h" +#include "llembeddedbrowserwindow.h" +#include "llnetworkaccessmanager.h" +#include "llstyle.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// singleton pattern - initialization +LLEmbeddedBrowser* LLEmbeddedBrowser::sInstance = 0; + +LLEmbeddedBrowserPrivate::LLEmbeddedBrowserPrivate() + : mErrorNum(0) + , mNativeWindowHandle(0) + , mNetworkAccessManager(0) + , mApplication(0) +#if QT_VERSION >= 0x040500 + , mDiskCache(0) +#endif + , mNetworkCookieJar(0) + , mHostLanguage( "en" ) + , mIgnoreSSLCertErrors(false) +{ + if (!qApp) + { + static int argc = 0; + static const char* argv[] = {""}; + QApplication::setAttribute(Qt::AA_MacPluginApplication); + QApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + + mApplication = new QApplication(argc, (char **)argv); + mApplication->addLibraryPath(qApp->applicationDirPath()); + } + qApp->setStyle(new LLStyle()); + mNetworkAccessManager = new LLNetworkAccessManager(this); +#if LL_DARWIN + // HACK: Qt installs CarbonEvent handlers that steal events from our main event loop. + // This uninstalls them. + // It's not clear whether calling this internal function is really a good idea. It's probably not. + // It does, however, seem to fix at least one problem ( https://jira.secondlife.com/browse/MOZ-12 ). + extern void qt_release_app_proc_handler(); + qt_release_app_proc_handler(); + + // This is defined and exported from qwidget_mac.mm. + // Calling it with false should prevent qwidget from bringing its process to the foreground, such as when bringing up a popup menu. + extern void qt_mac_set_raise_process(bool b); + qt_mac_set_raise_process(false); +#endif +} + +LLEmbeddedBrowserPrivate::~LLEmbeddedBrowserPrivate() +{ + delete mApplication; + delete mNetworkAccessManager; + delete mNetworkCookieJar; +} + + + +LLEmbeddedBrowser::LLEmbeddedBrowser() + : d(new LLEmbeddedBrowserPrivate) + , mPluginsEnabled( false ) + , mJavaScriptEnabled( false ) + , mCookiesEnabled( false ) +{ +} + +LLEmbeddedBrowser::~LLEmbeddedBrowser() +{ + if(d->mNetworkCookieJar) + { + d->mNetworkCookieJar->mBrowser = NULL; + } + + delete d; +} + +LLEmbeddedBrowser* LLEmbeddedBrowser::getInstance() +{ + if (!sInstance) + sInstance = new LLEmbeddedBrowser; + return sInstance; +} + +void LLEmbeddedBrowser::setLastError(int error_number) +{ + d->mErrorNum = error_number; +} + +void LLEmbeddedBrowser::clearLastError() +{ + d->mErrorNum = 0x0000; +} + +int LLEmbeddedBrowser::getLastError() +{ + return d->mErrorNum; +} + +std::string LLEmbeddedBrowser::getGREVersion() +{ + // take the string directly from Qt + return std::string(QT_VERSION_STR); +} + +bool LLEmbeddedBrowser::init(std::string application_directory, + std::string component_directory, + std::string profile_directory, + void* native_window_handle) +{ + Q_UNUSED(application_directory); + Q_UNUSED(component_directory); + Q_UNUSED(native_window_handle); + d->mStorageDirectory = QString::fromStdString(profile_directory); + QWebSettings::setIconDatabasePath(d->mStorageDirectory); + // The gif and jpeg libraries should be installed in component_directory/imageformats/ + QCoreApplication::addLibraryPath(QString::fromStdString(component_directory)); + + // turn on plugins by default + enablePlugins( true ); + + // Until QtWebkit defaults to 16 + QWebSettings::globalSettings()->setFontSize(QWebSettings::DefaultFontSize, 16); + QWebSettings::globalSettings()->setFontSize(QWebSettings::DefaultFixedFontSize, 16); + + + QWebSettings::globalSettings()->setAttribute(QWebSettings::OfflineStorageDatabaseEnabled, true); + QWebSettings::globalSettings()->setOfflineStoragePath(QDesktopServices::storageLocation(QDesktopServices::DataLocation)); + + // use default text encoding - not sure how this helps right now so commenting out until we + // understand how to use it a little better. + //QWebSettings::globalSettings()->setDefaultTextEncoding ( "" ); + + return reset(); +} + +bool LLEmbeddedBrowser::reset() +{ + foreach(LLEmbeddedBrowserWindow *window, d->windows) + delete window; + d->windows.clear(); + delete d->mNetworkAccessManager; + d->mNetworkAccessManager = new LLNetworkAccessManager(d); +#if QT_VERSION >= 0x040500 + d->mDiskCache = new QNetworkDiskCache(d->mNetworkAccessManager); + d->mDiskCache->setCacheDirectory(d->mStorageDirectory + "/cache"); + if (QLatin1String(qVersion()) != QLatin1String("4.5.1")) + d->mNetworkAccessManager->setCache(d->mDiskCache); +#endif + d->mNetworkCookieJar = new LLNetworkCookieJar(d->mNetworkAccessManager, this); + d->mNetworkAccessManager->setCookieJar(d->mNetworkCookieJar); + clearLastError(); + return true; +} + +bool LLEmbeddedBrowser::clearCache() +{ +#if QT_VERSION >= 0x040500 + if (d->mDiskCache) + { + d->mDiskCache->clear(); + return true; + } +#endif + return false; +} + +bool LLEmbeddedBrowser::enableProxy(bool enabled, std::string host_name, int port) +{ + QNetworkProxy proxy; + if (enabled) + { + proxy.setType(QNetworkProxy::HttpProxy); + QString q_host_name = QString::fromStdString(host_name); + proxy.setHostName(q_host_name); + proxy.setPort(port); + } + d->mNetworkAccessManager->setProxy(proxy); + return true; +} + +bool LLEmbeddedBrowser::clearAllCookies() +{ + if (!d->mNetworkCookieJar) + return false; + d->mNetworkCookieJar->clear(); + return true; +} + +void LLEmbeddedBrowser::setCookies(const std::string &cookies) +{ + if (d->mNetworkCookieJar) + { + d->mNetworkCookieJar->setCookiesFromRawForm(cookies); + } +} + +std::string LLEmbeddedBrowser::getAllCookies() +{ + std::string result; + + if (d->mNetworkCookieJar) + { + result = d->mNetworkCookieJar->getAllCookiesInRawForm(); + } + + return result; +} + +void LLEmbeddedBrowser::enableCookies( bool enabled ) +{ + mCookiesEnabled = enabled; + enableCookiesTransient( mCookiesEnabled ); +} + +void LLEmbeddedBrowser::enableCookiesTransient( bool enabled ) +{ + if ( d->mNetworkCookieJar ) + { + d->mNetworkCookieJar->mAllowCookies = enabled; + } +} + +bool LLEmbeddedBrowser::areCookiesEnabled() +{ + return mCookiesEnabled; +} + +void LLEmbeddedBrowser::enablePlugins( bool enabled ) +{ + mPluginsEnabled = enabled; // record state + enablePluginsTransient( mPluginsEnabled ); +} + +void LLEmbeddedBrowser::enablePluginsTransient( bool enabled ) +{ + QWebSettings* default_settings = QWebSettings::globalSettings(); + default_settings->setAttribute( QWebSettings::PluginsEnabled, enabled ); +} + +bool LLEmbeddedBrowser::arePluginsEnabled() +{ + return mPluginsEnabled; +} + +void LLEmbeddedBrowser::enableJavaScript( bool enabled ) +{ + mJavaScriptEnabled = enabled; // record state + enableJavaScriptTransient( mJavaScriptEnabled ); +} + +void LLEmbeddedBrowser::enableJavaScriptTransient( bool enabled ) +{ + QWebSettings* default_settings = QWebSettings::globalSettings(); + default_settings->setAttribute( QWebSettings::JavascriptEnabled, enabled ); + default_settings->setAttribute( QWebSettings::JavascriptCanOpenWindows, enabled ); +} + +bool LLEmbeddedBrowser::isJavaScriptEnabled() +{ + return mJavaScriptEnabled; +} + +bool LLEmbeddedBrowser::showWebInspector(bool show) +{ + QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, show); + foreach (LLEmbeddedBrowserWindow* window, d->windows) + { + window->showWebInspector(show); + } + return true; +} + +/* + Sets a string that should be addded to the user agent to identify the application +*/ +void LLEmbeddedBrowser::setBrowserAgentId(std::string id) +{ + QCoreApplication::setApplicationName(QString::fromStdString(id)); +} + +// updates value of 'hostLanguage' in JavaScript 'Navigator' obect that +// embedded pages can query to see what language the host app is set to +// IMPORTANT: call this before any windows are created - only gets passed +// to LLWebPage when new window is created +void LLEmbeddedBrowser::setHostLanguage( const std::string& host_language ) +{ + d->mHostLanguage = host_language; +} + +LLEmbeddedBrowserWindow* LLEmbeddedBrowser::createBrowserWindow(int width, int height, const std::string target) +{ + LLEmbeddedBrowserWindow *newWin = new LLEmbeddedBrowserWindow(); + if (newWin) + { + newWin->setSize(width, height); + newWin->setParent(this); + newWin->setHostLanguage(d->mHostLanguage); + clearLastError(); + d->windows.append(newWin); + + if(!target.empty() && (target != "_blank")) + { + newWin->setTarget(target); + } + + return newWin; + } + return 0; +} + +bool LLEmbeddedBrowser::destroyBrowserWindow(LLEmbeddedBrowserWindow* browser_window) +{ + // check if exists in windows list + if (d->windows.removeOne(browser_window)) + { + delete browser_window; + clearLastError(); + return true; + } + return false; +} + +int LLEmbeddedBrowser::getWindowCount() const +{ + return d->windows.size(); +} + +void LLEmbeddedBrowser::pump(int max_milliseconds) +{ +#if 0 + // This USED to be necessary on the mac, but with Qt 4.6 it seems to cause trouble loading some pages, + // and using processEvents() seems to work properly now. + // Leaving this here in case these issues ever come back. + + // On the Mac, calling processEvents hangs the viewer. + // I'm not entirely sure this does everything we need, but it seems to work better, and allows things like animated gifs to work. + qApp->sendPostedEvents(); + qApp->sendPostedEvents(0, QEvent::DeferredDelete); +#else + qApp->processEvents(QEventLoop::AllEvents, max_milliseconds); +#endif +} + +void LLEmbeddedBrowser::cookieChanged(const std::string &cookie, const std::string &url, bool dead) +{ + foreach (LLEmbeddedBrowserWindow* window, d->windows) + { + window->cookieChanged(cookie, url, dead); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::setCAFile(const std::string &ca_file) +{ + bool result = false; + //qDebug() << "LLEmbeddedBrowser::" << __FUNCTION__ << "attempting to read certs from file: " << QString::fromStdString(ca_file); + + // Extract the list of certificates from the specified file + QList certs = QSslCertificate::fromPath(QString::fromStdString(ca_file)); + + if(!certs.isEmpty()) + { + //qDebug() << "LLEmbeddedBrowser::" << __FUNCTION__ << "certs read: " << certs; + + // Set the default CA cert for Qt's SSL implementation. + QSslConfiguration config = QSslConfiguration::defaultConfiguration(); + config.setCaCertificates(certs); + QSslConfiguration::setDefaultConfiguration(config); + result = true; + } + + return result; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLEmbeddedBrowser::addCAFile(const std::string &ca_file) +{ + // Enabling this can help diagnose certificate verification issues. + const bool cert_debugging_on = false; + + if ( cert_debugging_on ) + { + //qDebug() << "\n\nLLEmbeddedBrowser::" << __FUNCTION__ << " ------------------- (Before add)"; + QSslCertificate cert; + foreach(cert, QSslSocket::defaultCaCertificates()) + { + //qDebug() << cert.issuerInfo(QSslCertificate::CommonName) << " --- " << cert.subjectInfo(QSslCertificate::CommonName); + } + } + + bool result = false; + //qDebug() << "LLEmbeddedBrowser::" << __FUNCTION__ << "attempting to read certs from file: " << QString::fromStdString(ca_file); + + if ( cert_debugging_on ) + { + //qDebug() << "\n\nLLEmbeddedBrowser::" << __FUNCTION__ << " ------------------- (From CA.pem)"; + QList certs = QSslCertificate::fromPath(QString::fromStdString(ca_file)); + QSslCertificate cert; + foreach(cert, certs) + { + //qDebug() << cert.issuerInfo(QSslCertificate::CommonName) << " --- " << cert.subjectInfo(QSslCertificate::CommonName); + } + } + + result = QSslSocket::addDefaultCaCertificates(QString::fromStdString(ca_file)); + + if ( cert_debugging_on ) + { + //qDebug() << "\n\nLLEmbeddedBrowser::" << __FUNCTION__ << " ------------------- (After add)"; + QSslCertificate cert; + foreach(cert, QSslSocket::defaultCaCertificates()) + { + //qDebug() << cert.issuerInfo(QSslCertificate::CommonName) << " --- " << cert.subjectInfo(QSslCertificate::CommonName); + } + } + + return result; +} + +void LLEmbeddedBrowser::setIgnoreSSLCertErrors(bool ignore) +{ + d->mIgnoreSSLCertErrors = ignore; +} + +bool LLEmbeddedBrowser::getIgnoreSSLCertErrors() +{ + return d->mIgnoreSSLCertErrors; +} + +const std::vector< std::string > LLEmbeddedBrowser::getInstalledCertsList() +{ + std::vector< std::string > cert_list; + + QSslCertificate cert; + foreach(cert, QSslSocket::defaultCaCertificates()) + { + QString cert_info=""; + + QString issuer_info=""; + issuer_info+="C="; + issuer_info+=cert.issuerInfo(QSslCertificate::CountryName); + issuer_info+=", ST="; + issuer_info+=cert.issuerInfo(QSslCertificate::StateOrProvinceName); + issuer_info+=", L="; + issuer_info+=cert.issuerInfo(QSslCertificate::LocalityName); + issuer_info+=", O="; + issuer_info+=cert.issuerInfo(QSslCertificate::Organization); + issuer_info+=", OU="; + issuer_info+=cert.issuerInfo(QSslCertificate::OrganizationalUnitName); + issuer_info+=", CN="; + issuer_info+=cert.issuerInfo(QSslCertificate::CommonName); + cert_info+=issuer_info; + cert_info+="\n"; + + QString subject_info=""; + subject_info+="C="; + subject_info+=cert.subjectInfo(QSslCertificate::CountryName); + subject_info+=", ST="; + subject_info+=cert.subjectInfo(QSslCertificate::StateOrProvinceName); + subject_info+=", L="; + subject_info+=cert.subjectInfo(QSslCertificate::LocalityName); + subject_info+=", O="; + subject_info+=cert.subjectInfo(QSslCertificate::Organization); + subject_info+=", OU="; + subject_info+=cert.subjectInfo(QSslCertificate::OrganizationalUnitName); + subject_info+=", CN="; + subject_info+=cert.subjectInfo(QSslCertificate::CommonName); + cert_info+=subject_info; + cert_info+="\n"; + + cert_info+="Not valid before: "; + cert_info+=cert.effectiveDate().toString(); + cert_info+="\n"; + cert_info+="Not valid after: "; + cert_info+=cert.expiryDate().toString(); + cert_info+="\n"; + + cert_list.push_back( llToStdString(cert_info) ); + } + return cert_list; +} + +// Second Life viewer specific functions +void LLEmbeddedBrowser::setSLObjectEnabled( bool enabled ) +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->setSLObjectEnabled( enabled ); + } +} + +void LLEmbeddedBrowser::setAgentLanguage( const std::string& agent_language ) +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->setAgentLanguage( agent_language ); + } +} + +void LLEmbeddedBrowser::setAgentRegion( const std::string& agent_region ) +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->setAgentRegion( agent_region ); + } +} + +void LLEmbeddedBrowser::setAgentLocation( double x, double y, double z ) +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->setAgentLocation( x, y, z ); + } +} + +void LLEmbeddedBrowser::setAgentGlobalLocation( double x, double y, double z ) +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->setAgentGlobalLocation( x, y, z ); + } +} + +void LLEmbeddedBrowser::setAgentOrientation( double angle ) +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->setAgentOrientation( angle ); + } +} + +void LLEmbeddedBrowser::setAgentMaturity( const std::string& agent_maturity ) +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->setAgentMaturity( agent_maturity ); + } +} + +void LLEmbeddedBrowser::emitLocation() +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->emitLocation(); + } +} + +void LLEmbeddedBrowser::emitMaturity() +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->emitMaturity(); + } +} + +void LLEmbeddedBrowser::emitLanguage() +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->emitLanguage(); + } +} + +void LLEmbeddedBrowser::setPageZoomFactor( double factor ) +{ + foreach ( LLEmbeddedBrowserWindow* window, d->windows ) + { + window->setPageZoomFactor( factor ); + } +} + +void LLEmbeddedBrowser::qtMessageHandler(QtMsgType type, const char *msg) +{ + std::string msg_type(""); + switch (type) + { + case QtDebugMsg: + msg_type="Debug"; + break; + case QtWarningMsg: + msg_type="Warning"; + break; + case QtCriticalMsg: + msg_type="Critical"; + break; + case QtFatalMsg: + msg_type="Fatal"; + break; + }; + + foreach ( LLEmbeddedBrowserWindow* window, sInstance->d->windows ) + { + + window->onQtDebugMessage( std::string( msg ), msg_type); + } +} + +void LLEmbeddedBrowser::enableQtMessageHandler( bool enable ) +{ + if ( enable ) + { + qInstallMsgHandler( qtMessageHandler ); + } + else + { + // remove handler + qInstallMsgHandler(0); + }; +} + +LLNetworkCookieJar::LLNetworkCookieJar(QObject* parent, LLEmbeddedBrowser *browser) + : NetworkCookieJar(parent) + , mAllowCookies(true) + , mBrowser(browser) +{ +} + +LLNetworkCookieJar::~LLNetworkCookieJar() +{ +} + +QList LLNetworkCookieJar::cookiesForUrl(const QUrl& url) const +{ + if (!mAllowCookies) + return QList(); + return NetworkCookieJar::cookiesForUrl(url); +} + +bool LLNetworkCookieJar::setCookiesFromUrl(const QList &cookie_list, const QUrl& url) +{ + if (!mAllowCookies) + return false; + return NetworkCookieJar::setCookiesFromUrl(cookie_list, url); +} + +void LLNetworkCookieJar::onCookieSetFromURL(const QNetworkCookie &cookie, const QUrl &url, bool already_dead) +{ +// qDebug() << "LLNetworkCookieJar::" << __FUNCTION__ << (already_dead?"set dead cookie":"set cookie ") << cookie; + + if(mBrowser) + { + QByteArray cookie_bytes = cookie.toRawForm(QNetworkCookie::Full); + std::string cookie_string(cookie_bytes.data(), cookie_bytes.size()); + std::string url_string = llToStdString(url); + mBrowser->cookieChanged(cookie_string, url_string, already_dead); + } +} + +void LLNetworkCookieJar::clear() +{ + clearCookies(); +} + +void LLNetworkCookieJar::setCookiesFromRawForm(const std::string &cookie_string) +{ + QByteArray cookie_bytearray(cookie_string.data(), cookie_string.size()); + QList cookie_list = QNetworkCookie::parseCookies(cookie_bytearray); + setCookies(cookie_list); +} + +std::string LLNetworkCookieJar::getAllCookiesInRawForm() +{ + std::string result; + + QList cookie_list = allCookies(); + + foreach (const QNetworkCookie &cookie, cookie_list) + { + QByteArray raw_form = cookie.toRawForm(QNetworkCookie::Full); + result.append(raw_form.data(), raw_form.size()); + result.append("\n"); + } + + return result; +} + +#include "llembeddedbrowserwindow_p.h" +#include + +QGraphicsWebView *LLEmbeddedBrowserPrivate::findView(QNetworkReply *reply) +{ + for (int i = 0; i < windows.count(); ++i) + if (windows[i]->d->mView->url() == reply->url()) + return windows[i]->d->mView; + return windows[0]->d->mView; +} + +bool LLEmbeddedBrowserPrivate::authRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password) +{ + bool result = false; + +// qDebug() << "LLEmbeddedBrowser::" << __FUNCTION__ << "requesting auth for url " << QString::fromStdString(in_url) << ", realm " << QString::fromStdString(in_realm); +// +// qDebug() << "LLEmbeddedBrowser::" << __FUNCTION__ << "window count is " << windows.count(); + + if(windows.count() > 1) + { + qDebug() << "LLEmbeddedBrowser::" << __FUNCTION__ << "WARNING: authRequest called with more than one window, using the first one"; + } + + LLEmbeddedBrowserWindow* window = windows.first(); + + if(window) + { + result = window->authRequest(in_url, in_realm, out_username, out_password); + } + + return result; +} + +bool LLEmbeddedBrowserPrivate::certError(const std::string &in_url, const std::string &in_msg) +{ + bool result = false; + + LLEmbeddedBrowserWindow* window = windows.first(); + if(window) + { + result = window->certError(in_url, in_msg); + } + + return result; +} diff --git a/indra/llqtwebkit/llembeddedbrowser.h b/indra/llqtwebkit/llembeddedbrowser.h new file mode 100644 index 000000000..c21a6b063 --- /dev/null +++ b/indra/llqtwebkit/llembeddedbrowser.h @@ -0,0 +1,115 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLEMBEDDEDBROWSER_H +#define LLEMBEDDEDBROWSER_H + +#include +#include +#include +#include + +class LLEmbeddedBrowserWindow; +class LLEmbeddedBrowserWindowObserver; + +class LLEmbeddedBrowserPrivate; +class LLEmbeddedBrowser +{ + public: + LLEmbeddedBrowser(); + virtual ~LLEmbeddedBrowser(); + + static LLEmbeddedBrowser* getInstance(); + + bool init(std::string application_directory, + std::string component_directory, + std::string profile_directory, + void* native_window_handle); + bool reset(); + bool clearCache(); + bool enableProxy(bool enabled, std::string host_name, int port); + bool clearAllCookies(); + void setCookies(const std::string &cookies); + std::string getAllCookies(); + + void enableCookies( bool enabled ); + void enableCookiesTransient( bool enabled ); + bool areCookiesEnabled(); + void enablePlugins( bool enabled ); + void enablePluginsTransient( bool enabled ); + bool arePluginsEnabled(); + void enableJavaScript( bool enabled ); + void enableJavaScriptTransient( bool enabled ); + bool isJavaScriptEnabled(); + + bool showWebInspector(bool show); + std::string getGREVersion(); + void setBrowserAgentId(std::string id); + void setHostLanguage( const std::string& host_language ); + LLEmbeddedBrowserWindow* createBrowserWindow(int width, int height, const std::string target); + bool destroyBrowserWindow(LLEmbeddedBrowserWindow* browser_window); + void setLastError(int error_number); + void clearLastError(); + int getLastError(); + int getWindowCount() const; + void pump(int max_milliseconds); + void cookieChanged(const std::string &cookie, const std::string &url, bool dead); + bool setCAFile(const std::string &ca_file); + bool addCAFile(const std::string &ca_file); + void setIgnoreSSLCertErrors(bool ignore); + bool getIgnoreSSLCertErrors(); + const std::vector< std::string > getInstalledCertsList(); + + void enableQtMessageHandler( bool enable ); + + void setPageZoomFactor( double factor ); + + // Second Life specific functions + void setSLObjectEnabled( bool enabled ); + void setAgentLanguage( const std::string& agent_language ); + void setAgentRegion( const std::string& agent_region ); + void setAgentLocation( double x, double y, double z ); + void setAgentGlobalLocation( double x, double y, double z ); + void setAgentOrientation( double angle ); + void setAgentMaturity( const std::string& agent_maturity ); + void emitLocation(); + void emitMaturity(); + void emitLanguage(); + + private: + friend class LLEmbeddedBrowserWindow; + friend class LLEmbeddedBrowserWindowPrivate; + LLEmbeddedBrowserPrivate *d; + bool mPluginsEnabled; + bool mJavaScriptEnabled; + bool mCookiesEnabled; + + static void qtMessageHandler(QtMsgType type, const char *msg); + + static LLEmbeddedBrowser* sInstance; +}; + +#endif // LLEMBEDDEDBROWSER_H + diff --git a/indra/llqtwebkit/llembeddedbrowser_p.h b/indra/llqtwebkit/llembeddedbrowser_p.h new file mode 100644 index 000000000..9f9f9cd02 --- /dev/null +++ b/indra/llqtwebkit/llembeddedbrowser_p.h @@ -0,0 +1,90 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLEMBEDDEDBROWSER_P_H +#define LLEMBEDDEDBROWSER_P_H + +#include +#include +#if QT_VERSION >= 0x040500 +#include +#endif + +#include "networkcookiejar.h" +#include "llembeddedbrowser.h" + +#include + +class LLEmbeddedBrowser; +class LLNetworkCookieJar : public NetworkCookieJar +{ +public: + LLNetworkCookieJar(QObject *parent, LLEmbeddedBrowser *browser); + ~LLNetworkCookieJar(); + + QList cookiesForUrl(const QUrl& url) const; + bool setCookiesFromUrl(const QList &cookie_list, const QUrl& url); + + /*virtual*/ void onCookieSetFromURL(const QNetworkCookie &cookie, const QUrl &url, bool already_dead); + + void clear(); + + void setCookiesFromRawForm(const std::string &cookie_string); + std::string getAllCookiesInRawForm(); + + bool mAllowCookies; + LLEmbeddedBrowser *mBrowser; +}; + +class LLNetworkAccessManager; +class LLEmbeddedBrowserPrivate +{ +public: + LLEmbeddedBrowserPrivate(); + ~LLEmbeddedBrowserPrivate(); + + bool authRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password); + bool certError(const std::string &in_url, const std::string &in_msg); + + int mErrorNum; + void* mNativeWindowHandle; + LLNetworkAccessManager *mNetworkAccessManager; + QApplication *mApplication; +#if QT_VERSION >= 0x040500 + QNetworkDiskCache *mDiskCache; +#endif + LLNetworkCookieJar *mNetworkCookieJar; + + QGraphicsWebView *findView(QNetworkReply *); + + QString mStorageDirectory; + QList windows; + + std::string mHostLanguage; + bool mIgnoreSSLCertErrors; +}; + +#endif + diff --git a/indra/llqtwebkit/llembeddedbrowserwindow.cpp b/indra/llqtwebkit/llembeddedbrowserwindow.cpp new file mode 100644 index 000000000..c990d5567 --- /dev/null +++ b/indra/llqtwebkit/llembeddedbrowserwindow.cpp @@ -0,0 +1,1136 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "llembeddedbrowserwindow.h" +#include "llembeddedbrowserwindow_p.h" + +#include "llembeddedbrowser.h" +#include "llembeddedbrowser_p.h" +#include "llnetworkaccessmanager.h" + +#ifdef STATIC_QT + #include + // Enable gif and jpeg plugins, since web pages look pretty bleak without gifs or jpegs. + // Qt 4.7 uses the system gif and jpeg libraries by default, so this is no longer necessary. +// Q_IMPORT_PLUGIN(qgif) +// Q_IMPORT_PLUGIN(qjpeg) +#ifndef LL_LINUX + // Qt also has its own translators for CJK text encodings we need to pull in. + Q_IMPORT_PLUGIN(qcncodecs) + Q_IMPORT_PLUGIN(qjpcodecs) + Q_IMPORT_PLUGIN(qkrcodecs) + Q_IMPORT_PLUGIN(qtwcodecs) +#endif +#endif + +//#define LLEMBEDDEDBROWSER_DEBUG 1 + +#ifdef LLEMBEDDEDBROWSER_DEBUG +#include +#endif + +#if LL_DARWIN || defined(STATIC_QT) + // Don't define qt_sendSpontaneousEvent on the mac -- it causes a multiply-defined symbol. + extern bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event); +#else + #include + bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event) + { + return QCoreApplication::sendSpontaneousEvent(receiver, event); + } +#endif + +LLEmbeddedBrowserWindow::LLEmbeddedBrowserWindow() +{ + d = new LLEmbeddedBrowserWindowPrivate(); + + d->mPage = new LLWebPage; + d->mInspector = new QWebInspector; + d->mInspector->setPage(d->mPage); + d->mPage->window = this; + d->mView = new LLWebView; + d->mPage->webView = d->mView; + d->mView->window = this; + d->mView->setPage(d->mPage); + d->mGraphicsScene = new LLGraphicsScene; + d->mGraphicsScene->window = this; + d->mGraphicsView = new QGraphicsView; + d->mGraphicsScene->addItem(d->mView); + d->mGraphicsView->setScene(d->mGraphicsScene); + d->mGraphicsScene->setStickyFocus(true); + d->mGraphicsView->viewport()->setParent(0); + + mEnableLoadingOverlay = false; +} + +LLEmbeddedBrowserWindow::~LLEmbeddedBrowserWindow() +{ + delete d; +} + +void LLEmbeddedBrowserWindow::setParent(LLEmbeddedBrowser* parent) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << parent; +#endif + d->mParent = parent; + if (parent) + { + d->mPage->setNetworkAccessManager(parent->d->mNetworkAccessManager); + } else + { + d->mPage->setNetworkAccessManager(0); + } +} + +void LLEmbeddedBrowserWindow::showWebInspector(bool show) +{ + if ( d ) + { + if ( d->mInspector ) + { + d->mInspector->setVisible( show ); + } + } +} + +void LLEmbeddedBrowserWindow::enableLoadingOverlay(bool enable) +{ + mEnableLoadingOverlay = enable; +} + +// change the background color that gets used between pages (usually white) +void LLEmbeddedBrowserWindow::setBackgroundColor(const uint8_t red, const uint8_t green, const uint8_t blue) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << red << green << blue; +#endif + d->backgroundColor = QColor(red, green, blue); +} + +// +void LLEmbeddedBrowserWindow::setEnabled(bool enabled) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << enabled; +#endif + d->mEnabled = enabled; +} + +// allow consumers of this class to observe events - add themselves as an observer +bool LLEmbeddedBrowserWindow::addObserver(LLEmbeddedBrowserWindowObserver* observer) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << observer; +#endif + return d->mEventEmitter.addObserver(observer); +} + +// allow consumers of this class to observe events - remove themselves as an observer +bool LLEmbeddedBrowserWindow::remObserver(LLEmbeddedBrowserWindowObserver* observer) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << observer; +#endif + return d->mEventEmitter.remObserver(observer); +} + +int LLEmbeddedBrowserWindow::getObserverNumber() +{ + return d->mEventEmitter.getObserverNumber(); +} + +// used by observers of this class to get the current URI +std::string& LLEmbeddedBrowserWindow::getCurrentUri() +{ + d->mCurrentUri = llToStdString(d->mPage->mainFrame()->url()); + return d->mCurrentUri; +} + +// utility method that is used by observers to retrieve data after an event +int16_t LLEmbeddedBrowserWindow::getPercentComplete() +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__; +#endif + return d->mPercentComplete; +} + +// utility method that is used by observers to retrieve data after an event +std::string& LLEmbeddedBrowserWindow::getStatusMsg() +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__; +#endif + return d->mStatusText; +} + +// render a page into memory and grab the window +unsigned char* LLEmbeddedBrowserWindow::grabWindow(int x, int y, int width, int height) +{ +#if LLEMBEDDEDBROWSER_DEBUG > 10 + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << x << y << width << height; +#endif + // only grab the window if it's enabled + if (!d->mEnabled) + return 0; + + if (!d->mDirty) + return d->mPageBuffer; + + Q_ASSERT(d->mImage.size() == d->mView->size()); + if (!d->mPage->mainFrame()->url().isValid()) + { + d->mImage.fill(d->backgroundColor.value()); + } else + { + QPainter painter(&d->mImage); + + QRectF r(x, y, width, height); + QRect g(0, 0, d->mView->width(), d->mView->height()); + d->mGraphicsView->render(&painter, r, g); + + d->mDirty = false; + + const bool spinner_enabled = false; + if ( spinner_enabled ) + { + const time_t seconds_before_show_overlay = 1; + + if ( mEnableLoadingOverlay && + d->mShowLoadingOverlay && + time(NULL) - d->mTimeLoadStarted >= seconds_before_show_overlay ) + { + painter.setRenderHint(QPainter::Antialiasing);; + + QBrush brush; + QPen pen; + + int size = width; + if ( height < width ) + size = height; + + const int symbol_translucency = 64; // 0=fully trans, 255=opaque + const int symbol_proportion_of_sceen = 8; // (1/8) + const int symbol_diameter = size/(symbol_proportion_of_sceen); + const int symbol_start_line = symbol_diameter*2/3; + const int symbol_end_line = symbol_diameter; + const int symbol_num_segments = 20; + const int symbol_line_width = size/60; + if ( size < 4 ) size = 4; + + QColor background_color(QColor(128,128,128,symbol_translucency)); + brush.setColor(background_color); + brush.setStyle(Qt::SolidPattern); + pen.setColor(background_color); + painter.setPen(pen); + painter.setBrush(brush); + painter.drawRect(0,0,width, height); + + painter.translate(QPoint(width/2, height/2)); + + static int offset=0; + painter.rotate(((qreal)(offset++%(symbol_num_segments))/(qreal)symbol_num_segments)*360.0f); + + for ( int count=0; countmDirty = true; // force dirty so updates happen frequently during load + } + } + + painter.end(); + if (d->mFlipBitmap) + { + d->mImage = d->mImage.mirrored(); + } + d->mImage = d->mImage.rgbSwapped(); + } + + d->mPageBuffer = d->mImage.bits(); + + return d->mPageBuffer; +} + +// return the buffer that contains the rendered page +unsigned char* LLEmbeddedBrowserWindow::getPageBuffer() +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__; +#endif + return d->mPageBuffer; +} + +int16_t LLEmbeddedBrowserWindow::getBrowserWidth() +{ +#if LLEMBEDDEDBROWSER_DEBUG > 10 + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__; +#endif + return d->mImage.width(); +} + +int16_t LLEmbeddedBrowserWindow::getBrowserHeight() +{ +#if LLEMBEDDEDBROWSER_DEBUG > 10 + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__; +#endif + return d->mImage.height(); +} + +int16_t LLEmbeddedBrowserWindow::getBrowserDepth() +{ +#if LLEMBEDDEDBROWSER_DEBUG > 10 + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__; +#endif + return 4; +} + +int32_t LLEmbeddedBrowserWindow::getBrowserRowSpan() +{ +#if LLEMBEDDEDBROWSER_DEBUG > 10 + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__; +#endif + return 4 * getBrowserWidth(); +} + +bool LLEmbeddedBrowserWindow::navigateTo(const std::string uri) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << QString::fromStdString(uri); +#endif + QUrl url = QUrl::fromUserInput(QString::fromStdString(uri)); + + d->mPage->triggerAction(QWebPage::Stop); + d->mPage->mainFrame()->setUrl(url); + d->mPage->mainFrame()->load(url); + return true; +} + +bool LLEmbeddedBrowserWindow::userAction(LLQtWebKit::EUserAction action) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << action; +#endif + bool result = true; + + switch(action) + { + case LLQtWebKit::UA_EDIT_CUT: + d->mPage->triggerAction(QWebPage::Cut); + break; + case LLQtWebKit::UA_EDIT_COPY: + d->mPage->triggerAction(QWebPage::Copy); + break; + case LLQtWebKit::UA_EDIT_PASTE: + d->mPage->triggerAction(QWebPage::Paste); + break; + case LLQtWebKit::UA_NAVIGATE_STOP: + d->mPage->triggerAction(QWebPage::Stop); + break; + case LLQtWebKit::UA_NAVIGATE_BACK: + d->mPage->triggerAction(QWebPage::Back); + break; + case LLQtWebKit::UA_NAVIGATE_FORWARD: + d->mPage->triggerAction(QWebPage::Forward); + break; + case LLQtWebKit::UA_NAVIGATE_RELOAD: + d->mPage->triggerAction(QWebPage::ReloadAndBypassCache); + break; + default: + result = false; + break; + } + + return result; +} + +bool LLEmbeddedBrowserWindow::userActionIsEnabled(LLQtWebKit::EUserAction action) +{ +#if LLEMBEDDEDBROWSER_DEBUG > 10 + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << action; +#endif + + bool result; + + switch(action) + { + case LLQtWebKit::UA_EDIT_CUT: + result = d->mPage->action(QWebPage::Cut)->isEnabled(); + break; + case LLQtWebKit::UA_EDIT_COPY: + result = d->mPage->action(QWebPage::Copy)->isEnabled(); + break; + case LLQtWebKit::UA_EDIT_PASTE: + result = d->mPage->action(QWebPage::Paste)->isEnabled(); + break; + case LLQtWebKit::UA_NAVIGATE_STOP: + result = true; + break; + case LLQtWebKit::UA_NAVIGATE_BACK: + result = d->mPage->history()->canGoBack(); + break; + case LLQtWebKit::UA_NAVIGATE_FORWARD: + result = d->mPage->history()->canGoForward(); + break; + case LLQtWebKit::UA_NAVIGATE_RELOAD: + result = true; + break; + default: + result = false; + break; + } + return result; +} + +// set the size of the browser window +bool LLEmbeddedBrowserWindow::setSize(int16_t width, int16_t height) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << width << height; +#endif + d->mPageBuffer = NULL; + d->mImage = QImage(QSize(width, height), QImage::Format_RGB32); + d->mGraphicsView->resize(width, height); + d->mView->resize(width, height); + d->mImage.fill(d->backgroundColor.rgb()); + return true; +} + +bool LLEmbeddedBrowserWindow::flipWindow(bool flip) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << flip; +#endif + d->mFlipBitmap = flip; + return true; +} + +static Qt::KeyboardModifiers convert_modifiers(LLQtWebKit::EKeyboardModifier modifiers) +{ + Qt::KeyboardModifiers result = Qt::NoModifier; + + if(modifiers & LLQtWebKit::KM_MODIFIER_SHIFT) + result |= Qt::ShiftModifier; + + if(modifiers & LLQtWebKit::KM_MODIFIER_CONTROL) + result |= Qt::ControlModifier; + + if(modifiers & LLQtWebKit::KM_MODIFIER_ALT) + result |= Qt::AltModifier; + + if(modifiers & LLQtWebKit::KM_MODIFIER_META) + result |= Qt::MetaModifier; + + return result; +} + +static Qt::MouseButton qt_button_from_button_number(int button) +{ + Qt::MouseButton result; + + switch(button) + { + default: result = Qt::NoButton; break; + case 0: result = Qt::LeftButton; break; + case 1: result = Qt::RightButton; break; + case 2: result = Qt::MidButton; break; + case 3: result = Qt::XButton1; break; + case 4: result = Qt::XButton2; break; + } + + return result; +} + +static QEvent::Type event_from_mouse_event(LLQtWebKit::EMouseEvent mouse_event) +{ + QEvent::Type result; + + switch(mouse_event) + { + default: + result = QEvent::None; + break; + + case LLQtWebKit::ME_MOUSE_MOVE: + result = QEvent::MouseMove; + break; + + case LLQtWebKit::ME_MOUSE_DOWN: + result = QEvent::MouseButtonPress; + break; + + case LLQtWebKit::ME_MOUSE_UP: + result = QEvent::MouseButtonRelease; + break; + + case LLQtWebKit::ME_MOUSE_DOUBLE_CLICK: + result = QEvent::MouseButtonDblClick; + break; + } + + return result; +} + +static QEvent::Type event_from_keyboard_event(LLQtWebKit::EKeyEvent keyboard_event) +{ + QEvent::Type result; + + switch(keyboard_event) + { + default: + result = QEvent::None; + break; + + case LLQtWebKit::KE_KEY_DOWN: + case LLQtWebKit::KE_KEY_REPEAT: + result = QEvent::KeyPress; + break; + + case LLQtWebKit::KE_KEY_UP: + result = QEvent::KeyRelease; + break; + } + + return result; +} + +void LLEmbeddedBrowserWindow::mouseEvent(LLQtWebKit::EMouseEvent mouse_event, int16_t button, int16_t x, int16_t y, LLQtWebKit::EKeyboardModifier modifiers) +{ +#if LLEMBEDDEDBROWSER_DEBUG > 10 + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << x << y; +#endif + + QEvent::Type type = event_from_mouse_event(mouse_event); + Qt::MouseButton qt_button = qt_button_from_button_number(button); + Qt::KeyboardModifiers qt_modifiers = convert_modifiers(modifiers); + + if(type == QEvent::MouseMove) + { + // Mouse move events should always use "no button". + qt_button = Qt::NoButton; + } + + // FIXME: should the current button state be updated before or after constructing the event? + switch(type) + { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + d->mCurrentMouseButtonState |= qt_button; + break; + + case QEvent::MouseButtonRelease: + d->mCurrentMouseButtonState &= ~qt_button; + break; + + default: + break; + } + + QMouseEvent event(type, QPoint(x, y), qt_button, d->mCurrentMouseButtonState, qt_modifiers); + + qt_sendSpontaneousEvent(d->mGraphicsView->viewport(), &event); +} + +void LLEmbeddedBrowserWindow::scrollWheelEvent(int16_t x, int16_t y, int16_t scroll_x, int16_t scroll_y, LLQtWebKit::EKeyboardModifier modifiers) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << x << y; +#endif + + Qt::KeyboardModifiers qt_modifiers = convert_modifiers(modifiers); + + if(scroll_y != 0) + { + QWheelEvent event(QPoint(x, y), scroll_y, d->mCurrentMouseButtonState, qt_modifiers, Qt::Vertical); + qApp->sendEvent(d->mGraphicsView->viewport(), &event); + } + + if(scroll_x != 0) + { + QWheelEvent event(QPoint(x, y), scroll_x, d->mCurrentMouseButtonState, qt_modifiers, Qt::Horizontal); + qApp->sendEvent(d->mGraphicsView->viewport(), &event); + } +} + + +// utility methods to set an error message so something else can look at it +void LLEmbeddedBrowserWindow::scrollByLines(int16_t lines) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << lines; +#endif + int currentScrollValue = d->mPage->mainFrame()->scrollBarValue(Qt::Vertical); + d->mPage->mainFrame()->setScrollBarValue(Qt::Vertical, currentScrollValue + lines); +} + +// Send a keyboard event with native event data. +void LLEmbeddedBrowserWindow::keyboardEvent( + LLQtWebKit::EKeyEvent key_event, + uint32_t key_code, + const char *utf8_text, + LLQtWebKit::EKeyboardModifier modifiers, + uint32_t native_scan_code, + uint32_t native_virtual_key, + uint32_t native_modifiers) +{ + QEvent::Type type = event_from_keyboard_event(key_event); + Qt::KeyboardModifiers qt_modifiers = convert_modifiers(modifiers); + bool auto_repeat = (key_event == LLQtWebKit::KE_KEY_REPEAT); + QString text = QString::fromUtf8(utf8_text); + + Qt::Key key = Qt::Key_unknown; + + switch (key_code) + { + case LLQtWebKit::KEY_RETURN: key = Qt::Key_Return; break; + case LLQtWebKit::KEY_LEFT: key = Qt::Key_Left; break; + case LLQtWebKit::KEY_RIGHT: key = Qt::Key_Right; break; + case LLQtWebKit::KEY_UP: key = Qt::Key_Up; break; + case LLQtWebKit::KEY_DOWN: key = Qt::Key_Down; break; + case LLQtWebKit::KEY_ESCAPE: key = Qt::Key_Escape; break; + case LLQtWebKit::KEY_BACKSPACE: key = Qt::Key_Backspace; break; + case LLQtWebKit::KEY_DELETE: key = Qt::Key_Delete; break; + case LLQtWebKit::KEY_SHIFT: key = Qt::Key_Shift; break; + case LLQtWebKit::KEY_CONTROL: key = Qt::Key_Control; break; + case LLQtWebKit::KEY_ALT: key = Qt::Key_Alt; break; + case LLQtWebKit::KEY_HOME: key = Qt::Key_Home; break; + case LLQtWebKit::KEY_END: key = Qt::Key_End; break; + case LLQtWebKit::KEY_PAGE_UP: key = Qt::Key_PageUp; break; + case LLQtWebKit::KEY_PAGE_DOWN: key = Qt::Key_PageDown; break; + case LLQtWebKit::KEY_HYPHEN: key = Qt::Key_hyphen; break; + case LLQtWebKit::KEY_EQUALS: key = Qt::Key_Equal; break; + case LLQtWebKit::KEY_INSERT: key = Qt::Key_Insert; break; + case LLQtWebKit::KEY_CAPSLOCK: key = Qt::Key_CapsLock; break; + case LLQtWebKit::KEY_TAB: key = Qt::Key_Tab; break; + case LLQtWebKit::KEY_ADD: key = Qt::Key_Plus; break; + case LLQtWebKit::KEY_SUBTRACT: key = Qt::Key_Minus; break; + case LLQtWebKit::KEY_MULTIPLY: key = Qt::Key_Asterisk; break; + case LLQtWebKit::KEY_DIVIDE: key = Qt::Key_Slash; break; + case LLQtWebKit::KEY_F1: key = Qt::Key_F1; break; + case LLQtWebKit::KEY_F2: key = Qt::Key_F2; break; + case LLQtWebKit::KEY_F3: key = Qt::Key_F3; break; + case LLQtWebKit::KEY_F4: key = Qt::Key_F4; break; + case LLQtWebKit::KEY_F5: key = Qt::Key_F5; break; + case LLQtWebKit::KEY_F6: key = Qt::Key_F6; break; + case LLQtWebKit::KEY_F7: key = Qt::Key_F7; break; + case LLQtWebKit::KEY_F8: key = Qt::Key_F8; break; + case LLQtWebKit::KEY_F9: key = Qt::Key_F9; break; + case LLQtWebKit::KEY_F10: key = Qt::Key_F10; break; + case LLQtWebKit::KEY_F11: key = Qt::Key_F11; break; + case LLQtWebKit::KEY_F12: key = Qt::Key_F12; break; + + case LLQtWebKit::KEY_PAD_UP: key = Qt::Key_Up; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_DOWN: key = Qt::Key_Down; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_LEFT: key = Qt::Key_Left; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_RIGHT: key = Qt::Key_Right; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_HOME: key = Qt::Key_Home; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_END: key = Qt::Key_End; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_PGUP: key = Qt::Key_PageUp; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_PGDN: key = Qt::Key_PageDown; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_CENTER: key = Qt::Key_5; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_INS: key = Qt::Key_Insert; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_DEL: key = Qt::Key_Delete; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_RETURN: key = Qt::Key_Enter; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_ADD: key = Qt::Key_Plus; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_SUBTRACT: key = Qt::Key_Minus; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_MULTIPLY: key = Qt::Key_Asterisk; qt_modifiers |= Qt::KeypadModifier; break; + case LLQtWebKit::KEY_PAD_DIVIDE: key = Qt::Key_Slash; qt_modifiers |= Qt::KeypadModifier; break; + + case LLQtWebKit::KEY_NONE: key = Qt::Key_unknown; break; + + default: + key = (Qt::Key)toupper(key_code); + break; + } + + + QKeyEvent *event = + QKeyEvent::createExtendedKeyEvent( + type, + key, + qt_modifiers, + native_scan_code, + native_virtual_key, + native_modifiers, + text, + auto_repeat, + text.count()); + + qApp->sendEvent(d->mGraphicsScene, event); + + delete event; +} + + +// give focus to the browser so that input keyboard events work +void LLEmbeddedBrowserWindow::focusBrowser(bool focus_browser) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << focus_browser; +#endif + QEvent ev(QEvent::WindowActivate); + qApp->sendEvent(d->mGraphicsScene, &ev); + + QEvent ev2(QEvent::ActivationChange); + qApp->sendEvent(d->mGraphicsScene, &ev2); + + QFocusEvent event(focus_browser ? QEvent::FocusIn : QEvent::FocusOut, Qt::ActiveWindowFocusReason); + qApp->sendEvent(d->mPage, &event); +} + +void LLEmbeddedBrowserWindow::setWindowId(int window_id) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << window_id; +#endif + d->mWindowId = window_id; +} + +int LLEmbeddedBrowserWindow::getWindowId() +{ + return d->mWindowId; +} + +void LLEmbeddedBrowserWindow::proxyWindowOpened(const std::string target, const std::string uuid) +{ + LLWebPageOpenShim *shim = findShim(uuid); + if(!shim) + { + // We don't already have a shim with this uuid -- create one. + shim = new LLWebPageOpenShim(this, d->mPage); + d->mProxyPages.push_back(shim); + +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow::proxyWindowOpened: page list size is " << d->mProxyPages.size(); +#endif + } + + shim->setProxy(target, uuid); +} + +void LLEmbeddedBrowserWindow::proxyWindowClosed(const std::string uuid) +{ + LLWebPageOpenShim *shim = findShim(uuid); + if(shim) + { + deleteShim(shim); + } +} + +std::string LLEmbeddedBrowserWindow::evaluateJavaScript(std::string script) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << QString::fromStdString(script); +#endif + QString q_script = QString::fromStdString(script); + QString result = d->mPage->mainFrame()->evaluateJavaScript(q_script).toString(); + return llToStdString(result); +} + +void LLEmbeddedBrowserWindow::setHostLanguage(const std::string host_language) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << QString::fromStdString(host_language); +#endif + if ( d ) + if ( d->mPage ) + d->mPage->setHostLanguage( host_language ); +} + +void LLEmbeddedBrowserWindow::navigateErrorPage( int http_status_code ) +{ + LLEmbeddedBrowserWindowEvent event(getWindowId()); + event.setIntValue( http_status_code ); + + d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onNavigateErrorPage, event); +} + +void LLEmbeddedBrowserWindow::setNoFollowScheme(std::string scheme) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__ << QString::fromStdString(scheme); +#endif + d->mNoFollowScheme = QString::fromStdString(scheme); + // The scheme part of the url is what is before '://' + d->mNoFollowScheme = d->mNoFollowScheme.mid(0, d->mNoFollowScheme.indexOf("://")); +} + +std::string LLEmbeddedBrowserWindow::getNoFollowScheme() +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow" << __FUNCTION__; +#endif + return llToStdString(d->mNoFollowScheme); +} + +void LLEmbeddedBrowserWindow::prependHistoryUrl(std::string url) +{ +#ifdef WEBHISTORYPATCH + // *HACK: we only have a URL here, we set a "" title and "current time" as + // last visited time. + d->mPage->history()->prependItem(QString::fromStdString(url), + QString::fromAscii(""), + QDateTime::currentDateTime()); +#else + Q_UNUSED(url); +#endif +} + +void LLEmbeddedBrowserWindow::clearHistory() +{ + d->mPage->history()->clear(); +} + +std::string LLEmbeddedBrowserWindow::dumpHistory() +{ + std::ostringstream oss; + const QList &items = d->mPage->history()->backItems(9999); + oss << "cur: " << d->mPage->history()->currentItemIndex() << ":" + << d->mPage->history()->currentItem().url().toString().toAscii().data() << "\n"; + for (int i=0; i< items.count(); i++) { + oss << items[i].url().toString().toAscii().data() << "\n"; + } + return oss.str(); +} + +void LLEmbeddedBrowserWindow::cookieChanged(const std::string &cookie, const std::string &url, bool dead) +{ + LLEmbeddedBrowserWindowEvent event(getWindowId()); + event.setEventUri(url); + event.setStringValue(cookie); + event.setIntValue((int)dead); + + d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onCookieChanged, event); +} + +QWebPage *LLEmbeddedBrowserWindow::createWindow() +{ + QWebPage *result = NULL; + if(d->mOpeningSelf) + { + // Special case: opening self to set target, etc. +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow::createWindow: opening self to set target name. "; +#endif + result = d->mPage; + d->mOpeningSelf = false; + } + else + { + LLWebPageOpenShim *shim = new LLWebPageOpenShim(this, d->mPage); + d->mProxyPages.push_back(shim); + result = shim; + +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow::createWindow: page list size is " << d->mProxyPages.size(); +#endif + } + + return result; +} + +LLWebPageOpenShim *LLEmbeddedBrowserWindow::findShim(const std::string &uuid) +{ + LLEmbeddedBrowserWindowPrivate::ProxyList::iterator iter; + for(iter = d->mProxyPages.begin(); iter != d->mProxyPages.end(); iter++) + { + if((*iter)->matchesUUID(uuid)) + return *iter; + } + + return NULL; +} + +void LLEmbeddedBrowserWindow::deleteShim(LLWebPageOpenShim *shim) +{ + shim->window = 0; + shim->deleteLater(); + d->mProxyPages.remove(shim); + +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow::deleteShim: page list size is " << d->mProxyPages.size(); +#endif +} + +void LLEmbeddedBrowserWindow::setTarget(const std::string &target) +{ +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow::setTarget: setting target to " << QString::fromStdString(target); +#endif + + d->mOpeningSelf = true; + + std::stringstream s; + s << "window.open(\"\",\"" << target << "\");"; + + evaluateJavaScript(s.str()); +} + +std::string LLEmbeddedBrowserWindow::requestFilePicker() +{ + std::string filename_chosen; + + LLEmbeddedBrowserWindowEvent event(getWindowId()); + event.setEventUri(getCurrentUri()); + event.setStringValue("*.png;*.jpg"); + + // If there's at least one observer registered, call it with the event. + LLEmbeddedBrowserWindowPrivate::Emitter::iterator i = d->mEventEmitter.begin(); + if(i != d->mEventEmitter.end()) + { + filename_chosen = (*i)->onRequestFilePicker(event); + } + + return filename_chosen; +} + +bool LLEmbeddedBrowserWindow::authRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password) +{ + bool result = false; + +#ifdef LLEMBEDDEDBROWSER_DEBUG + qDebug() << "LLEmbeddedBrowserWindow::authRequest: requesting auth for url " << QString::fromStdString(in_url) << ", realm " << QString::fromStdString(in_realm); +#endif + + // If there's at least one observer registered, send it the auth request. + LLEmbeddedBrowserWindowPrivate::Emitter::iterator i = d->mEventEmitter.begin(); + if(i != d->mEventEmitter.end()) + { + result = (*i)->onAuthRequest(in_url, in_realm, out_username, out_password); + } + + return result; +} + +bool LLEmbeddedBrowserWindow::certError(const std::string &in_url, const std::string &in_msg) +{ + bool result = false; + + // If there's at least one observer registered, send it the auth request. + LLEmbeddedBrowserWindowPrivate::Emitter::iterator i = d->mEventEmitter.begin(); + if(i != d->mEventEmitter.end()) + { + result = (*i)->onCertError(in_url, in_msg); + } + + return result; +} + +void LLEmbeddedBrowserWindow::onQtDebugMessage( const std::string& msg, const std::string& msg_type) +{ + // If there's at least one observer registered, send it the auth request. + LLEmbeddedBrowserWindowPrivate::Emitter::iterator i = d->mEventEmitter.begin(); + if(i != d->mEventEmitter.end()) + { + (*i)->onQtDebugMessage(msg, msg_type); + } +} + +void LLEmbeddedBrowserWindow::setWhiteListRegex( const std::string& regex ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setWhiteListRegex( regex ); +} + +// Second Life viewer specific functions +void LLEmbeddedBrowserWindow::setSLObjectEnabled( bool enabled ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setSLObjectEnabled( enabled ); +} + +void LLEmbeddedBrowserWindow::setAgentLanguage( const std::string& agent_language ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setAgentLanguage( agent_language ); +} + +void LLEmbeddedBrowserWindow::setAgentRegion( const std::string& agent_region ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setAgentRegion( agent_region ); +} + +void LLEmbeddedBrowserWindow::setAgentLocation( double x, double y, double z ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setAgentLocation( x, y, z ); +} + +void LLEmbeddedBrowserWindow::setAgentGlobalLocation( double x, double y, double z ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setAgentGlobalLocation( x, y, z ); +} + +void LLEmbeddedBrowserWindow::setAgentOrientation( double angle ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setAgentOrientation( angle ); +} + +void LLEmbeddedBrowserWindow::setAgentMaturity( const std::string& agent_maturity ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setAgentMaturity( agent_maturity ); +} + +void LLEmbeddedBrowserWindow::emitLocation() +{ + if ( d ) + if ( d->mPage ) + d->mPage->emitLocation(); +} + +void LLEmbeddedBrowserWindow::emitMaturity() +{ + if ( d ) + if ( d->mPage ) + d->mPage->emitMaturity(); +} + +void LLEmbeddedBrowserWindow::emitLanguage() +{ + if ( d ) + if ( d->mPage ) + d->mPage->emitLanguage(); +} + +void LLEmbeddedBrowserWindow::setPageZoomFactor( double factor ) +{ + if ( d ) + if ( d->mPage ) + d->mPage->setPageZoomFactor( factor ); +} + +LLGraphicsScene::LLGraphicsScene() + : QGraphicsScene() + , window(0) +{ + connect(this, SIGNAL(changed(const QList &)), + this, SLOT(repaintRequestedSlot(const QList &))); +} + +void LLGraphicsScene::repaintRequestedSlot(const QList ®ions) +{ + if (!window) + return; + window->d->mDirty = true; + for (int i = 0; i < regions.count(); ++i) + { + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(window->getCurrentUri()); + event.setRectValue(regions[i].x(), regions[i].y(), regions[i].width(), regions[i].height()); + + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onPageChanged, event); + } +} + +#include +#include +LLWebView::LLWebView(QGraphicsItem *parent) + : QGraphicsWebView(parent) + , window(0) +{ +} + +bool LLWebView::event(QEvent* event) +{ + if (window && event->type() == QEvent::CursorChange) { + QCursor cursor = this->cursor(); + if (currentShape != cursor.shape()) { + currentShape = cursor.shape(); + LLQtWebKit::ECursor llcursor; + switch(currentShape) + { + case Qt::ArrowCursor: + llcursor = LLQtWebKit::C_ARROW; + break; + case Qt::PointingHandCursor: + llcursor = LLQtWebKit::C_POINTINGHAND; + break; + case Qt::IBeamCursor: + llcursor = LLQtWebKit::C_IBEAM; + break; + case Qt::SplitVCursor: + llcursor = LLQtWebKit::C_SPLITV; + break; + case Qt::SplitHCursor: + llcursor = LLQtWebKit::C_SPLITH; + break; + default: + qWarning() << "Unhandled cursor shape:" << currentShape; + llcursor = LLQtWebKit::C_ARROW; + } + + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(window->getCurrentUri()); + event.setIntValue((int)llcursor); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onCursorChanged, event); + } + + return true; + } + return QGraphicsWebView::event(event); +} + + +std::string llToStdString(const QString &s) +{ + return llToStdString(s.toUtf8()); +} + +std::string llToStdString(const QByteArray &bytes) +{ + return std::string(bytes.constData(), bytes.size()); +} + +std::string llToStdString(const QUrl &url) +{ + return llToStdString(url.toEncoded()); +} diff --git a/indra/llqtwebkit/llembeddedbrowserwindow.h b/indra/llqtwebkit/llembeddedbrowserwindow.h new file mode 100644 index 000000000..0c8080c15 --- /dev/null +++ b/indra/llqtwebkit/llembeddedbrowserwindow.h @@ -0,0 +1,185 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLEMBEDDEDBROWSERWINDOW_H +#define LLEMBEDDEDBROWSERWINDOW_H + +#include +#include +#include +#if defined _MSC_VER && _MSC_VER < 1600 +#include "pstdint.h" +#else +#include // Use the C99 official header +#endif + +#include "llqtwebkit.h" + +class LLEmbeddedBrowser; +class LLWebPageOpenShim; +class QWebPage; + +//////////////////////////////////////////////////////////////////////////////// +// class for a "window" that holds a browser - there can be lots of these +class LLEmbeddedBrowserWindowPrivate; +class LLEmbeddedBrowserWindow +{ +public: + LLEmbeddedBrowserWindow(); + virtual ~LLEmbeddedBrowserWindow(); + + // housekeeping + void setParent(LLEmbeddedBrowser* parent); + bool setSize(int16_t width, int16_t height); + void focusBrowser(bool focus_browser); + void scrollByLines(int16_t lines); + void setWindowId(int window_id); + int getWindowId(); + void proxyWindowOpened(const std::string target, const std::string uuid); + void proxyWindowClosed(const std::string uuid); + + // random accessors + int16_t getPercentComplete(); + std::string& getStatusMsg(); + std::string& getCurrentUri(); + + // memory buffer management + unsigned char* grabWindow(int x, int y, int width, int height); + bool flipWindow(bool flip); + unsigned char* getPageBuffer(); + int16_t getBrowserWidth(); + int16_t getBrowserHeight(); + int16_t getBrowserDepth(); + int32_t getBrowserRowSpan(); + + // set background color that you see in between pages - default is white but sometimes useful to change + void setBackgroundColor(const uint8_t red, const uint8_t green, const uint8_t blue); + + // can turn off updates to a page - e.g. when it's hidden by your windowing system + void setEnabled(bool enabledIn); + + // navigation + bool userAction(LLQtWebKit::EUserAction action); + bool userActionIsEnabled(LLQtWebKit::EUserAction action); + bool navigateTo(const std::string uri); + + // javascript access/control + std::string evaluateJavaScript(std::string script); + + // redirection when you hit an error page + void navigateErrorPage( int http_status_code ); + + // host language setting + void setHostLanguage(const std::string host_language); + + // mouse & keyboard events + void mouseEvent(LLQtWebKit::EMouseEvent mouse_event, int16_t button, int16_t x, int16_t y, LLQtWebKit::EKeyboardModifier modifiers); + void scrollWheelEvent(int16_t x, int16_t y, int16_t scroll_x, int16_t scroll_y, LLQtWebKit::EKeyboardModifier modifiers); + void keyboardEvent( + LLQtWebKit::EKeyEvent key_event, + uint32_t key_code, + const char *utf8_text, + LLQtWebKit::EKeyboardModifier modifiers, + uint32_t native_scan_code, + uint32_t native_virtual_key, + uint32_t native_modifiers); + + // allow consumers of this class and to observe browser events + bool addObserver(LLEmbeddedBrowserWindowObserver* observer); + bool remObserver(LLEmbeddedBrowserWindowObserver* observer); + int getObserverNumber(); + + // accessor/mutator for scheme that browser doesn't follow - e.g. secondlife.com:// + void setNoFollowScheme(std::string scheme); + std::string getNoFollowScheme(); + + // prepend the current history with the given url + void prependHistoryUrl(std::string url); + // clear the URL history + void clearHistory(); + std::string dumpHistory(); + + void cookieChanged(const std::string &cookie, const std::string &url, bool dead); + + QWebPage *createWindow(); + + LLWebPageOpenShim *findShim(const std::string &uuid); + void deleteShim(LLWebPageOpenShim *shim); + void setTarget(const std::string &target); + + std::string requestFilePicker(); + + void showWebInspector(bool enabled); + + bool authRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password); + bool certError(const std::string &in_url, const std::string &in_msg); + + void onQtDebugMessage( const std::string& msg, const std::string& msg_type); + + void enableLoadingOverlay(bool enable); + + void setWhiteListRegex( const std::string& regex ); + + void setPageZoomFactor( double factor ); + + // Second Life specific functions + void setSLObjectEnabled( bool enabled ); + void setAgentLanguage( const std::string& agent_language ); + void setAgentRegion( const std::string& agent_region ); + void setAgentLocation( double x, double y, double z ); + void setAgentGlobalLocation( double x, double y, double z ); + void setAgentOrientation( double angle ); + void setAgentMaturity( const std::string& agent_maturity ); + void emitLocation(); + void emitMaturity(); + void emitLanguage(); + +private: + friend class LLWebPage; + friend class LLWebPageOpenShim; + friend class LLGraphicsScene; + friend class LLWebView; + friend class LLEmbeddedBrowserPrivate; + LLEmbeddedBrowserWindowPrivate *d; + bool mEnableLoadingOverlay; + +}; + + +// QString::toStdString converts to ascii, not utf8. Define our own versions that do utf8. + +#ifdef QSTRING_H +std::string llToStdString(const QString &s); +#endif + +#ifdef QBYTEARRAY_H +std::string llToStdString(const QByteArray &bytes); +#endif + +#ifdef QURL_H +std::string llToStdString(const QUrl &url); +#endif + +#endif // LLEMBEDEDDBROWSERWINDOW_H diff --git a/indra/llqtwebkit/llembeddedbrowserwindow_p.h b/indra/llqtwebkit/llembeddedbrowserwindow_p.h new file mode 100644 index 000000000..27b36d947 --- /dev/null +++ b/indra/llqtwebkit/llembeddedbrowserwindow_p.h @@ -0,0 +1,251 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLEMBEDDEDBROWSERWINDOW_P_H +#define LLEMBEDDEDBROWSERWINDOW_P_H + +#include "llwebpage.h" +#include "llwebpageopenshim.h" + +#include +#include +#include +#include +#include + +/////////////////////////////////////////////////////////////////////////////// +// manages the process of storing and emitting events that the consumer +// of the embedding class can observe +template< class T > +class LLEmbeddedBrowserWindowEmitter +{ + public: + LLEmbeddedBrowserWindowEmitter() { }; + ~LLEmbeddedBrowserWindowEmitter() { }; + + typedef typename T::EventType EventType; + typedef std::list< T* > ObserverContainer; + typedef typename ObserverContainer::iterator iterator; + typedef void(T::*observerMethod)(const EventType&); + + /////////////////////////////////////////////////////////////////////////////// + // + bool addObserver(T* observer) + { + if (! observer) + return false; + + if (std::find(observers.begin(), observers.end(), observer) != observers.end()) + return false; + + observers.push_back(observer); + + return true; + } + + /////////////////////////////////////////////////////////////////////////////// + // + bool remObserver(T* observer) + { + if (! observer) + return false; + + observers.remove(observer); + + return true; + } + + /////////////////////////////////////////////////////////////////////////////// + // + void update(observerMethod method, const EventType& msg) + { + typename std::list< T* >::iterator iter = observers.begin(); + + while(iter != observers.end()) + { + ((*iter)->*method)(msg); + ++iter; + } + } + + int getObserverNumber() + { + return observers.size(); + } + + iterator begin() + { + return observers.begin(); + } + + iterator end() + { + return observers.end(); + } + + protected: + ObserverContainer observers; +}; + +#include "llqtwebkit.h" +#include "llembeddedbrowserwindow.h" +#include +#include + +class LLGraphicsScene : public QGraphicsScene +{ + Q_OBJECT + +public: + LLGraphicsScene(); + LLEmbeddedBrowserWindow *window; + + void mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) { + QGraphicsScene::mouseMoveEvent(mouseEvent); + mouseEvent->setAccepted(true); + mouseEvent->setButtons(Qt::LeftButton); + } + +private slots: + void repaintRequestedSlot(const QList &); + friend class LLEmbeddedBrowserWindow; +}; + + +class LLWebView : public QGraphicsWebView +{ + Q_OBJECT + +public: + LLWebView(QGraphicsItem *parent = 0); + LLEmbeddedBrowserWindow *window; + + static QUrl guessUrlFromString(const QString &string); + + int width() const { return boundingRect().width(); } + int height() const { return boundingRect().height(); } + +protected: + bool event(QEvent *event); + + Qt::CursorShape currentShape; +}; + +class LLEmbeddedBrowserWindowPrivate +{ + public: + LLEmbeddedBrowserWindowPrivate() + : mParent(0) + , mPage(0) + , mView(0) + , mGraphicsScene(0) + , mGraphicsView(0) + , mInspector(0) + , mCurrentMouseButtonState(Qt::NoButton) + , mPercentComplete(0) + , mShowLoadingOverlay(false) + , mTimeLoadStarted(0) + , mStatusText("") + , mTitle("") + , mCurrentUri("") + , mNoFollowScheme("secondlife") + , mWindowId(-1) + , mEnabled(true) + , mFlipBitmap(false) + , mPageBuffer(NULL) + , mDirty(false) + , mOpeningSelf(false) + { + } + + ~LLEmbeddedBrowserWindowPrivate() + { + while(!mProxyPages.empty()) + { + ProxyList::iterator iter = mProxyPages.begin(); + (*iter)->window = 0; + (*iter)->deleteLater(); + } + + if(mGraphicsScene) + { + mGraphicsScene->window = 0; + } + if(mPage) + { + mPage->window = 0; + } + if(mView) + { + mView->deleteLater(); + } + if(mGraphicsScene) + { + mGraphicsScene->deleteLater(); + } + if(mGraphicsView) + { + mGraphicsView->viewport()->setParent(mGraphicsView); + mGraphicsView->deleteLater(); + } + if(mInspector) + { + mInspector->deleteLater(); + } + } + + typedef LLEmbeddedBrowserWindowEmitter< LLEmbeddedBrowserWindowObserver> Emitter; + Emitter mEventEmitter; + QImage mImage; + LLEmbeddedBrowser *mParent; + LLWebPage *mPage; + typedef std::list ProxyList; + ProxyList mProxyPages; + + LLWebView *mView; + QWebInspector* mInspector; + LLGraphicsScene *mGraphicsScene; + QGraphicsView *mGraphicsView; + Qt::MouseButtons mCurrentMouseButtonState; + + int16_t mPercentComplete; + bool mShowLoadingOverlay; + time_t mTimeLoadStarted; + std::string mStatusText; + std::string mTitle; + std::string mCurrentUri; + QString mNoFollowScheme; + int mWindowId; + bool mEnabled; + bool mFlipBitmap; + unsigned char* mPageBuffer; + QColor backgroundColor; + bool mDirty; + bool mOpeningSelf; +}; + + +#endif + diff --git a/indra/llqtwebkit/lljsobject.cpp b/indra/llqtwebkit/lljsobject.cpp new file mode 100644 index 000000000..f5abfa702 --- /dev/null +++ b/indra/llqtwebkit/lljsobject.cpp @@ -0,0 +1,153 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include "lljsobject.h" + +LLJsObject::LLJsObject( QObject* parent ) : + QObject( parent ) +{ + mEnabled = false; + + mAgentLanguage = QString(); + mAgentMaturity = QString(); + mAgentRegion = QString(); + + mAgentLocation[ "x" ] = 0.0; + mAgentLocation[ "y" ] = 0.0; + mAgentLocation[ "z" ] = 0.0; + + mAgentGlobalLocation[ "x" ] = 0.0; + mAgentGlobalLocation[ "y" ] = 0.0; + mAgentGlobalLocation[ "z" ] = 0.0; +} + +void LLJsObject::setSLObjectEnabled( bool enabled ) +{ + mEnabled = enabled; +} + +bool LLJsObject::getSLObjectEnabled() +{ + return mEnabled; +} + +void LLJsObject::setAgentLanguage( const QString& agent_language ) +{ + if ( mEnabled ) + { + mAgentLanguage = agent_language; + } + else + { + mAgentLanguage = QString(); + } +} + +void LLJsObject::setAgentRegion( const QString& agent_region ) +{ + if ( mEnabled ) + { + mAgentRegion = agent_region; + } + else + { + mAgentRegion = QString(); + } +} + +void LLJsObject::setAgentMaturity( const QString& agent_maturity ) +{ + if ( mEnabled ) + { + mAgentMaturity = agent_maturity; + } + else + { + mAgentMaturity = QString(); + } +} + +void LLJsObject::setAgentLocation( const QVariantMap agent_location ) +{ + if ( mEnabled ) + { + mAgentLocation = agent_location; + } + else + { + mAgentLocation[ "x" ] = 0.0; + mAgentLocation[ "y" ] = 0.0; + mAgentLocation[ "z" ] = 0.0; + } +} + +void LLJsObject::setAgentGlobalLocation( const QVariantMap agent_global_location ) +{ + if ( mEnabled ) + { + mAgentGlobalLocation = agent_global_location; + } + else + { + mAgentGlobalLocation[ "x" ] = 0.0; + mAgentGlobalLocation[ "y" ] = 0.0; + mAgentGlobalLocation[ "z" ] = 0.0; + } +} + +void LLJsObject::setAgentOrientation( const double angle ) +{ + if ( mEnabled ) + { + mAgentOrientation = angle; + } + else + { + mAgentOrientation = 0.0; + } +} + +void LLJsObject::emitLocation() +{ + QVariantMap agent_location; + + agent_location[ "region" ] = mAgentRegion; + agent_location[ "location" ] = mAgentLocation; + agent_location[ "orientation" ] = mAgentOrientation; + agent_location[ "globalLocation" ] = mAgentGlobalLocation; + + emit getLocation( agent_location ); +} + +void LLJsObject::emitMaturity() +{ + emit getMaturity( mAgentMaturity ); +} + +void LLJsObject::emitLanguage() +{ + emit getLanguage( mAgentLanguage ); +} diff --git a/indra/llqtwebkit/lljsobject.h b/indra/llqtwebkit/lljsobject.h new file mode 100644 index 000000000..806a8a8a1 --- /dev/null +++ b/indra/llqtwebkit/lljsobject.h @@ -0,0 +1,71 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLJSOBJECT_H +#define LLJSOBJECT_H + +#include +#include +#include + +class LLJsObject : + public QObject +{ + Q_OBJECT + + public: + LLJsObject( QObject* parent = 0 ); + + void setSLObjectEnabled( bool enabled ); + bool getSLObjectEnabled(); + + void setAgentLanguage( const QString& agent_language ); + void setAgentRegion( const QString& agent_region ); + void setAgentMaturity( const QString& agent_maturity ); + void setAgentLocation( const QVariantMap agent_location ); + void setAgentGlobalLocation( const QVariantMap agent_global_location ); + void setAgentOrientation( const double angle ); + + void emitLocation(); + void emitMaturity(); + void emitLanguage(); + + signals: + void getLocation( const QVariantMap agent_location ); + void getMaturity( const QString agent_maturity ); + void getLanguage( const QString agent_language ); + + private: + bool mEnabled; + + QString mAgentLanguage; + QString mAgentMaturity; + QString mAgentRegion; + QVariantMap mAgentLocation; + QVariantMap mAgentGlobalLocation; + double mAgentOrientation; +}; + +#endif // LLJSOBJECT_H diff --git a/indra/llqtwebkit/llnetworkaccessmanager.cpp b/indra/llqtwebkit/llnetworkaccessmanager.cpp new file mode 100644 index 000000000..2a51f1340 --- /dev/null +++ b/indra/llqtwebkit/llnetworkaccessmanager.cpp @@ -0,0 +1,247 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include "llnetworkaccessmanager.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "llembeddedbrowserwindow.h" +#include "llembeddedbrowser_p.h" + +#include "ui_passworddialog.h" + + +LLNetworkAccessManager::LLNetworkAccessManager(LLEmbeddedBrowserPrivate* browser,QObject* parent) + : QNetworkAccessManager(parent) + , mBrowser(browser) +{ + connect(this, SIGNAL(finished(QNetworkReply*)), + this, SLOT(finishLoading(QNetworkReply*))); + connect(this, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)), + this, SLOT(authenticationRequiredSlot(QNetworkReply*, QAuthenticator*))); + connect(this, SIGNAL(sslErrors( QNetworkReply *, const QList &)), + this, SLOT(sslErrorsSlot( QNetworkReply *, const QList & ))); +} + +QNetworkReply *LLNetworkAccessManager::createRequest(Operation op, const QNetworkRequest &request, + QIODevice *outgoingData) +{ + + // Create a local copy of the request we can modify. + QNetworkRequest mutable_request(request); + + // Set an Accept-Language header in the request, based on what the host has set through setHostLanguage. + mutable_request.setRawHeader(QByteArray("Accept-Language"), QByteArray(mBrowser->mHostLanguage.c_str())); + + // this is undefine'd in 4.7.1 and leads to caching issues - setting it here explicitly + mutable_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferNetwork); + + if(op == GetOperation) + { + // GET requests should not have a Content-Type header, but it seems somebody somewhere is adding one. + // This removes it. + mutable_request.setRawHeader("Content-Type", QByteArray()); + } + +// qDebug() << "headers for request:" << mutable_request.rawHeaderList(); + + // and pass this through to the parent implementation + return QNetworkAccessManager::createRequest(op, mutable_request, outgoingData); +} + +void LLNetworkAccessManager::sslErrorsSlot(QNetworkReply* reply, const QList& errors) +{ + // Enabling this can help diagnose certificate verification issues. + const bool ssl_debugging_on = false; + + // flag that indicates if the error that brought us here is one we care about or not + bool valid_ssl_error = false; + + foreach( const QSslError &error, errors ) + { + if ( ssl_debugging_on ) + { + qDebug() << "SSL error details are (" << (int)(error.error()) << ") - " << error.error(); + } + + // SSL "error" codes we don't care about - if we get one of these, we want to continue + if ( error.error() != QSslError::NoError + // many more in src/network/ssl/qsslerror.h + ) + { + if ( ssl_debugging_on ) + { + qDebug() << "Found valid SSL error - will not ignore"; + } + + valid_ssl_error = true; + } + else + { + if ( ssl_debugging_on ) + { + qDebug() << "Found invalid SSL error - will ignore and continue"; + } + } + } + + if ( ssl_debugging_on ) + { + qDebug() << "LLNetworkAccessManager" << __FUNCTION__ << "errors: " << errors + << ", peer certificate chain: "; + + QSslCertificate cert; + foreach(cert, reply->sslConfiguration().peerCertificateChain()) + { + qDebug() << " cert: " << cert + << ", issuer = " << cert.issuerInfo(QSslCertificate::CommonName) + << ", subject = " << cert.subjectInfo(QSslCertificate::CommonName); + } + } + + if ( valid_ssl_error ) + { + std::string url = llToStdString(reply->url()); + QString err_msg=""; + foreach( const QSslError &error, errors ) + { + err_msg+=error.errorString(); + err_msg+="\n"; + + QSslCertificate cert = error.certificate(); + + QString issuer_info=""; + issuer_info+="C="; + issuer_info+=cert.issuerInfo(QSslCertificate::CountryName); + issuer_info+=", ST="; + issuer_info+=cert.issuerInfo(QSslCertificate::StateOrProvinceName); + issuer_info+=", L="; + issuer_info+=cert.issuerInfo(QSslCertificate::LocalityName); + issuer_info+=", O="; + issuer_info+=cert.issuerInfo(QSslCertificate::Organization); + issuer_info+=", OU="; + issuer_info+=cert.issuerInfo(QSslCertificate::OrganizationalUnitName); + issuer_info+=", CN="; + issuer_info+=cert.issuerInfo(QSslCertificate::CommonName); + err_msg+=issuer_info; + err_msg+="\n"; + + QString subject_info=""; + subject_info+="C="; + subject_info+=cert.subjectInfo(QSslCertificate::CountryName); + subject_info+=", ST="; + subject_info+=cert.subjectInfo(QSslCertificate::StateOrProvinceName); + subject_info+=", L="; + subject_info+=cert.subjectInfo(QSslCertificate::LocalityName); + subject_info+=", O="; + subject_info+=cert.subjectInfo(QSslCertificate::Organization); + subject_info+=", OU="; + subject_info+=cert.subjectInfo(QSslCertificate::OrganizationalUnitName); + subject_info+=", CN="; + subject_info+=cert.subjectInfo(QSslCertificate::CommonName); + err_msg+=subject_info; + err_msg+="\n"; + + err_msg+="Not valid before: "; + err_msg+=cert.effectiveDate().toString(); + err_msg+="\n"; + err_msg+="Not valid after: "; + err_msg+=cert.expiryDate().toString(); + err_msg+="\n"; + err_msg+="----------\n"; + } + + if(mBrowser->certError(url, llToStdString(err_msg))) + { + // signal we should ignore and continue processing + reply->ignoreSslErrors(); + } + else + { + // The user canceled, don't return yet so we can test ignore variable + } + } + + // we the SSL error is invalid (in our opinion) or we explicitly ignore all SSL errors + if ( valid_ssl_error == false || ( mBrowser && mBrowser->mIgnoreSSLCertErrors ) ) + { + // signal we should ignore and continue processing + reply->ignoreSslErrors(); + }; +} + +void LLNetworkAccessManager::finishLoading(QNetworkReply* reply) +{ + QVariant val = reply->attribute( QNetworkRequest::HttpStatusCodeAttribute ); + int http_status_code = val.toInt(); + if ( http_status_code >=400 && http_status_code <=499 ) + { + if (mBrowser) + { + std::string current_url = llToStdString(reply->url()); + foreach (LLEmbeddedBrowserWindow *window, mBrowser->windows) + { + if (window->getCurrentUri() == current_url) + { + window->navigateErrorPage( http_status_code ); + } + } + } + } + + // tests if navigation request resulted in a cache hit - useful for testing so leaving here for the moment. + //QVariant from_cache = reply->attribute( QNetworkRequest::SourceIsFromCacheAttribute ); + //QString url = QString(reply->url().toEncoded()); + //qDebug() << url << " --- from cache?" << fromCache.toBool() << "\n"; +} + +void LLNetworkAccessManager:: authenticationRequiredSlot(QNetworkReply *reply, QAuthenticator *authenticator) +{ + std::string username; + std::string password; + std::string url = llToStdString(reply->url()); + std::string realm = llToStdString(authenticator->realm()); + + if(mBrowser->authRequest(url, realm, username, password)) + { + // Got credentials to try, attempt auth with them. + authenticator->setUser(QString::fromStdString(username)); + authenticator->setPassword(QString::fromStdString(password)); + } + else + { + // The user cancelled, don't attempt auth. + } +} + diff --git a/indra/llqtwebkit/llnetworkaccessmanager.h b/indra/llqtwebkit/llnetworkaccessmanager.h new file mode 100644 index 000000000..478b679aa --- /dev/null +++ b/indra/llqtwebkit/llnetworkaccessmanager.h @@ -0,0 +1,57 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLNETWORKACCESSMANAGER_H +#define LLNETWORKACCESSMANAGER_H + +#include +#include + +#include "ui_passworddialog.h" + +class QGraphicsProxyWidget; + +class LLEmbeddedBrowserPrivate; +class LLNetworkAccessManager: public QNetworkAccessManager +{ + Q_OBJECT +public: + LLNetworkAccessManager(LLEmbeddedBrowserPrivate* browser, QObject* parent = 0); + +protected: + virtual QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, + QIODevice *outgoingData = 0); +private slots: + void finishLoading(QNetworkReply* reply); + void authenticationRequiredSlot(QNetworkReply *reply, QAuthenticator *authenticator); + void sslErrorsSlot(QNetworkReply* reply, const QList& errors); + +private: + LLEmbeddedBrowserPrivate* mBrowser; + +}; + +#endif // LLNETWORKACCESSMANAGER_H + diff --git a/indra/llqtwebkit/llqtwebkit.cpp b/indra/llqtwebkit/llqtwebkit.cpp new file mode 100644 index 000000000..2be066d11 --- /dev/null +++ b/indra/llqtwebkit/llqtwebkit.cpp @@ -0,0 +1,820 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include +#include +#include + +#include "llqtwebkit.h" + +#include "llembeddedbrowser.h" +#include "llembeddedbrowserwindow.h" + +LLQtWebKit* LLQtWebKit::sInstance = 0; + +//////////////////////////////////////////////////////////////////////////////// +// +LLQtWebKit::LLQtWebKit() : + mMaxBrowserWindows(16) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLQtWebKit* LLQtWebKit::getInstance() +{ + if (! sInstance) + { + sInstance = new LLQtWebKit; + } + + return sInstance; +} + +//////////////////////////////////////////////////////////////////////////////// +// +LLQtWebKit::~LLQtWebKit() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::init(std::string application_directory, + std::string component_directory, + std::string profile_directory, + void* native_window_handle) +{ + return LLEmbeddedBrowser::getInstance()->init(application_directory, + component_directory, + profile_directory, + native_window_handle); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLQtWebKit::getLastError() +{ + return LLEmbeddedBrowser::getInstance()->getLastError(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::reset() +{ + mBrowserWindowMap.clear(); + return LLEmbeddedBrowser::getInstance()->reset(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::clearCache() +{ + return LLEmbeddedBrowser::getInstance()->clearCache(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLQtWebKit::getVersion() +{ + const int majorVersion = 2; + const int minorVersion = 2; + + // number of hours since "time began" for this library - used to identify builds of same version + const int magicNumber = static_cast< int >((time(NULL) / 3600L) - (321190L)); + + // return as a string for now - don't think we need to expose actual version numbers + std::ostringstream codec; + codec << std::setw(1) << std::setfill('0'); + codec << majorVersion << "."; + codec << std::setw(2) << std::setfill('0'); + codec << minorVersion << "."; + codec << std::setw(5) << std::setfill('0'); + codec << magicNumber; + codec << " (QtWebKit version "; + codec << LLEmbeddedBrowser::getInstance()->getGREVersion(); + codec << ")"; + + return codec.str(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::setBrowserAgentId(std::string id) +{ + LLEmbeddedBrowser::getInstance()->setBrowserAgentId(id); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::enableProxy(bool enabled, std::string host_name, int port) +{ + return LLEmbeddedBrowser::getInstance()->enableProxy(enabled, host_name, port); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::setHostLanguage(const std::string& host_language ) +{ + LLEmbeddedBrowser::getInstance()->setHostLanguage(host_language); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLQtWebKit::createBrowserWindow(int width, int height, const std::string target) +{ + LLEmbeddedBrowserWindow* browser_window = LLEmbeddedBrowser::getInstance()->createBrowserWindow(width, height, target); + + if (browser_window) + { + // arbitrary limit so we don't exhaust system resources + int id(0); + while (++id < mMaxBrowserWindows) + { + std::pair< BrowserWindowMapIter, bool > result = mBrowserWindowMap.insert(std::make_pair(id, browser_window)); + + // find first place the insert succeeds and use that index as the id + if (result.second) + { + browser_window->setWindowId(id); + return id; + } + } + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::proxyWindowOpened(int browser_window_id, const std::string target, const std::string uuid) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->proxyWindowOpened(target, uuid); + } +} +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::proxyWindowClosed(int browser_window_id, const std::string uuid) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->proxyWindowClosed(uuid); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::destroyBrowserWindow(int browser_window_id) +{ + // don't use the utility method here since we need the iteratorator to remove the entry from the map + BrowserWindowMapIter iterator = mBrowserWindowMap.find(browser_window_id); + LLEmbeddedBrowserWindow* browser_window = (*iterator).second; + + if (browser_window) + { + LLEmbeddedBrowser::getInstance()->destroyBrowserWindow(browser_window); + } + + mBrowserWindowMap.erase(iterator); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::setBackgroundColor(int browser_window_id, const int red, const int green, const int blue) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->setBackgroundColor(red, green, blue); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::setEnabled(int browser_window_id, bool enabled) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->setEnabled(enabled); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::setSize(int browser_window_id, int width, int height) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->setSize(width, height); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::scrollByLines(int browser_window_id, int lines) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->scrollByLines(lines); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::addObserver(int browser_window_id, LLEmbeddedBrowserWindowObserver* subject) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->addObserver(subject); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::remObserver(int browser_window_id, LLEmbeddedBrowserWindowObserver* subject) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->remObserver(subject); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::navigateTo(int browser_window_id, const std::string uri) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->navigateTo(uri) ? true : false; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::userAction(int browser_window_id, EUserAction action) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->userAction(action); + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::userActionIsEnabled(int browser_window_id, EUserAction action) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->userActionIsEnabled(action); + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// +const unsigned char* LLQtWebKit::grabBrowserWindow(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->grabWindow(0, 0, browser_window->getBrowserWidth(), browser_window->getBrowserHeight()); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +const unsigned char* LLQtWebKit::getBrowserWindowPixels(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->getPageBuffer(); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::flipWindow(int browser_window_id, bool flip) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->flipWindow(flip); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLQtWebKit::getBrowserWidth(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->getBrowserWidth(); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLQtWebKit::getBrowserHeight(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->getBrowserHeight(); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLQtWebKit::getBrowserDepth(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->getBrowserDepth(); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +int LLQtWebKit::getBrowserRowSpan(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->getBrowserRowSpan(); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::mouseEvent(int browser_window_id, EMouseEvent mouse_event, int button, int x, int y, EKeyboardModifier modifiers) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->mouseEvent(mouse_event, button, x, y, modifiers); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::scrollWheelEvent(int browser_window_id, int x, int y, int scroll_x, int scroll_y, EKeyboardModifier modifiers) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->scrollWheelEvent(x, y, scroll_x, scroll_y, modifiers); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::keyboardEvent( + int browser_window_id, + EKeyEvent key_event, + uint32_t key_code, + const char *utf8_text, + EKeyboardModifier modifiers, + uint32_t native_scan_code, + uint32_t native_virtual_key, + uint32_t native_modifiers) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->keyboardEvent(key_event, key_code, utf8_text, modifiers, native_scan_code, native_virtual_key, native_modifiers); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::focusBrowser(int browser_window_id, bool focus_browser) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->focusBrowser(focus_browser); + return true; + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::setNoFollowScheme(int browser_window_id, std::string scheme) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->setNoFollowScheme(scheme); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLQtWebKit::getNoFollowScheme(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->getNoFollowScheme(); + } + + return (""); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::pump(int max_milliseconds) +{ + LLEmbeddedBrowser::getInstance()->pump(max_milliseconds); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::enableCookies(bool enabled) +{ + LLEmbeddedBrowser::getInstance()->enableCookies( enabled ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::clearAllCookies() +{ + return LLEmbeddedBrowser::getInstance()->clearAllCookies(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::setCookies(const std::string &cookies) +{ + return LLEmbeddedBrowser::getInstance()->setCookies(cookies); +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLQtWebKit::getAllCookies() +{ + return LLEmbeddedBrowser::getInstance()->getAllCookies(); +} + + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::enablePlugins(bool enabled) +{ + LLEmbeddedBrowser::getInstance()->enablePlugins(enabled); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::enableJavaScript(bool enabled) +{ + LLEmbeddedBrowser::getInstance()->enableJavaScript(enabled); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::showWebInspector(bool show) +{ + return LLEmbeddedBrowser::getInstance()->showWebInspector(show); +} + +//////////////////////////////////////////////////////////////////////////////// +// +std::string LLQtWebKit::evaluateJavaScript(int browser_window_id, const std::string script) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->evaluateJavaScript(script); + } + + return ""; +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::prependHistoryUrl(int browser_window_id, std::string url) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->prependHistoryUrl(url); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::clearHistory(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->clearHistory(); + } +} + +std::string LLQtWebKit::dumpHistory(int browser_window_id) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + return browser_window->dumpHistory(); + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::setCAFile(const std::string &ca_file) +{ + return LLEmbeddedBrowser::getInstance()->setCAFile(ca_file); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::addCAFile(const std::string &ca_file) +{ + return LLEmbeddedBrowser::getInstance()->addCAFile(ca_file); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void LLQtWebKit::setIgnoreSSLCertErrors(bool ignore) +{ + LLEmbeddedBrowser::getInstance()->setIgnoreSSLCertErrors(ignore); +} + +//////////////////////////////////////////////////////////////////////////////// +// +bool LLQtWebKit::getIgnoreSSLCertErrors() +{ + return LLEmbeddedBrowser::getInstance()-> getIgnoreSSLCertErrors(); +} + +//////////////////////////////////////////////////////////////////////////////// +// +const std::vector< std::string > LLQtWebKit::getInstalledCertsList() +{ + return LLEmbeddedBrowser::getInstance()->getInstalledCertsList(); +} + +//////////////////////////////////////////////////////////////////////////////// +// utility method to get an LLEmbeddedBrowserWindow* from a window id (int) +LLEmbeddedBrowserWindow* LLQtWebKit::getBrowserWindowFromWindowId(int browser_window_id) +{ + BrowserWindowMapIter iterator = mBrowserWindowMap.find(browser_window_id); + + if (iterator != mBrowserWindowMap.end()) + return (*iterator).second; + else + return 0; +} + +LLEmbeddedBrowserWindowObserver::~LLEmbeddedBrowserWindowObserver() +{ +} + +void LLEmbeddedBrowserWindowObserver::onCursorChanged(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onPageChanged(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onNavigateBegin(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onNavigateComplete(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onUpdateProgress(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onStatusTextChange(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onTitleChange(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onLocationChange(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onNavigateErrorPage(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onClickLinkHref(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onClickLinkNoFollow(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onCookieChanged(const EventType&) +{ +} + +std::string LLEmbeddedBrowserWindowObserver::onRequestFilePicker(const EventType&) +{ + return std::string(); +} + +void LLEmbeddedBrowserWindowObserver::onWindowCloseRequested(const EventType&) +{ +} + +void LLEmbeddedBrowserWindowObserver::onWindowGeometryChangeRequested(const EventType&) +{ +} + +bool LLEmbeddedBrowserWindowObserver::onAuthRequest(const std::string &, const std::string &, std::string &, std::string &) +{ + return false; +} + +bool LLEmbeddedBrowserWindowObserver::onCertError(const std::string &, const std::string &) +{ + return false; // cancel and abort after cert error +} + +void LLEmbeddedBrowserWindowObserver::onQtDebugMessage( const std::string &, const std::string &) +{ +} + +void LLEmbeddedBrowserWindowObserver::onLinkHovered(const EventType&) +{ +} + +// set the regex used to determine if a page is trusted or not +void LLQtWebKit::setWhiteListRegex( int browser_window_id, const std::string& regex ) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->setWhiteListRegex(regex); + } +} + +// Second Life viewer specific functions +void LLQtWebKit::setSLObjectEnabled( bool enabled ) +{ + LLEmbeddedBrowser::getInstance()->setSLObjectEnabled( enabled ); +} + +void LLQtWebKit::setAgentLanguage( const std::string& agent_language ) +{ + LLEmbeddedBrowser::getInstance()->setAgentLanguage( agent_language ); +} + +void LLQtWebKit::setAgentRegion( const std::string& agent_region ) +{ + LLEmbeddedBrowser::getInstance()->setAgentRegion( agent_region ); +} + +void LLQtWebKit::setAgentLocation( double x, double y, double z ) +{ + LLEmbeddedBrowser::getInstance()->setAgentLocation( x, y, z ); +} + +void LLQtWebKit::setAgentGlobalLocation( double x, double y, double z ) +{ + LLEmbeddedBrowser::getInstance()->setAgentGlobalLocation( x, y, z ); +} + +void LLQtWebKit::setAgentOrientation( double angle ) +{ + LLEmbeddedBrowser::getInstance()->setAgentOrientation( angle ); +} + + +void LLQtWebKit::setAgentMaturity( const std::string& agent_maturity ) +{ + LLEmbeddedBrowser::getInstance()->setAgentMaturity( agent_maturity ); +} + +void LLQtWebKit::emitLocation() +{ + LLEmbeddedBrowser::getInstance()->emitLocation(); +} + +void LLQtWebKit::emitMaturity() +{ + LLEmbeddedBrowser::getInstance()->emitMaturity(); +} + +void LLQtWebKit::emitLanguage() +{ + LLEmbeddedBrowser::getInstance()->emitLanguage(); +} + +void LLQtWebKit::enableQtMessageHandler( bool enable ) +{ + LLEmbeddedBrowser::getInstance()->enableQtMessageHandler( enable ); +} + +void LLQtWebKit::enableLoadingOverlay( int browser_window_id, bool enable) +{ + LLEmbeddedBrowserWindow* browser_window = getBrowserWindowFromWindowId(browser_window_id); + if (browser_window) + { + browser_window->enableLoadingOverlay( enable ); + } +} + +void LLQtWebKit::setPageZoomFactor( double factor ) +{ + LLEmbeddedBrowser::getInstance()->setPageZoomFactor( factor ); +} diff --git a/indra/llqtwebkit/llqtwebkit.h b/indra/llqtwebkit/llqtwebkit.h new file mode 100644 index 000000000..8e7ebd390 --- /dev/null +++ b/indra/llqtwebkit/llqtwebkit.h @@ -0,0 +1,470 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLQTWEBKIT_H +#define LLQTWEBKIT_H + +#if defined _MSC_VER && _MSC_VER < 1600 +// no pstdint.h in the client where this header is used +typedef unsigned long uint32_t; +#else +#include // Use the C99 official header +#endif + +#include +#include +#include + +class LLEmbeddedBrowser; +class LLEmbeddedBrowserWindow; + +// Use this to conditionalize code that depends on particular changes to the llqtwebkit API. +// This can be useful for times when we're waiting for a rebuild on one platform or another. +// When you bump this number, please note what the changes were in a comment below the #define, +// and keep the existing comments as history. +#define LLQTWEBKIT_API_VERSION 16 +// version 16: + // Added LLQtWebKit::enableLoadingOverlay() +// version 15: + // Added LLQtWebKit::setPageZoomFactor() +// version 14: + // Added LLEmbeddedBrowserWindowObserver::onQtDebugMessage +// version 13: + // Added LLEmbeddedBrowserWindowObserver::onCertError +// version 12: + // Pass over value to indicate if host for current URL is trusted as per whitelist regex or not +// version 11: + // Added initial support for url/host whitelist via a regex +// version 10: + // Added initial support for creating and displaying the Qt Web Inspector +// version 9: + // Added initial support for exposing certain Second Life viewer/agent variables to JavaScript +// version 8: + // Removed calls to set/clear 404 redirects and made the API now emit an event that the + // consumer can catch and decide what to do when an HTTP status code after navigate is 400-499 +// version 7: + // Added LLEmbeddedBrowserWindowEvent::setNavigationType() && LLEmbeddedBrowserWindowEvent::getNavigationType() + // Used to pass (and retrieve) the type of navigation event that caused a link to be activated. +// version 6: + // Added LLQtWebKit::addCAFile() +// version 5: + // Added LLEmbeddedBrowserWindowObserver::onLinkHovered +// version 4: + // Added LLEmbeddedBrowserWindowObserver::onAuthRequest +// version 3: + // Added setIgnoreSSLCertErrors and getIgnoreSSLCertErrors +// version 2: + // Changed the usage of the event parameters in onClickLinkHref and onClickLinkNoFollow events slightly. + // The clicked URI for both should now be retrieved with getEventUri() instead of getStringValue(). + // The "target" string in onClickLinkHref is now retrieved with getStringValue() instead of getStringValue2(). + // The contents of getStringValue2() in the onClickLinkHref event is now a unique ID for the window proxy the click targets. + // Removed the "link target type" concept, since it doesn't really belong here. + // Removed most of the construtor variants in LLEmbeddedBrowserWindowEvent and added setters in their place. + // Removed setCaretColor, since it's done nothing for some time now. + // Added LLEmbeddedBrowserWindowObserver::onWindowGeometryChangeRequested + // Added +// version 1: + // Added the LLQTWEBKIT_API_VERSION define. + // Added LLEmbeddedBrowserWindowObserver::onWindowCloseRequested + +//////////////////////////////////////////////////////////////////////////////// +// data class that is passed with an event +class LLEmbeddedBrowserWindowEvent +{ + public: + LLEmbeddedBrowserWindowEvent(int window_id) : + mEventWindowId(window_id) + { + }; + + virtual ~LLEmbeddedBrowserWindowEvent() {} + + void setEventUri(const std::string &uri) { mEventUri = uri; } + void setNavigationType(const std::string &type) { mNavigationType = type; } + void setTrustedHost(const bool trusted) { mTrustedHost = trusted; } + void setIntValue(int val) { mIntVal = val; } + void setStringValue(const std::string &val) { mStringVal = val; } + void setStringValue2(const std::string &val) { mStringVal2 = val; } + void setRectValue(int x, int y, int width, int height) + { + mXVal = x; + mYVal = y; + mWidthVal = width; + mHeightVal = height; + } + + int getEventWindowId() const { return mEventWindowId; } + std::string getEventUri() const { return mEventUri; } + std::string getNavigationType() const { return mNavigationType; } + bool getTrustedHost() const { return mTrustedHost; } + int getIntValue() const { return mIntVal; }; + std::string getStringValue() const { return mStringVal; } + std::string getStringValue2() const { return mStringVal2; } + void getRectValue(int& x, int& y, int& width, int& height) const + { + x = mXVal; + y = mYVal; + width = mWidthVal; + height = mHeightVal; + }; + + private: + int mEventWindowId; + std::string mEventUri; + std::string mNavigationType; + bool mTrustedHost; + int mIntVal; + std::string mStringVal; + std::string mStringVal2; + int mXVal; + int mYVal; + int mWidthVal; + int mHeightVal; +}; + +//////////////////////////////////////////////////////////////////////////////// +// derrive from this class and override these methods to observe these events +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +class LLEmbeddedBrowserWindowObserver +{ + public: + virtual ~LLEmbeddedBrowserWindowObserver(); + typedef LLEmbeddedBrowserWindowEvent EventType; + + virtual void onCursorChanged(const EventType& event); + virtual void onPageChanged(const EventType& event); + virtual void onNavigateBegin(const EventType& event); + virtual void onNavigateComplete(const EventType& event); + virtual void onNavigateErrorPage(const EventType& event); + virtual void onUpdateProgress(const EventType& event); + virtual void onStatusTextChange(const EventType& event); + virtual void onTitleChange(const EventType& event); + virtual void onLocationChange(const EventType& event); + virtual void onClickLinkHref(const EventType& event); + virtual void onClickLinkNoFollow(const EventType& event); + virtual void onCookieChanged(const EventType& event); + // mStringVal will be the cookie in RFC 2109 string format + // mEventUri will be the url that caused the cookie change + // mIntVal will be true if the cookie is dead (i.e. being deleted), false otherwise + virtual std::string onRequestFilePicker(const EventType& event); + virtual void onWindowCloseRequested(const EventType& event); + virtual void onWindowGeometryChangeRequested(const EventType& event); + + // This should return true to attempt auth, or false to cancel. + virtual bool onAuthRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password); + + // This should return true to continue after cert error, or false to cancel and abort. + virtual bool onCertError(const std::string &in_url, const std::string &in_msg); + + virtual void onLinkHovered(const EventType& event); + // mEventURI will be the link + // mStringVal will be the title + // mStringVal2 will be the text + + // catch qDebug() messages from Qt and pipe them back to host application + virtual void onQtDebugMessage( const std::string& msg, const std::string& msg_type); +}; +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +//////////////////////////////////////////////////////////////////////////////// +// main library class + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +class LLQtWebKit +{ + public: + typedef enum e_cursor + { + C_ARROW, + C_IBEAM, + C_SPLITV, + C_SPLITH, + C_POINTINGHAND + } ECursor; + + typedef enum e_user_action + { + UA_EDIT_CUT, + UA_EDIT_COPY, + UA_EDIT_PASTE, + UA_NAVIGATE_STOP, + UA_NAVIGATE_BACK, + UA_NAVIGATE_FORWARD, + UA_NAVIGATE_RELOAD + } EUserAction; + + typedef enum e_key_event + { + KE_KEY_DOWN, + KE_KEY_REPEAT, + KE_KEY_UP + }EKeyEvent; + + typedef enum e_mouse_event + { + ME_MOUSE_MOVE, + ME_MOUSE_DOWN, + ME_MOUSE_UP, + ME_MOUSE_DOUBLE_CLICK + }EMouseEvent; + + typedef enum e_mouse_button + { + MB_MOUSE_BUTTON_LEFT, + MB_MOUSE_BUTTON_RIGHT, + MB_MOUSE_BUTTON_MIDDLE, + MB_MOUSE_BUTTON_EXTRA_1, + MB_MOUSE_BUTTON_EXTRA_2, + }EMouseButton; + + typedef enum e_keyboard_modifier + { + KM_MODIFIER_NONE = 0x00, + KM_MODIFIER_SHIFT = 0x01, + KM_MODIFIER_CONTROL = 0x02, + KM_MODIFIER_ALT = 0x04, + KM_MODIFIER_META = 0x08 + }EKeyboardModifier; + + virtual ~LLQtWebKit(); + + // singleton access + static LLQtWebKit* getInstance(); + + // housekeeping + bool init(std::string application_directory, + std::string component_directory, + std::string profile_directory, + void* native_window_handle); + bool reset(); + bool clearCache(); + int getLastError(); + std::string getVersion(); + void setBrowserAgentId(std::string id); + bool enableProxy(bool enabled, std::string host_name, int port); + + void enableCookies(bool enabled); + bool clearAllCookies(); + + // The following two functions accept and return cookies in the same format that's used for the Set-Cookie: HTTP header + // as defined in RFC 2109 ( http://www.ietf.org/rfc/rfc2109.txt ). The string should not contain the literal "Set-Cookie:", + // just the cookie itself. + // Multiple cookies within the string are separated by a newline character ('\n') + void setCookies(const std::string &cookies); + std::string getAllCookies(); + + void enablePlugins(bool enabled); + void enableJavaScript(bool enabled); + + // Web inspector - Firebug-esque debugger + bool showWebInspector(bool show); + + // updates value of 'hostLanguage' in JavaScript 'Navigator' obect that + // embedded pages can query to see what language the host app is set to + void setHostLanguage(const std::string& host_language); + + // browser window - creation/deletion, mutation etc. + int createBrowserWindow(int width, int height, const std::string target = std::string("")); + void proxyWindowOpened(int browser_window_id, const std::string target, const std::string uuid); + void proxyWindowClosed(int browser_window_id, const std::string uuid); + bool destroyBrowserWindow(int browser_window_id); + bool setSize(int browser_window_id, int width, int height); + bool scrollByLines(int browser_window_id, int lines); + bool setBackgroundColor(int browser_window_id, const int red, const int green, const int blue); + bool setEnabled(int browser_window_id, bool enabled); + + // add/remove yourself as an observer on browser events - see LLEmbeddedBrowserWindowObserver declaration + bool addObserver(int browser_window_id, LLEmbeddedBrowserWindowObserver* subject); + bool remObserver(int browser_window_id, LLEmbeddedBrowserWindowObserver* subject); + + // navigation - self explanatory + bool navigateTo(int browser_window_id, const std::string uri); + bool userAction(int browser_window_id, EUserAction action); + bool userActionIsEnabled(int browser_window_id, EUserAction action); + + // javascript access/control + std::string evaluateJavaScript(int browser_window_id, const std::string script); + + // set/clear URL to redirect to when a 404 page is reached + bool set404RedirectUrl(int browser_window_in, std::string redirect_url); + bool clr404RedirectUrl(int browser_window_in); + + // access to rendered bitmap data + const unsigned char* grabBrowserWindow(int browser_window_id); // renders page to memory and returns pixels + const unsigned char* getBrowserWindowPixels(int browser_window_id); // just returns pixels - no render + bool flipWindow(int browser_window_id, bool flip); // optionally flip window (pixels) you get back + int getBrowserWidth(int browser_window_id); // current browser width (can vary slightly after page is rendered) + int getBrowserHeight(int browser_window_id); // current height + int getBrowserDepth(int browser_window_id); // depth in bytes + int getBrowserRowSpan(int browser_window_id); // width in pixels * depth in bytes + + // mouse/keyboard interaction + bool mouseEvent(int browser_window_id, EMouseEvent mouse_event, int button, int x, int y, EKeyboardModifier modifiers); // send a mouse event to a browser window at given XY in browser space + bool scrollWheelEvent(int browser_window_id, int x, int y, int scroll_x, int scroll_y, EKeyboardModifier modifiers); + bool keyboardEvent( + int browser_window_id, + EKeyEvent key_event, + uint32_t key_code, + const char *utf8_text, + EKeyboardModifier modifiers, + uint32_t native_scan_code = 0, + uint32_t native_virtual_key = 0, + uint32_t native_modifiers = 0); + + bool focusBrowser(int browser_window_id, bool focus_browser); // set/remove focus to given browser window + + // accessor/mutator for scheme that browser doesn't follow - e.g. secondlife.com:// + void setNoFollowScheme(int browser_window_id, std::string scheme); + std::string getNoFollowScheme(int browser_window_id); + + void pump(int max_milliseconds); + + void prependHistoryUrl(int browser_window_id, std::string url); + void clearHistory(int browser_window_id); + std::string dumpHistory(int browser_window_id); + + // Specify a path to a .pem file containing a list of CA certificates the browser should trust. + // NOTE that this will replace the default list of root certs (not add to it). + // If the file isn't found or doesn't contain any certs in the correct format, this call will have no effect and will return false. + // NOTE: Using this function causes strange cert verification issues on the Mac. + // Using addCAFile() instead seems to work better. + bool setCAFile(const std::string &ca_file); + + // This behaves similarly, but instead of replacing the entire list it appends additional trusted root certs to the current list. + bool addCAFile(const std::string &ca_file); + + // Set a flag causing all SSL cert errors to be ignored. + // NOTE: this should only be used for testing, as it negates the security model of https. + void setIgnoreSSLCertErrors(bool ignore); + bool getIgnoreSSLCertErrors(); + + const std::vector< std::string > getInstalledCertsList(); + + void enableQtMessageHandler( bool enable ); + + void enableLoadingOverlay( int browser_window_id, bool enable); + + // Copied from indra_constants.h. + // The key_code argument to keyboardEvent should either be one of these or a 7-bit ascii character. + enum keyCodes + { + // Leading zeroes ensure that these won't sign-extend when assigned to a larger type. + KEY_RETURN = 0x0081, + KEY_LEFT = 0x0082, + KEY_RIGHT = 0x0083, + KEY_UP = 0x0084, + KEY_DOWN = 0x0085, + KEY_ESCAPE = 0x0086, + KEY_BACKSPACE = 0x0087, + KEY_DELETE = 0x0088, + KEY_SHIFT = 0x0089, + KEY_CONTROL = 0x008A, + KEY_ALT = 0x008B, + KEY_HOME = 0x008C, + KEY_END = 0x008D, + KEY_PAGE_UP = 0x008E, + KEY_PAGE_DOWN = 0x008F, + KEY_HYPHEN = 0x0090, + KEY_EQUALS = 0x0091, + KEY_INSERT = 0x0092, + KEY_CAPSLOCK = 0x0093, + KEY_TAB = 0x0094, + KEY_ADD = 0x0095, + KEY_SUBTRACT = 0x0096, + KEY_MULTIPLY = 0x0097, + KEY_DIVIDE = 0x0098, + KEY_F1 = 0x00A1, + KEY_F2 = 0x00A2, + KEY_F3 = 0x00A3, + KEY_F4 = 0x00A4, + KEY_F5 = 0x00A5, + KEY_F6 = 0x00A6, + KEY_F7 = 0x00A7, + KEY_F8 = 0x00A8, + KEY_F9 = 0x00A9, + KEY_F10 = 0x00AA, + KEY_F11 = 0x00AB, + KEY_F12 = 0x00AC, + + KEY_PAD_UP = 0x00C0, + KEY_PAD_DOWN = 0x00C1, + KEY_PAD_LEFT = 0x00C2, + KEY_PAD_RIGHT = 0x00C3, + KEY_PAD_HOME = 0x00C4, + KEY_PAD_END = 0x00C5, + KEY_PAD_PGUP = 0x00C6, + KEY_PAD_PGDN = 0x00C7, + KEY_PAD_CENTER = 0x00C8, // the 5 in the middle + KEY_PAD_INS = 0x00C9, + KEY_PAD_DEL = 0x00CA, + KEY_PAD_RETURN = 0x00CB, + KEY_PAD_ADD = 0x00CC, + KEY_PAD_SUBTRACT = 0x00CD, + KEY_PAD_MULTIPLY = 0x00CE, + KEY_PAD_DIVIDE = 0x00CF, + + KEY_NONE = 0x00FF // not sent from keyboard. For internal use only. + }; + + // set the regex used to determine if a page is trusted or not + void setWhiteListRegex( int browser_window_id, const std::string& regex ); + + // Second Life specific functions + // (Note, this is a departure from the generic nature of this library) + void setSLObjectEnabled( bool enabled ); // enable or disaable feature + void setAgentLanguage( const std::string& agent_language ); // viewer language selected by agent + void setAgentRegion( const std::string& agent_region ); // name of region where agent is located + void setAgentLocation( double x, double y, double z ); // agent's x,y,z location within a region + void setAgentGlobalLocation( double x, double y, double z ); // agent's x,y,z location within the current grid + void setAgentOrientation( double angle ); // direction (0..359) agent is facing + void setAgentMaturity( const std::string& agent_maturity ); // selected maturity level of agent + void emitLocation(); + void emitMaturity(); + void emitLanguage(); + + // set the zoom factor for web pages ( can be less than 0.0) + void setPageZoomFactor( double factor ); + + private: + LLQtWebKit(); + LLEmbeddedBrowserWindow* getBrowserWindowFromWindowId(int browser_window_id); + static LLQtWebKit* sInstance; + const int mMaxBrowserWindows; + typedef std::map< int, LLEmbeddedBrowserWindow* > BrowserWindowMap; + typedef std::map< int, LLEmbeddedBrowserWindow* >::iterator BrowserWindowMapIter; + BrowserWindowMap mBrowserWindowMap; +}; + +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#endif // LLQTWEBKIT_H diff --git a/indra/llqtwebkit/llqtwebkit.pri b/indra/llqtwebkit/llqtwebkit.pri new file mode 100644 index 000000000..4f85aa423 --- /dev/null +++ b/indra/llqtwebkit/llqtwebkit.pri @@ -0,0 +1,47 @@ +DEPENDPATH += $$PWD +INCLUDEPATH += $$PWD + +!mac { +unix { + DEFINES += LL_LINUX +} +} + +mac { + DEFINES += LL_OSX +} + +win32{ + DEFINES += _WINDOWS +} + +# Input +HEADERS += llembeddedbrowser.h \ + llembeddedbrowser_p.h \ + llembeddedbrowserwindow.h \ + llembeddedbrowserwindow_p.h \ + llnetworkaccessmanager.h \ + llqtwebkit.h \ + llwebpage.h \ + llwebpageopenshim.h \ + llstyle.h \ + lljsobject.h + +SOURCES += llembeddedbrowser.cpp \ + llembeddedbrowserwindow.cpp \ + llnetworkaccessmanager.cpp \ + llqtwebkit.cpp \ + llwebpage.cpp \ + llwebpageopenshim.cpp \ + llstyle.cpp \ + lljsobject.cpp + +FORMS += passworddialog.ui + +RCC_DIR = .rcc +UI_DIR = .ui +MOC_DIR = .moc +OBJECTS_DIR = .obj + +include(static.pri) +include(qtwebkit_cookiejar/src/src.pri) diff --git a/indra/llqtwebkit/llqtwebkit.pro b/indra/llqtwebkit/llqtwebkit.pro new file mode 100644 index 000000000..b6ff077bd --- /dev/null +++ b/indra/llqtwebkit/llqtwebkit.pro @@ -0,0 +1,18 @@ +TEMPLATE = lib +CONFIG += static staticlib # we always build as static lib whether Qt is static or not +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +include(llqtwebkit.pri) + +QT += webkit opengl network gui + +win32:CONFIG(debug,debug|release) { + TARGET = llqtwebkitd +} + +RCC_DIR = $$PWD/.rcc +UI_DIR = $$PWD/.ui +MOC_DIR = $$PWD/.moc +OBJECTS_DIR = $$PWD/.obj diff --git a/indra/llqtwebkit/llstyle.cpp b/indra/llqtwebkit/llstyle.cpp new file mode 100644 index 000000000..ecd2b3eb8 --- /dev/null +++ b/indra/llqtwebkit/llstyle.cpp @@ -0,0 +1,79 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "llstyle.h" + +#include "llembeddedbrowserwindow_p.h" +#include +#include +#include + +LLStyle::LLStyle() + : QPlastiqueStyle() +{ +} + +void LLStyle::drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget) const +{ +#ifdef Q_WS_MAC + if (control == QStyle::CC_ScrollBar) { + QStyleOptionSlider* opt = (QStyleOptionSlider*)option; + const QPoint topLeft = opt->rect.topLeft(); + painter->translate(topLeft); + opt->rect.moveTo(QPoint(0, 0)); + painter->fillRect(opt->rect, opt->palette.background()); + } +#endif + QPlastiqueStyle::drawComplexControl(control, option, painter, widget); +} + +void LLStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const +{ + switch(element) + { + case CE_ScrollBarAddLine: + case CE_ScrollBarSubLine: + // This fixes the "scrollbar arrows pointing the wrong way" bug. + if (const QStyleOptionSlider *scrollBar = qstyleoption_cast(option)) + { + // Make the State_Horizontal bit in the option's state field match its orientation field. + QStyleOptionSlider localOption(*scrollBar); + if(localOption.orientation == Qt::Horizontal) + { + localOption.state |= State_Horizontal; + } + else + { + localOption.state &= ~State_Horizontal; + } + QPlastiqueStyle::drawControl(element, &localOption, painter, widget); + return; + } + + break; + } + + QPlastiqueStyle::drawControl(element, option, painter, widget); +} diff --git a/indra/llqtwebkit/llstyle.h b/indra/llqtwebkit/llstyle.h new file mode 100644 index 000000000..77c09b3bb --- /dev/null +++ b/indra/llqtwebkit/llstyle.h @@ -0,0 +1,42 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLSTYLE_H +#define LLSTYLE_H + +#include + +class LLStyle : public QPlastiqueStyle +{ + +public: + explicit LLStyle(); + void drawComplexControl(ComplexControl control, const QStyleOptionComplex *option, QPainter *painter, const QWidget *widget = 0) const; + void drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const; + +}; + +#endif + diff --git a/indra/llqtwebkit/llwebpage.cpp b/indra/llqtwebkit/llwebpage.cpp new file mode 100644 index 000000000..113c0c186 --- /dev/null +++ b/indra/llqtwebkit/llwebpage.cpp @@ -0,0 +1,536 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "llwebpage.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "llqtwebkit.h" +#include "llembeddedbrowser.h" +#include "llembeddedbrowserwindow.h" +#include "llembeddedbrowserwindow_p.h" +#include "lljsobject.h" + +LLWebPage::LLWebPage(QObject *parent) + : QWebPage(parent) + , window(0) + , mHostLanguage( "en" ) + , mWhiteListRegex( "" ) +{ + mJsObject = new LLJsObject( parent ); + + connect(this, SIGNAL(loadProgress(int)), + this, SLOT(loadProgressSlot(int))); + connect(this, SIGNAL(linkHovered(const QString &, const QString &, const QString &)), + this, SLOT(linkHoveredSlot(const QString &, const QString &, const QString &))); + connect(this, SIGNAL(statusBarMessage(const QString &)), + this, SLOT(statusBarMessageSlot(const QString &))); + connect(mainFrame(), SIGNAL(urlChanged(const QUrl&)), + this, SLOT(urlChangedSlot(const QUrl&))); + connect(this, SIGNAL(loadStarted()), + this, SLOT(loadStarted())); + connect(this, SIGNAL(loadFinished(bool)), + this, SLOT(loadFinished(bool))); + connect(this, SIGNAL(windowCloseRequested()), + this, SLOT(windowCloseRequested())); + connect(this, SIGNAL(geometryChangeRequested(const QRect&)), + this, SLOT(geometryChangeRequested(const QRect&))); + connect(mainFrame(), SIGNAL(titleChanged(const QString&)), + this, SLOT(titleChangedSlot(const QString&))); + connect(mainFrame(), SIGNAL(javaScriptWindowObjectCleared()), + this, SLOT(extendNavigatorObject())); +} + +LLWebPage::~LLWebPage() +{ + delete mJsObject; +} + +void LLWebPage::loadProgressSlot(int progress) +{ + if (!window) + return; + window->d->mPercentComplete = progress; + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(window->getCurrentUri()); + event.setIntValue(progress); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onUpdateProgress, event); + + if ( progress >= 100 ) + window->d->mShowLoadingOverlay = false; + + window->d->mDirty = true; + window->grabWindow(0,0,webView->boundingRect().width(),webView->boundingRect().height()); + + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onPageChanged, event); +} + +void LLWebPage::linkHoveredSlot(const QString &link, const QString &title, const QString &textContent) +{ + if (!window) + return; + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(llToStdString(link)); + event.setStringValue(llToStdString(title)); + event.setStringValue2(llToStdString(textContent)); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onLinkHovered, event); +} + +void LLWebPage::statusBarMessageSlot(const QString& text) +{ + if (!window) + return; + window->d->mStatusText = llToStdString(text); + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(window->getCurrentUri()); + event.setStringValue(window->d->mStatusText); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onStatusTextChange, event); +} + +void LLWebPage::titleChangedSlot(const QString& text) +{ + if (!window) + return; + window->d->mTitle = llToStdString(text); + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(window->getCurrentUri()); + event.setStringValue(window->d->mTitle); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onTitleChange, event); +} + +// set the regex used to determine if a page is trusted or not +void LLWebPage::setWhiteListRegex( const std::string& regex ) +{ + mWhiteListRegex = regex; +} + +void LLWebPage::configureTrustedPage( bool is_trusted ) +{ + // action happens in browser window parent + LLEmbeddedBrowser* parent_browser = 0; + if ( window && window->d && window->d->mParent ) + { + parent_browser = window->d->mParent; + if ( parent_browser ) + { + if ( is_trusted ) + { + //qDebug() << "Whitelist passed - turning on"; + + // trusted so turn everything on + parent_browser->enableJavaScriptTransient( true ); + parent_browser->enableCookiesTransient( true ); + parent_browser->enablePluginsTransient( true ); + } + else + { + //qDebug() << "Whitelist failed - reverting to default state"; + + // restore default state set by client + parent_browser->enableJavaScript( parent_browser->isJavaScriptEnabled() ); + parent_browser->enableCookies( parent_browser->areCookiesEnabled() ); + parent_browser->enablePlugins( parent_browser->arePluginsEnabled() ); + } + } + } +} + +bool LLWebPage::checkRegex( const QUrl& url ) +{ + QRegExp reg_exp( QString::fromStdString( mWhiteListRegex ) ); + reg_exp.setCaseSensitivity( Qt::CaseInsensitive ); + reg_exp.setMinimal( true ); + + if ( reg_exp.exactMatch( url.host() ) ) + { + return true; + } + else + { + return false; + } +} + +void LLWebPage::checkWhiteList( const QUrl& url ) +{ + if ( mWhiteListRegex.length() ) + { + if ( checkRegex( url ) ) + { + configureTrustedPage( true ); // page is "trusted" - go ahead and configure it as such + } + else + { + configureTrustedPage( false ); // page is "NOT trusted" - go ahead and configure it as such + } + } + else + // no regex specified, don't do anything (i.e. don't change trust state) + { + } +} + +void LLWebPage::urlChangedSlot(const QUrl& url) +{ + if (!window) + return; + + checkWhiteList( url ); + + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(window->getCurrentUri()); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onLocationChange, event); +} + +bool LLWebPage::event(QEvent *event) +{ + bool result = QWebPage::event(event); + + if (event->type() == QEvent::GraphicsSceneMousePress) + currentPoint = ((QGraphicsSceneMouseEvent*)event)->pos().toPoint(); + else if(event->type() == QEvent::GraphicsSceneMouseRelease) + currentPoint = QPoint(); + + return result; +} + +bool LLWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type) +{ + Q_UNUSED( frame ); + + if (!window) + return false; + + if (request.url().scheme() == window->d->mNoFollowScheme) + { + QString encodedUrl = request.url().toEncoded(); + // QUrl is turning foo:///home/bar into foo:/home/bar for some reason while Firefox does not + // http://bugs.webkit.org/show_bug.cgi?id=24695 + if (!encodedUrl.startsWith(window->d->mNoFollowScheme + "://")) { + encodedUrl = encodedUrl.mid(window->d->mNoFollowScheme.length() + 1); + encodedUrl = window->d->mNoFollowScheme + "://" + encodedUrl; + } + std::string rawUri = llToStdString(encodedUrl); + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(rawUri); + + // pass over the navigation type as per this page: http://apidocs.meego.com/1.1/core/html/qt4/qwebpage.html#NavigationType-enum + // pass as strings because telling everyone who needs to know about enums is too invasive. + std::string nav_type("unknown"); + if (type == QWebPage::NavigationTypeLinkClicked) nav_type="clicked"; + else + if (type == QWebPage::NavigationTypeFormSubmitted) nav_type="form_submited"; + else + if (type == QWebPage::NavigationTypeBackOrForward) nav_type="back_forward"; + else + if (type == QWebPage::NavigationTypeReload) nav_type="reloaded"; + else + if (type == QWebPage::NavigationTypeFormResubmitted) nav_type="form_resubmited"; + event.setNavigationType(nav_type); + + if ( mWhiteListRegex.length() ) + { + if ( frame ) + { + if ( checkRegex( frame->url() ) ) + { + event.setTrustedHost( true ); + } + else + { + event.setTrustedHost( false ); + } + } + else + // no frame - no trust (TODO: when can this happen?) + { + event.setTrustedHost( false ); + } + } + else + // no regex is like switching it off and indicating everything is trusted + { + event.setTrustedHost( true ); + } + + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onClickLinkNoFollow, event); + + //qDebug() << "LLWebPage::acceptNavigationRequest: sending onClickLinkNoFollow, NavigationType is " << type << ", url is " << QString::fromStdString(rawUri) ; + return false; + } + + + return true; +} + + +void LLWebPage::loadStarted() +{ + if (!window) + return; + + QUrl url( QString::fromStdString( window->getCurrentUri() ) ); + checkWhiteList( url ); + + window->d->mShowLoadingOverlay = true; + + window->d->mTimeLoadStarted=time(NULL); + + window->d->mDirty = true; + window->grabWindow(0,0,webView->boundingRect().width(),webView->boundingRect().height()); + + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(window->getCurrentUri()); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onNavigateBegin, event); +} + +void LLWebPage::loadFinished(bool) +{ + if (!window) + return; + + window->d->mShowLoadingOverlay = false; + + window->d->mDirty = true; + window->grabWindow(0,0,webView->boundingRect().width(),webView->boundingRect().height()); + + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setEventUri(window->getCurrentUri()); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onPageChanged, event); + + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onNavigateComplete, event); +} + +void LLWebPage::windowCloseRequested() +{ + if (!window) + return; + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onWindowCloseRequested, event); +} + +void LLWebPage::geometryChangeRequested(const QRect& geom) +{ + if (!window) + return; + + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + // empty UUID indicates this is targeting the main window +// event.setStringValue(window->getUUID()); + event.setRectValue(geom.x(), geom.y(), geom.width(), geom.height()); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onWindowGeometryChangeRequested, event); +} + +QString LLWebPage::chooseFile(QWebFrame* parentFrame, const QString& suggestedFile) +{ + Q_UNUSED(parentFrame); + Q_UNUSED(suggestedFile); + + return QString::fromStdString( window->requestFilePicker() ); +} + +void LLWebPage::javaScriptAlert(QWebFrame* frame, const QString& msg) +{ + Q_UNUSED(frame); + QMessageBox *msgBox = new QMessageBox; + msgBox->setWindowTitle(tr("JavaScript Alert - %1").arg(mainFrame()->url().host())); + msgBox->setText(msg); + msgBox->addButton(QMessageBox::Ok); + + QGraphicsProxyWidget *proxy = webView->scene()->addWidget(msgBox); + proxy->setWindowFlags(Qt::Window); // this makes the item a panel (and will make it get a window 'frame') + proxy->setPanelModality(QGraphicsItem::SceneModal); + proxy->setPos((webView->boundingRect().width() - msgBox->sizeHint().width())/2, + (webView->boundingRect().height() - msgBox->sizeHint().height())/2); + proxy->setActive(true); // make it the active item + + connect(msgBox, SIGNAL(finished(int)), proxy, SLOT(deleteLater())); + msgBox->show(); + + webView->scene()->setFocusItem(proxy); +} + +bool LLWebPage::javaScriptConfirm(QWebFrame* frame, const QString& msg) +{ + Q_UNUSED(frame); + Q_UNUSED(msg); + qWarning() << "LLWebPage::" << __FUNCTION__ << "not implemented" << msg << "returning true"; + return true; +} + +bool LLWebPage::javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result) +{ + Q_UNUSED(frame); + Q_UNUSED(msg); + Q_UNUSED(defaultValue); + Q_UNUSED(result); + qWarning() << "LLWebPage::" << __FUNCTION__ << "not implemented" << msg << defaultValue << "returning false"; + return false; +} + +void LLWebPage::extendNavigatorObject() +{ + // legacy - will go away in the future + QString q_host_language = QString::fromStdString( mHostLanguage ); + mainFrame()->evaluateJavaScript(QString("navigator.hostLanguage=\"%1\"").arg( q_host_language )); + + // the new way + if ( mJsObject ) + { + bool enabled = mJsObject->getSLObjectEnabled(); + if ( enabled ) + { + mainFrame()->addToJavaScriptWindowObject("slviewer", mJsObject ); + }; + }; +} + +QWebPage *LLWebPage::createWindow(WebWindowType type) +{ + Q_UNUSED(type); + QWebPage *result = NULL; + + if(window) + { + result = window->createWindow(); + } + + return result; +} + +void LLWebPage::setHostLanguage(const std::string& host_language) +{ + mHostLanguage = host_language; +} + +bool LLWebPage::supportsExtension(QWebPage::Extension extension) const +{ + if (extension == QWebPage::ErrorPageExtension) + return true; + return false; +} + +bool LLWebPage::extension(Extension, const ExtensionOption* option, ExtensionReturn* output) +{ + const QWebPage::ErrorPageExtensionOption* info = static_cast(option); + QWebPage::ErrorPageExtensionReturn* errorPage = static_cast(output); + + errorPage->content = QString("Failed loading page

%1

") + .arg(info->errorString).toUtf8(); + + return true; +} + +// Second Life viewer specific functions +void LLWebPage::setSLObjectEnabled( bool enabled ) +{ + if ( mJsObject ) + mJsObject->setSLObjectEnabled( enabled ); +} + +void LLWebPage::setAgentLanguage( const std::string& agent_language ) +{ + if ( mJsObject ) + mJsObject->setAgentLanguage( QString::fromStdString( agent_language ) ); +} + +void LLWebPage::setAgentRegion( const std::string& agent_region ) +{ + if ( mJsObject ) + mJsObject->setAgentRegion( QString::fromStdString( agent_region ) ); +} + +void LLWebPage::setAgentLocation( double x, double y, double z ) +{ + if ( mJsObject ) + { + QVariantMap location; + location["x"] = x; + location["y"] = y; + location["z"] = z; + mJsObject->setAgentLocation( location ); + } +} + +void LLWebPage::setAgentGlobalLocation( double x, double y, double z ) +{ + if ( mJsObject ) + { + QVariantMap global_location; + global_location["x"] = x; + global_location["y"] = y; + global_location["z"] = z; + mJsObject->setAgentGlobalLocation( global_location ); + } +} + +void LLWebPage::setAgentOrientation( double angle ) +{ + if ( mJsObject ) + { + mJsObject->setAgentOrientation( angle ); + } +} + +void LLWebPage::setAgentMaturity( const std::string& agent_maturity ) +{ + if ( mJsObject ) + mJsObject->setAgentMaturity( QString::fromStdString( agent_maturity ) ); +} + +void LLWebPage::emitLocation() +{ + if ( mJsObject ) + mJsObject->emitLocation(); +} + +void LLWebPage::emitMaturity() +{ + if ( mJsObject ) + mJsObject->emitMaturity(); +} + +void LLWebPage::emitLanguage() +{ + if ( mJsObject ) + mJsObject->emitLanguage(); +} + +void LLWebPage::setPageZoomFactor( double factor ) +{ + if ( webView ) + { + webView->setZoomFactor( factor ); + } +} \ No newline at end of file diff --git a/indra/llqtwebkit/llwebpage.h b/indra/llqtwebkit/llwebpage.h new file mode 100644 index 000000000..1a882254f --- /dev/null +++ b/indra/llqtwebkit/llwebpage.h @@ -0,0 +1,108 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLWEBPAGE_H +#define LLWEBPAGE_H + +class QGraphicsWebView; +#include +#include "llqtwebkit.h" + +class LLEmbeddedBrowserWindow; +class LLJsObject; + +class LLWebPage : public QWebPage +{ + Q_OBJECT + + public: + LLWebPage(QObject *parent = 0); + ~LLWebPage(); + + LLEmbeddedBrowserWindow *window; + bool event(QEvent *event); + + QGraphicsWebView *webView; + + void setHostLanguage(const std::string& host_language); + virtual bool supportsExtension(QWebPage::Extension extension) const; + virtual bool extension(Extension extension, const ExtensionOption* option, ExtensionReturn* output); + + // set the regex used to determine if a page is trusted or not + void setWhiteListRegex( const std::string& regex ); + + // check the whitelist and update browser config as appropriate + void checkWhiteList( const QUrl& url ); + + // code to change settings if page is known to be trusted goes here + void configureTrustedPage( bool is_trusted ); + + // Second Life specific functions + void setAgentRegion( const std::string& agent_region ); + void setAgentLocation( double x, double y, double z ); + void setAgentGlobalLocation( double x, double y, double z ); + void setAgentOrientation( double angle ); + void setSLObjectEnabled( bool enabled ); + void setAgentLanguage( const std::string& agent_language ); + void setAgentMaturity( const std::string& agent_maturity ); + void emitLocation(); + void emitMaturity(); + void emitLanguage(); + + void setPageZoomFactor( double factor ); + + protected: + bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type); + + public slots: + void loadProgressSlot(int progress); + void linkHoveredSlot(const QString &link, const QString &title, const QString &textContent); + void statusBarMessageSlot(const QString &); + void titleChangedSlot(const QString &); + void urlChangedSlot(const QUrl& url); + void loadStarted(); + void loadFinished(bool ok); + void windowCloseRequested(); + void geometryChangeRequested(const QRect& geom); + + private slots: + void extendNavigatorObject(); + + protected: + QString chooseFile(QWebFrame* parentFrame, const QString& suggestedFile); + void javaScriptAlert(QWebFrame* frame, const QString& msg); + bool javaScriptConfirm(QWebFrame* frame, const QString& msg); + bool javaScriptPrompt(QWebFrame* frame, const QString& msg, const QString& defaultValue, QString* result); + QWebPage *createWindow(WebWindowType type); + + private: + bool checkRegex( const QUrl& url ); + QPoint currentPoint; + std::string mHostLanguage; + std::string mWhiteListRegex; + LLJsObject* mJsObject; +}; + +#endif diff --git a/indra/llqtwebkit/llwebpageopenshim.cpp b/indra/llqtwebkit/llwebpageopenshim.cpp new file mode 100644 index 000000000..af9627907 --- /dev/null +++ b/indra/llqtwebkit/llwebpageopenshim.cpp @@ -0,0 +1,176 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "llwebpageopenshim.h" + +#include +#include +#include +#include + +#include "llqtwebkit.h" +#include "llembeddedbrowserwindow.h" +#include "llembeddedbrowserwindow_p.h" + +LLWebPageOpenShim::LLWebPageOpenShim(LLEmbeddedBrowserWindow *in_window, QObject *parent) + : QWebPage(parent) + , window(in_window) + , mOpeningSelf(false) + , mGeometryChangeRequested(false) + , mHasSentUUID(false) +{ +// qDebug() << "LLWebPageOpenShim created"; + + connect(this, SIGNAL(windowCloseRequested()), + this, SLOT(windowCloseRequested())); + connect(this, SIGNAL(geometryChangeRequested(const QRect&)), + this, SLOT(geometryChangeRequested(const QRect&))); + + // Create a unique UUID for this proxy + mUUID = llToStdString(QUuid::createUuid().toString()); + + // mTarget starts out as the empty string, which is what we want. +} + +LLWebPageOpenShim::~LLWebPageOpenShim() +{ +// qDebug() << "LLWebPageOpenShim destroyed"; +} + +void LLWebPageOpenShim::windowCloseRequested() +{ +// qDebug() << "LLWebPageOpenShim::windowCloseRequested"; + if(window) + { + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setStringValue(mUUID); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onWindowCloseRequested, event); + } +} + +void LLWebPageOpenShim::geometryChangeRequested(const QRect& geom) +{ +// qDebug() << "LLWebPageOpenShim::geometryChangeRequested: " << geom ; + + // This seems to happen before acceptNavigationRequest is called. If this is the case, delay sending the message until afterwards. + + if(window && mHasSentUUID) + { + LLEmbeddedBrowserWindowEvent event(window->getWindowId()); + event.setStringValue(mUUID); + event.setRectValue(geom.x(), geom.y(), geom.width(), geom.height()); + window->d->mEventEmitter.update(&LLEmbeddedBrowserWindowObserver::onWindowGeometryChangeRequested, event); + } + else + { + mGeometry = geom; + mGeometryChangeRequested = true; + } + +} + +bool LLWebPageOpenShim::matchesTarget(const std::string target) +{ + return (target == mTarget); +} + +bool LLWebPageOpenShim::matchesUUID(const std::string uuid) +{ + return (uuid == mUUID); +} + +void LLWebPageOpenShim::setProxy(const std::string &target, const std::string &uuid) +{ + mTarget = target; + mUUID = uuid; + + mHasSentUUID = false; + + mOpeningSelf = true; + + mainFrame()->evaluateJavaScript(QString("window.open("", \"%1\");").arg( QString::fromStdString(target) )); +} + +bool LLWebPageOpenShim::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type) +{ + Q_UNUSED(type); + if (!window) + { + return false; + } + + if(mOpeningSelf) + { + qDebug() << "LLWebPageOpenShim::acceptNavigationRequest: reopening self to set target name."; + return true; + } + +#if 0 + qDebug() << "LLWebPageOpenShim::acceptNavigationRequest called, NavigationType is " << type + << ", web frame is " << frame + << ", frame->page is " << frame->page() + << ", url is " << request.url() + << ", frame name is " << frame->frameName() + ; +#endif + + if (request.url().scheme() == QString("file")) + { + // For some reason, I'm seeing a spurious call to this function with a file:/// URL that points to the current working directory. + // Ignoring file:/// URLs here isn't a perfect solution (since it could potentially break content in local HTML files), + // but it's the best I could come up with for now. + + return false; + } + + // The name of the incoming frame has been set to the link target that was used when opening this window. + std::string click_href = llToStdString(request.url()); + mTarget = llToStdString(frame->frameName()); + + // build event based on the data we have and emit it + LLEmbeddedBrowserWindowEvent event( window->getWindowId()); + event.setEventUri(click_href); + event.setStringValue(mTarget); + event.setStringValue2(mUUID); + + window->d->mEventEmitter.update( &LLEmbeddedBrowserWindowObserver::onClickLinkHref, event ); + + mHasSentUUID = true; + + if(mGeometryChangeRequested) + { + geometryChangeRequested(mGeometry); + mGeometryChangeRequested = false; + } + + return false; +} + +QWebPage *LLWebPageOpenShim::createWindow(WebWindowType type) +{ + Q_UNUSED(type); + + return this; +} diff --git a/indra/llqtwebkit/llwebpageopenshim.h b/indra/llqtwebkit/llwebpageopenshim.h new file mode 100644 index 000000000..322f832ce --- /dev/null +++ b/indra/llqtwebkit/llwebpageopenshim.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef LLWEBPAGEOPENSHIM_H +#define LLWEBPAGEOPENSHIM_H + +#include + +class LLEmbeddedBrowserWindow; +class LLWebPageOpenShim : public QWebPage +{ + Q_OBJECT + + public: + LLWebPageOpenShim(LLEmbeddedBrowserWindow *in_window, QObject *parent = 0); + ~LLWebPageOpenShim(); + LLEmbeddedBrowserWindow *window; + bool matchesTarget(const std::string target); + bool matchesUUID(const std::string uuid); + void setProxy(const std::string &target, const std::string &uuid); + + public slots: + void windowCloseRequested(); + void geometryChangeRequested(const QRect& geom); + + protected: + bool acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, NavigationType type); + QWebPage *createWindow(WebWindowType type); + + private: + std::string mUUID; + std::string mTarget; + bool mOpeningSelf; + bool mGeometryChangeRequested; + bool mHasSentUUID; + QRect mGeometry; + +}; + +#endif + diff --git a/indra/llqtwebkit/passworddialog.ui b/indra/llqtwebkit/passworddialog.ui new file mode 100644 index 000000000..033514eff --- /dev/null +++ b/indra/llqtwebkit/passworddialog.ui @@ -0,0 +1,137 @@ + + + PasswordDialog + + + + 0 + 0 + 394 + 183 + + + + Dialog + + + + + + Qt::Vertical + + + + 20 + 12 + + + + + + + + + + + true + + + + + + + + 32 + 32 + + + + icon + + + + + + + Qt::Vertical + + + + 20 + 13 + + + + + + + + User name: + + + + + + + + + + Password: + + + + + + + QLineEdit::Password + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + PasswordDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + PasswordDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/indra/llqtwebkit/pstdint.h b/indra/llqtwebkit/pstdint.h new file mode 100644 index 000000000..b36f63db3 --- /dev/null +++ b/indra/llqtwebkit/pstdint.h @@ -0,0 +1,728 @@ +/* A portable stdint.h + **************************************************************************** + * BSD License: + **************************************************************************** + * + * Copyright (c) 2005-2007 Paul Hsieh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR 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. + * + **************************************************************************** + * + * Version 0.1.10 + * + * The ANSI C standard committee, for the C99 standard, specified the + * inclusion of a new standard include file called stdint.h. This is + * a very useful and long desired include file which contains several + * very precise definitions for integer scalar types that is + * critically important for making portable several classes of + * applications including cryptography, hashing, variable length + * integer libraries and so on. But for most developers its likely + * useful just for programming sanity. + * + * The problem is that most compiler vendors have decided not to + * implement the C99 standard, and the next C++ language standard + * (which has a lot more mindshare these days) will be a long time in + * coming and its unknown whether or not it will include stdint.h or + * how much adoption it will have. Either way, it will be a long time + * before all compilers come with a stdint.h and it also does nothing + * for the extremely large number of compilers available today which + * do not include this file, or anything comparable to it. + * + * So that's what this file is all about. Its an attempt to build a + * single universal include file that works on as many platforms as + * possible to deliver what stdint.h is supposed to. A few things + * that should be noted about this file: + * + * 1) It is not guaranteed to be portable and/or present an identical + * interface on all platforms. The extreme variability of the + * ANSI C standard makes this an impossibility right from the + * very get go. Its really only meant to be useful for the vast + * majority of platforms that possess the capability of + * implementing usefully and precisely defined, standard sized + * integer scalars. Systems which are not intrinsically 2s + * complement may produce invalid constants. + * + * 2) There is an unavoidable use of non-reserved symbols. + * + * 3) Other standard include files are invoked. + * + * 4) This file may come in conflict with future platforms that do + * include stdint.h. The hope is that one or the other can be + * used with no real difference. + * + * 5) In the current verison, if your platform can't represent + * int32_t, int16_t and int8_t, it just dumps out with a compiler + * error. + * + * 6) 64 bit integers may or may not be defined. Test for their + * presence with the test: #ifdef INT64_MAX or #ifdef UINT64_MAX. + * Note that this is different from the C99 specification which + * requires the existence of 64 bit support in the compiler. If + * this is not defined for your platform, yet it is capable of + * dealing with 64 bits then it is because this file has not yet + * been extended to cover all of your system's capabilities. + * + * 7) (u)intptr_t may or may not be defined. Test for its presence + * with the test: #ifdef PTRDIFF_MAX. If this is not defined + * for your platform, then it is because this file has not yet + * been extended to cover all of your system's capabilities, not + * because its optional. + * + * 8) The following might not been defined even if your platform is + * capable of defining it: + * + * WCHAR_MIN + * WCHAR_MAX + * (u)int64_t + * PTRDIFF_MIN + * PTRDIFF_MAX + * (u)intptr_t + * + * 9) The following have not been defined: + * + * WINT_MIN + * WINT_MAX + * + * 10) The criteria for defining (u)int_least(*)_t isn't clear, + * except for systems which don't have a type that precisely + * defined 8, 16, or 32 bit types (which this include file does + * not support anyways). Default definitions have been given. + * + * 11) The criteria for defining (u)int_fast(*)_t isn't something I + * would trust to any particular compiler vendor or the ANSI C + * committee. It is well known that "compatible systems" are + * commonly created that have very different performance + * characteristics from the systems they are compatible with, + * especially those whose vendors make both the compiler and the + * system. Default definitions have been given, but its strongly + * recommended that users never use these definitions for any + * reason (they do *NOT* deliver any serious guarantee of + * improved performance -- not in this file, nor any vendor's + * stdint.h). + * + * 12) The following macros: + * + * PRINTF_INTMAX_MODIFIER + * PRINTF_INT64_MODIFIER + * PRINTF_INT32_MODIFIER + * PRINTF_INT16_MODIFIER + * PRINTF_LEAST64_MODIFIER + * PRINTF_LEAST32_MODIFIER + * PRINTF_LEAST16_MODIFIER + * PRINTF_INTPTR_MODIFIER + * + * are strings which have been defined as the modifiers required + * for the "d", "u" and "x" printf formats to correctly output + * (u)intmax_t, (u)int64_t, (u)int32_t, (u)int16_t, (u)least64_t, + * (u)least32_t, (u)least16_t and (u)intptr_t types respectively. + * PRINTF_INTPTR_MODIFIER is not defined for some systems which + * provide their own stdint.h. PRINTF_INT64_MODIFIER is not + * defined if INT64_MAX is not defined. These are an extension + * beyond what C99 specifies must be in stdint.h. + * + * In addition, the following macros are defined: + * + * PRINTF_INTMAX_HEX_WIDTH + * PRINTF_INT64_HEX_WIDTH + * PRINTF_INT32_HEX_WIDTH + * PRINTF_INT16_HEX_WIDTH + * PRINTF_INT8_HEX_WIDTH + * PRINTF_INTMAX_DEC_WIDTH + * PRINTF_INT64_DEC_WIDTH + * PRINTF_INT32_DEC_WIDTH + * PRINTF_INT16_DEC_WIDTH + * PRINTF_INT8_DEC_WIDTH + * + * Which specifies the maximum number of characters required to + * print the number of that type in either hexadecimal or decimal. + * These are an extension beyond what C99 specifies must be in + * stdint.h. + * + * Compilers tested (all with 0 warnings at their highest respective + * settings): Borland Turbo C 2.0, WATCOM C/C++ 11.0 (16 bits and 32 + * bits), Microsoft Visual C++ 6.0 (32 bit), Microsoft Visual Studio + * .net (VC7), Intel C++ 4.0, GNU gcc v3.3.3 + * + * This file should be considered a work in progress. Suggestions for + * improvements, especially those which increase coverage are strongly + * encouraged. + * + * Acknowledgements + * + * The following people have made significant contributions to the + * development and testing of this file: + * + * Chris Howie + * John Steele Scott + * Dave Thorup + * + */ + +#include +#include +#include + +/* + * For gcc with _STDINT_H, fill in the PRINTF_INT*_MODIFIER macros, and + * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. + */ + +#if ((defined(__STDC__) && __STDC__ && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (defined(_STDINT_H) || defined(_STDINT_H_)) )) && !defined (_PSTDINT_H_INCLUDED) +#include +#define _PSTDINT_H_INCLUDED +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +# endif +# ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +# endif +# ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +# endif +# ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +# endif +# ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +# endif +# ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +# endif +# ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +# endif +# ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif + +/* + * Something really weird is going on with Open Watcom. Just pull some of + * these duplicated definitions from Open Watcom's stdint.h file for now. + */ + +# if defined (__WATCOMC__) && __WATCOMC__ >= 1250 +# if !defined (INT64_C) +# define INT64_C(x) (x + (INT64_MAX - INT64_MAX)) +# endif +# if !defined (UINT64_C) +# define UINT64_C(x) (x + (UINT64_MAX - UINT64_MAX)) +# endif +# if !defined (INT32_C) +# define INT32_C(x) (x + (INT32_MAX - INT32_MAX)) +# endif +# if !defined (UINT32_C) +# define UINT32_C(x) (x + (UINT32_MAX - UINT32_MAX)) +# endif +# if !defined (INT16_C) +# define INT16_C(x) (x) +# endif +# if !defined (UINT16_C) +# define UINT16_C(x) (x) +# endif +# if !defined (INT8_C) +# define INT8_C(x) (x) +# endif +# if !defined (UINT8_C) +# define UINT8_C(x) (x) +# endif +# if !defined (UINT64_MAX) +# define UINT64_MAX 18446744073709551615ULL +# endif +# if !defined (INT64_MAX) +# define INT64_MAX 9223372036854775807LL +# endif +# if !defined (UINT32_MAX) +# define UINT32_MAX 4294967295UL +# endif +# if !defined (INT32_MAX) +# define INT32_MAX 2147483647L +# endif +# if !defined (INTMAX_MAX) +# define INTMAX_MAX INT64_MAX +# endif +# if !defined (INTMAX_MIN) +# define INTMAX_MIN INT64_MIN +# endif +# endif +#endif + +#ifndef _PSTDINT_H_INCLUDED +#define _PSTDINT_H_INCLUDED + +#ifndef SIZE_MAX +# define SIZE_MAX (~(size_t)0) +#endif + +/* + * Deduce the type assignments from limits.h under the assumption that + * integer sizes in bits are powers of 2, and follow the ANSI + * definitions. + */ + +#ifndef UINT8_MAX +# define UINT8_MAX 0xff +#endif +#ifndef uint8_t +# if (UCHAR_MAX == UINT8_MAX) || defined (S_SPLINT_S) + typedef unsigned char uint8_t; +# define UINT8_C(v) ((uint8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef INT8_MAX +# define INT8_MAX 0x7f +#endif +#ifndef INT8_MIN +# define INT8_MIN INT8_C(0x80) +#endif +#ifndef int8_t +# if (SCHAR_MAX == INT8_MAX) || defined (S_SPLINT_S) + typedef signed char int8_t; +# define INT8_C(v) ((int8_t) v) +# else +# error "Platform not supported" +# endif +#endif + +#ifndef UINT16_MAX +# define UINT16_MAX 0xffff +#endif +#ifndef uint16_t +#if (UINT_MAX == UINT16_MAX) || defined (S_SPLINT_S) + typedef unsigned int uint16_t; +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +# define UINT16_C(v) ((uint16_t) (v)) +#elif (USHRT_MAX == UINT16_MAX) + typedef unsigned short uint16_t; +# define UINT16_C(v) ((uint16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT16_MAX +# define INT16_MAX 0x7fff +#endif +#ifndef INT16_MIN +# define INT16_MIN INT16_C(0x8000) +#endif +#ifndef int16_t +#if (INT_MAX == INT16_MAX) || defined (S_SPLINT_S) + typedef signed int int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "" +# endif +#elif (SHRT_MAX == INT16_MAX) + typedef signed short int16_t; +# define INT16_C(v) ((int16_t) (v)) +# ifndef PRINTF_INT16_MODIFIER +# define PRINTF_INT16_MODIFIER "h" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef UINT32_MAX +# define UINT32_MAX (0xffffffffUL) +#endif +#ifndef uint32_t +#if (ULONG_MAX == UINT32_MAX) || defined (S_SPLINT_S) + typedef unsigned long uint32_t; +# define UINT32_C(v) v ## UL +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (UINT_MAX == UINT32_MAX) + typedef unsigned int uint32_t; +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +# define UINT32_C(v) v ## U +#elif (USHRT_MAX == UINT32_MAX) + typedef unsigned short uint32_t; +# define UINT32_C(v) ((unsigned short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +#ifndef INT32_MAX +# define INT32_MAX (0x7fffffffL) +#endif +#ifndef INT32_MIN +# define INT32_MIN INT32_C(0x80000000) +#endif +#ifndef int32_t +#if (LONG_MAX == INT32_MAX) || defined (S_SPLINT_S) + typedef signed long int32_t; +# define INT32_C(v) v ## L +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "l" +# endif +#elif (INT_MAX == INT32_MAX) + typedef signed int int32_t; +# define INT32_C(v) v +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#elif (SHRT_MAX == INT32_MAX) + typedef signed short int32_t; +# define INT32_C(v) ((short) (v)) +# ifndef PRINTF_INT32_MODIFIER +# define PRINTF_INT32_MODIFIER "" +# endif +#else +#error "Platform not supported" +#endif +#endif + +/* + * The macro stdint_int64_defined is temporarily used to record + * whether or not 64 integer support is available. It must be + * defined for any 64 integer extensions for new platforms that are + * added. + */ + +#undef stdint_int64_defined +#if (defined(__STDC__) && defined(__STDC_VERSION__)) || defined (S_SPLINT_S) +# if (__STDC__ && __STDC_VERSION >= 199901L) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# endif +#endif + +#if !defined (stdint_int64_defined) +# if defined(__GNUC__) +# define stdint_int64_defined + __extension__ typedef long long int64_t; + __extension__ typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif defined(__MWERKS__) || defined (__SUNPRO_C) || defined (__SUNPRO_CC) || defined (__APPLE_CC__) || defined (_LONG_LONG) || defined (_CRAYC) || defined (S_SPLINT_S) +# define stdint_int64_defined + typedef long long int64_t; + typedef unsigned long long uint64_t; +# define UINT64_C(v) v ## ULL +# define INT64_C(v) v ## LL +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "ll" +# endif +# elif (defined(__WATCOMC__) && defined(__WATCOM_INT64__)) || (defined(_MSC_VER) && _INTEGRAL_MAX_BITS >= 64) || (defined (__BORLANDC__) && __BORLANDC__ > 0x460) || defined (__alpha) || defined (__DECC) +# define stdint_int64_defined + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; +# define UINT64_C(v) v ## UI64 +# define INT64_C(v) v ## I64 +# ifndef PRINTF_INT64_MODIFIER +# define PRINTF_INT64_MODIFIER "I64" +# endif +# endif +#endif + +#if !defined (LONG_LONG_MAX) && defined (INT64_C) +# define LONG_LONG_MAX INT64_C (9223372036854775807) +#endif +#ifndef ULONG_LONG_MAX +# define ULONG_LONG_MAX UINT64_C (18446744073709551615) +#endif + +#if !defined (INT64_MAX) && defined (INT64_C) +# define INT64_MAX INT64_C (9223372036854775807) +#endif +#if !defined (INT64_MIN) && defined (INT64_C) +# define INT64_MIN INT64_C (-9223372036854775808) +#endif +#if !defined (UINT64_MAX) && defined (INT64_C) +# define UINT64_MAX UINT64_C (18446744073709551615) +#endif + +/* + * Width of hexadecimal for number field. + */ + +#ifndef PRINTF_INT64_HEX_WIDTH +# define PRINTF_INT64_HEX_WIDTH "16" +#endif +#ifndef PRINTF_INT32_HEX_WIDTH +# define PRINTF_INT32_HEX_WIDTH "8" +#endif +#ifndef PRINTF_INT16_HEX_WIDTH +# define PRINTF_INT16_HEX_WIDTH "4" +#endif +#ifndef PRINTF_INT8_HEX_WIDTH +# define PRINTF_INT8_HEX_WIDTH "2" +#endif + +#ifndef PRINTF_INT64_DEC_WIDTH +# define PRINTF_INT64_DEC_WIDTH "20" +#endif +#ifndef PRINTF_INT32_DEC_WIDTH +# define PRINTF_INT32_DEC_WIDTH "10" +#endif +#ifndef PRINTF_INT16_DEC_WIDTH +# define PRINTF_INT16_DEC_WIDTH "5" +#endif +#ifndef PRINTF_INT8_DEC_WIDTH +# define PRINTF_INT8_DEC_WIDTH "3" +#endif + +/* + * Ok, lets not worry about 128 bit integers for now. Moore's law says + * we don't need to worry about that until about 2040 at which point + * we'll have bigger things to worry about. + */ + +#ifdef stdint_int64_defined + typedef int64_t intmax_t; + typedef uint64_t uintmax_t; +# define INTMAX_MAX INT64_MAX +# define INTMAX_MIN INT64_MIN +# define UINTMAX_MAX UINT64_MAX +# define UINTMAX_C(v) UINT64_C(v) +# define INTMAX_C(v) INT64_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT64_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT64_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT64_DEC_WIDTH +# endif +#else + typedef int32_t intmax_t; + typedef uint32_t uintmax_t; +# define INTMAX_MAX INT32_MAX +# define UINTMAX_MAX UINT32_MAX +# define UINTMAX_C(v) UINT32_C(v) +# define INTMAX_C(v) INT32_C(v) +# ifndef PRINTF_INTMAX_MODIFIER +# define PRINTF_INTMAX_MODIFIER PRINTF_INT32_MODIFIER +# endif +# ifndef PRINTF_INTMAX_HEX_WIDTH +# define PRINTF_INTMAX_HEX_WIDTH PRINTF_INT32_HEX_WIDTH +# endif +# ifndef PRINTF_INTMAX_DEC_WIDTH +# define PRINTF_INTMAX_DEC_WIDTH PRINTF_INT32_DEC_WIDTH +# endif +#endif + +/* + * Because this file currently only supports platforms which have + * precise powers of 2 as bit sizes for the default integers, the + * least definitions are all trivial. Its possible that a future + * version of this file could have different definitions. + */ + +#ifndef stdint_least_defined + typedef int8_t int_least8_t; + typedef uint8_t uint_least8_t; + typedef int16_t int_least16_t; + typedef uint16_t uint_least16_t; + typedef int32_t int_least32_t; + typedef uint32_t uint_least32_t; +# define PRINTF_LEAST32_MODIFIER PRINTF_INT32_MODIFIER +# define PRINTF_LEAST16_MODIFIER PRINTF_INT16_MODIFIER +# define UINT_LEAST8_MAX UINT8_MAX +# define INT_LEAST8_MAX INT8_MAX +# define UINT_LEAST16_MAX UINT16_MAX +# define INT_LEAST16_MAX INT16_MAX +# define UINT_LEAST32_MAX UINT32_MAX +# define INT_LEAST32_MAX INT32_MAX +# define INT_LEAST8_MIN INT8_MIN +# define INT_LEAST16_MIN INT16_MIN +# define INT_LEAST32_MIN INT32_MIN +# ifdef stdint_int64_defined + typedef int64_t int_least64_t; + typedef uint64_t uint_least64_t; +# define PRINTF_LEAST64_MODIFIER PRINTF_INT64_MODIFIER +# define UINT_LEAST64_MAX UINT64_MAX +# define INT_LEAST64_MAX INT64_MAX +# define INT_LEAST64_MIN INT64_MIN +# endif +#endif +#undef stdint_least_defined + +/* + * The ANSI C committee pretending to know or specify anything about + * performance is the epitome of misguided arrogance. The mandate of + * this file is to *ONLY* ever support that absolute minimum + * definition of the fast integer types, for compatibility purposes. + * No extensions, and no attempt to suggest what may or may not be a + * faster integer type will ever be made in this file. Developers are + * warned to stay away from these types when using this or any other + * stdint.h. + */ + +typedef int_least8_t int_fast8_t; +typedef uint_least8_t uint_fast8_t; +typedef int_least16_t int_fast16_t; +typedef uint_least16_t uint_fast16_t; +typedef int_least32_t int_fast32_t; +typedef uint_least32_t uint_fast32_t; +#define UINT_FAST8_MAX UINT_LEAST8_MAX +#define INT_FAST8_MAX INT_LEAST8_MAX +#define UINT_FAST16_MAX UINT_LEAST16_MAX +#define INT_FAST16_MAX INT_LEAST16_MAX +#define UINT_FAST32_MAX UINT_LEAST32_MAX +#define INT_FAST32_MAX INT_LEAST32_MAX +#define INT_FAST8_MIN INT_LEAST8_MIN +#define INT_FAST16_MIN INT_LEAST16_MIN +#define INT_FAST32_MIN INT_LEAST32_MIN +#ifdef stdint_int64_defined + typedef int_least64_t int_fast64_t; + typedef uint_least64_t uint_fast64_t; +# define UINT_FAST64_MAX UINT_LEAST64_MAX +# define INT_FAST64_MAX INT_LEAST64_MAX +# define INT_FAST64_MIN INT_LEAST64_MIN +#endif + +#undef stdint_int64_defined + +/* + * Whatever piecemeal, per compiler thing we can do about the wchar_t + * type limits. + */ + +#if defined(__WATCOMC__) || defined(_MSC_VER) || defined (__GNUC__) +# include +# ifndef WCHAR_MIN +# define WCHAR_MIN 0 +# endif +# ifndef WCHAR_MAX +# define WCHAR_MAX ((wchar_t)-1) +# endif +#endif + +/* + * Whatever piecemeal, per compiler/platform thing we can do about the + * (u)intptr_t types and limits. + */ + +#if defined (_MSC_VER) && defined (_UINTPTR_T_DEFINED) +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +#ifndef STDINT_H_UINTPTR_T_DEFINED +# if defined (__alpha__) || defined (__ia64__) || defined (__x86_64__) || defined (_WIN64) +# define stdint_intptr_bits 64 +# elif defined (__WATCOMC__) || defined (__TURBOC__) +# if defined(__TINY__) || defined(__SMALL__) || defined(__MEDIUM__) +# define stdint_intptr_bits 16 +# else +# define stdint_intptr_bits 32 +# endif +# elif defined (__i386__) || defined (_WIN32) || defined (WIN32) +# define stdint_intptr_bits 32 +# elif defined (__INTEL_COMPILER) +/* TODO -- what will Intel do about x86-64? */ +# endif + +# ifdef stdint_intptr_bits +# define stdint_intptr_glue3_i(a,b,c) a##b##c +# define stdint_intptr_glue3(a,b,c) stdint_intptr_glue3_i(a,b,c) +# ifndef PRINTF_INTPTR_MODIFIER +# define PRINTF_INTPTR_MODIFIER stdint_intptr_glue3(PRINTF_INT,stdint_intptr_bits,_MODIFIER) +# endif +# ifndef PTRDIFF_MAX +# define PTRDIFF_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef PTRDIFF_MIN +# define PTRDIFF_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef UINTPTR_MAX +# define UINTPTR_MAX stdint_intptr_glue3(UINT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MAX +# define INTPTR_MAX stdint_intptr_glue3(INT,stdint_intptr_bits,_MAX) +# endif +# ifndef INTPTR_MIN +# define INTPTR_MIN stdint_intptr_glue3(INT,stdint_intptr_bits,_MIN) +# endif +# ifndef INTPTR_C +# define INTPTR_C(x) stdint_intptr_glue3(INT,stdint_intptr_bits,_C)(x) +# endif +# ifndef UINTPTR_C +# define UINTPTR_C(x) stdint_intptr_glue3(UINT,stdint_intptr_bits,_C)(x) +# endif + typedef stdint_intptr_glue3(uint,stdint_intptr_bits,_t) uintptr_t; + typedef stdint_intptr_glue3( int,stdint_intptr_bits,_t) intptr_t; +# else +/* TODO -- This following is likely wrong for some platforms, and does + nothing for the definition of uintptr_t. */ + typedef ptrdiff_t intptr_t; +# endif +# define STDINT_H_UINTPTR_T_DEFINED +#endif + +/* + * Assumes sig_atomic_t is signed and we have a 2s complement machine. + */ + +#ifndef SIG_ATOMIC_MAX +# define SIG_ATOMIC_MAX ((((sig_atomic_t) 1) << (sizeof (sig_atomic_t)*CHAR_BIT-1)) - 1) +#endif + +#endif diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/CMakeLists.txt b/indra/llqtwebkit/qtwebkit_cookiejar/CMakeLists.txt new file mode 100644 index 000000000..635765c83 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/CMakeLists.txt @@ -0,0 +1,3 @@ +# -*- cmake -*- + +add_subdirectory(src) diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/autotest/trie/trie.pro b/indra/llqtwebkit/qtwebkit_cookiejar/autotest/trie/trie.pro new file mode 100644 index 000000000..c031e1971 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/autotest/trie/trie.pro @@ -0,0 +1,15 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +win32: CONFIG += console +mac:CONFIG -= app_bundle + +CONFIG += qtestlib + +include(../../src/src.pri) + +# Input +SOURCES += tst_trie.cpp +HEADERS += diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/autotest/trie/tst_trie.cpp b/indra/llqtwebkit/qtwebkit_cookiejar/autotest/trie/tst_trie.cpp new file mode 100644 index 000000000..e4bdc6d4e --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/autotest/trie/tst_trie.cpp @@ -0,0 +1,270 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include + +class tst_Trie : public QObject +{ + Q_OBJECT + +public slots: + void initTestCase(); + void cleanupTestCase(); + void init(); + void cleanup(); + +private slots: + void trie_data(); + void trie(); + + void insert_data(); + void insert(); + void clear(); + void find_data(); + void find(); + void remove_data(); + void remove(); + void all(); +}; + +// Subclass that exposes the protected functions. +class SubTrie : public Trie +{ +public: + +}; + +// This will be called before the first test function is executed. +// It is only called once. +void tst_Trie::initTestCase() +{ +} + +// This will be called after the last test function is executed. +// It is only called once. +void tst_Trie::cleanupTestCase() +{ +} + +// This will be called before each test function is executed. +void tst_Trie::init() +{ +} + +// This will be called after every test function. +void tst_Trie::cleanup() +{ +} + +void tst_Trie::trie_data() +{ +} + +void tst_Trie::trie() +{ + SubTrie t; + t.clear(); + QCOMPARE(t.find(QStringList()), QList()); + QCOMPARE(t.remove(QStringList(), -1), false); + QCOMPARE(t.all(), QList()); + t.insert(QStringList(), -1); +} + +void tst_Trie::insert_data() +{ +#if 0 + QTest::addColumn("key"); + QTest::addColumn("value"); + QTest::newRow("null") << QStringList() << T(); +#endif +} + +// public void insert(QStringList const& key, T value) +void tst_Trie::insert() +{ +#if 0 + QFETCH(QStringList, key); + QFETCH(T, value); + + SubTrie t>; + + t>.insert(key, value); +#endif + QSKIP("Test is not implemented.", SkipAll); +} + +// public void clear() +void tst_Trie::clear() +{ + SubTrie t; + t.insert(QStringList(), 0); + t.clear(); + QCOMPARE(t.find(QStringList()), QList()); + QCOMPARE(t.all(), QList()); +} + +Q_DECLARE_METATYPE(QStringList) +typedef QList IntList; +Q_DECLARE_METATYPE(IntList) +void tst_Trie::find_data() +{ + QTest::addColumn("keys"); + QTest::addColumn("values"); + QTest::addColumn("find"); + QTest::addColumn("found"); + + QTest::newRow("null") << QStringList() << IntList() << QStringList() << IntList(); + + QStringList wiki = (QStringList() << "t,e,a" << "i" << "t,e,n" << "i,n" << "i,n,n" << "t,o"); + IntList wikiNum = (IntList() << 3 << 11 << 12 << 5 << 9 << 7); + + QTest::newRow("wikipedia-0") + << wiki + << wikiNum + << (QStringList() << "t") + << (IntList()); + + QTest::newRow("wikipedia-1") + << wiki + << wikiNum + << (QStringList() << "t" << "o") + << (IntList() << 7); + + QTest::newRow("wikipedia-2") + << (wiki << "t,o") + << (wikiNum << 4) + << (QStringList() << "t" << "o") + << (IntList() << 7 << 4); + + QTest::newRow("wikipedia-3") + << wiki + << wikiNum + << (QStringList() << "i" << "n" << "n") + << (IntList() << 9); + +} + +// public QList const find(QStringList const& key) +void tst_Trie::find() +{ + QFETCH(QStringList, keys); + QFETCH(IntList, values); + QFETCH(QStringList, find); + QFETCH(IntList, found); + + SubTrie t; + for (int i = 0; i < keys.count(); ++i) + t.insert(keys[i].split(","), values[i]); + QCOMPARE(t.all(), values); + QCOMPARE(t.find(find), found); +} + +void tst_Trie::remove_data() +{ + QTest::addColumn("keys"); + QTest::addColumn("values"); + QTest::addColumn("removeKey"); + QTest::addColumn("removeValue"); + QTest::addColumn("removed"); + + QTest::newRow("null") << QStringList() << IntList() << QStringList() << -1 << false; + + QStringList wiki = (QStringList() << "t,e,a" << "i" << "t,e,n" << "i,n" << "i,n,n" << "t,o"); + IntList wikiNum = (IntList() << 3 << 11 << 12 << 5 << 9 << 7); + + QTest::newRow("valid key-0") + << wiki + << wikiNum + << (QStringList() << "t") + << -1 + << false; + + QTest::newRow("valid key-1") + << wiki + << wikiNum + << (QStringList() << "t" << "o") + << -1 + << false; + + QTest::newRow("valid key-2") + << wiki + << wikiNum + << (QStringList() << "t" << "o" << "w") + << 2 + << false; + + QTest::newRow("valid key-3") + << wiki + << wikiNum + << (QStringList() << "t" << "o") + << 7 + << true; + + QTest::newRow("valid key-4") + << wiki + << wikiNum + << (QStringList() << "i" << "n") + << 3 + << false; + + QTest::newRow("valid key-5") + << wiki + << wikiNum + << (QStringList() << "i" << "n") + << 5 + << true; + +} + +// public bool remove(QStringList const& key, T value) +void tst_Trie::remove() +{ + QFETCH(QStringList, keys); + QFETCH(IntList, values); + QFETCH(QStringList, removeKey); + QFETCH(int, removeValue); + QFETCH(bool, removed); + + SubTrie t; + for (int i = 0; i < keys.count(); ++i) + t.insert(keys[i].split(","), values[i]); + QCOMPARE(t.all(), values); + QCOMPARE(t.remove(removeKey, removeValue), removed); + if (removed) + values.removeOne(removeValue); + QCOMPARE(t.all(), values); +} + +void tst_Trie::all() +{ + SubTrie t; + // hmm everyone else tests this it seems + QSKIP("Test is not implemented.", SkipAll); +} + +QTEST_MAIN(tst_Trie) +#include "tst_trie.moc" + diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/benchmark/networkcookiejar/cookiejar.pro b/indra/llqtwebkit/qtwebkit_cookiejar/benchmark/networkcookiejar/cookiejar.pro new file mode 100644 index 000000000..350fbc0f6 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/benchmark/networkcookiejar/cookiejar.pro @@ -0,0 +1,17 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . + +win32: CONFIG += console +mac:CONFIG -= app_bundle + +CONFIG += qtestlib + +include(../../src/src.pri) +#include(../../../dev/code/webweaver/src/iris.pri) +#include(../../../dev/arora/src/src.pri) + +# Input +SOURCES += main.cpp +HEADERS += diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/benchmark/networkcookiejar/main.cpp b/indra/llqtwebkit/qtwebkit_cookiejar/benchmark/networkcookiejar/main.cpp new file mode 100644 index 000000000..863d6b0be --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/benchmark/networkcookiejar/main.cpp @@ -0,0 +1,159 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include +#include + +class CookieJarBenchmark: public QObject { + Q_OBJECT + +private slots: + void setCookiesFromUrl(); + void cookiesForUrl(); + void player(); + +private: + QNetworkCookieJar *getJar(bool populate = true); + QList generateCookies(int size); +}; + +QNetworkCookieJar *CookieJarBenchmark::getJar(bool populate) +{ + QNetworkCookieJar *jar; + if (qgetenv("JAR") == "CookieJar") { + jar = new NetworkCookieJar(this); + } else { + jar = new QNetworkCookieJar(this); + } + if (!populate) + return jar; + + // pre populate + for (int i = 0; i < 500; ++i) { + QList cookies = generateCookies(1); + QUrl url = QUrl(QString("http://%1").arg(cookies[0].domain())); + jar->setCookiesFromUrl(cookies, url); + } + + return jar; +} + +QList CookieJarBenchmark::generateCookies(int size) +{ + QList cookies; + for (int i = 0; i < size; ++i) { + QNetworkCookie cookie; + + QString tld; + int c = qrand() % 3; + if (c == 0) tld = "com"; + if (c == 1) tld = "net"; + if (c == 2) tld = "org"; + + QString mid; + int size = qrand() % 6 + 3; + while (mid.count() < size) + mid += QString(QChar::fromAscii(qrand() % 26 + 65)); + + QString sub; + c = qrand() % 3; + if (c == 0) sub = "."; + if (c == 1) sub = ".www."; + if (c == 2) sub = ".foo"; + + cookie.setDomain(QString("%1%2.%3").arg(sub).arg(mid).arg(tld)); + cookie.setName("a"); + cookie.setValue("b"); + cookie.setPath("/"); + cookies.append(cookie); + } + return cookies; +} + +void CookieJarBenchmark::setCookiesFromUrl() +{ + QNetworkCookieJar *jar = getJar(); + QList cookies = generateCookies(1); + QUrl url = QUrl(QString("http://%1").arg(cookies[0].domain())); + QBENCHMARK { + jar->setCookiesFromUrl(cookies, url); + } + delete jar; +} + +void CookieJarBenchmark::cookiesForUrl() +{ + QNetworkCookieJar *jar = getJar(); + QList cookies = generateCookies(1); + cookies[0].setDomain("www.foo.tld"); + QUrl url = QUrl("http://www.foo.tld"); + //QUrl url = QUrl(QString("http://foo%1/").arg(cookies[0].domain())); + jar->setCookiesFromUrl(cookies, url); + //qDebug() << cookies << url; + int c = 0; + QBENCHMARK { + c += jar->cookiesForUrl(url).count(); + } + delete jar; +} + +// Grab the cookie.log file from the manualtest/browser directory +void CookieJarBenchmark::player() +{ + QBENCHMARK { + QFile file("cookie.log"); + file.open(QFile::ReadOnly); + QDataStream stream(&file); + QNetworkCookieJar *jar = getJar(false); + while (!stream.atEnd()) { + QString command; + QUrl url; + stream >> command; + stream >> url; + //qDebug() << command << url; + if (command == "cookiesForUrl") { + jar->cookiesForUrl(url); + } + if (command == "setCookiesFromUrl") { + QByteArray data; + stream >> data; + QDataStream dstream(&data, QIODevice::ReadWrite); + qint32 c; + dstream >> c; + QList cookies; + for (int i = 0; i < c; ++i) { + QByteArray text; + dstream >> text; + cookies += QNetworkCookie::parseCookies(text); + } + jar->setCookiesFromUrl(cookies, url); + } + } + } +} + +QTEST_MAIN(CookieJarBenchmark) +#include "main.moc" diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/browser/browser.pro b/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/browser/browser.pro new file mode 100644 index 000000000..a363bbefc --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/browser/browser.pro @@ -0,0 +1,11 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Wed Jan 7 13:19:00 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +include(../../src/src.pri) +# Input +SOURCES += main.cpp diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/browser/main.cpp b/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/browser/main.cpp new file mode 100644 index 000000000..6d21759fc --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/browser/main.cpp @@ -0,0 +1,85 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include +#include +#include + +QFile file; +QDataStream stream; + +class CookieLog : public NetworkCookieJar { + + Q_OBJECT + +public: + CookieLog(QObject *parent = 0) : NetworkCookieJar(parent) + { + file.setFileName("cookie.log"); + file.open(QFile::WriteOnly); + stream.setDevice(&file); + }; + + virtual QList cookiesForUrl(const QUrl & url) const + { + stream << QString("cookiesForUrl") << url; + QList cookies = NetworkCookieJar::cookiesForUrl(url); + //stream << "#" << cookies; + file.flush(); + return cookies; + } + + virtual bool setCookiesFromUrl(const QList &cookieList, const QUrl &url) + { + QByteArray data; + QDataStream dstream(&data, QIODevice::ReadWrite); + qint32 c = cookieList.count(); + dstream << c; + qDebug() << cookieList.count(); + for (int i = 0; i < c; ++i) + dstream << cookieList[i].toRawForm(); + dstream.device()->close(); + stream << QString("setCookiesFromUrl") << url << data;// << cookieList; + bool set = NetworkCookieJar::setCookiesFromUrl(cookieList, url); + file.flush(); + return set; + } + +}; + +int main(int argc, char**argv) { + QApplication application(argc, argv); + QWebView view; + QString url = "http://www.google.com"; + if (argc > 1) + url = argv[1]; + view.load(QUrl(url)); + view.page()->networkAccessManager()->setCookieJar(new CookieLog()); + view.show(); + return application.exec(); +} + +#include "main.moc" diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/fuzz/fuzz.pro b/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/fuzz/fuzz.pro new file mode 100644 index 000000000..0ad65f1bf --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/fuzz/fuzz.pro @@ -0,0 +1,12 @@ +###################################################################### +# Automatically generated by qmake (2.01a) Wed Jan 7 13:19:00 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +include(../../src/src.pri) +DEFINES += QT_NO_CAST_FROM_ASCII QT_STRICT_ITERATOR +# Input +SOURCES += main.cpp diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/fuzz/main.cpp b/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/fuzz/main.cpp new file mode 100644 index 000000000..28c79a1c2 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/manualtest/fuzz/main.cpp @@ -0,0 +1,100 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include + +QStringList generateKey() { + QStringList key; + int size = qrand() % 20 + 3; + while (key.count() < size) + key += QString(QChar::fromAscii(qrand() % 26 + 64)); + return key; +} + +void basicCheck() { + QStringList list; + list << QLatin1String("to") << QLatin1String("tea") << QLatin1String("ten") << QLatin1String("i") << QLatin1String("in") << QLatin1String("inn"); + Trie trie; + for (int i = 0; i < list.count(); ++i) { + QString key = list[i]; + QStringList keyList; + for (int j = 0; j < key.count(); ++j) + keyList.append(QString(key[j])); + trie.insert(keyList, i); + } + QByteArray data; + { + QDataStream stream(&data, QIODevice::ReadWrite); + stream << trie; + } + Trie trie2; + { + QDataStream stream(&data, QIODevice::ReadOnly); + stream >> trie2; + } + for (int i = 0; i < list.count(); ++i) { + QString key = list[i]; + QStringList keyList; + for (int j = 0; j < key.count(); ++j) + keyList.append(QString(key[j])); + QList x = trie2.find(keyList); + qDebug() << x.count() << i << x[0] << i; + qDebug() << trie2.remove(keyList, i); + qDebug() << trie2.find(keyList).count(); + } +} + +int main(int argc, char **argv) { + Q_UNUSED(argc); + Q_UNUSED(argv); + + basicCheck(); + + QHash hash; + Trie t; + while (hash.count() < 500) { + qDebug() << hash.count(); + QStringList key = generateKey(); + int value = qrand() % 50000; + hash[key.join(QLatin1String(","))] = value; + t.insert(key, value); + + QHashIterator i(hash); + while (i.hasNext()) { + i.next(); + if (t.find(i.key().split(QLatin1Char(','))).count() == 0) + qDebug() << i.key(); + Q_ASSERT(t.find(i.key().split(QLatin1Char(','))).count() > 0); + if (qrand() % 500 == 0) { + t.remove(i.key().split(QLatin1Char(',')), i.value()); + hash.remove(i.key()); + } + //cout << i.key() << ": " << i.value() << endl; + } + } + return 0; +} + diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/src/CMakeLists.txt b/indra/llqtwebkit/qtwebkit_cookiejar/src/CMakeLists.txt new file mode 100644 index 000000000..20bacf3eb --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/src/CMakeLists.txt @@ -0,0 +1,27 @@ +# -*- cmake -*- + +project(networkcookiejar) + +set(networkcookiejar_SOURCE_FILES + networkcookiejar.cpp + ) + +set(networkcookiejar_HEADER_FILES + networkcookiejar.h + networkcookiejar_p.h + trie_p.h + twoleveldomains_p.h + + ) + +QT4_WRAP_CPP(networkcookiejar_HEADERS_MOC ${networkcookiejar_HEADER_FILES}) + +add_library(networkcookiejar + ${networkcookiejar_SOURCE_FILES} + ${networkcookiejar_HEADERS_MOC} + ${networkcookiejar_UI_MOC} +) + +add_dependencies(networkcookiejar prepare) + +target_link_libraries(networkcookiejar) \ No newline at end of file diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar.cpp b/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar.cpp new file mode 100644 index 000000000..274d9e1c1 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar.cpp @@ -0,0 +1,444 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include "networkcookiejar.h" +#include "networkcookiejar_p.h" +#include "twoleveldomains_p.h" + +//#define NETWORKCOOKIEJAR_DEBUG + +#ifndef QT_NO_DEBUG +// ^ Prevent being left on in a released product by accident +// qDebug any cookies that are rejected for further inspection +#define NETWORKCOOKIEJAR_LOGREJECTEDCOOKIES +#include +#endif + +#include +#include + +#if defined(NETWORKCOOKIEJAR_DEBUG) +#include +#endif + + +NetworkCookieJar::NetworkCookieJar(QObject *parent) + : QNetworkCookieJar(parent) +{ + d = new NetworkCookieJarPrivate; +} + +NetworkCookieJar::~NetworkCookieJar() +{ + delete d; +} + +static QStringList splitHost(const QString &host) { + QStringList parts = host.split(QLatin1Char('.'), QString::KeepEmptyParts); + // Remove empty components that are on the start and end + while (!parts.isEmpty() && parts.last().isEmpty()) + parts.removeLast(); + while (!parts.isEmpty() && parts.first().isEmpty()) + parts.removeFirst(); + return parts; +} + +inline static bool shorterPaths(const QNetworkCookie &c1, const QNetworkCookie &c2) +{ + return c2.path().length() < c1.path().length(); +} + +QList NetworkCookieJar::cookiesForUrl(const QUrl &url) const +{ +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << url; +#endif + // Generate split host + QString host = url.host(); + if (url.scheme().toLower() == QLatin1String("file")) + host = QLatin1String("localhost"); + QStringList urlHost = splitHost(host); + + // Get all the cookies for url + QList cookies = d->tree.find(urlHost); + if (urlHost.count() > 2) { + int top = 2; + if (d->matchesBlacklist(urlHost.last())) + top = 3; + + urlHost.removeFirst(); + while (urlHost.count() >= top) { + cookies += d->tree.find(urlHost); + urlHost.removeFirst(); + } + } + + // Prevent doing anything expensive in the common case where + // there are no cookies to check + if (cookies.isEmpty()) + return cookies; + + QDateTime now = QDateTime::currentDateTime().toTimeSpec(Qt::UTC); + const QString urlPath = d->urlPath(url); + const bool isSecure = url.scheme().toLower() == QLatin1String("https"); + QList::iterator i = cookies.begin(); + for (; i != cookies.end();) { + if (!d->matchingPath(*i, urlPath)) { +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << __FUNCTION__ << "Ignoring cookie, path does not match" << *i << urlPath; +#endif + i = cookies.erase(i); + continue; + } + if (!isSecure && i->isSecure()) { + i = cookies.erase(i); +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << __FUNCTION__ << "Ignoring cookie, security mismatch" + << *i << !isSecure; +#endif + continue; + } + if (!i->isSessionCookie() && now > i->expirationDate()) { + // remove now (expensive short term) because there will + // probably be many more cookiesForUrl calls for this host + d->tree.remove(splitHost(i->domain()), *i); +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << __FUNCTION__ << "Ignoring cookie, expiration issue" + << *i << now; +#endif + i = cookies.erase(i); + continue; + } + ++i; + } + + // shorter paths should go first + qSort(cookies.begin(), cookies.end(), shorterPaths); +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << "returning" << cookies.count(); + qDebug() << cookies; +#endif + return cookies; +} + +static const qint32 NetworkCookieJarMagic = 0xae; + +QByteArray NetworkCookieJar::saveState () const +{ + int version = 1; + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + + stream << qint32(NetworkCookieJarMagic); + stream << qint32(version); + stream << d->tree; + return data; +} + +bool NetworkCookieJar::restoreState(const QByteArray &state) +{ + int version = 1; + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + if (stream.atEnd()) + return false; + qint32 marker; + qint32 v; + stream >> marker; + stream >> v; + if (marker != NetworkCookieJarMagic || v != version) + return false; + stream >> d->tree; + if (stream.status() != QDataStream::Ok) { + d->tree.clear(); + return false; + } + return true; +} + +/*! + Remove any session cookies or cookies that have expired. + */ +void NetworkCookieJar::endSession() +{ + const QList cookies = d->tree.all(); + QDateTime now = QDateTime::currentDateTime().toTimeSpec(Qt::UTC); + QList::const_iterator i = cookies.constBegin(); + for (; i != cookies.constEnd();) { + if (i->isSessionCookie() + || (!i->isSessionCookie() && now > i->expirationDate())) { + d->tree.remove(splitHost(i->domain()), *i); + } + ++i; + } +} + +static const int maxCookiePathLength = 1024; + +bool NetworkCookieJar::setCookiesFromUrl(const QList &cookieList, const QUrl &url) +{ +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << url; + qDebug() << cookieList; +#endif + QDateTime now = QDateTime::currentDateTime().toTimeSpec(Qt::UTC); + bool changed = false; + QString fullUrlPath = url.path(); + QString defaultPath = fullUrlPath.mid(0, fullUrlPath.lastIndexOf(QLatin1Char('/')) + 1); + if (defaultPath.isEmpty()) + defaultPath = QLatin1Char('/'); + + QString urlPath = d->urlPath(url); + foreach (QNetworkCookie cookie, cookieList) { + if (cookie.path().length() > maxCookiePathLength) + continue; + + bool alreadyDead = !cookie.isSessionCookie() && cookie.expirationDate() < now; + + if (cookie.path().isEmpty()) { + cookie.setPath(defaultPath); + } + // Matching the behavior of Firefox, no path checking is done when setting cookies + // Safari does something even odder, when that paths don't match it keeps + // the cookie, but changes the paths to the default path +#if 0 + else if (!d->matchingPath(cookie, urlPath)) { +#ifdef NETWORKCOOKIEJAR_LOGREJECTEDCOOKIES + qDebug() << "NetworkCookieJar::" << __FUNCTION__ + << "Blocked cookie because: path doesn't match: " << cookie << url; +#endif + continue; + } +#endif + + if (cookie.domain().isEmpty()) { + QString host = url.host().toLower(); + if (host.isEmpty()) + continue; + cookie.setDomain(host); + } else if (!d->matchingDomain(cookie, url)) { +#ifdef NETWORKCOOKIEJAR_LOGREJECTEDCOOKIES + qDebug() << "NetworkCookieJar::" << __FUNCTION__ + << "Blocked cookie because: domain doesn't match: " << cookie << url; +#endif + continue; + } + + // replace/remove existing cookies + removeCookie(cookie); + + // Notify derived class + onCookieSetFromURL(cookie, url, alreadyDead); + + if (alreadyDead) + continue; + + changed = true; + d->tree.insert(splitHost(cookie.domain()), cookie); + } + + return changed; +} + +QList NetworkCookieJar::allCookies() const +{ +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__; +#endif + return d->tree.all(); +} + +void NetworkCookieJar::clearCookies() +{ +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__; +#endif + d->tree.clear(); +} + +void NetworkCookieJar::setCookies(const QList &cookieList) +{ +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << cookieList.count(); +#endif + + QDateTime now = QDateTime::currentDateTime().toTimeSpec(Qt::UTC); + + foreach (const QNetworkCookie &cookie, cookieList) + { + // If a matching cookie is already in the list, remove it. + removeCookie(cookie); + + if(!cookie.isSessionCookie() && cookie.expirationDate() < now) + { +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << "removing cookie: " << cookie; +#endif + // This cookie has expired -- don't re-add it + } + else + { +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << "adding cookie: " << cookie; +#endif + // this cookie has not expired -- save it + d->tree.insert(splitHost(cookie.domain()), cookie); + } + + } +} + +void NetworkCookieJar::setAllCookies(const QList &cookieList) +{ +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << cookieList.count(); +#endif + clearCookies(); + setCookies(cookieList); +} + +void NetworkCookieJar::removeCookie(const QNetworkCookie &cookie) +{ +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << "removing cookie: " << cookie; +#endif + + // If a cookie with the matching domain, path, and name exists in the cookiejar, remove it. + QString domain = cookie.domain(); + Q_ASSERT(!domain.isEmpty()); + QStringList urlHost = splitHost(domain); + + QList cookies = d->tree.find(urlHost); + QList::const_iterator it = cookies.constBegin(); + for (; it != cookies.constEnd(); ++it) + { + if (cookie.name() == it->name() && + domain == it->domain() && + cookie.path() == it->path()) + { +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << "found matching cookie: " << *it; +#endif + d->tree.remove(urlHost, *it); + break; + } + } +} + +void NetworkCookieJar::dump() +{ +#if defined(NETWORKCOOKIEJAR_DEBUG) + qDebug() << "NetworkCookieJar::" << __FUNCTION__ << "dumping all cookies: "; + QList cookies = allCookies(); + foreach (const QNetworkCookie &cookie, cookies) + { + qDebug() << " " << cookie; + } +#endif +} + +QString NetworkCookieJarPrivate::urlPath(const QUrl &url) const +{ + QString urlPath = url.path(); + if (!urlPath.endsWith(QLatin1Char('/'))) + urlPath += QLatin1Char('/'); + return urlPath; +} + +bool NetworkCookieJarPrivate::matchingPath(const QNetworkCookie &cookie, const QString &urlPath) const +{ + QString cookiePath = cookie.path(); + if (!cookiePath.endsWith(QLatin1Char('/'))) + cookiePath += QLatin1Char('/'); + + return urlPath.startsWith(cookiePath); +} + +bool NetworkCookieJarPrivate::matchesBlacklist(const QString &string) const +{ + if (!setSecondLevelDomain) { + // Alternatively to save a little bit of ram we could just + // use bsearch on twoLevelDomains in place + for (int j = 0; twoLevelDomains[j]; ++j) + secondLevelDomains += QLatin1String(twoLevelDomains[j]); + setSecondLevelDomain = true; + } + QStringList::const_iterator i = + qBinaryFind(secondLevelDomains.constBegin(), secondLevelDomains.constEnd(), string); + return (i != secondLevelDomains.constEnd()); +} + +bool NetworkCookieJarPrivate::matchingDomain(const QNetworkCookie &cookie, const QUrl &url) const +{ + QString domain = cookie.domain().simplified().toLower(); + domain.remove(QLatin1Char(' ')); + QStringList parts = splitHost(domain); + if (parts.isEmpty()) + return false; + + // When there is only one part only file://localhost/ is accepted + if (parts.count() == 1) { + QString s = parts.first(); + if (parts.first() != QLatin1String("localhost")) + return false; + if (url.scheme().toLower() == QLatin1String("file")) + return true; + } + + // Check for blacklist + if (parts.count() == 2 && matchesBlacklist(parts.last())) + return false; + + QStringList urlParts = url.host().toLower().split(QLatin1Char('.'), QString::SkipEmptyParts); + if (urlParts.isEmpty()) + return false; + while (urlParts.count() > parts.count()) + urlParts.removeFirst(); + + for (int j = 0; j < urlParts.count(); ++j) { + if (urlParts.at(j) != parts.at(j)) { + return false; + } + } + + return true; +} + +void NetworkCookieJar::setSecondLevelDomains(const QStringList &secondLevelDomains) +{ + d->setSecondLevelDomain = true; + d->secondLevelDomains = secondLevelDomains; + qSort(d->secondLevelDomains); +} + + +void NetworkCookieJar::onCookieSetFromURL(const QNetworkCookie &cookie, const QUrl &url, bool already_dead) +{ + Q_UNUSED(cookie); + Q_UNUSED(url); + Q_UNUSED(already_dead); + + // Derived classes can use this to track cookie changes. +} diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar.h b/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar.h new file mode 100644 index 000000000..86b14fa16 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar.h @@ -0,0 +1,61 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef NETWORKCOOKIEJAR_H +#define NETWORKCOOKIEJAR_H + +#include + +class NetworkCookieJarPrivate; +class NetworkCookieJar : public QNetworkCookieJar { + Q_OBJECT +public: + NetworkCookieJar(QObject *parent = 0); + ~NetworkCookieJar(); + + virtual QList cookiesForUrl(const QUrl & url) const; + virtual bool setCookiesFromUrl(const QList &cookieList, const QUrl &url); + +protected: + QByteArray saveState() const; + bool restoreState(const QByteArray &state); + void endSession(); + + QList allCookies() const; + void clearCookies(); + void setCookies(const QList &cookieList); + void setAllCookies(const QList &cookieList); + void removeCookie(const QNetworkCookie &cookie); + void dump(); + void setSecondLevelDomains(const QStringList &secondLevelDomains); + + virtual void onCookieSetFromURL(const QNetworkCookie &cookie, const QUrl &url, bool already_dead); + +private: + NetworkCookieJarPrivate *d; +}; + +#endif + diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar_p.h b/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar_p.h new file mode 100644 index 000000000..d8f22cfce --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/src/networkcookiejar_p.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef NETWORKCOOKIEJARPRIVATE_H +#define NETWORKCOOKIEJARPRIVATE_H + +#include "trie_p.h" + +QT_BEGIN_NAMESPACE +QDataStream &operator<<(QDataStream &stream, const QNetworkCookie &cookie) +{ + stream << cookie.toRawForm(); + return stream; +} + +QDataStream &operator>>(QDataStream &stream, QNetworkCookie &cookie) +{ + QByteArray value; + stream >> value; + QList newCookies = QNetworkCookie::parseCookies(value); + if (!newCookies.isEmpty()) + cookie = newCookies.first(); + return stream; +} +QT_END_NAMESPACE + +class NetworkCookieJarPrivate { +public: + NetworkCookieJarPrivate() + : setSecondLevelDomain(false) + {} + + Trie tree; + mutable bool setSecondLevelDomain; + mutable QStringList secondLevelDomains; + + bool matchesBlacklist(const QString &string) const; + bool matchingDomain(const QNetworkCookie &cookie, const QUrl &url) const; + QString urlPath(const QUrl &url) const; + bool matchingPath(const QNetworkCookie &cookie, const QString &urlPath) const; +}; + +#endif + diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/src/src.pri b/indra/llqtwebkit/qtwebkit_cookiejar/src/src.pri new file mode 100644 index 000000000..78ac273a1 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/src/src.pri @@ -0,0 +1,5 @@ +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +HEADERS += trie_p.h networkcookiejar.h twoleveldomains_p.h networkcookiejar_p.h +SOURCES += networkcookiejar.cpp diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/src/trie_p.h b/indra/llqtwebkit/qtwebkit_cookiejar/src/trie_p.h new file mode 100644 index 000000000..a4959a198 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/src/trie_p.h @@ -0,0 +1,247 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef TRIE_H +#define TRIE_H + +//#define TRIE_DEBUG + +#include + +#if defined(TRIE_DEBUG) +#include +#endif + +/* + A Trie tree (prefix tree) where the lookup takes m in the worst case. + + The key is stored in *reverse* order + + Example: + Keys: x,a y,a + + Trie: + a + | \ + x y +*/ + +template +class Trie { +public: + Trie(); + ~Trie(); + + void clear(); + void insert(const QStringList &key, const T &value); + bool remove(const QStringList &key, const T &value); + QList find(const QStringList &key) const; + QList all() const; + + inline bool contains(const QStringList &key) const; + inline bool isEmpty() const { return children.isEmpty() && values.isEmpty(); } + +private: + const Trie* walkTo(const QStringList &key) const; + Trie* walkTo(const QStringList &key, bool create = false); + + template friend QDataStream &operator<<(QDataStream &, const Trie&); + template friend QDataStream &operator>>(QDataStream &, Trie&); + + QList values; + QStringList childrenKeys; + QList > children; +}; + +template +Trie::Trie() { +} + +template +Trie::~Trie() { +} + +template +void Trie::clear() { +#if defined(TRIE_DEBUG) + qDebug() << "Trie::" << __FUNCTION__; +#endif + values.clear(); + childrenKeys.clear(); + children.clear(); +} + +template +bool Trie::contains(const QStringList &key) const { + return walkTo(key); +} + +template +void Trie::insert(const QStringList &key, const T &value) { +#if defined(TRIE_DEBUG) + qDebug() << "Trie::" << __FUNCTION__ << key << value; +#endif + Trie *node = walkTo(key, true); + if (node) + node->values.append(value); +} + +template +bool Trie::remove(const QStringList &key, const T &value) { +#if defined(TRIE_DEBUG) + qDebug() << "Trie::" << __FUNCTION__ << key << value; +#endif + Trie *node = walkTo(key, true); + if (node) { + bool removed = node->values.removeOne(value); + if (!removed) + return false; + + // A faster implementation of removing nodes up the tree + // can be created if profile shows this to be slow + QStringList subKey = key; + while (node->values.isEmpty() + && node->children.isEmpty() + && !subKey.isEmpty()) { + QString currentLevelKey = subKey.first(); + QStringList parentKey = subKey.mid(1); + Trie *parent = walkTo(parentKey, false); + Q_ASSERT(parent); + QStringList::iterator iterator; + iterator = qBinaryFind(parent->childrenKeys.begin(), + parent->childrenKeys.end(), + currentLevelKey); + Q_ASSERT(iterator != parent->childrenKeys.end()); + int index = iterator - parent->childrenKeys.begin(); + parent->children.removeAt(index); + parent->childrenKeys.removeAt(index); + + node = parent; + subKey = parentKey; + } + return removed; + } + return false; +} + +template +QList Trie::find(const QStringList &key) const { +#if defined(TRIE_DEBUG) + qDebug() << "Trie::" << __FUNCTION__ << key; +#endif + const Trie *node = walkTo(key); + if (node) + return node->values; + return QList(); +} + +template +QList Trie::all() const { +#if defined(TRIE_DEBUG) + qDebug() << "Trie::" << __FUNCTION__; +#endif + QList all = values; + for (int i = 0; i < children.count(); ++i) + all += children[i].all(); + return all; +} + +template +QDataStream &operator<<(QDataStream &out, const Trie&trie) { + out << trie.values; + out << trie.childrenKeys; + out << trie.children; + Q_ASSERT(trie.childrenKeys.count() == trie.children.count()); + return out; +} + +template +QDataStream &operator>>(QDataStream &in, Trie &trie) { + trie.clear(); + if (in.status() != QDataStream::Ok) + return in; + in >> trie.values; + in >> trie.childrenKeys; + in >> trie.children; + //Q_ASSERT(trie.childrenKeys.count() == trie.children.count()); + if (trie.childrenKeys.count() != trie.children.count()) + in.setStatus(QDataStream::ReadCorruptData); + return in; +} + +// Very fast const walk +template +const Trie* Trie::walkTo(const QStringList &key) const { + const Trie *node = this; + QStringList::const_iterator childIterator; + QStringList::const_iterator begin, end; + + int depth = key.count() - 1; + while (depth >= 0) { + const QString currentLevelKey = key.at(depth--); + begin = node->childrenKeys.constBegin(); + end = node->childrenKeys.constEnd(); + childIterator = qBinaryFind(begin, end, currentLevelKey); + if (childIterator == end) + return 0; + node = &node->children.at(childIterator - begin); + } + return node; +} + +template +Trie* Trie::walkTo(const QStringList &key, bool create) { + QStringList::iterator iterator; + Trie *node = this; + QStringList::iterator begin, end; + int depth = key.count() - 1; + while (depth >= 0) { + const QString currentLevelKey = key.at(depth--); + begin = node->childrenKeys.begin(); + end = node->childrenKeys.end(); + iterator = qBinaryFind(begin, end, currentLevelKey); +#if defined(TRIE_DEBUG) + qDebug() << "\t" << node << key << currentLevelKey << node->childrenKeys; +#endif + int index = -1; + if (iterator == end) { + if (!create) + return 0; + iterator = qLowerBound(begin, + end, + currentLevelKey); + index = iterator - begin; + node->childrenKeys.insert(iterator, currentLevelKey); + node->children.insert(index, Trie()); + } else { + index = iterator - begin; + } + Q_ASSERT(index >= 0 && index < node->children.count()); + node = &node->children[index]; + } + return node; +} + +#endif diff --git a/indra/llqtwebkit/qtwebkit_cookiejar/src/twoleveldomains_p.h b/indra/llqtwebkit/qtwebkit_cookiejar/src/twoleveldomains_p.h new file mode 100644 index 000000000..e4c046cb6 --- /dev/null +++ b/indra/llqtwebkit/qtwebkit_cookiejar/src/twoleveldomains_p.h @@ -0,0 +1,92 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +// Updated from https://wiki.mozilla.org/TLD_List#External_Links +// To set a custom list use NetworkCookieJar::setSecondLevelDomains() +static const char *const twoLevelDomains[] = { + "ao", + "ar", + "arpa", + "bd", + "bn", + "br", + "co", + "cr", + "cy", + "do", + "eg", + "et", + "fj", + "fk", + "gh", + "gn", + "gu", + "id", + "il", + "jm", + "ke", + "kh", + "ki", + "kw", + "kz", + "lb", + "lc", + "lr", + "ls", + "ml", + "mm", + "mv", + "mw", + "mx", + "my", + "ng", + "ni", + "np", + "nz", + "om", + "pa", + "pe", + "pg", + "pw", + "py", + "qa", + "sa", + "sb", + "sv", + "sy", + "th", + "tn", + "tz", + "uk", + "uy", + "va", + "ve", + "ye", + "yu", + "za", + "zm", + "zw", + 0 +}; diff --git a/indra/llqtwebkit/static.pri b/indra/llqtwebkit/static.pri new file mode 100644 index 000000000..8c282347d --- /dev/null +++ b/indra/llqtwebkit/static.pri @@ -0,0 +1,18 @@ +# Detect if Qt is static or shared +CONFIG(debug, debug|release) { + win32:PRL = $$[QT_INSTALL_LIBS] QtGui.prl +} else { + win32:PRL = $$[QT_INSTALL_LIBS] QtGuid.prl +} + +unix:!mac: PRL = $$[QT_INSTALL_LIBS] libQtGui.prl +mac: PRL = $$[QT_INSTALL_LIBS] QtGui.framework/QtGui.prl +include($$join(PRL, "/")) + +contains(QMAKE_PRL_CONFIG, static) { + DEFINES += STATIC_QT + QTPLUGIN += qgif +} else { + DEFINES += SHARED_QT +} + diff --git a/indra/llqtwebkit/tests/3dgl/3dgl.cpp b/indra/llqtwebkit/tests/3dgl/3dgl.cpp new file mode 100644 index 000000000..c07c5d89c --- /dev/null +++ b/indra/llqtwebkit/tests/3dgl/3dgl.cpp @@ -0,0 +1,269 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ +#define FREEGLUT_STATIC + +#include "zpr.h" +#include "llqtwebkit.h" + +#include +#include +#include +#include + +bool gDebugMode = false; +int gBrowserWindowId=-1; +GLuint gBrowserTexture=-1; +GLuint gCheckerTexture=-1; + +// manually make part of the browser texture transparent - for testing - LLQtWebKit will handle this eventually +void alphaize_browser_texture(unsigned char* texture_pixels) +{ + const int texture_depth=4; + + int texture_width = LLQtWebKit::getInstance()->getBrowserWidth(gBrowserWindowId); + int texture_height = LLQtWebKit::getInstance()->getBrowserHeight(gBrowserWindowId); + + const int num_squares=16; + int sqr1_alpha=0xff; + int sqr2_alpha=0x00; + + for(int y1=0;y1pump(200); + LLQtWebKit::getInstance()->grabBrowserWindow( gBrowserWindowId ); + glutPostRedisplay(); + }; +} + +void display(void) +{ + glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + + glBindTexture(GL_TEXTURE_2D, gCheckerTexture); + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2f(1.0f, 0.0f); glVertex3f(-0.8f, -0.8f, -0.8f); + glTexCoord2f(1.0f, 1.0f); glVertex3f(-0.8f, 0.8f, -0.8f); + glTexCoord2f(0.0f, 1.0f); glVertex3f( 0.8f, 0.8f, -0.8f); + glTexCoord2f(0.0f, 0.0f); glVertex3f( 0.8f, -0.8f, -0.8f); + glEnd(); + + glBindTexture(GL_TEXTURE_2D, gBrowserTexture); + if(!gDebugMode) + { + const unsigned char* browser_pixels=LLQtWebKit::getInstance()->getBrowserWindowPixels(gBrowserWindowId); + if(browser_pixels) + { + int texture_width = LLQtWebKit::getInstance()->getBrowserWidth(gBrowserWindowId); + int texture_height = LLQtWebKit::getInstance()->getBrowserHeight(gBrowserWindowId); + int texture_depth = 4; + + unsigned char* texture_pixels = new unsigned char[texture_width*texture_height*texture_depth]; + memcpy(texture_pixels, browser_pixels, texture_width*texture_height*texture_depth); + alphaize_browser_texture(texture_pixels); + + glTexSubImage2D(GL_TEXTURE_2D, 0, + 0, 0, + texture_width, texture_height, + GL_RGBA, + GL_UNSIGNED_BYTE, + texture_pixels); + + delete [] texture_pixels; + }; + }; + glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); + glTexCoord2f(0.0f, 0.0f); glVertex3f(-0.8f, -0.8f, 0.8f); + glTexCoord2f(1.0f, 0.0f); glVertex3f( 0.8f, -0.8f, 0.8f); + glTexCoord2f(1.0f, 1.0f); glVertex3f( 0.8f, 0.8f, 0.8f); + glTexCoord2f(0.0f, 1.0f); glVertex3f(-0.8f, 0.8f, 0.8f); + glEnd(); + + glPopMatrix(); + + glutSwapBuffers(); +} + +GLuint make_rgba_texture(int texture_width, int texture_height) +{ + const int texture_depth=4; + + unsigned char* texture_pixels = new unsigned char[texture_width*texture_height*texture_depth]; + + const int num_squares=rand()%10+10; + int sqr1_r=rand()%0xa0+0x20; + int sqr1_g=rand()%0xa0+0x20; + int sqr1_b=rand()%0xa0+0x20; + int sqr1_alpha=0xff; + + int sqr2_r=rand()%0xa0+0x20; + int sqr2_g=rand()%0xa0+0x20; + int sqr2_b=rand()%0xa0+0x20; + int sqr2_alpha=0x00; + + for(int y1=0;y1init(std::string(), app_dir, profile_dir, GetDesktopWindow()); + + LLQtWebKit::getInstance()->enableJavaScript(true); + LLQtWebKit::getInstance()->enableCookies(true); + LLQtWebKit::getInstance()->enablePlugins(true); + + const std::string start_url("http://news.google.com"); + //const std::string start_url("http://www.youtube.com/watch?v=4Z3r9X8OahA&feature=rbl_entertainment"); + gBrowserWindowId=LLQtWebKit::getInstance()->createBrowserWindow(browser_width, browser_height); + LLQtWebKit::getInstance()->setSize(gBrowserWindowId, browser_width, browser_height); + LLQtWebKit::getInstance()->flipWindow(gBrowserWindowId, true); + LLQtWebKit::getInstance()->navigateTo(gBrowserWindowId, start_url); + } + + gCheckerTexture = make_rgba_texture( browser_width, browser_height); + + glutMainLoop(); + + return 0; +} diff --git a/indra/llqtwebkit/tests/3dgl/3dgl.pro b/indra/llqtwebkit/tests/3dgl/3dgl.pro new file mode 100644 index 000000000..6285128ef --- /dev/null +++ b/indra/llqtwebkit/tests/3dgl/3dgl.pro @@ -0,0 +1,38 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +INCLUDEPATH += ../../ +CONFIG -= app_bundle + +QT += webkit opengl network + +!mac { +unix { + DEFINES += LL_LINUX + LIBS += -lglui -lglut + LIBS += $$PWD/../../libllqtwebkit.a +} +} + +mac { + DEFINES += LL_OSX + LIBS += -framework GLUT -framework OpenGL + LIBS += $$PWD/libllqtwebkit.dylib +} + +win32 { + DEFINES += _WINDOWS + INCLUDEPATH += ../ + INCLUDEPATH += $$PWD/../../stage/packages/include + DESTDIR=../build + release { + LIBS += $$PWD/../../Release/llqtwebkit.lib + LIBS += $$PWD/../build/freeglut_static.lib + LIBS += comdlg32.lib + } +} + +include(../../static.pri) + +SOURCES += 3dgl.cpp zpr.c diff --git a/indra/llqtwebkit/tests/3dgl/zpr.c b/indra/llqtwebkit/tests/3dgl/zpr.c new file mode 100644 index 000000000..65373efc5 --- /dev/null +++ b/indra/llqtwebkit/tests/3dgl/zpr.c @@ -0,0 +1,429 @@ +#include +#include +#include +#include + +#include "zpr.h" + +/* This code was originally C++ :-) */ + +#define bool int +#define true 1 +#define false 0 + +static double _left = 0.0; +static double _right = 0.0; +static double _bottom = 0.0; +static double _top = 0.0; +static double _zNear = -10.0; +static double _zFar = 10.0; + +static int _mouseX = 0; +static int _mouseY = 0; +static bool _mouseLeft = false; +static bool _mouseMiddle = false; +static bool _mouseRight = false; + +static double _dragPosX = 0.0; +static double _dragPosY = 0.0; +static double _dragPosZ = 0.0; + +static double _matrix[16]; +static double _matrixInverse[16]; + +static double vlen(double x,double y,double z); +static void pos(double *px,double *py,double *pz,const int x,const int y,const int *viewport); +static void getMatrix(); +static void invertMatrix(const GLdouble *m, GLdouble *out ); + +static void zprReshape(int w,int h); +static void zprMouse(int button, int state, int x, int y); +static void zprMotion(int x, int y); + +static void zprPick(GLdouble x, GLdouble y,GLdouble delX, GLdouble delY); + +/* Configurable center point for zooming and rotation */ + +GLfloat zprReferencePoint[4] = { 0,0,0,0 }; + +void +zprInit() +{ + getMatrix(); + + glutReshapeFunc(zprReshape); + glutMouseFunc(zprMouse); + glutMotionFunc(zprMotion); +} + +static void +zprReshape(int w,int h) +{ + glViewport(0,0,w,h); + + _top = 1.0; + _bottom = -1.0; + _left = -(double)w/(double)h; + _right = -_left; + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(_left,_right,_bottom,_top,_zNear,_zFar); + + glMatrixMode(GL_MODELVIEW); +} + +static void +zprMouse(int button, int state, int x, int y) +{ + GLint viewport[4]; + + /* Do picking */ + if (state==GLUT_DOWN) + zprPick(x,glutGet(GLUT_WINDOW_HEIGHT)-1-y,3,3); + + _mouseX = x; + _mouseY = y; + + if (state==GLUT_UP) + switch (button) + { + case GLUT_LEFT_BUTTON: _mouseLeft = false; break; + case GLUT_MIDDLE_BUTTON: _mouseMiddle = false; break; + case GLUT_RIGHT_BUTTON: _mouseRight = false; break; + } + else + switch (button) + { + case GLUT_LEFT_BUTTON: _mouseLeft = true; break; + case GLUT_MIDDLE_BUTTON: _mouseMiddle = true; break; + case GLUT_RIGHT_BUTTON: _mouseRight = true; break; + } + + glGetIntegerv(GL_VIEWPORT,viewport); + pos(&_dragPosX,&_dragPosY,&_dragPosZ,x,y,viewport); + glutPostRedisplay(); +} + +static void +zprMotion(int x, int y) +{ + bool changed = false; + + const int dx = x - _mouseX; + const int dy = y - _mouseY; + + GLint viewport[4]; + glGetIntegerv(GL_VIEWPORT,viewport); + + if (dx==0 && dy==0) + return; + + if (_mouseMiddle || (_mouseLeft && _mouseRight)) + { + double s = exp((double)dy*0.01); + + glTranslatef( zprReferencePoint[0], zprReferencePoint[1], zprReferencePoint[2]); + glScalef(s,s,s); + glTranslatef(-zprReferencePoint[0],-zprReferencePoint[1],-zprReferencePoint[2]); + + changed = true; + } + else + if (_mouseLeft) + { + double ax,ay,az; + double bx,by,bz; + double angle; + + ax = dy; + ay = dx; + az = 0.0; + angle = vlen(ax,ay,az)/(double)(viewport[2]+1)*180.0; + + /* Use inverse matrix to determine local axis of rotation */ + + bx = _matrixInverse[0]*ax + _matrixInverse[4]*ay + _matrixInverse[8] *az; + by = _matrixInverse[1]*ax + _matrixInverse[5]*ay + _matrixInverse[9] *az; + bz = _matrixInverse[2]*ax + _matrixInverse[6]*ay + _matrixInverse[10]*az; + + glTranslatef( zprReferencePoint[0], zprReferencePoint[1], zprReferencePoint[2]); + glRotatef(angle,bx,by,bz); + glTranslatef(-zprReferencePoint[0],-zprReferencePoint[1],-zprReferencePoint[2]); + + changed = true; + } + else + if (_mouseRight) + { + double px,py,pz; + + pos(&px,&py,&pz,x,y,viewport); + + glLoadIdentity(); + glTranslatef(px-_dragPosX,py-_dragPosY,pz-_dragPosZ); + glMultMatrixd(_matrix); + + _dragPosX = px; + _dragPosY = py; + _dragPosZ = pz; + + changed = true; + } + + _mouseX = x; + _mouseY = y; + + if (changed) + { + getMatrix(); + glutPostRedisplay(); + } +} + +/***************************************************************** + * Utility functions + *****************************************************************/ + +static double +vlen(double x,double y,double z) +{ + return sqrt(x*x+y*y+z*z); +} + +static void +pos(double *px,double *py,double *pz,const int x,const int y,const int *viewport) +{ + /* + Use the ortho projection and viewport information + to map from mouse co-ordinates back into world + co-ordinates + */ + + *px = (double)(x-viewport[0])/(double)(viewport[2]); + *py = (double)(y-viewport[1])/(double)(viewport[3]); + + *px = _left + (*px)*(_right-_left); + *py = _top + (*py)*(_bottom-_top); + *pz = _zNear; +} + +static void +getMatrix() +{ + glGetDoublev(GL_MODELVIEW_MATRIX,_matrix); + invertMatrix(_matrix,_matrixInverse); +} + +/* + * From Mesa-2.2\src\glu\project.c + * + * Compute the inverse of a 4x4 matrix. Contributed by scotter@lafn.org + */ + +static void +invertMatrix(const GLdouble *m, GLdouble *out ) +{ + +/* NB. OpenGL Matrices are COLUMN major. */ +#define MAT(m,r,c) (m)[(c)*4+(r)] + +/* Here's some shorthand converting standard (row,column) to index. */ +#define m11 MAT(m,0,0) +#define m12 MAT(m,0,1) +#define m13 MAT(m,0,2) +#define m14 MAT(m,0,3) +#define m21 MAT(m,1,0) +#define m22 MAT(m,1,1) +#define m23 MAT(m,1,2) +#define m24 MAT(m,1,3) +#define m31 MAT(m,2,0) +#define m32 MAT(m,2,1) +#define m33 MAT(m,2,2) +#define m34 MAT(m,2,3) +#define m41 MAT(m,3,0) +#define m42 MAT(m,3,1) +#define m43 MAT(m,3,2) +#define m44 MAT(m,3,3) + + GLdouble det; + GLdouble d12, d13, d23, d24, d34, d41; + GLdouble tmp[16]; /* Allow out == in. */ + + /* Inverse = adjoint / det. (See linear algebra texts.)*/ + + /* pre-compute 2x2 dets for last two rows when computing */ + /* cofactors of first two rows. */ + d12 = (m31*m42-m41*m32); + d13 = (m31*m43-m41*m33); + d23 = (m32*m43-m42*m33); + d24 = (m32*m44-m42*m34); + d34 = (m33*m44-m43*m34); + d41 = (m34*m41-m44*m31); + + tmp[0] = (m22 * d34 - m23 * d24 + m24 * d23); + tmp[1] = -(m21 * d34 + m23 * d41 + m24 * d13); + tmp[2] = (m21 * d24 + m22 * d41 + m24 * d12); + tmp[3] = -(m21 * d23 - m22 * d13 + m23 * d12); + + /* Compute determinant as early as possible using these cofactors. */ + det = m11 * tmp[0] + m12 * tmp[1] + m13 * tmp[2] + m14 * tmp[3]; + + /* Run singularity test. */ + if (det == 0.0) { + /* printf("invert_matrix: Warning: Singular matrix.\n"); */ +/* memcpy(out,_identity,16*sizeof(double)); */ + } + else { + GLdouble invDet = 1.0 / det; + /* Compute rest of inverse. */ + tmp[0] *= invDet; + tmp[1] *= invDet; + tmp[2] *= invDet; + tmp[3] *= invDet; + + tmp[4] = -(m12 * d34 - m13 * d24 + m14 * d23) * invDet; + tmp[5] = (m11 * d34 + m13 * d41 + m14 * d13) * invDet; + tmp[6] = -(m11 * d24 + m12 * d41 + m14 * d12) * invDet; + tmp[7] = (m11 * d23 - m12 * d13 + m13 * d12) * invDet; + + /* Pre-compute 2x2 dets for first two rows when computing */ + /* cofactors of last two rows. */ + d12 = m11*m22-m21*m12; + d13 = m11*m23-m21*m13; + d23 = m12*m23-m22*m13; + d24 = m12*m24-m22*m14; + d34 = m13*m24-m23*m14; + d41 = m14*m21-m24*m11; + + tmp[8] = (m42 * d34 - m43 * d24 + m44 * d23) * invDet; + tmp[9] = -(m41 * d34 + m43 * d41 + m44 * d13) * invDet; + tmp[10] = (m41 * d24 + m42 * d41 + m44 * d12) * invDet; + tmp[11] = -(m41 * d23 - m42 * d13 + m43 * d12) * invDet; + tmp[12] = -(m32 * d34 - m33 * d24 + m34 * d23) * invDet; + tmp[13] = (m31 * d34 + m33 * d41 + m34 * d13) * invDet; + tmp[14] = -(m31 * d24 + m32 * d41 + m34 * d12) * invDet; + tmp[15] = (m31 * d23 - m32 * d13 + m33 * d12) * invDet; + + memcpy(out, tmp, 16*sizeof(GLdouble)); + } + +#undef m11 +#undef m12 +#undef m13 +#undef m14 +#undef m21 +#undef m22 +#undef m23 +#undef m24 +#undef m31 +#undef m32 +#undef m33 +#undef m34 +#undef m41 +#undef m42 +#undef m43 +#undef m44 +#undef MAT +} + +/***************************************** Picking ****************************************************/ + +static void (*selection)(void) = NULL; +static void (*pick)(GLint name) = NULL; + +void zprSelectionFunc(void (*f)(void)) +{ + selection = f; +} + +void zprPickFunc(void (*f)(GLint name)) +{ + pick = f; +} + +/* Draw in selection mode */ + +static void +zprPick(GLdouble x, GLdouble y,GLdouble delX, GLdouble delY) +{ + GLuint buffer[1024]; + const int bufferSize = sizeof(buffer)/sizeof(GLuint); + + GLint viewport[4]; + GLdouble projection[16]; + + GLint hits; + GLint i,j,k; + + GLint min = -1; + GLuint minZ = -1; + + glSelectBuffer(bufferSize,buffer); /* Selection buffer for hit records */ + glRenderMode(GL_SELECT); /* OpenGL selection mode */ + glInitNames(); /* Clear OpenGL name stack */ + + glMatrixMode(GL_PROJECTION); + glPushMatrix(); /* Push current projection matrix */ + glGetIntegerv(GL_VIEWPORT,viewport); /* Get the current viewport size */ + glGetDoublev(GL_PROJECTION_MATRIX,projection); /* Get the projection matrix */ + glLoadIdentity(); /* Reset the projection matrix */ + gluPickMatrix(x,y,delX,delY,viewport); /* Set the picking matrix */ + glMultMatrixd(projection); /* Apply projection matrix */ + + glMatrixMode(GL_MODELVIEW); + + if (selection) + selection(); /* Draw the scene in selection mode */ + + hits = glRenderMode(GL_RENDER); /* Return to normal rendering mode */ + + /* Diagnostic output to stdout */ + + #ifndef NDEBUG + if (hits!=0) + { + printf("hits = %d\n",hits); + + for (i=0,j=0; i rotate + * Middle button -> zoom + * Right button -> pan + * + * Picking is also provided via two configurable callbacks: + * + * void zprSelectionFunc(void (*f)(void)) + * + * The draw function to be called in OpenGL selection + * mode in response to a mouse-down button event. + * + * void zprPickFunc(void (*f)(GLint name)) + * + * The callback function which will receive the + * top-most item of the name stack of the closest selection + * hit. If there is no selection hit, -1 + * + * Limitations + * ----------- + * + * Works best with zprReferencePoint appropriately configured. + * Works best with ortho projection. + * You may need to use glEnable(GL_NORMALIZATION) for correct lighting. + * Near and far clip planes are hard-coded. + * Zooming and rotation is centered on the origin. + * Only one window can use the callbacks at one time. + * + */ + +#ifdef WIN32 +#include +#endif + +#define FREEGLUT_STATIC + +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* + * + */ + +/* Mouse Manipulation API */ + +void zprInit(); + +extern GLfloat zprReferencePoint[4]; + +/* Picking API (Optional) */ + +extern void zprSelectionFunc(void (*f)(void)); /* Selection-mode draw function */ +extern void zprPickFunc(void (*f)(GLint name)); /* Pick event handling function */ + +/* + * + */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/indra/llqtwebkit/tests/qttestapp/main.cpp b/indra/llqtwebkit/tests/qttestapp/main.cpp new file mode 100644 index 000000000..3ab3a9c17 --- /dev/null +++ b/indra/llqtwebkit/tests/qttestapp/main.cpp @@ -0,0 +1,341 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#include +#include + +class WebPage : public QWidget, LLEmbeddedBrowserWindowObserver +{ + Q_OBJECT + +signals: + void locationChanged(const QString &); + void canGoBack(bool); + void canGoForward(bool); + +public: + WebPage(QWidget *parent = 0); + ~WebPage(); + + void onCursorChanged(const EventType& event); + void onPageChanged(const EventType& event); + void onNavigateBegin(const EventType& event); + void onNavigateComplete(const EventType& event); + void onUpdateProgress(const EventType& event); + void onStatusTextChange(const EventType& event); + void onLocationChange(const EventType& event); + void onClickLinkHref(const EventType& event); + void onClickLinkNoFollow(const EventType& event); + +public slots: + void goBack(); + void goForward(); + void reload(); + void loadUrl(const QString &); + +protected: + void resizeEvent(QResizeEvent *event); + void paintEvent(QPaintEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); +private: + void updateSLvariables(); + int mBrowserWindowId; +}; + +WebPage::WebPage(QWidget *parent) + : QWidget(parent) +{ + setMouseTracking(true); + std::string applicationDir = std::string(); + std::string componentDir = applicationDir; + std::string profileDir = applicationDir + "\\" + "testGL_profile"; + LLQtWebKit::getInstance()->init(applicationDir, componentDir, profileDir, 0); + + mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow(width(), height()); + + // observer events that LLQtWebKit emits + LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this ); + + // append details to agent string + LLQtWebKit::getInstance()->setBrowserAgentId("testqtapp"); + + // don't flip bitmap + LLQtWebKit::getInstance()->flipWindow(mBrowserWindowId, false); + + // test Second Life viewer specific functions + LLQtWebKit::getInstance()->setSLObjectEnabled( true ); // true means feature is turned on + LLQtWebKit::getInstance()->setAgentLanguage( "tst-en" ); // viewer language selected by agent + LLQtWebKit::getInstance()->setAgentRegion( "QtTestAppRegion" ); // name of region where agent is located + LLQtWebKit::getInstance()->setAgentLocation( 9.8, 7.6, 5.4 ); // agent's x,y,z location within a region + LLQtWebKit::getInstance()->setAgentGlobalLocation( 119.8, 227.6, 335.4 ); // agent's x,y,z location within the grid + LLQtWebKit::getInstance()->setAgentMaturity( "Very immature" ); // selected maturity level of agent + LLQtWebKit::getInstance()->setAgentOrientation( (rand()%36000)/100.0f ); // direction avatar is facing + LLQtWebKit::getInstance()->emitLocation(); + LLQtWebKit::getInstance()->emitLanguage(); + LLQtWebKit::getInstance()->emitMaturity(); + + // go to the "home page" + LLQtWebKit::getInstance()->navigateTo(mBrowserWindowId, "http://callum-linden.s3.amazonaws.com/browsertest.html"); +} + +WebPage::~WebPage() +{ + // unhook observer + LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this ); + + // clean up + LLQtWebKit::getInstance()->reset(); +} + +void WebPage::updateSLvariables() +{ + // randomly update SL values to test + LLQtWebKit::getInstance()->setAgentOrientation( (rand()%36000)/100.0f ); + LLQtWebKit::getInstance()->setAgentLocation( (rand()%25600)/100.0f, (rand()%25600)/100.0f, (rand()%25600)/100.0f ); + LLQtWebKit::getInstance()->setAgentGlobalLocation( (rand()%25600)/100.0f, (rand()%25600)/100.0f, (rand()%25600)/100.0f ); + + if ( rand() % 2 ) + LLQtWebKit::getInstance()->setAgentLanguage( "One language" ); + else + LLQtWebKit::getInstance()->setAgentLanguage( "Another language" ); + + if ( rand() % 2 ) + LLQtWebKit::getInstance()->setAgentRegion( "Region Wibble" ); + else + LLQtWebKit::getInstance()->setAgentRegion( "Region Flasm" ); + + if ( rand() % 2 ) + LLQtWebKit::getInstance()->setAgentMaturity( "Adults only" ); + else + LLQtWebKit::getInstance()->setAgentMaturity( "Children only" ); + + LLQtWebKit::getInstance()->emitLocation(); + LLQtWebKit::getInstance()->emitLanguage(); + LLQtWebKit::getInstance()->emitMaturity(); +} + +void WebPage::onCursorChanged(const EventType& event) +{ + //qDebug() << __FUNCTION__ << QString::fromStdString(event.getEventUri()); + switch (event.getIntValue()) { + case LLQtWebKit::C_ARROW: setCursor(QCursor(Qt::ArrowCursor)); break; + case LLQtWebKit::C_IBEAM: setCursor(QCursor(Qt::IBeamCursor)); break; + case LLQtWebKit::C_SPLITV: setCursor(QCursor(Qt::SplitHCursor)); break; + case LLQtWebKit::C_SPLITH: setCursor(QCursor(Qt::SplitVCursor)); break; + case LLQtWebKit::C_POINTINGHAND: setCursor(QCursor(Qt::PointingHandCursor)); break; + default: break; + } +} + +void WebPage::onPageChanged(const EventType& event) +{ + Q_UNUSED(event); + LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId ); + //qDebug() << __FUNCTION__ << QString::fromStdString(event.getEventUri()); + update(); +} + +void WebPage::onNavigateBegin(const EventType& event) +{ + Q_UNUSED(event); + //qDebug() << __FUNCTION__ << QString::fromStdString(event.getEventUri()); +} + +void WebPage::onNavigateComplete(const EventType& event) +{ + Q_UNUSED(event); + //qDebug() << __FUNCTION__ << QString::fromStdString(event.getEventUri()); +} + +void WebPage::onUpdateProgress(const EventType& event) +{ + Q_UNUSED(event); +} + +void WebPage::onStatusTextChange(const EventType& event) +{ + Q_UNUSED(event); +} + +void WebPage::onLocationChange(const EventType& event) +{ + //qDebug() << __FUNCTION__; + emit locationChanged(QString::fromStdString(event.getEventUri())); + //void canGoBack(bool); + //void canGoForward(bool); +} + +void WebPage::onClickLinkHref(const EventType& event) +{ + Q_UNUSED(event); +} + +void WebPage::onClickLinkNoFollow(const EventType& event) +{ + Q_UNUSED(event); +} + +void WebPage::resizeEvent(QResizeEvent *event) +{ + LLQtWebKit::getInstance()->setSize(mBrowserWindowId, event->size().width(), event->size().height()); + QWidget::resizeEvent(event); +} + +void WebPage::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + int width = LLQtWebKit::getInstance()->getBrowserWidth(mBrowserWindowId); + int height = LLQtWebKit::getInstance()->getBrowserHeight(mBrowserWindowId); + const unsigned char* pixels = LLQtWebKit::getInstance()->getBrowserWindowPixels(mBrowserWindowId); + QImage image(pixels, width, height, QImage::Format_RGB32); + image = image.rgbSwapped(); + QPainter painter(this); + painter.drawImage(QPoint(0, 0), image); +} + +void WebPage::mouseDoubleClickEvent(QMouseEvent *event) +{ + LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, + LLQtWebKit::ME_MOUSE_DOUBLE_CLICK, + LLQtWebKit::MB_MOUSE_BUTTON_LEFT, + event->x(), event->y(), + LLQtWebKit::KM_MODIFIER_NONE ); +} + +void WebPage::mouseMoveEvent(QMouseEvent *event) +{ + updateSLvariables(); + + LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, + LLQtWebKit::ME_MOUSE_MOVE, + LLQtWebKit::MB_MOUSE_BUTTON_LEFT, + event->x(), event->y(), + LLQtWebKit::KM_MODIFIER_NONE ); +} + +void WebPage::mousePressEvent(QMouseEvent *event) +{ + LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, + LLQtWebKit::ME_MOUSE_DOWN, + LLQtWebKit::MB_MOUSE_BUTTON_LEFT, + event->x(), event->y(), + LLQtWebKit::KM_MODIFIER_NONE ); +} + +void WebPage::mouseReleaseEvent(QMouseEvent *event) +{ + LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, + LLQtWebKit::ME_MOUSE_UP, + LLQtWebKit::MB_MOUSE_BUTTON_LEFT, + event->x(), event->y(), + LLQtWebKit::KM_MODIFIER_NONE ); + + LLQtWebKit::getInstance()->focusBrowser(mBrowserWindowId, true); +} + +void WebPage::keyPressEvent(QKeyEvent *event) +{ + Q_UNUSED(event); +} + +void WebPage::keyReleaseEvent(QKeyEvent *event) +{ + Q_UNUSED(event); + //LLQtWebKit::getInstance()->unicodeInput(mBrowserWindowId, event->text().at(0).unicode(),LLQtWebKit::KM_MODIFIER_NONE); +} + +void WebPage::goBack() +{ + LLQtWebKit::getInstance()->userAction(mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK); +} + +void WebPage::goForward() +{ + LLQtWebKit::getInstance()->userAction(mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD); +} + +void WebPage::reload() +{ + LLQtWebKit::getInstance()->userAction(mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_RELOAD); +} + +void WebPage::loadUrl(const QString &url) +{ + LLQtWebKit::getInstance()->navigateTo(mBrowserWindowId, url.toStdString()); +} + +#include "ui_window.h" + +class Window : public QDialog, public Ui_Dialog +{ + Q_OBJECT +public: + Window(QWidget *parent = 0); + +public slots: + void loadUrl(); +}; + +Window::Window(QWidget *parent) + : QDialog(parent) +{ + setupUi(this); + connect(webpage, SIGNAL(locationChanged(const QString &)), + location, SLOT(setText(const QString &))); + connect(webpage, SIGNAL(canGoBack(bool)), + backButton, SLOT(setEnabled(bool))); + connect(webpage, SIGNAL(canGoForward(bool)), + forwardButton, SLOT(setEnabled(bool))); + connect(backButton, SIGNAL(clicked()), + webpage, SLOT(goBack())); + connect(forwardButton, SIGNAL(clicked()), + webpage, SLOT(goForward())); + connect(reloadButton, SIGNAL(clicked()), + webpage, SLOT(reload())); + connect(location, SIGNAL(returnPressed()), + this, SLOT(loadUrl())); +} + +void Window::loadUrl() +{ + webpage->loadUrl(location->text()); +} + + +int main(int argc, char **argv) +{ + QApplication application(argc, argv); + Window window; + window.show(); + return application.exec(); +} + +#include "main.moc" diff --git a/indra/llqtwebkit/tests/qttestapp/qttestapp.pro b/indra/llqtwebkit/tests/qttestapp/qttestapp.pro new file mode 100644 index 000000000..5a0ca33cf --- /dev/null +++ b/indra/llqtwebkit/tests/qttestapp/qttestapp.pro @@ -0,0 +1,38 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +INCLUDEPATH += ../../ +CONFIG -= app_bundle + +include(../../static.pri) + +QT += webkit opengl network + +unix { + LIBS += $$PWD/../../libllqtwebkit.a +} + +!mac { +unix { + DEFINES += LL_LINUX +} +} + +mac { + DEFINES += LL_OSX +} + + +win32{ + DEFINES += _WINDOWS + INCLUDEPATH += ../ + DESTDIR=../build + release { + LIBS += $$PWD/../../Release/llqtwebkit.lib + } +} + +# Input +SOURCES += main.cpp +FORMS += window.ui diff --git a/indra/llqtwebkit/tests/qttestapp/webpage.h b/indra/llqtwebkit/tests/qttestapp/webpage.h new file mode 100644 index 000000000..e69de29bb diff --git a/indra/llqtwebkit/tests/qttestapp/window.ui b/indra/llqtwebkit/tests/qttestapp/window.ui new file mode 100644 index 000000000..6125821cc --- /dev/null +++ b/indra/llqtwebkit/tests/qttestapp/window.ui @@ -0,0 +1,79 @@ + + + Dialog + + + + 0 + 0 + 766 + 613 + + + + Dialog + + + + 0 + + + + + 6 + + + 6 + + + 6 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + WebPage + QWidget +
webpage.h
+ 1 +
+
+ + +
diff --git a/indra/llqtwebkit/tests/ssltest/ssltest.cpp b/indra/llqtwebkit/tests/ssltest/ssltest.cpp new file mode 100644 index 000000000..fcbf80314 --- /dev/null +++ b/indra/llqtwebkit/tests/ssltest/ssltest.cpp @@ -0,0 +1,229 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef _WINDOWS +extern "C" { +#include +} +#endif + +#ifdef _WINDOWS +#include +#include +#endif + +#include +#include +#include + +#ifdef LL_OSX +// I'm not sure why STATIC_QT is getting defined, but the Q_IMPORT_PLUGIN thing doesn't seem to be necessary on the mac. +#undef STATIC_QT +#endif + +#ifdef STATIC_QT +#include +Q_IMPORT_PLUGIN(qgif) +#endif + +#include "llqtwebkit.h" + +class sslTest : + public LLEmbeddedBrowserWindowObserver +{ + public: + sslTest( std::string url, bool ignore_ca_file, bool ignore_ssl_errors ) : + mBrowserWindowWidth( 512 ), + mBrowserWindowHeight( 512 ), + mBrowserWindowHandle( 0 ), + mNavigateInProgress( true ) + { +#ifdef _WINDOWS + std::string cwd = std::string( _getcwd( NULL, 1024) ); + std::string profile_dir = cwd + "\\" + "ssltest_profile"; + void* native_window_handle = (void*)GetDesktopWindow(); + std::string ca_file_loc = cwd + "\\" + "CA.pem"; +#else + std::string cwd = std::string( getcwd( NULL, 1024) ); + std::string profile_dir = cwd + "/" + "ssltest_profile"; + void* native_window_handle = 0; + std::string ca_file_loc = cwd + "/" + "CA.pem"; +#endif + std::cout << "ssltest> === begin ===" << std::endl; + std::cout << "ssltest> current working dir is " << cwd << std::endl; + std::cout << "ssltest> profiles dir location is " << profile_dir << std::endl; + + LLQtWebKit::getInstance()->init( cwd, cwd, profile_dir, native_window_handle ); + + LLQtWebKit::getInstance()->enableJavaScript( true ); + LLQtWebKit::getInstance()->enablePlugins( true ); + + mBrowserWindowHandle = LLQtWebKit::getInstance()->createBrowserWindow( mBrowserWindowWidth, mBrowserWindowHeight ); + LLQtWebKit::getInstance()->setSize( mBrowserWindowHandle, mBrowserWindowWidth, mBrowserWindowHeight ); + + LLQtWebKit::getInstance()->addObserver( mBrowserWindowHandle, this ); + + if ( ! ignore_ca_file ) + { + std::cout << "ssltest> Expected certificate authority file location is " << ca_file_loc << std::endl; + LLQtWebKit::getInstance()->setCAFile( ca_file_loc.c_str() ); + } + else + { + std::cout << "ssltest> Not loading certificate authority file" << std::endl; + }; + + if ( ignore_ssl_errors ) + { + LLQtWebKit::getInstance()->setIgnoreSSLCertErrors( true ); + std::cout << "ssltest> Ignoring SSL errors " << std::endl; + } + else + { + std::cout << "ssltest> Not ignoring SSL errors " << std::endl; + }; + + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowHandle, url ); + + std::cout << "ssltest> navigating to " << url << std::endl; + }; + + bool idle( void ) + { + LLQtWebKit::getInstance()->pump( 100 ); + +#if _WINDOWS + MSG msg; + while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) + { + GetMessage( &msg, NULL, 0, 0 ); + TranslateMessage( &msg ); + DispatchMessage( &msg ); + }; +#endif + return mNavigateInProgress; + }; + + ~sslTest() + { + LLQtWebKit::getInstance()->remObserver( mBrowserWindowHandle, this ); + LLQtWebKit::getInstance()->reset(); + std::cout << "ssltest> === end ===" << std::endl; + }; + + void onNavigateBegin( const EventType& eventIn ) + { + mNavigateInProgress = true; + std::cout << "ssltest> Event: begin navigation to " << eventIn.getEventUri() << std::endl; + }; + + void onNavigateComplete( const EventType& eventIn ) + { + std::cout << "ssltest> Event: end navigation to " << eventIn.getEventUri() << std::endl; + mNavigateInProgress = false; + }; + + void onUpdateProgress( const EventType& eventIn ) + { + std::cout << "ssltest> Event: progress value updated to " << eventIn.getIntValue() << std::endl; + }; + + void onStatusTextChange( const EventType& eventIn ) + { + std::cout << "ssltest> Event: status updated to " << eventIn.getStringValue() << std::endl; + }; + + void onTitleChange( const EventType& eventIn ) + { + std::cout << "ssltest> Event: title changed to " << eventIn.getStringValue() << std::endl; + }; + + void onLocationChange( const EventType& eventIn ) + { + std::cout << "ssltest> Event: location changed to " << eventIn.getStringValue() << std::endl; + }; + + bool onCertError(const std::string &in_url, const std::string &in_msg) + { + std::cout << "ssltest> Cert error triggered\n" << in_url << "\n" << in_msg << std::endl; + return true; + } + + private: + int mBrowserWindowWidth; + int mBrowserWindowHeight; + int mBrowserWindowHandle; + bool mNavigateInProgress; +}; + +int main( int argc, char* argv[] ) +{ + bool ingore_ssl_errors = false; + bool ignore_ca_file = false; + + for( int i = 1; i < argc; ++i ) + { + if ( std::string( argv[ i ] ) == "--help" ) + { + std::cout << std::endl << "ssltest [--ignoresslerrors] [--ignorecafile]" << std::endl; + std::cout << "Looks for cert file CA.pem in the current working directory"; + + exit( 0 ); + }; + + if ( std::string( argv[ i ] ) == "--ignoresslerrors" ) + ingore_ssl_errors = true; + + if ( std::string( argv[ i ] ) == "--ignorecafile" ) + ignore_ca_file = true; + }; + + std::string url ( "https://my.secondlife.com/callum.linden" ); + for( int i = 1; i < argc; ++i ) + { + if ( std::string( argv[ i ] ).substr( 0, 2 ) != "--" ) + { + url = std::string( argv[ i ] ); + break; + }; + }; + + std::cout << std::endl << " --------- sslTest application starting --------- " << std::endl; + std::cout << "ssltest> URL specified is " << url << std::endl; + + sslTest* app = new sslTest( url, ignore_ca_file, ingore_ssl_errors ); + + bool result = app->idle(); + while( result ) + { + result = app->idle(); + }; + + delete app; + + std::cout << " --------- sslTest application ending --------- " << std::endl; + + return 0; +} diff --git a/indra/llqtwebkit/tests/ssltest/ssltest.pro b/indra/llqtwebkit/tests/ssltest/ssltest.pro new file mode 100644 index 000000000..981e35211 --- /dev/null +++ b/indra/llqtwebkit/tests/ssltest/ssltest.pro @@ -0,0 +1,28 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +INCLUDEPATH += ../../ +CONFIG -= app_bundle +CONFIG += console + +QT += webkit network + +mac { + DEFINES += LL_OSX + LIBS += $$PWD/libllqtwebkit.dylib +} + +win32 { + DEFINES += _WINDOWS + INCLUDEPATH += ../ + DESTDIR=../build + LIBS += user32.lib + release { + LIBS += $$PWD/../../Release/llqtwebkit.lib + } +} + +include(../../static.pri) + +SOURCES += ssltest.cpp diff --git a/indra/llqtwebkit/tests/testgl/testgl.cpp b/indra/llqtwebkit/tests/testgl/testgl.cpp new file mode 100644 index 000000000..6dfb11a72 --- /dev/null +++ b/indra/llqtwebkit/tests/testgl/testgl.cpp @@ -0,0 +1,1002 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef _WINDOWS +extern "C" { +#include +} +#endif + +#ifdef _WINDOWS +#include +#include // file choser dialog +#include // for local file access +#endif + +#include +#include +#include +#include +#include + +#ifdef LL_OSX +// I'm not sure why STATIC_QT is getting defined, but the Q_IMPORT_PLUGIN thing doesn't seem to be necessary on the mac. +#undef STATIC_QT +#endif + +#ifdef STATIC_QT +#include +Q_IMPORT_PLUGIN(qgif) +#endif + +#ifdef LL_OSX +#include +#include +#else +#define FREEGLUT_STATIC +#include "GL/glut.h" +#endif +#include "llqtwebkit.h" + +#ifdef _WINDOWS + #define PATH_SEPARATOR "\\" +#else + #define PATH_SEPARATOR "/" +#endif + + +//////////////////////////////////////////////////////////////////////////////// +// +std::string chooseFileName() +{ +#ifdef _WINDOWS + OPENFILENAMEA ofn ; + static char szFile[_MAX_PATH] ; + + ZeroMemory( &ofn , sizeof( ofn) ); + ofn.lStructSize = sizeof ( ofn ); + ofn.hwndOwner = NULL ; + ofn.lpstrFile = szFile ; + ofn.lpstrFile[0] = '\0'; + ofn.nMaxFile = sizeof( szFile ); + ofn.lpstrFilter = "All\0*.*\0Images\0*.jpg;*.png\0"; + ofn.nFilterIndex =1; + ofn.lpstrFileTitle = NULL ; + ofn.nMaxFileTitle = 0 ; + ofn.lpstrInitialDir=NULL ; + ofn.Flags = OFN_PATHMUSTEXIST|OFN_FILEMUSTEXIST ; + + GetOpenFileNameA( &ofn ); + + return ofn.lpstrFile; +#else + return ""; +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +// Implementation of the test app - implemented as a class and derrives from +// the observer so we can catch events emitted by LLQtWebKit +// +class testGL : + public LLEmbeddedBrowserWindowObserver +{ + public: + testGL() : + mAppWindowWidth( 800 ), // dimensions of the app window - can be anything + mAppWindowHeight( 900 ), + mBrowserWindowWidth( mAppWindowWidth ), // dimensions of the embedded browser - can be anything + mBrowserWindowHeight( mAppWindowHeight ), // but looks best when it's the same as the app window + mAppTextureWidth( -1 ), // dimensions of the texture that the browser is rendered into + mAppTextureHeight( -1 ), // calculated at initialization + mAppTexture( 0 ), + mBrowserWindowId( 0 ), + mAppWindowName( "testGL" ), + mCwd(), + mHomeUrl(), + mNeedsUpdate( true ) // flag to indicate if browser texture needs an update + { +#ifdef _WINDOWS // to remove warning on Windows + mCwd = _getcwd(NULL, 1024); +#else + mCwd = getcwd(NULL, 1024); +#endif + mHomeUrl = "http://callum-linden.s3.amazonaws.com/browsertest.html"; + std::cout << "LLQtWebKit version: " << LLQtWebKit::getInstance()->getVersion() << std::endl; + + std::cout << "Current working directory is " << mCwd << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void init( const std::string argv0, const std::string argv1 ) + { + // OpenGL initialization + glClearColor( 0.0f, 0.0f, 0.0f, 0.5f); + glEnable( GL_COLOR_MATERIAL ); + glColorMaterial( GL_FRONT, GL_AMBIENT_AND_DIFFUSE ); + glEnable( GL_TEXTURE_2D ); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); + glEnable( GL_CULL_FACE ); + + // calculate texture size required (next power of two above browser window size + for ( mAppTextureWidth = 1; mAppTextureWidth < mBrowserWindowWidth; mAppTextureWidth <<= 1 ) + { + }; + + for ( mAppTextureHeight = 1; mAppTextureHeight < mBrowserWindowHeight; mAppTextureHeight <<= 1 ) + { + }; + + // create the texture used to display the browser data + glGenTextures( 1, &mAppTexture ); + glBindTexture( GL_TEXTURE_2D, mAppTexture ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + glTexImage2D( GL_TEXTURE_2D, 0, + GL_RGB, + mAppTextureWidth, mAppTextureHeight, + 0, GL_RGB, GL_UNSIGNED_BYTE, 0 ); + + // create a single browser window and set things up. + mProfileDir = mCwd + PATH_SEPARATOR + "testGL_profile"; + std::cout << "Profiles dir location is " << mProfileDir << std::endl; + + mCookiePath = mProfileDir + PATH_SEPARATOR + "cookies.txt"; + std::cout << "Cookies.txt file location is " << mCookiePath << std::endl; + + LLQtWebKit::getInstance()->init( mApplicationDir, mApplicationDir, mProfileDir, getNativeWindowHandle() ); + + LLQtWebKit::getInstance()->enableQtMessageHandler( false ); + + // set host language test (in reality, string will be language code passed into client) + // IMPORTANT: must be called before createBrowserWindow(...) + LLQtWebKit::getInstance()->setHostLanguage( "EN-AB-CD-EF" ); + + // set up features + LLQtWebKit::getInstance()->enableJavaScript( true ); + LLQtWebKit::getInstance()->enableCookies( true ); + LLQtWebKit::getInstance()->enablePlugins( true ); + + // make a browser window + mBrowserWindowId = LLQtWebKit::getInstance()->createBrowserWindow( mBrowserWindowWidth, mBrowserWindowHeight ); + + // tell LLQtWebKit about the size of the browser window + LLQtWebKit::getInstance()->setSize( mBrowserWindowId, mBrowserWindowWidth, mBrowserWindowHeight ); + + // observer events that LLQtWebKit emits + LLQtWebKit::getInstance()->addObserver( mBrowserWindowId, this ); + + // append details to agent string + LLQtWebKit::getInstance()->setBrowserAgentId( mAppWindowName ); + + // don't flip bitmap + LLQtWebKit::getInstance()->flipWindow( mBrowserWindowId, false ); + + // only "trust" pages whose host match this regex + LLQtWebKit::getInstance()->setWhiteListRegex( mBrowserWindowId, "^([^.]+\\.)*amazonaws\\.com$" ); + + LLQtWebKit::getInstance()->enableLoadingOverlay( mBrowserWindowId, true ); + + // Attempt to read cookies from the cookie file and send them to llqtwebkit. + { + std::ifstream cookie_file(mCookiePath.c_str(), std::ios_base::in); + std::string cookies; + + while(cookie_file.good() && !cookie_file.eof()) + { + std::string tmp; + std::getline(cookie_file, tmp); + cookies += tmp; + cookies += "\n"; + } + + if(!cookies.empty()) + { + LLQtWebKit::getInstance()->setCookies(cookies); + } + } + + #if 0 + const std::vector before=LLQtWebKit::getInstance()->getInstalledCertsList(); + std::cout << "Certs before CA.pem load: " << before.size() << " items" << std::endl; + for(int i=0;isetCAFile( ca_pem_file_loc.c_str() ); + std::cout << "Expected CA.pem file location is " << ca_pem_file_loc << std::endl; + + #if 0 + const std::vector after=LLQtWebKit::getInstance()->getInstalledCertsList(); + std::cout << "Certs after CA.pem load: " << after.size() << " items" << std::endl; + for(int i=0;isetSLObjectEnabled( true ); // true means the feature is turned on + LLQtWebKit::getInstance()->setAgentLanguage( "tst-en" ); // viewer language selected by agent + LLQtWebKit::getInstance()->setAgentRegion( "TestGL region" ); // name of region where agent is located + LLQtWebKit::getInstance()->setAgentLocation( 9.8, 7.6, 5.4 ); // agent's x,y,z location within a region + LLQtWebKit::getInstance()->setAgentGlobalLocation( 1234.5, 6789.0, 3456.7 ); // agent's x,y,z location within a region + LLQtWebKit::getInstance()->setAgentOrientation( 175.69 ); // direction (0..359) agent is facing + LLQtWebKit::getInstance()->setAgentMaturity( "Very immature" ); // selected maturity level of agent + + // go to the "home page" or URL passed in via command line + if ( ! argv1.empty() ) + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, argv1 ); + else + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, mHomeUrl ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void reset( void ) + { + // Get cookies from this instance + std::string cookies = LLQtWebKit::getInstance()->getAllCookies(); + + // Dump cookies to stdout +// std::cout << "Cookies:" << std::endl; +// std::cout << cookies; + + // and save them to cookies.txt in the profile directory + { + std::ofstream cookie_file(mCookiePath.c_str(), std::ios_base::out|std::ios_base::trunc); + + if(cookie_file.good()) + { + cookie_file << cookies; + } + + cookie_file.close(); + } + + // unhook observer + LLQtWebKit::getInstance()->remObserver( mBrowserWindowId, this ); + + // clean up + LLQtWebKit::getInstance()->reset(); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void reshape( int widthIn, int heightIn ) + { + if ( heightIn == 0 ) + heightIn = 1; + + LLQtWebKit::getInstance()->setSize(mBrowserWindowId, widthIn, heightIn ); + mNeedsUpdate = true; + + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + + glViewport( 0, 0, widthIn, heightIn ); + glOrtho( 0.0f, widthIn, heightIn, 0.0f, -1.0f, 1.0f ); + + // we use these elsewhere so save + mAppWindowWidth = widthIn; + mAppWindowHeight = heightIn; + mBrowserWindowWidth = widthIn; + mBrowserWindowHeight = heightIn; + + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + + mNeedsUpdate = true; + idle(); + + glutPostRedisplay(); + }; + + void updateSLvariables() + { + if ( rand() % 2 ) + LLQtWebKit::getInstance()->setAgentRegion( "Region Wibble" ); + else + LLQtWebKit::getInstance()->setAgentRegion( "Region Flasm" ); + LLQtWebKit::getInstance()->setAgentLocation( (rand()%25600)/100.0f, (rand()%25600)/100.0f, (rand()%25600)/100.0f ); + LLQtWebKit::getInstance()->setAgentGlobalLocation( (rand()%25600)/10.0f, (rand()%25600)/10.0f, (rand()%25600)/10.0f ); + LLQtWebKit::getInstance()->setAgentOrientation( (rand()%3600)/10.0f ); + LLQtWebKit::getInstance()->emitLocation(); + + if ( rand() % 2 ) + LLQtWebKit::getInstance()->setAgentLanguage( "One language" ); + else + LLQtWebKit::getInstance()->setAgentLanguage( "Another language" ); + LLQtWebKit::getInstance()->emitLanguage(); + + if ( rand() % 2 ) + LLQtWebKit::getInstance()->setAgentMaturity( "Adults only" ); + else + LLQtWebKit::getInstance()->setAgentMaturity( "Children only" ); + LLQtWebKit::getInstance()->emitMaturity(); + } + + //////////////////////////////////////////////////////////////////////////////// + // + void idle() + { + static time_t starttime = time( NULL ); + if ( time( NULL ) - starttime ) + { + updateSLvariables(); + time( &starttime ); + }; + + LLQtWebKit::getInstance()->pump(100); + + // onPageChanged event sets this + if ( mNeedsUpdate ) + // grab a page but don't reset 'needs update' flag until we've written it to the texture in display() + LLQtWebKit::getInstance()->grabBrowserWindow( mBrowserWindowId ); + + // lots of updates for smooth motion + glutPostRedisplay(); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void display() + { + // clear screen + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + + glLoadIdentity(); + + // use the browser texture + glBindTexture( GL_TEXTURE_2D, mAppTexture ); + + // valid window ? + if ( mBrowserWindowId ) + { + // needs to be updated? + if ( mNeedsUpdate ) + { + // grab the page + const unsigned char* pixels = LLQtWebKit::getInstance()->getBrowserWindowPixels( mBrowserWindowId ); + if ( pixels ) + { + // write them into the texture + glTexSubImage2D( GL_TEXTURE_2D, 0, + 0, 0, + // because sometimes the rowspan != width * bytes per pixel (mBrowserWindowWidth) + LLQtWebKit::getInstance()->getBrowserRowSpan( mBrowserWindowId ) / LLQtWebKit::getInstance()->getBrowserDepth( mBrowserWindowId ), + mBrowserWindowHeight, +#ifdef _WINDOWS + LLQtWebKit::getInstance()->getBrowserDepth(mBrowserWindowId ) == 3 ? GL_RGBA : GL_RGBA, +#elif defined(__APPLE__) + GL_RGBA, +#elif defined(LL_LINUX) + GL_RGBA, +#endif + GL_UNSIGNED_BYTE, + pixels ); + }; + + // flag as already updated + mNeedsUpdate = false; + }; + }; + + // scale the texture so that it fits the screen + GLfloat textureScaleX = ( GLfloat )mBrowserWindowWidth / ( GLfloat )mAppTextureWidth; + GLfloat textureScaleY = ( GLfloat )mBrowserWindowHeight / ( GLfloat )mAppTextureHeight; + + // draw the single quad full screen (orthographic) + glMatrixMode( GL_TEXTURE ); + glPushMatrix(); + glScalef( textureScaleX, textureScaleY, 1.0f ); + + glEnable( GL_TEXTURE_2D ); + glColor3f( 1.0f, 1.0f, 1.0f ); + glBegin( GL_QUADS ); + glTexCoord2f( 1.0f, 0.0f ); + glVertex2d( mAppWindowWidth, 0 ); + + glTexCoord2f( 0.0f, 0.0f ); + glVertex2d( 0, 0 ); + + glTexCoord2f( 0.0f, 1.0f ); + glVertex2d( 0, mAppWindowHeight ); + + glTexCoord2f( 1.0f, 1.0f ); + glVertex2d( mAppWindowWidth, mAppWindowHeight ); + glEnd(); + + glMatrixMode( GL_TEXTURE ); + glPopMatrix(); + + glutSwapBuffers(); + }; + + //////////////////////////////////////////////////////////////////////////////// + // convert a GLUT keyboard modifier to an LLQtWebKit one + // (only valid in mouse and keyboard callbacks + LLQtWebKit::EKeyboardModifier getLLQtWebKitKeyboardModifierCode() + { + int result = LLQtWebKit::KM_MODIFIER_NONE; + + int modifiers = glutGetModifiers(); + + if ( GLUT_ACTIVE_SHIFT & modifiers ) + { + result |= LLQtWebKit::KM_MODIFIER_SHIFT; + } + + if ( GLUT_ACTIVE_CTRL & modifiers ) + result |= LLQtWebKit::KM_MODIFIER_CONTROL; + + if ( GLUT_ACTIVE_ALT & modifiers ) + result |= LLQtWebKit::KM_MODIFIER_ALT; + + return (LLQtWebKit::EKeyboardModifier)result; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseButton( int button, int state, int xIn, int yIn ) + { + // texture is scaled to fit the screen so we scale mouse coords in the same way + xIn = ( xIn * mBrowserWindowWidth ) / mAppWindowWidth; + yIn = ( yIn * mBrowserWindowHeight ) / mAppWindowHeight; + + if ( button == GLUT_LEFT_BUTTON ) + { + if ( state == GLUT_DOWN ) + { + // send event to LLQtWebKit + LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, + LLQtWebKit::ME_MOUSE_DOWN, + LLQtWebKit::MB_MOUSE_BUTTON_LEFT, + xIn, yIn, + getLLQtWebKitKeyboardModifierCode() ); + } + else + if ( state == GLUT_UP ) + { + // send event to LLQtWebKit + LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, + LLQtWebKit::ME_MOUSE_UP, + LLQtWebKit::MB_MOUSE_BUTTON_LEFT, + xIn, yIn, + getLLQtWebKitKeyboardModifierCode() ); + + + // this seems better than sending focus on mouse down (still need to improve this) + LLQtWebKit::getInstance()->focusBrowser( mBrowserWindowId, true ); + }; + }; + + // force a GLUT update + glutPostRedisplay(); + } + + //////////////////////////////////////////////////////////////////////////////// + // + void mouseMove( int xIn , int yIn ) + { + // texture is scaled to fit the screen so we scale mouse coords in the same way + xIn = ( xIn * mBrowserWindowWidth ) / mAppWindowWidth; + yIn = ( yIn * mBrowserWindowHeight ) / mAppWindowHeight; + + // send event to LLQtWebKit + LLQtWebKit::getInstance()->mouseEvent( mBrowserWindowId, + LLQtWebKit::ME_MOUSE_MOVE, + LLQtWebKit::MB_MOUSE_BUTTON_LEFT, + xIn, yIn, + LLQtWebKit::KM_MODIFIER_NONE ); + + + // force a GLUT update + glutPostRedisplay(); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void keyboard( unsigned char keyIn, bool isDown) + { + // ESC key exits + if ( keyIn == 27 ) + { + reset(); + + exit( 0 ); + }; + + // Translate some keys + switch(keyIn) + { + case 127: + // Turn delete char into backspace + keyIn = LLQtWebKit::KEY_BACKSPACE; + break; + case '\r': + case '\n': + // Turn CR and NL into enter key + keyIn = LLQtWebKit::KEY_RETURN; + break; + + case '\t': + keyIn = LLQtWebKit::KEY_TAB; + break; + + default: + break; + } + + // control-H goes home + if ( keyIn == 8 ) + { + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, mHomeUrl ); + } + // control-B navigates back + else if ( keyIn == 2 ) + { + LLQtWebKit::getInstance()->userAction(mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_BACK); + } + // control-F navigates forward + else if ( keyIn == 6 ) + { + LLQtWebKit::getInstance()->userAction(mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_FORWARD); + } + // control-R reloads + else if ( keyIn == 18 ) + { + LLQtWebKit::getInstance()->userAction(mBrowserWindowId, LLQtWebKit::UA_NAVIGATE_RELOAD ); + } + // control-I toggles inspector + else if ( keyIn == 23 ) + { + LLQtWebKit::getInstance()->showWebInspector( true ); + } + else if ( keyIn == '1' ) + { + if ( getLLQtWebKitKeyboardModifierCode() == LLQtWebKit::KM_MODIFIER_CONTROL ) + { + LLQtWebKit::getInstance()->setPageZoomFactor( 1.0 ); + } + } + else if ( keyIn == '2' ) + { + if ( getLLQtWebKitKeyboardModifierCode() == LLQtWebKit::KM_MODIFIER_CONTROL ) + { + LLQtWebKit::getInstance()->setPageZoomFactor( 2.0 ); + } + } + + char text[2]; + if(keyIn < 0x80) + { + text[0] = (char)keyIn; + } + else + { + text[0] = 0; + } + + text[1] = 0; + + std::cerr << "key " << (isDown?"down ":"up ") << (int)keyIn << ", modifiers = " << (int)getLLQtWebKitKeyboardModifierCode() << std::endl; + + // send event to LLQtWebKit + LLQtWebKit::getInstance()->keyboardEvent(mBrowserWindowId, isDown?LLQtWebKit::KE_KEY_DOWN:LLQtWebKit::KE_KEY_UP, keyIn, text, getLLQtWebKitKeyboardModifierCode() ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void keyboardSpecial( int specialIn, bool isDown) + { + uint32_t key = LLQtWebKit::KEY_NONE; + + switch(specialIn) + { + case GLUT_KEY_F1: key = LLQtWebKit::KEY_F1; break; + case GLUT_KEY_F2: key = LLQtWebKit::KEY_F2; break; + case GLUT_KEY_F3: key = LLQtWebKit::KEY_F3; break; + case GLUT_KEY_F4: key = LLQtWebKit::KEY_F4; break; + case GLUT_KEY_F5: key = LLQtWebKit::KEY_F5; break; + case GLUT_KEY_F6: key = LLQtWebKit::KEY_F6; break; + case GLUT_KEY_F7: key = LLQtWebKit::KEY_F7; break; + case GLUT_KEY_F8: key = LLQtWebKit::KEY_F8; break; + case GLUT_KEY_F9: key = LLQtWebKit::KEY_F9; break; + case GLUT_KEY_F10: key = LLQtWebKit::KEY_F10; break; + case GLUT_KEY_F11: key = LLQtWebKit::KEY_F11; break; + case GLUT_KEY_F12: key = LLQtWebKit::KEY_F12; break; + case GLUT_KEY_LEFT: key = LLQtWebKit::KEY_LEFT; break; + case GLUT_KEY_UP: key = LLQtWebKit::KEY_UP; break; + case GLUT_KEY_RIGHT: key = LLQtWebKit::KEY_RIGHT; break; + case GLUT_KEY_DOWN: key = LLQtWebKit::KEY_DOWN; break; + case GLUT_KEY_PAGE_UP: key = LLQtWebKit::KEY_PAGE_UP; break; + case GLUT_KEY_PAGE_DOWN: key = LLQtWebKit::KEY_PAGE_DOWN;break; + case GLUT_KEY_HOME: key = LLQtWebKit::KEY_HOME; break; + case GLUT_KEY_END: key = LLQtWebKit::KEY_END; break; + case GLUT_KEY_INSERT: key = LLQtWebKit::KEY_INSERT; break; + + default: + break; + } + + if(key != LLQtWebKit::KEY_NONE) + { + keyboard(key, isDown); + } + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onPageChanged( const EventType& /*eventIn*/ ) + { + // flag that an update is required - page grab happens in idle() so we don't stall + mNeedsUpdate = true; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onNavigateBegin( const EventType& eventIn ) + { + std::cout << "Event: begin navigation to " << eventIn.getEventUri() << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onNavigateComplete( const EventType& eventIn ) + { + std::cout << "Event: end navigation to " << eventIn.getEventUri() << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onUpdateProgress( const EventType& eventIn ) + { + std::cout << "Event: progress value updated to " << eventIn.getIntValue() << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onStatusTextChange( const EventType& eventIn ) + { + std::cout << "Event: status updated to " << eventIn.getStringValue() << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onTitleChange( const EventType& eventIn ) + { + std::cout << "Event: title changed to " << eventIn.getStringValue() << std::endl; + glutSetWindowTitle( eventIn.getStringValue().c_str() ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onLocationChange( const EventType& eventIn ) + { + std::cout << "Event: location changed to " << eventIn.getStringValue() << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onClickLinkHref( const EventType& eventIn ) + { + std::string uuid = eventIn.getStringValue2(); + + std::cout << "Event: clicked on link:" << std::endl; + std::cout << " URL:" << eventIn.getEventUri() << std::endl; + std::cout << " target:" << eventIn.getStringValue() << std::endl; + std::cout << " UUID:" << uuid << std::endl; + std::cout << std::endl; + + // Since we never actually open the window, send a "proxy window closed" back to webkit to keep it from leaking. + LLQtWebKit::getInstance()->proxyWindowClosed(mBrowserWindowId, uuid); + }; + + // virtual + void onClickLinkNoFollow(const EventType& eventIn) + { + std::cout << "Clink link no-follow --" << std::endl; + std::cout << " URL:" << eventIn.getEventUri() << std::endl; + std::cout << " type:" << eventIn.getNavigationType() << std::endl; + std::cout << " trusted:" << eventIn.getTrustedHost() << std::endl; + } + + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onCookieChanged( const EventType& eventIn ) + { + int dead = eventIn.getIntValue(); + std::cout << (dead?"deleting cookie: ":"setting cookie: ") << eventIn.getStringValue() << std::endl; + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + std::string onRequestFilePicker( const EventType& ) + { + std::string fn = chooseFileName(); + return fn; + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + bool onAuthRequest(const std::string &in_url, const std::string &in_realm, std::string &out_username, std::string &out_password) + { + std::cout << "Auth request, url = " << in_url << ", realm = " << in_realm << std::endl; + out_username = ""; // replace these temporarily with site username/password as required. + out_password = ""; + return false; + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + bool onCertError(const std::string &in_url, const std::string &in_msg) + { + std::cout << "Cert error, url = " << in_url << ", message = " << in_msg << std::endl; + return false; // cancel (return true to ignore errors and continue) + } + + virtual void onQtDebugMessage( const std::string& msg, const std::string& msg_type) + { + std::cout << "QtDebugMsg [" << msg_type << "]> " << msg << std::endl; + } + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onLinkHovered( const EventType& eventIn ) + { + std::cout + << "Link hovered, link = " << eventIn.getEventUri() + << ", title = " << eventIn.getStringValue() + << ", text = " << eventIn.getStringValue2() + << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onWindowCloseRequested( const EventType& ) + { + std::cout << "Event: window close requested" << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onNavigateErrorPage( const EventType& event ) + { + std::cout << "Error page hit with code of " << event.getIntValue() << " - navigating to another URL" << std::endl; + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowId, "http://bestbuy.com" ); + }; + + //////////////////////////////////////////////////////////////////////////////// + // virtual + void onWindowGeometryChangeRequested( const EventType& eventIn) + { + int x, y, width, height; + eventIn.getRectValue(x, y, width, height); + + std::cout << "Event: window geometry change requested" << std::endl; + std::cout << " uuid: " << eventIn.getStringValue() << std::endl; + std::cout << " location: (" << x << ", " << y << ")" << std::endl; + std::cout << " size: (" << width << ", " << height << ")" << std::endl; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + int getAppWindowWidth() + { + return mAppWindowWidth; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + int getAppWindowHeight() + { + return mAppWindowHeight; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + std::string getAppWindowName() + { + return mAppWindowName; + }; + + //////////////////////////////////////////////////////////////////////////////// + // + void* getNativeWindowHandle() + { + // My implementation of the embedded browser needs a native window handle + // Can't get this via GLUT so had to use this hack + #ifdef _WINDOWS + return FindWindow( NULL, (LPCWSTR)mAppWindowName.c_str() ); + #else + #ifdef LL_OSX + // not needed on osx + return 0; + #else + //#error "You will need an implementation of this method" + return 0; + #endif + #endif + }; + + private: + int mAppWindowWidth; + int mAppWindowHeight; + int mBrowserWindowWidth; + int mBrowserWindowHeight; + int mAppTextureWidth; + int mAppTextureHeight; + GLuint mAppTexture; + int mBrowserWindowId; + std::string mAppWindowName; + std::string mHomeUrl; + std::string mCwd; + bool mNeedsUpdate; + std::string mApplicationDir; + std::string mProfileDir; + std::string mCookiePath; +}; + +testGL* theApp; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutReshape( int widthIn, int heightIn ) +{ + if ( theApp ) + theApp->reshape( widthIn, heightIn ); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutDisplay() +{ + if ( theApp ) + theApp->display(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutIdle() +{ + if ( theApp ) + theApp->idle(); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutKeyboard( unsigned char keyIn, int /*xIn*/, int /*yIn*/ ) +{ + if ( theApp ) + { + theApp->keyboard( keyIn, true ); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutKeyboardUp( unsigned char keyIn, int /*xIn*/, int /*yIn*/ ) +{ + if ( theApp ) + { + theApp->keyboard( keyIn, false ); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutSpecial( int specialIn, int /*xIn*/, int /*yIn*/ ) +{ + if ( theApp ) + { + theApp->keyboardSpecial( specialIn, true ); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutSpecialUp( int specialIn, int /*xIn*/, int /*yIn*/ ) +{ + if ( theApp ) + { + theApp->keyboardSpecial( specialIn, false ); + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseMove( int xIn , int yIn ) +{ + if ( theApp ) + theApp->mouseMove( xIn, yIn ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +void glutMouseButton( int buttonIn, int stateIn, int xIn, int yIn ) +{ + if ( theApp ) + theApp->mouseButton( buttonIn, stateIn, xIn, yIn ); +} + +//////////////////////////////////////////////////////////////////////////////// +// +int main( int argc, char* argv[] ) +{ + glutInit( &argc, argv ); + glutInitDisplayMode( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB ); + + // implementation in a class so we can observer events + // means we need this painful GLUT <--> class shim... + theApp = new testGL; + + if ( theApp ) + { + glutInitWindowPosition( 80, 0 ); + glutInitWindowSize( theApp->getAppWindowWidth(), theApp->getAppWindowHeight() ); + + glutCreateWindow( theApp->getAppWindowName().c_str() ); + + std::string url = ""; + if ( 2 == argc ) + url = std::string( argv[ 1 ] ); + + theApp->init( std::string( argv[ 0 ] ), url ); + + glutKeyboardFunc( glutKeyboard ); + glutKeyboardUpFunc( glutKeyboardUp ); + glutSpecialFunc( glutSpecial ); + glutSpecialUpFunc( glutSpecialUp ); + + glutMouseFunc( glutMouseButton ); + glutPassiveMotionFunc( glutMouseMove ); + glutMotionFunc( glutMouseMove ); + + glutDisplayFunc( glutDisplay ); + glutReshapeFunc( glutReshape ); + + glutIdleFunc( glutIdle ); + + glutMainLoop(); + + std::cout << "glutMainLoop returned" << std::endl; + + delete theApp; + }; + + return 0; +} diff --git a/indra/llqtwebkit/tests/testgl/testgl.pro b/indra/llqtwebkit/tests/testgl/testgl.pro new file mode 100644 index 000000000..42692d68d --- /dev/null +++ b/indra/llqtwebkit/tests/testgl/testgl.pro @@ -0,0 +1,38 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +INCLUDEPATH += ../../ +CONFIG -= app_bundle + +QT += webkit opengl network + +!mac { +unix { + DEFINES += LL_LINUX + LIBS += -lglui -lglut + LIBS += $$PWD/../../libllqtwebkit.a +} +} + +mac { + DEFINES += LL_OSX + LIBS += -framework GLUT -framework OpenGL + LIBS += $$PWD/libllqtwebkit.dylib +} + +win32 { + DEFINES += _WINDOWS + INCLUDEPATH += ../ + INCLUDEPATH += $$PWD/../../stage/packages/include + DESTDIR=../build + release { + LIBS += $$PWD/../../Release/llqtwebkit.lib + LIBS += $$PWD/../build/freeglut_static.lib + LIBS += comdlg32.lib + } +} + +include(../../static.pri) + +SOURCES += testgl.cpp diff --git a/indra/llqtwebkit/tests/textmode/textmode.cpp b/indra/llqtwebkit/tests/textmode/textmode.cpp new file mode 100644 index 000000000..019fba902 --- /dev/null +++ b/indra/llqtwebkit/tests/textmode/textmode.cpp @@ -0,0 +1,292 @@ +/* Copyright (c) 2006-2010, Linden Research, Inc. + * + * LLQtWebKit Source Code + * The source code in this file ("Source Code") is provided by Linden Lab + * to you under the terms of the GNU General Public License, version 2.0 + * ("GPL"), unless you have obtained a separate licensing agreement + * ("Other License"), formally executed by you and Linden Lab. Terms of + * the GPL can be found in GPL-license.txt in this distribution, or online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/gplv2 + * + * There are special exceptions to the terms and conditions of the GPL as + * it is applied to this Source Code. View the full text of the exception + * in the file FLOSS-exception.txt in this software distribution, or + * online at + * http://secondlifegrid.net/technology-programs/license-virtual-world/viewerlicensing/flossexception + * + * By copying, modifying or distributing this software, you acknowledge + * that you have read and understood your obligations described above, + * and agree to abide by those obligations. + * + * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO + * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, + * COMPLETENESS OR PERFORMANCE. + */ + +#ifndef _WINDOWS +extern "C" { +#include +} +#endif + +#ifdef _WINDOWS +#include +#include +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef LL_OSX +// I'm not sure why STATIC_QT is getting defined, but the Q_IMPORT_PLUGIN thing doesn't seem to be necessary on the mac. +#undef STATIC_QT +#endif + +#ifdef STATIC_QT +#include +Q_IMPORT_PLUGIN(qgif) +#endif + +#include "llqtwebkit.h" + +class textMode : + public LLEmbeddedBrowserWindowObserver +{ + public: + textMode( std::string url, bool ignore_ca_file, bool ignore_ssl_errors ) : + mBrowserWindowWidth( 512 ), + mBrowserWindowHeight( 512 ), + mBrowserWindowHandle( 0 ), + mNavigateInProgress( true ), + mLogLine( "" ) + { + +#ifdef _WINDOWS + std::string cwd = std::string( _getcwd( NULL, 1024) ); + std::string profile_dir = cwd + "\\" + "textmode_profile"; + void* native_window_handle = (void*)GetDesktopWindow(); + std::string ca_file_loc = cwd + "\\" + "CA.pem"; +#else + std::string cwd = std::string( getcwd( NULL, 1024) ); + std::string profile_dir = cwd + "/" + "textmode_profile"; + void* native_window_handle = 0; + std::string ca_file_loc = cwd + "/" + "CA.pem"; +#endif + mLogLine << "Current working dir is " << cwd << std::endl; + mLogLine << "Profiles dir is " << profile_dir; + writeLine( mLogLine.str() ); + + LLQtWebKit::getInstance()->init( cwd, cwd, profile_dir, native_window_handle ); + + LLQtWebKit::getInstance()->enableQtMessageHandler( true ); + + LLQtWebKit::getInstance()->enableJavaScript( true ); + LLQtWebKit::getInstance()->enablePlugins( true ); + + mBrowserWindowHandle = LLQtWebKit::getInstance()->createBrowserWindow( mBrowserWindowWidth, mBrowserWindowHeight ); + LLQtWebKit::getInstance()->setSize( mBrowserWindowHandle, mBrowserWindowWidth, mBrowserWindowHeight ); + + LLQtWebKit::getInstance()->addObserver( mBrowserWindowHandle, this ); + + if ( ! ignore_ca_file ) + { + mLogLine.str(""); + mLogLine << "Expected certificate authority file location is " << ca_file_loc; + writeLine( mLogLine.str() ); + LLQtWebKit::getInstance()->setCAFile( ca_file_loc.c_str() ); + } + else + { + mLogLine.str(""); + mLogLine << "Not loading certificate authority file"; + writeLine( mLogLine.str() ); + }; + + if ( ignore_ssl_errors ) + { + LLQtWebKit::getInstance()->setIgnoreSSLCertErrors( true ); + mLogLine.str(""); + mLogLine << "Ignoring SSL errors"; + writeLine( mLogLine.str() ); + } + else + { + mLogLine.str(""); + mLogLine << "Not ignoring SSL errors"; + writeLine( mLogLine.str() ); + }; + + mLogLine.str(""); + mLogLine << "Navigating to " << url; + writeLine( mLogLine.str() ); + + LLQtWebKit::getInstance()->navigateTo( mBrowserWindowHandle, url ); + }; + + bool idle( void ) + { + LLQtWebKit::getInstance()->pump( 100 ); + +#if _WINDOWS + MSG msg; + while ( PeekMessage( &msg, NULL, 0, 0, PM_NOREMOVE ) ) + { + GetMessage( &msg, NULL, 0, 0 ); + TranslateMessage( &msg ); + DispatchMessage( &msg ); + }; +#endif + return mNavigateInProgress; + }; + + ~textMode() + { + LLQtWebKit::getInstance()->remObserver( mBrowserWindowHandle, this ); + LLQtWebKit::getInstance()->reset(); + }; + + void onNavigateBegin( const EventType& eventIn ) + { + mNavigateInProgress = true; + mLogLine.str(""); + mLogLine << "Event: Begin navigation to " << eventIn.getEventUri(); + writeLine( mLogLine.str() ); + }; + + void onNavigateComplete( const EventType& eventIn ) + { + mLogLine.str(""); + mLogLine << "Event: End navigation to " << eventIn.getEventUri(); + writeLine( mLogLine.str() ); + mNavigateInProgress = false; + }; + + void onUpdateProgress( const EventType& eventIn ) + { + mLogLine.str(""); + mLogLine << "Event: progress value updated to " << eventIn.getIntValue(); + writeLine( mLogLine.str() ); + }; + + void onStatusTextChange( const EventType& eventIn ) + { + mLogLine.str(""); + mLogLine << "Event: status updated to " << eventIn.getStringValue(); + writeLine( mLogLine.str() ); + }; + + void onTitleChange( const EventType& eventIn ) + { + mLogLine.str(""); + mLogLine << "Event: title change to " << eventIn.getStringValue(); + writeLine( mLogLine.str() ); + }; + + void onLocationChange( const EventType& eventIn ) + { + mLogLine.str(""); + mLogLine << "Event: location changed to " << eventIn.getStringValue(); + writeLine( mLogLine.str() ); + }; + + bool onCertError(const std::string &in_url, const std::string &in_msg) + { + mLogLine.str(""); + mLogLine << "Cert error triggered: " << std::endl << in_url << "\n" << in_msg; + writeLine( mLogLine.str() ); + return true; + } + + void onCookieChanged(const EventType& event) + { + std::string url = event.getEventUri(); + std::string cookie = event.getStringValue(); + int dead = event.getIntValue(); + mLogLine.str(""); + if ( ! dead ) + mLogLine << "Cookie added:" << cookie; + else + mLogLine << "Cookie deleted:" << cookie; + writeLine( mLogLine.str() ); + } + + virtual void onQtDebugMessage( const std::string& msg, const std::string& msg_type) + { + mLogLine.str(""); + mLogLine << "QtDebugMsg (" << msg_type << "): " << msg.substr(msg.length() - 1); + writeLine( mLogLine.str() ); + } + + void writeLine( std::string line ) + { + double elapsed_seconds = (double)clock() / (double)CLOCKS_PER_SEC; + + std::cout << "[" << std::setprecision(7) << std::setw(3) << std::setfill('0') << elapsed_seconds << "] "; + const int max_len = 140; + if ( line.length() > max_len ) + { + std::cout << line.substr(0, max_len); + std::cout << "...."; + } + else + { + std::cout << line; + } + std::cout << std::endl; + //std::cout << std::endl; + } + + private: + int mBrowserWindowWidth; + int mBrowserWindowHeight; + int mBrowserWindowHandle; + bool mNavigateInProgress; + std::ostringstream mLogLine; +}; + +int main( int argc, char* argv[] ) +{ + bool ingore_ssl_errors = false; + bool ignore_ca_file = false; + + for( int i = 1; i < argc; ++i ) + { + if ( std::string( argv[ i ] ) == "--help" ) + { + std::cout << std::endl << "textmode " << std::endl; + exit( 0 ); + }; + + if ( std::string( argv[ i ] ) == "--ignoresslerrors" ) + ingore_ssl_errors = true; + + if ( std::string( argv[ i ] ) == "--ignorecafile" ) + ignore_ca_file = true; + }; + + std::string url ( "https://my.secondlife.com/callum.linden" ); + for( int i = 1; i < argc; ++i ) + { + if ( std::string( argv[ i ] ).substr( 0, 2 ) != "--" ) + { + url = std::string( argv[ i ] ); + break; + }; + }; + + textMode* app = new textMode( url, ignore_ca_file, ingore_ssl_errors ); + + bool result = app->idle(); + while( result ) + { + result = app->idle(); + }; + + delete app; + + return 0; +} diff --git a/indra/llqtwebkit/tests/textmode/textmode.pro b/indra/llqtwebkit/tests/textmode/textmode.pro new file mode 100644 index 000000000..d41e1ea68 --- /dev/null +++ b/indra/llqtwebkit/tests/textmode/textmode.pro @@ -0,0 +1,28 @@ +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += . +INCLUDEPATH += ../../ +CONFIG -= app_bundle +CONFIG += console + +QT += webkit network + +mac { + DEFINES += LL_OSX + LIBS += $$PWD/libllqtwebkit.dylib +} + +win32 { + DEFINES += _WINDOWS + INCLUDEPATH += ../ + DESTDIR=../build + LIBS += user32.lib + release { + LIBS += $$PWD/../../Release/llqtwebkit.lib + } +} + +include(../../static.pri) + +SOURCES += textmode.cpp diff --git a/indra/llqtwebkit/win32/3p-qt-vars.bat b/indra/llqtwebkit/win32/3p-qt-vars.bat new file mode 100644 index 000000000..5ea118848 --- /dev/null +++ b/indra/llqtwebkit/win32/3p-qt-vars.bat @@ -0,0 +1,6 @@ +@echo off +echo Setting up a Qt environment using 3p-qt HG repository +set QTDIR=C:\Work\3p-llqtwebkit\stage +set PATH=C:\Work\3p-llqtwebkit\stage\bin;%PATH% +set QMAKESPEC=win32-msvc2010 +call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\Tools\vsvars32.bat" diff --git a/indra/llqtwebkit/win32/Qt Command Prompt (3p-qt).lnk b/indra/llqtwebkit/win32/Qt Command Prompt (3p-qt).lnk new file mode 100644 index 000000000..460a9e9cf Binary files /dev/null and b/indra/llqtwebkit/win32/Qt Command Prompt (3p-qt).lnk differ diff --git a/indra/plugins/webkit/CMakeLists.txt b/indra/plugins/webkit/CMakeLists.txt index dcd14e695..40a2e4053 100644 --- a/indra/plugins/webkit/CMakeLists.txt +++ b/indra/plugins/webkit/CMakeLists.txt @@ -7,6 +7,7 @@ include(LLCommon) include(LLImage) include(LLPlugin) include(LLMath) +include(LLQtWebkit) include(LLRender) include(LLWindow) include(UI) diff --git a/indra/plugins/webkit/media_plugin_webkit.cpp b/indra/plugins/webkit/media_plugin_webkit.cpp index 9809a9e5e..ebd183305 100644 --- a/indra/plugins/webkit/media_plugin_webkit.cpp +++ b/indra/plugins/webkit/media_plugin_webkit.cpp @@ -161,7 +161,7 @@ private: checkEditState(); - if(mInitState == INIT_STATE_NAVIGATE_COMPLETE) + if(mInitState >= INIT_STATE_NAVIGATE_COMPLETE) { if(!mInitialNavigateURL.empty()) { @@ -284,7 +284,7 @@ private: bool result = LLQtWebKit::getInstance()->init( application_dir, component_dir, mProfileDir, native_window_handle ); if ( result ) { - mInitState = INIT_STATE_INITIALIZED; + setInitState(INIT_STATE_INITIALIZED); // debug spam sent to viewer and displayed in the log as usual postDebugMessage( "browser initialized okay" );