From 96bf7b880976f225ca390aab1cb1492e8d79ea4c Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 14 May 2010 11:25:51 +0200 Subject: Updated WebKit to 5cf023650a8da206a8cf3130e9d4820b95e1bc7c Integrated changes: * Doc day API doc overhaul || || [Qt] GraphicsLayer: depth-test causes flicker in certain situations || || || [Qt] emit initialLayoutCompleted signal from FrameLoaderClientQt::dispatchDidFirstVisuallyNonEmptyLayout || || || [Qt] Detect debug mode consistently || || || [Qt] Update the Symbian version for the user agent || || || [Qt] Improve QtLauncher user agent dialog resize || || || Ignore invalid values for various CanvasRenderingContext2D properties || || || [Qt] tst_QWebPage::inputMethods failing on Maemo5 || || || [Qt] REGRESSION(r58497) tst_QGraphicsWebView::crashOnViewlessWebPages() is failing || --- src/3rdparty/webkit/.tag | 2 +- src/3rdparty/webkit/ChangeLog | 9 + src/3rdparty/webkit/JavaScriptCore/ChangeLog | 12 ++ .../webkit/JavaScriptCore/JavaScriptCore.pri | 2 +- .../webkit/JavaScriptCore/JavaScriptCore.pro | 2 +- .../webkit/JavaScriptCore/qt/api/QtScript.pro | 2 +- src/3rdparty/webkit/VERSION | 2 +- src/3rdparty/webkit/WebCore/ChangeLog | 43 +++++ src/3rdparty/webkit/WebCore/WebCore.pro | 2 +- .../html/canvas/CanvasRenderingContext2D.cpp | 10 +- .../platform/graphics/qt/GraphicsLayerQt.cpp | 8 - src/3rdparty/webkit/WebKit.pri | 2 +- src/3rdparty/webkit/WebKit/qt/Api/qwebdatabase.cpp | 10 +- src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp | 20 ++- src/3rdparty/webkit/WebKit/qt/Api/qwebframe.cpp | 10 +- .../webkit/WebKit/qt/Api/qwebhistoryinterface.cpp | 23 ++- .../webkit/WebKit/qt/Api/qwebinspector.cpp | 45 ++--- .../webkit/WebKit/qt/Api/qwebkitversion.cpp | 66 +++++++- src/3rdparty/webkit/WebKit/qt/Api/qwebpage.cpp | 184 +++++++++++++++++---- .../webkit/WebKit/qt/Api/qwebpluginfactory.cpp | 36 +++- .../webkit/WebKit/qt/Api/qwebsecurityorigin.cpp | 24 ++- src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.cpp | 128 +++++++------- src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.h | 2 +- src/3rdparty/webkit/WebKit/qt/ChangeLog | 99 +++++++++++ .../qt/WebCoreSupport/FrameLoaderClientQt.cpp | 6 +- src/3rdparty/webkit/WebKit/qt/docs/qtwebkit.qdoc | 109 ++++++------ .../qtwebkit_qwebinspector_snippet.cpp | 2 - .../qt/docs/webkitsnippets/webelement/main.cpp | 56 +++++++ .../qgraphicswebview/tst_qgraphicswebview.cpp | 8 +- .../WebKit/qt/tests/qwebpage/tst_qwebpage.cpp | 23 ++- 30 files changed, 718 insertions(+), 229 deletions(-) diff --git a/src/3rdparty/webkit/.tag b/src/3rdparty/webkit/.tag index 14af7e9..9d754a4 100644 --- a/src/3rdparty/webkit/.tag +++ b/src/3rdparty/webkit/.tag @@ -1 +1 @@ -5cf023650a8da206a8cf3130e9d4820b95e1bc7c +3d774b9df1f963452b1cfe34f9fafad0d399372a diff --git a/src/3rdparty/webkit/ChangeLog b/src/3rdparty/webkit/ChangeLog index 70eff7d..a0cf2d0 100644 --- a/src/3rdparty/webkit/ChangeLog +++ b/src/3rdparty/webkit/ChangeLog @@ -1,3 +1,12 @@ +2010-05-12 Laszlo Gombos + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Detect debug mode consistently + https://bugs.webkit.org/show_bug.cgi?id=38863 + + * WebKit.pri: + 2010-04-09 Simon Hausmann Unreviewed crash fix. diff --git a/src/3rdparty/webkit/JavaScriptCore/ChangeLog b/src/3rdparty/webkit/JavaScriptCore/ChangeLog index 1610036..a3e6586 100644 --- a/src/3rdparty/webkit/JavaScriptCore/ChangeLog +++ b/src/3rdparty/webkit/JavaScriptCore/ChangeLog @@ -1,3 +1,15 @@ +2010-05-12 Laszlo Gombos + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Detect debug mode consistently + https://bugs.webkit.org/show_bug.cgi?id=38863 + + * JavaScriptCore.pri: + * JavaScriptCore.pro: + * jsc.pro: + * qt/api/QtScript.pro: + 2010-05-10 Laszlo Gombos Reviewed by Darin Adler. diff --git a/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri b/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri index b7f6665..b3f74a9 100644 --- a/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri +++ b/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pri @@ -1,6 +1,6 @@ # JavaScriptCore - Qt4 build info VPATH += $$PWD -CONFIG(debug, debug|release) { +!CONFIG(release, debug|release) { # Output in JavaScriptCore/ JAVASCRIPTCORE_DESTDIR = debug # Use a config-specific target to prevent parallel builds file clashes on Mac diff --git a/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pro b/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pro index 8e086b3..22fcc91 100644 --- a/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pro +++ b/src/3rdparty/webkit/JavaScriptCore/JavaScriptCore.pro @@ -21,7 +21,7 @@ CONFIG(QTDIR_build) { # This line was extracted from qbase.pri instead of including the whole file win32|mac:!macx-xcode:CONFIG += debug_and_release } else { - CONFIG(debug, debug|release) { + !CONFIG(release, debug|release) { OBJECTS_DIR = obj/debug } else { # Release OBJECTS_DIR = obj/release diff --git a/src/3rdparty/webkit/JavaScriptCore/qt/api/QtScript.pro b/src/3rdparty/webkit/JavaScriptCore/qt/api/QtScript.pro index 88629c7..3c2691e 100644 --- a/src/3rdparty/webkit/JavaScriptCore/qt/api/QtScript.pro +++ b/src/3rdparty/webkit/JavaScriptCore/qt/api/QtScript.pro @@ -7,7 +7,7 @@ INCLUDEPATH += $$PWD CONFIG += building-libs isEmpty(JSC_GENERATED_SOURCES_DIR):JSC_GENERATED_SOURCES_DIR = ../../generated -CONFIG(debug, debug|release) { +!CONFIG(release, debug|release) { OBJECTS_DIR = obj/debug } else { # Release OBJECTS_DIR = obj/release diff --git a/src/3rdparty/webkit/VERSION b/src/3rdparty/webkit/VERSION index b8bac54..a440c03 100644 --- a/src/3rdparty/webkit/VERSION +++ b/src/3rdparty/webkit/VERSION @@ -4,4 +4,4 @@ This is a snapshot of the Qt port of WebKit from and has the sha1 checksum - 57d10d5c05e59bbf7de8189ff47dd18d1be996dc + 5cf023650a8da206a8cf3130e9d4820b95e1bc7c diff --git a/src/3rdparty/webkit/WebCore/ChangeLog b/src/3rdparty/webkit/WebCore/ChangeLog index 76b4eff..93d00e4 100644 --- a/src/3rdparty/webkit/WebCore/ChangeLog +++ b/src/3rdparty/webkit/WebCore/ChangeLog @@ -1,3 +1,46 @@ +2010-05-12 Laszlo Gombos + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] Detect debug mode consistently + https://bugs.webkit.org/show_bug.cgi?id=38863 + + No new tests as there is no new functionality. + + * WebCore.pro: + +2010-05-14 Andreas Kling + + Reviewed by Darin Adler. + + Ignore invalid values for various CanvasRenderingContext2D properties + (lineWidth, miterLimit, shadowOffsetX, shadowOffsetY and shadowBlur) + + https://bugs.webkit.org/show_bug.cgi?id=38841 + + Test: fast/canvas/canvas-invalid-values.html + + * html/canvas/CanvasRenderingContext2D.cpp: + (WebCore::CanvasRenderingContext2D::setLineWidth): + (WebCore::CanvasRenderingContext2D::setMiterLimit): + (WebCore::CanvasRenderingContext2D::setShadowOffsetX): + (WebCore::CanvasRenderingContext2D::setShadowOffsetY): + (WebCore::CanvasRenderingContext2D::setShadowBlur): + +2010-05-12 Noam Rosenthal + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] GraphicsLayer: depth-test causes flicker in certain situations + + This patch removes the simplistic 2D depth test as it leads to flickering side effects. + https://bugs.webkit.org/show_bug.cgi?id=38370 + + Tested by http://webkit.org/blog-files/3d-transforms/morphing-cubes.html + + * platform/graphics/qt/GraphicsLayerQt.cpp: + (WebCore::GraphicsLayerQtImpl::updateTransform): + 2010-04-29 James Robinson Reviewed by Simon Fraser. diff --git a/src/3rdparty/webkit/WebCore/WebCore.pro b/src/3rdparty/webkit/WebCore/WebCore.pro index 254d17b..689c5ff 100644 --- a/src/3rdparty/webkit/WebCore/WebCore.pro +++ b/src/3rdparty/webkit/WebCore/WebCore.pro @@ -59,7 +59,7 @@ CONFIG(standalone_package) { isEmpty(WC_GENERATED_SOURCES_DIR):WC_GENERATED_SOURCES_DIR = generated isEmpty(JSC_GENERATED_SOURCES_DIR):JSC_GENERATED_SOURCES_DIR = ../JavaScriptCore/generated - CONFIG(debug, debug|release) { + !CONFIG(release, debug|release) { OBJECTS_DIR = obj/debug } else { # Release OBJECTS_DIR = obj/release diff --git a/src/3rdparty/webkit/WebCore/html/canvas/CanvasRenderingContext2D.cpp b/src/3rdparty/webkit/WebCore/html/canvas/CanvasRenderingContext2D.cpp index 8add19c..823ab59 100644 --- a/src/3rdparty/webkit/WebCore/html/canvas/CanvasRenderingContext2D.cpp +++ b/src/3rdparty/webkit/WebCore/html/canvas/CanvasRenderingContext2D.cpp @@ -209,7 +209,7 @@ float CanvasRenderingContext2D::lineWidth() const void CanvasRenderingContext2D::setLineWidth(float width) { - if (!(width > 0)) + if (!(isfinite(width) && width > 0)) return; state().m_lineWidth = width; GraphicsContext* c = drawingContext(); @@ -259,7 +259,7 @@ float CanvasRenderingContext2D::miterLimit() const void CanvasRenderingContext2D::setMiterLimit(float limit) { - if (!(limit > 0)) + if (!(isfinite(limit) && limit > 0)) return; state().m_miterLimit = limit; GraphicsContext* c = drawingContext(); @@ -275,6 +275,8 @@ float CanvasRenderingContext2D::shadowOffsetX() const void CanvasRenderingContext2D::setShadowOffsetX(float x) { + if (!isfinite(x)) + return; state().m_shadowOffset.setWidth(x); applyShadow(); } @@ -286,6 +288,8 @@ float CanvasRenderingContext2D::shadowOffsetY() const void CanvasRenderingContext2D::setShadowOffsetY(float y) { + if (!isfinite(y)) + return; state().m_shadowOffset.setHeight(y); applyShadow(); } @@ -297,6 +301,8 @@ float CanvasRenderingContext2D::shadowBlur() const void CanvasRenderingContext2D::setShadowBlur(float blur) { + if (!(isfinite(blur) && blur >= 0)) + return; state().m_shadowBlur = blur; applyShadow(); } diff --git a/src/3rdparty/webkit/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp b/src/3rdparty/webkit/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp index 1c4c275..d9394e1 100644 --- a/src/3rdparty/webkit/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp +++ b/src/3rdparty/webkit/WebCore/platform/graphics/qt/GraphicsLayerQt.cpp @@ -357,14 +357,6 @@ void GraphicsLayerQtImpl::updateTransform() return; } - // Simplistic depth test - we stack the item behind its parent if its computed z is lower than the parent's computed z at the item's center point. - if (parent) { - const QPointF centerPointMappedToRoot = rootLayer()->mapFromItem(this, m_size.width() / 2, m_size.height() / 2); - setFlag(ItemStacksBehindParent, - m_transformRelativeToRootLayer.mapPoint(FloatPoint3D(centerPointMappedToRoot.x(), centerPointMappedToRoot.y(), 0)).z() < - parent->m_transformRelativeToRootLayer.mapPoint(FloatPoint3D(centerPointMappedToRoot.x(), centerPointMappedToRoot.y(), 0)).z()); - } - // The item is front-facing or backface-visibility is on. setVisible(true); diff --git a/src/3rdparty/webkit/WebKit.pri b/src/3rdparty/webkit/WebKit.pri index e737039..a3ccd9d 100644 --- a/src/3rdparty/webkit/WebKit.pri +++ b/src/3rdparty/webkit/WebKit.pri @@ -22,7 +22,7 @@ building-libs { QMAKE_FRAMEWORKPATH = $$OUTPUT_DIR/lib $$QMAKE_FRAMEWORKPATH } else { win32-*|wince* { - CONFIG(debug, debug|release):build_pass: QTWEBKITLIBNAME = $${QTWEBKITLIBNAME}d + !CONFIG(release, debug|release):build_pass: QTWEBKITLIBNAME = $${QTWEBKITLIBNAME}d QTWEBKITLIBNAME = $${QTWEBKITLIBNAME}$${QT_MAJOR_VERSION} win32-g++: LIBS += -l$$QTWEBKITLIBNAME else: LIBS += $${QTWEBKITLIBNAME}.lib diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebdatabase.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebdatabase.cpp index 4e8fd30..ba039c7 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebdatabase.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebdatabase.cpp @@ -39,8 +39,10 @@ using namespace WebCore; access on a local computer through JavaScript. QWebDatabase is the C++ interface to these databases. - To get access to all databases defined by a security origin, use QWebSecurityOrigin::databases(). - Each database has an internal name(), as well as a user-friendly name, provided by displayName(). + Databases are grouped together in security origins. To get access to all databases defined by + a security origin, use QWebSecurityOrigin::databases(). Each database has an internal name(), + as well as a user-friendly name, provided by displayName(). These names are specified when + creating the database in the JavaScript code. WebKit uses SQLite to create and access the local SQL databases. The location of the database file in the local file system is returned by fileName(). You can access the database directly @@ -49,7 +51,7 @@ using namespace WebCore; For each database the web site can define an expectedSize(). The current size of the database in bytes is returned by size(). - For more information refer to the \l{http://dev.w3.org/html5/webdatabase/}{HTML 5 Draft Standard}. + For more information refer to the \l{http://dev.w3.org/html5/webdatabase/}{HTML5 Web SQL Database Draft Standard}. \sa QWebSecurityOrigin */ @@ -80,7 +82,7 @@ QString QWebDatabase::name() const } /*! - Returns the name of the database as seen by the user. + Returns the name of the database in a format that is suitable for display to the user. */ QString QWebDatabase::displayName() const { diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp index 955206d..69146a2 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebelement.cpp @@ -84,13 +84,27 @@ public: \snippet webkitsnippets/webelement/main.cpp Traversing with QWebElement + Individual elements can be inspected or changed using methods such as attribute() + or setAttribute(). For examle, to capture the user's input in a text field for later + use (auto-completion), a browser could do something like this: + + \snippet webkitsnippets/webelement/main.cpp autocomplete1 + + When the same page is later revisited, the browser can fill in the text field automatically + by modifying the value attribute of the input element: + + \snippet webkitsnippets/webelement/main.cpp autocomplete2 + + Another use case is to emulate a click event on an element. The following + code snippet demonstrates how to call the JavaScript DOM method click() of + a submit button: + + \snippet webkitsnippets/webelement/main.cpp Calling a DOM element method + The underlying content of QWebElement is explicitly shared. Creating a copy of a QWebElement does not create a copy of the content. Instead, both instances point to the same element. - The element's attributes can be read using attribute() and modified with - setAttribute(). - The contents of child elements can be converted to plain text with toPlainText(); to XHTML using toInnerXml(). To include the element's tag in the output, use toOuterXml(). diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebframe.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebframe.cpp index 394ea17..44cc3b5 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebframe.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebframe.cpp @@ -552,8 +552,10 @@ void QWebFramePrivate::renderRelativeCoords(GraphicsContext* context, QWebFrame: can connect to the web page's \l{QWebPage::}{frameCreated()} signal to be notified when a new frame is created. - The hitTestContent() function can be used to programmatically examine the - contents of a frame. + There are multiple ways to programmatically examine the contents of a frame. + The hitTestContent() function can be used to find elements by coordinate. + For access to the underlying DOM tree, there is documentElement(), + findAllElements() and findFirstElement(). A QWebFrame can be printed onto a QPrinter using the print() function. This function is marked as a slot and can be conveniently connected to @@ -781,6 +783,10 @@ static inline QUrl ensureAbsoluteUrl(const QUrl &url) \property QWebFrame::url \brief the url of the frame currently viewed + Setting this property clears the view and loads the URL. + + By default, this property contains an empty, invalid URL. + \sa urlChanged() */ diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebhistoryinterface.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebhistoryinterface.cpp index 80567d1..61cf5af 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebhistoryinterface.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebhistoryinterface.cpp @@ -41,7 +41,7 @@ static void gCleanupInterface() /*! Sets a new default interface, \a defaultInterface, that will be used by all of WebKit - for managing history. + to keep track of visited links. If an interface without a parent has already been set, the old interface will be deleted. When the application exists QWebHistoryInterface will automatically delete the @@ -68,8 +68,9 @@ void QWebHistoryInterface::setDefaultInterface(QWebHistoryInterface* defaultInte } /*! - Returns the default interface that will be used by WebKit. If no - default interface has been set, QtWebkit will not track history. + Returns the default interface that will be used by WebKit. If no default interface has been set, + Webkit will not keep track of visited links and a null pointer will be returned. + \sa setDefaultInterface */ QWebHistoryInterface* QWebHistoryInterface::defaultInterface() { @@ -84,11 +85,15 @@ QWebHistoryInterface* QWebHistoryInterface::defaultInterface() \inmodule QtWebKit The QWebHistoryInterface is an interface that can be used to - implement link history. It contains two pure virtual methods that - are called by the WebKit engine. addHistoryEntry() is used to add - pages that have been visited to the interface, while - historyContains() is used to query whether this page has been - visited by the user. + keep track of visited links. It contains two pure virtual methods that + are called by the WebKit engine: addHistoryEntry() is used to add + urls that have been visited to the interface, while + historyContains() is used to query whether the given url has been + visited by the user. By default the QWebHistoryInterface is not set, so WebKit does not keep + track of visited links. + + \note The history tracked by QWebHistoryInterface is not specific to an instance of QWebPage + but applies to all pages. */ /*! @@ -100,7 +105,7 @@ QWebHistoryInterface::QWebHistoryInterface(QObject* parent) } /*! - Destructor. If this is currently the default interface it will be unset. + Destroys the interface. If this is currently the default interface it will be unset. */ QWebHistoryInterface::~QWebHistoryInterface() { diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebinspector.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebinspector.cpp index c0e5277..802ea98 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebinspector.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebinspector.cpp @@ -31,17 +31,23 @@ /*! \class QWebInspector \since 4.6 + \inmodule QtWebKit \brief The QWebInspector class allows the placement and control of a QWebPage's inspector. - The inspector allows you to see a page current hierarchy and loading - statistics. + The inspector can display a page's hierarchy, its loading statistics and + the current state of its individual elements. It is mostly used by web + developers. - The QWebPage to be inspected is determined with the setPage() method. + The QWebPage to be inspected must be specified using the setPage() method. A typical use of QWebInspector follows: \snippet webkitsnippets/qtwebkit_qwebinspector_snippet.cpp 0 + A QWebInspector can be made visible either programmatically using + setVisible(), or by the user through the attached QWebPage's context + menu. + \note A QWebInspector will display a blank widget if either: \list \o page() is null @@ -61,17 +67,16 @@ \section1 Inspector configuration persistence The inspector allows the user to configure some options through its - interface (e.g. the resource tracking "Always enable" option). - These settings are persisted automatically by QtWebKit using QSettings. - - However since the QSettings object is instantiated using the empty - constructor, QCoreApplication::setOrganizationName() and - QCoreApplication::setApplicationName() must be called within your - application to enable the persistence of these options. + user interface (e.g. the resource tracking "Always enable" option). + These settings will be persisted automatically by QtWebKit only if + your application previously called QCoreApplication::setOrganizationName() + and QCoreApplication::setApplicationName(). + See QSettings's default constructor documentation for an explanation + of why this is necessary. */ /*! - Constructs an empty QWebInspector with parent \a parent. + Constructs an unbound QWebInspector with \a parent as its parent. */ QWebInspector::QWebInspector(QWidget* parent) : QWidget(parent) @@ -89,16 +94,16 @@ QWebInspector::~QWebInspector() } /*! - Sets the QWebPage to be inspected. - - There can only be one QWebInspector associated with a QWebPage - and vices versa. + Bind this inspector to the QWebPage to be inspected. - Calling with \a page as null will break the current association, if any. - - If \a page is already associated to another QWebInspector, the association - will be replaced and the previous QWebInspector will have no page - associated. + \bold {Notes:} + \list + \o There can only be one QWebInspector associated with a QWebPage + and vice versa. + \o Calling this method with a null \a page will break the current association, if any. + \o If \a page is already associated to another QWebInspector, the association + will be replaced and the previous QWebInspector will become unbound + \endlist \sa page() */ diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebkitversion.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebkitversion.cpp index 062839f..181913b 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebkitversion.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebkitversion.cpp @@ -22,11 +22,21 @@ #include /*! - + \relates QWebPage + \since 4.6 Returns the version number of WebKit at run-time as a string (for - example, "531.3"). This is the version of WebKit the application - was compiled against. + example, "531.3"). + + This version is commonly used in WebKit based browsers as part + of the user agent string. Web servers and JavaScript might use + it to identify the presence of certain WebKit engine features + and behaviour. + The evolution of this version is bound to the releases of Apple's + Safari browser. For a version specific to the QtWebKit library, + see QTWEBKIT_VERSION + + \sa QWebPage::userAgentForUrl() */ QString qWebKitVersion() { @@ -34,11 +44,13 @@ QString qWebKitVersion() } /*! - + \relates QWebPage + \since 4.6 Returns the 'major' version number of WebKit at run-time as an integer (for example, 531). This is the version of WebKit the application was compiled against. + \sa qWebKitVersion() */ int qWebKitMajorVersion() { @@ -46,13 +58,57 @@ int qWebKitMajorVersion() } /*! - + \relates QWebPage + \since 4.6 Returns the 'minor' version number of WebKit at run-time as an integer (for example, 3). This is the version of WebKit the application was compiled against. + \sa qWebKitVersion() */ int qWebKitMinorVersion() { return WEBKIT_MINOR_VERSION; } + +/*! + \macro QTWEBKIT_VERSION + \relates QWebPage + + This macro expands a numeric value of the form 0xMMNNPP (MM = + major, NN = minor, PP = patch) that specifies QtWebKit's version + number. For example, if you compile your application against QtWebKit + 2.1.2, the QTWEBKIT_VERSION macro will expand to 0x020102. + + You can use QTWEBKIT_VERSION to use the latest QtWebKit API where + available. + + \sa QT_VERSION +*/ + +/*! + \macro QTWEBKIT_VERSION_STR + \relates QWebPage + + This macro expands to a string that specifies QtWebKit's version number + (for example, "2.1.2"). This is the version against which the + application is compiled. + + \sa QTWEBKIT_VERSION +*/ + +/*! + \macro QTWEBKIT_VERSION_CHECK + \relates QWebPage + + Turns the major, minor and patch numbers of a version into an + integer, 0xMMNNPP (MM = major, NN = minor, PP = patch). This can + be compared with another similarly processed version id, for example + in a preprocessor statement: + + \code + #if QTWEBKIT_VERSION >= QTWEBKIT_VERSION_CHECK(2, 1, 0) + // code to use API new in QtWebKit 2.1.0 + #endif + \endcode +*/ diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebpage.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebpage.cpp index e9ebce5..c5f508f 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebpage.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebpage.cpp @@ -1627,7 +1627,7 @@ InspectorController* QWebPagePrivate::inspectorController() /*! \enum QWebPage::FindFlag - This enum describes the options available to QWebPage's findText() function. The options + This enum describes the options available to the findText() function. The options can be OR-ed together from the following list: \value FindBackward Searches backwards instead of forwards. @@ -1648,6 +1648,8 @@ InspectorController* QWebPagePrivate::inspectorController() \value DelegateExternalLinks When activating links that point to documents not stored on the local filesystem or an equivalent - such as the Qt resource system - then linkClicked() is emitted. \value DelegateAllLinks Whenever a link is activated the linkClicked() signal is emitted. + + \sa QWebPage::linkDelegationPolicy */ /*! @@ -1662,6 +1664,8 @@ InspectorController* QWebPagePrivate::inspectorController() \value NavigationTypeReload The user activated the reload action. \value NavigationTypeFormResubmitted An HTML form was submitted a second time. \value NavigationTypeOther A navigation to another document using a method not listed above. + + \sa acceptNavigationRequest() */ /*! @@ -1671,7 +1675,7 @@ InspectorController* QWebPagePrivate::inspectorController() Actions only have an effect when they are applicable. The availability of actions can be be determined by checking \l{QAction::}{isEnabled()} on the - action returned by \l{QWebPage::}{action()}. + action returned by action(). One method of enabling the text editing, cursor movement, and text selection actions is by setting \l contentEditable to true. @@ -1753,6 +1757,8 @@ InspectorController* QWebPagePrivate::inspectorController() /*! \enum QWebPage::WebWindowType + This enum describes the types of window that can be created by the createWindow() function. + \value WebBrowserWindow The window is a regular web browser window. \value WebModalDialog The window acts as modal dialog. */ @@ -1769,11 +1775,13 @@ InspectorController* QWebPagePrivate::inspectorController() to provide functionality like QWebView in a widget-less environment. QWebPage's API is very similar to QWebView, as you are still provided with - common functions like action() (known as \l{QWebView::}{pageAction()} in - QWebView), triggerAction(), findText() and settings(). More QWebView-like - functions can be found in the main frame of QWebPage, obtained via - QWebPage::mainFrame(). For example, the load(), setUrl() and setHtml() - unctions for QWebPage can be accessed using QWebFrame. + common functions like action() (known as + \l{QWebView::pageAction()}{pageAction}() in QWebView), triggerAction(), + findText() and settings(). More QWebView-like functions can be found in the + main frame of QWebPage, obtained via the mainFrame() function. For example, + the \l{QWebFrame::load()}{load}(), \l{QWebFrame::setUrl()}{setUrl}() and + \l{QWebFrame::setHtml()}{setHtml}() functions for QWebPage can be accessed + using QWebFrame. The loadStarted() signal is emitted when the page begins to load.The loadProgress() signal, on the other hand, is emitted whenever an element @@ -1877,7 +1885,8 @@ QWebFrame *QWebPage::currentFrame() const /*! \since 4.6 - Returns the frame at the given point \a pos. + Returns the frame at the given point \a pos, or 0 if there is no frame at + that position. \sa mainFrame(), currentFrame() */ @@ -1996,7 +2005,7 @@ bool QWebPage::javaScriptConfirm(QWebFrame *frame, const QString& msg) result should be written to \a result and true should be returned. If the prompt was not cancelled by the user, the implementation should return true and the result string must not be null. - The default implementation uses QInputDialog::getText. + The default implementation uses QInputDialog::getText(). */ bool QWebPage::javaScriptPrompt(QWebFrame *frame, const QString& msg, const QString& defaultValue, QString* result) { @@ -2210,6 +2219,8 @@ QSize QWebPage::viewportSize() const By default, for a newly-created Web page, this property contains a size with zero width and height. + + \sa QWebFrame::render(), preferredContentsSize */ void QWebPage::setViewportSize(const QSize &size) const { @@ -2238,11 +2249,12 @@ QSize QWebPage::preferredContentsSize() const /*! \property QWebPage::preferredContentsSize \since 4.6 - \brief the size of the fixed layout + \brief the preferred size of the contents - The size affects the layout of the page in the viewport. If set to a fixed size of - 1024x768 for example then webkit will layout the page as if the viewport were that size - rather than something different. + If this property is set to a valid size, it is used to lay out the page. + If it is not set (the default), the viewport size is used instead. + + \sa viewportSize */ void QWebPage::setPreferredContentsSize(const QSize &size) const { @@ -2590,9 +2602,11 @@ QAction *QWebPage::action(WebAction action) const /*! \property QWebPage::modified - \brief whether the page contains unsubmitted form data + \brief whether the page contains unsubmitted form data, or the contents have been changed. By default, this property is false. + + \sa contentsChanged(), contentEditable, undoStack() */ bool QWebPage::isModified() const { @@ -2608,6 +2622,8 @@ bool QWebPage::isModified() const #ifndef QT_NO_UNDOSTACK /*! Returns a pointer to the undo stack used for editable content. + + \sa modified */ QUndoStack *QWebPage::undoStack() const { @@ -2730,7 +2746,7 @@ bool QWebPage::event(QEvent *ev) } /*! - Similar to QWidget::focusNextPrevChild it focuses the next focusable web element + Similar to QWidget::focusNextPrevChild() it focuses the next focusable web element if \a next is true; otherwise the previous element is focused. Returns true if it can find a new focusable element, or false if it can't. @@ -2757,6 +2773,8 @@ bool QWebPage::focusNextPrevChild(bool next) If this property is enabled the contents of the page can be edited by the user through a visible cursor. If disabled (the default) only HTML elements in the web page with their \c{contenteditable} attribute set are editable. + + \sa modified, contentsChanged(), WebAction */ void QWebPage::setContentEditable(bool editable) { @@ -2926,17 +2944,21 @@ void QWebPage::updatePositionDependentActions(const QPoint &pos) as a result of the user clicking on a "file upload" button in a HTML form where multiple file selection is allowed. - \omitvalue ErrorPageExtension (introduced in Qt 4.6) + \value ErrorPageExtension Whether the web page can provide an error page when loading fails. + (introduced in Qt 4.6) + + \sa ChooseMultipleFilesExtensionOption, ChooseMultipleFilesExtensionReturn, ErrorPageExtensionOption, ErrorPageExtensionReturn */ /*! \enum QWebPage::ErrorDomain \since 4.6 - \internal - \value QtNetwork - \value Http - \value WebKit + This enum describes the domain of an ErrorPageExtensionOption object (i.e. the layer in which the error occurred). + + \value QtNetwork The error occurred in the QtNetwork layer; the error code is of type QNetworkReply::NetworkError. + \value Http The error occurred in the HTTP layer; the error code is a HTTP status code (see QNetworkRequest::HttpStatusCodeAttribute). + \value WebKit The error is an internal WebKit error. */ /*! @@ -2946,7 +2968,18 @@ void QWebPage::updatePositionDependentActions(const QPoint &pos) \inmodule QtWebKit - \sa QWebPage::extension() + \sa QWebPage::extension() QWebPage::ExtensionReturn +*/ + + +/*! + \class QWebPage::ExtensionReturn + \since 4.4 + \brief The ExtensionReturn class provides an output result from a QWebPage's extension. + + \inmodule QtWebKit + + \sa QWebPage::extension() QWebPage::ExtensionOption */ /*! @@ -2957,12 +2990,38 @@ void QWebPage::updatePositionDependentActions(const QPoint &pos) \inmodule QtWebKit - The ErrorPageExtensionOption class holds the \a url for which an error occoured as well as + The ErrorPageExtensionOption class holds the \a url for which an error occurred as well as the associated \a frame. The error itself is reported by an error \a domain, the \a error code as well as \a errorString. - \sa QWebPage::ErrorPageExtensionReturn + \sa QWebPage::extension() QWebPage::ErrorPageExtensionReturn +*/ + +/*! + \variable QWebPage::ErrorPageExtensionOption::url + \brief the url for which an error occurred +*/ + +/*! + \variable QWebPage::ErrorPageExtensionOption::frame + \brief the frame associated with the error +*/ + +/*! + \variable QWebPage::ErrorPageExtensionOption::domain + \brief the domain that reported the error +*/ + +/*! + \variable QWebPage::ErrorPageExtensionOption::error + \brief the error code. Interpretation of the value depends on the \a domain + \sa QWebPage::ErrorDomain +*/ + +/*! + \variable QWebPage::ErrorPageExtensionOption::errorString + \brief a string that describes the error */ /*! @@ -2983,7 +3042,7 @@ void QWebPage::updatePositionDependentActions(const QPoint &pos) External objects such as stylesheets or images referenced in the HTML are located relative to \a baseUrl. - \sa QWebPage::ErrorPageExtensionOption, QString::toUtf8() + \sa QWebPage::extension() QWebPage::ErrorPageExtensionOption, QString::toUtf8() */ /*! @@ -2992,6 +3051,29 @@ void QWebPage::updatePositionDependentActions(const QPoint &pos) Constructs a new error page object. */ + +/*! + \variable QWebPage::ErrorPageExtensionReturn::contentType + \brief the error page's content type +*/ + +/*! + \variable QWebPage::ErrorPageExtensionReturn::encoding + \brief the error page encoding +*/ + +/*! + \variable QWebPage::ErrorPageExtensionReturn::baseUrl + \brief the base url + + External objects such as stylesheets or images referenced in the HTML are located relative to this url. +*/ + +/*! + \variable QWebPage::ErrorPageExtensionReturn::content + \brief the HTML content of the error page +*/ + /*! \class QWebPage::ChooseMultipleFilesExtensionOption \since 4.5 @@ -3003,7 +3085,22 @@ void QWebPage::updatePositionDependentActions(const QPoint &pos) The ChooseMultipleFilesExtensionOption class holds the frame originating the request and the suggested filenames which might be provided. - \sa QWebPage::chooseFile(), QWebPage::ChooseMultipleFilesExtensionReturn + \sa QWebPage::extension() QWebPage::chooseFile(), QWebPage::ChooseMultipleFilesExtensionReturn +*/ + +/*! + \variable QWebPage::ChooseMultipleFilesExtensionOption::parentFrame + \brief The frame in which the request originated +*/ + +/*! + \variable QWebPage::ChooseMultipleFilesExtensionOption::suggestedFileNames + \brief The suggested filenames +*/ + +/*! + \variable QWebPage::ChooseMultipleFilesExtensionReturn::fileNames + \brief The selected filenames */ /*! @@ -3017,14 +3114,17 @@ void QWebPage::updatePositionDependentActions(const QPoint &pos) The ChooseMultipleFilesExtensionReturn class holds the filenames selected by the user when the extension is invoked. - \sa QWebPage::ChooseMultipleFilesExtensionOption + \sa QWebPage::extension() QWebPage::ChooseMultipleFilesExtensionOption */ /*! This virtual function can be reimplemented in a QWebPage subclass to provide support for extensions. The \a option argument is provided as input to the extension; the output results can be stored in \a output. - The behavior of this function is determined by \a extension. + The behavior of this function is determined by \a extension. The \a option + and \a output values are typically casted to the corresponding types (for + example, ChooseMultipleFilesExtensionOption and + ChooseMultipleFilesExtensionReturn for ChooseMultipleFilesExtension). You can call supportsExtension() to check if an extension is supported by the page. @@ -3116,6 +3216,8 @@ QWebSettings *QWebPage::settings() const A suggested filename may be provided in \a suggestedFile. The frame originating the request is provided as \a parentFrame. + + \sa ChooseMultipleFilesExtension */ QString QWebPage::chooseFile(QWebFrame *parentFrame, const QString& suggestedFile) { @@ -3369,6 +3471,15 @@ QString QWebPage::userAgentForUrl(const QUrl&) const case QSysInfo::SV_9_4: firstPartTemp += QString::fromLatin1("/9.4"); break; + case QSysInfo::SV_SF_2: + firstPartTemp += QString::fromLatin1("^2"); + break; + case QSysInfo::SV_SF_3: + firstPartTemp += QString::fromLatin1("^3"); + break; + case QSysInfo::SV_SF_4: + firstPartTemp += QString::fromLatin1("^4"); + break; default: firstPartTemp += QString::fromLatin1("/Unknown"); } @@ -3479,7 +3590,7 @@ quint64 QWebPage::totalBytes() const /*! Returns the number of bytes that were received from the network to render the current page. - \sa totalBytes() + \sa totalBytes(), loadProgress() */ quint64 QWebPage::bytesReceived() const { @@ -3511,7 +3622,7 @@ quint64 QWebPage::bytesReceived() const This signal is emitted when a load of the page is finished. \a ok will indicate whether the load was successful or any error occurred. - \sa loadStarted() + \sa loadStarted(), ErrorPageExtension */ /*! @@ -3538,12 +3649,15 @@ quint64 QWebPage::bytesReceived() const \fn void QWebPage::frameCreated(QWebFrame *frame) This signal is emitted whenever the page creates a new \a frame. + + \sa currentFrame() */ /*! \fn void QWebPage::selectionChanged() - This signal is emitted whenever the selection changes. + This signal is emitted whenever the selection changes, either interactively + or programmatically (e.g. by calling triggerAction() with a selection action). \sa selectedText() */ @@ -3555,7 +3669,7 @@ quint64 QWebPage::bytesReceived() const This signal is emitted whenever the text in form elements changes as well as other editable content. - \sa contentEditable, QWebFrame::toHtml(), QWebFrame::toPlainText() + \sa contentEditable, modified, QWebFrame::toHtml(), QWebFrame::toPlainText() */ /*! @@ -3631,9 +3745,9 @@ quint64 QWebPage::bytesReceived() const \fn void QWebPage::microFocusChanged() This signal is emitted when for example the position of the cursor in an editable form - element changes. It is used inform input methods about the new on-screen position where - the user is able to enter text. This signal is usually connected to QWidget's updateMicroFocus() - slot. + element changes. It is used to inform input methods about the new on-screen position where + the user is able to enter text. This signal is usually connected to the + QWidget::updateMicroFocus() slot. */ /*! @@ -3674,6 +3788,8 @@ quint64 QWebPage::bytesReceived() const This signal is emitted whenever the web site shown in \a frame is asking to store data to the database \a databaseName and the quota allocated to that web site is exceeded. + + \sa QWebDatabase */ /*! diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebpluginfactory.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebpluginfactory.cpp index 8ff13b1..f715430 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebpluginfactory.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebpluginfactory.cpp @@ -23,25 +23,40 @@ /*! \class QWebPluginFactory \since 4.4 - \brief The QWebPluginFactory class creates plugins to be embedded into web - pages. + \brief The QWebPluginFactory class is used to embed custom data types in web pages. \inmodule QtWebKit - QWebPluginFactory is a factory for creating plugins for QWebPage. A plugin - factory can be installed on a QWebPage using QWebPage::setPluginFactory(). + The HTML \c{} tag is used to embed arbitrary content into a web page, + for example: + + \code + + \endcode + + QtWebkit will natively handle the most basic data types like \c{text/html} and + \c{image/jpeg}, but for any advanced or custom data types you will need to + provide a handler yourself. + + QWebPluginFactory is a factory for creating plugins for QWebPage, where each + plugin provides support for one or more data types. A plugin factory can be + installed on a QWebPage using QWebPage::setPluginFactory(). \note The plugin factory is only used if plugins are enabled through QWebSettings. - You can provide a QWebPluginFactory by implementing the plugins() and the - create() method. For plugins() it is necessary to describe the plugins the + You provide a QWebPluginFactory by implementing the plugins() and the + create() methods. For plugins() it is necessary to describe the plugins the factory can create, including a description and the supported MIME types. The MIME types each plugin can handle should match the ones specified in - in the HTML \c{} tag. + in the HTML \c{} tag of your content. The create() method is called if the requested MIME type is supported. The implementation has to return a new instance of the plugin requested for the given MIME type and the specified URL. + + The plugins themselves are subclasses of QObject, but currently only plugins + based on either QWidget or QGraphicsWidget are supported. + */ @@ -183,6 +198,7 @@ void QWebPluginFactory::refreshPlugins() /*! \enum QWebPluginFactory::Extension + \internal This enum describes the types of extensions that the plugin factory can support. Before using these extensions, you should verify that the extension is supported by calling supportsExtension(). @@ -192,6 +208,7 @@ void QWebPluginFactory::refreshPlugins() /*! \class QWebPluginFactory::ExtensionOption + \internal \since 4.4 \brief The ExtensionOption class provides an extended input argument to QWebPluginFactory's extension support. @@ -202,6 +219,7 @@ void QWebPluginFactory::refreshPlugins() /*! \class QWebPluginFactory::ExtensionReturn + \internal \since 4.4 \brief The ExtensionOption class provides an extended output argument to QWebPluginFactory's extension support. @@ -214,6 +232,8 @@ void QWebPluginFactory::refreshPlugins() This virtual function can be reimplemented in a QWebPluginFactory subclass to provide support for extensions. The \a option argument is provided as input to the extension; the output results can be stored in \a output. + \internal + The behaviour of this function is determined by \a extension. You can call supportsExtension() to check if an extension is supported by the factory. @@ -233,6 +253,8 @@ bool QWebPluginFactory::extension(Extension extension, const ExtensionOption *op /*! This virtual function returns true if the plugin factory supports \a extension; otherwise false is returned. + \internal + \sa extension() */ bool QWebPluginFactory::supportsExtension(Extension extension) const diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebsecurityorigin.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebsecurityorigin.cpp index 6c26bd7..7179eaa 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebsecurityorigin.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebsecurityorigin.cpp @@ -63,6 +63,16 @@ void QWEBKIT_EXPORT qt_drt_setDomainRelaxationForbiddenForURLScheme(bool forbidd \c{http://www.malicious.com/evil.html} from accessing \c{http://www.example.com/}'s resources, because they are of a different security origin. + By default local schemes like \c{file://} and \c{qrc://} are concidered to be in the same + security origin, and can access each other's resources. You can add additional local schemes + by using QWebSecurityOrigin::addLocalScheme(), or override the default same-origin behavior + by setting QWebSettings::LocalContentCanAccessFileUrls to \c{false}. + + \note Local resources are by default restricted from accessing remote content, which + means your \c{file://} will not be able to access \c{http://domain.com/foo.html}. You + can relax this restriction by setting QWebSettings::LocalContentCanAccessRemoteUrls to + \c{true}. + Call QWebFrame::securityOrigin() to get the QWebSecurityOrigin for a frame in a web page, and use host(), scheme() and port() to identify the security origin. @@ -219,7 +229,11 @@ QList QWebSecurityOrigin::databases() const \since 4.6 Adds the given \a scheme to the list of schemes that are considered equivalent - to the \c file: scheme. They are not subject to cross domain restrictions. + to the \c file: scheme. + + Cross domain restrictions depend on the two web settings QWebSettings::LocalContentCanAccessFileUrls + and QWebSettings::LocalContentCanAccessFileUrls. By default all local schemes are concidered to be + in the same security origin, and local schemes can not access remote content. */ void QWebSecurityOrigin::addLocalScheme(const QString& scheme) { @@ -231,6 +245,9 @@ void QWebSecurityOrigin::addLocalScheme(const QString& scheme) Removes the given \a scheme from the list of local schemes. + \note You can not remove the \c{file://} scheme from the list + of local schemes. + \sa addLocalScheme() */ void QWebSecurityOrigin::removeLocalScheme(const QString& scheme) @@ -240,7 +257,10 @@ void QWebSecurityOrigin::removeLocalScheme(const QString& scheme) /*! \since 4.6 - Returns a list of all the schemes that were set by the application as local schemes, + Returns a list of all the schemes concidered to be local. + + By default this is \c{file://} and \c{qrc://}. + \sa addLocalScheme(), removeLocalScheme() */ QStringList QWebSecurityOrigin::localSchemes() diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.cpp b/src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.cpp index ae7f6a8..115f9fc 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.cpp +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.cpp @@ -289,8 +289,8 @@ QWebSettings* QWebSettings::globalSettings() function. The \l{QWebSettings::WebAttribute}{WebAttribute} enum further describes each attribute. - QWebSettings also configures global properties such as the Web page memory - cache and the Web page icon database, local database storage and offline + QWebSettings also configures global properties such as the web page memory + cache, icon database, local database storage and offline applications storage. \section1 Enabling Plugins @@ -299,8 +299,8 @@ QWebSettings* QWebSettings::globalSettings() \l{QWebSettings::PluginsEnabled}{PluginsEnabled} attribute. For many applications, this attribute is enabled for all pages by setting it on the \l{globalSettings()}{global settings object}. QtWebKit will always ignore this setting - \when processing Qt plugins. The decision to allow a Qt plugin is made by the client - \in its reimplementation of QWebPage::createPlugin. + when processing Qt plugins. The decision to allow a Qt plugin is made by the client + in its reimplementation of QWebPage::createPlugin(). \section1 Web Application Support @@ -339,7 +339,7 @@ QWebSettings* QWebSettings::globalSettings() \value MinimumFontSize The hard minimum font size. \value MinimumLogicalFontSize The minimum logical font size that is applied - after zooming with QWebFrame's textSizeMultiplier(). + when zooming out with QWebFrame::setTextSizeMultiplier(). \value DefaultFontSize The default font size for regular text. \value DefaultFixedFontSize The default font size for fixed-pitch text. */ @@ -361,66 +361,74 @@ QWebSettings* QWebSettings::globalSettings() This enum describes various attributes that are configurable through QWebSettings. \value AutoLoadImages Specifies whether images are automatically loaded in - web pages. + web pages. This is enabled by default. \value DnsPrefetchEnabled Specifies whether QtWebkit will try to pre-fetch DNS entries to - speed up browsing. This only works as a global attribute. Only for Qt 4.6 and later. + speed up browsing. This only works as a global attribute. Only for Qt 4.6 and later. This is disabled by default. \value JavascriptEnabled Enables or disables the running of JavaScript - programs. + programs. This is enabled by default \value JavaEnabled Enables or disables Java applets. Currently Java applets are not supported. - \value PluginsEnabled Enables or disables plugins in Web pages. Qt plugins - with a mimetype such as "application/x-qt-plugin" are not affected by this setting. + \value PluginsEnabled Enables or disables plugins in Web pages (e.g. using NPAPI). Qt plugins + with a mimetype such as "application/x-qt-plugin" are not affected by this setting. This is disabled by default. \value PrivateBrowsingEnabled Private browsing prevents WebKit from - recording visited pages in the history and storing web page icons. + recording visited pages in the history and storing web page icons. This is disabled by default. \value JavascriptCanOpenWindows Specifies whether JavaScript programs - can open new windows. + can open new windows. This is disabled by default. \value JavascriptCanAccessClipboard Specifies whether JavaScript programs - can read or write to the clipboard. + can read or write to the clipboard. This is disabled by default. \value DeveloperExtrasEnabled Enables extra tools for Web developers. Currently this enables the "Inspect" element in the context menu as - well as the use of QWebInspector which controls the WebKit WebInspector - for web site debugging. + well as the use of QWebInspector which controls the web inspector + for web site debugging. This is disabled by default. \value SpatialNavigationEnabled Enables or disables the Spatial Navigation feature, which consists in the ability to navigate between focusable elements in a Web page, such as hyperlinks and form controls, by using - Left, Right, Up and Down arrow keys. For example, if an user presses the + Left, Right, Up and Down arrow keys. For example, if a user presses the Right key, heuristics determine whether there is an element he might be - trying to reach towards the right, and if there are multiple elements, - which element he probably wants. + trying to reach towards the right and which element he probably wants. + This is disabled by default. \value LinksIncludedInFocusChain Specifies whether hyperlinks should be - included in the keyboard focus chain. - \value ZoomTextOnly Specifies whether the zoom factor on a frame applies to - only the text or all content. + included in the keyboard focus chain. This is enabled by default. + \value ZoomTextOnly Specifies whether the zoom factor on a frame applies + only to the text or to all content. This is disabled by default. \value PrintElementBackgrounds Specifies whether the background color and images - are also drawn when the page is printed. + are also drawn when the page is printed. This is enabled by default. \value OfflineStorageDatabaseEnabled Specifies whether support for the HTML 5 - offline storage feature is enabled or not. Disabled by default. + offline storage feature is enabled or not. This is disabled by default. \value OfflineWebApplicationCacheEnabled Specifies whether support for the HTML 5 - web application cache feature is enabled or not. Disabled by default. + web application cache feature is enabled or not. This is disabled by default. \value LocalStorageEnabled Specifies whether support for the HTML 5 - local storage feature is enabled or not. Disabled by default. + local storage feature is enabled or not. This is disabled by default. \value LocalStorageDatabaseEnabled \e{This enum value is deprecated.} Use QWebSettings::LocalStorageEnabled instead. - \value LocalContentCanAccessRemoteUrls Specifies whether locally loaded documents are allowed to access remote urls. - \value LocalContentCanAccessFileUrls Specifies whether locally loaded documents are allowed to access other local urls. - \value XSSAuditorEnabled Specifies whether load requests should be monitored for cross-site scripting attempts. + \value LocalContentCanAccessRemoteUrls Specifies whether locally loaded documents are + allowed to access remote urls. This is disabled by default. For more information + about security origins and local vs. remote content see QWebSecurityOrigin. + \value LocalContentCanAccessFileUrls Specifies whether locally loaded documents are + allowed to access other local urls. This is enabled by default. For more information + about security origins and local vs. remote content see QWebSecurityOrigin. + \value XSSAuditingEnabled Specifies whether load requests should be monitored for cross-site + scripting attempts. Suspicious scripts will be blocked and reported in the inspector's + JavaScript console. Enabling this feature might have an impact on performance + and it is disabled by default. \value AcceleratedCompositingEnabled This feature, when used in conjunction with QGraphicsWebView, accelerates animations of web content. CSS animations of the transform and opacity properties will be rendered by composing the cached content of the animated elements. - This feature is enabled by default + This is enabled by default. \value TiledBackingStoreEnabled This setting enables the tiled backing store feature for a QGraphicsWebView. With the tiled backing store enabled, the web page contents in and around the current visible area is speculatively cached to bitmap tiles. The tiles are automatically kept in sync with the web page as it changes. Enabling tiling can significantly speed up painting heavy operations like scrolling. Enabling the feature increases memory consumption. It does not work well with contents using CSS fixed positioning (see also \l{QGraphicsWebView::}{resizesToContents} property). - \l{QGraphicsWebView::}{tiledBackingStoreFrozen} property allows application to temporarily freeze the contents of the backing store. + \l{QGraphicsWebView::}{tiledBackingStoreFrozen} property allows application to temporarily + freeze the contents of the backing store. This is disabled by default. \value FrameFlatteningEnabled With this setting each subframe is expanded to its contents. On touch devices, it is desired to not have any scrollable sub parts of the page as it results in a confusing user experience, with scrolling sometimes scrolling sub parts and at other times scrolling the page itself. For this reason iframes and framesets are barely usable on touch devices. This will flatten all the frames to become one scrollable page. - Disabled by default. + This is disabled by default. */ /*! @@ -525,7 +533,8 @@ void QWebSettings::resetFontSize(FontSize type) with UTF-8 and Base64 encoded data, such as: "data:text/css;charset=utf-8;base64,cCB7IGJhY2tncm91bmQtY29sb3I6IHJlZCB9Ow==" - NOTE: In case the base 64 data is not valid the style will not be applied. + + \note If the base64 data is not valid, the style will not be applied. \sa userStyleSheetUrl() */ @@ -576,9 +585,11 @@ QString QWebSettings::defaultTextEncoding() const Sets the path of the icon database to \a path. The icon database is used to store "favicons" associated with web sites. - \a path must point to an existing directory where the icons are stored. + \a path must point to an existing directory. Setting an empty path disables the icon database. + + \sa iconDatabasePath(), clearIconDatabase() */ void QWebSettings::setIconDatabasePath(const QString& path) { @@ -621,7 +632,7 @@ void QWebSettings::clearIconDatabase() /*! Returns the web site's icon for \a url. - If the web site does not specify an icon, or the icon is not in the + If the web site does not specify an icon \bold OR if the icon is not in the database, a null QIcon is returned. \note The returned icon's size is arbitrary. @@ -658,7 +669,7 @@ QWebPluginDatabase *QWebSettings::pluginDatabase() Sets \a graphic to be drawn when QtWebKit needs to draw an image of the given \a type. - For example, when an image cannot be loaded the pixmap specified by + For example, when an image cannot be loaded, the pixmap specified by \l{QWebSettings::WebGraphic}{MissingImageGraphic} is drawn instead. \sa webGraphic() @@ -676,9 +687,6 @@ void QWebSettings::setWebGraphic(WebGraphic type, const QPixmap& graphic) Returns a previously set pixmap used to draw replacement graphics of the specified \a type. - For example, when an image cannot be loaded the pixmap specified by - \l{QWebSettings::WebGraphic}{MissingImageGraphic} is drawn instead. - \sa setWebGraphic() */ QPixmap QWebSettings::webGraphic(WebGraphic type) @@ -719,8 +727,7 @@ void QWebSettings::clearMemoryCaches() Sets the maximum number of pages to hold in the memory page cache to \a pages. The Page Cache allows for a nicer user experience when navigating forth or back - to pages in the forward/back history, by pausing and resuming up to \a pages - per page group. + to pages in the forward/back history, by pausing and resuming up to \a pages. For more information about the feature, please refer to: @@ -792,8 +799,8 @@ QString QWebSettings::fontFamily(FontFamily which) const } /*! - Resets the actual font family to the default font family, specified by - \a which. + Resets the actual font family specified by \a which to the one set + in the global QWebSettings instance. This function has no effect on the global QWebSettings instance. */ @@ -835,7 +842,9 @@ bool QWebSettings::testAttribute(WebAttribute attr) const /*! \fn void QWebSettings::resetAttribute(WebAttribute attribute) - Resets the setting of \a attribute. + Resets the setting of \a attribute to the value specified in the + global QWebSettings instance. + This function has no effect on the global QWebSettings instance. \sa globalSettings() @@ -851,12 +860,15 @@ void QWebSettings::resetAttribute(WebAttribute attr) /*! \since 4.5 - Sets the path for HTML5 offline storage to \a path. + Sets \a path as the save location for HTML5 client-side database storage data. - \a path must point to an existing directory where the databases are stored. + \a path must point to an existing directory. Setting an empty path disables the feature. + Support for client-side databases can enabled by setting the + \l{QWebSettings::OfflineStorageDatabaseEnabled}{OfflineStorageDatabaseEnabled} attribute. + \sa offlineStoragePath() */ void QWebSettings::setOfflineStoragePath(const QString& path) @@ -869,7 +881,7 @@ void QWebSettings::setOfflineStoragePath(const QString& path) /*! \since 4.5 - Returns the path of the HTML5 offline storage or an empty string if the + Returns the path of the HTML5 client-side database storage or an empty string if the feature is disabled. \sa setOfflineStoragePath() @@ -906,22 +918,24 @@ qint64 QWebSettings::offlineStorageDefaultQuota() /*! \since 4.6 - \relates QWebSettings Sets the path for HTML5 offline web application cache storage to \a path. An application cache acts like an HTTP cache in some sense. For documents - that use the application cache via JavaScript, the loader mechinery will + that use the application cache via JavaScript, the loader engine will first ask the application cache for the contents, before hitting the network. The feature is described in details at: http://dev.w3.org/html5/spec/Overview.html#appcache - \a path must point to an existing directory where the cache is stored. + \a path must point to an existing directory. Setting an empty path disables the feature. + Support for offline web application cache storage can enabled by setting the + \l{QWebSettings::OfflineWebApplicationCacheEnabled}{OfflineWebApplicationCacheEnabled} attribute. + \sa offlineWebApplicationCachePath() */ void QWebSettings::setOfflineWebApplicationCachePath(const QString& path) @@ -933,7 +947,6 @@ void QWebSettings::setOfflineWebApplicationCachePath(const QString& path) /*! \since 4.6 - \relates QWebSettings Returns the path of the HTML5 offline web application cache storage or an empty string if the feature is disabled. @@ -980,7 +993,6 @@ qint64 QWebSettings::offlineWebApplicationCacheQuota() /*! \since 4.6 - \relates QWebSettings Sets the path for HTML5 local storage to \a path. @@ -1025,7 +1037,6 @@ QUrl QWebSettings::inspectorUrl() const /*! \since 4.6 - \relates QWebSettings Returns the path for HTML5 local storage. @@ -1038,13 +1049,14 @@ QString QWebSettings::localStoragePath() const /*! \since 4.6 - \relates QWebSettings - Enables WebKit persistent data and sets the path to \a path. - If the \a path is empty the path for persistent data is set to the - user-specific data location specified by - \l{QDesktopServices::DataLocation}{DataLocation}. - + Enables WebKit data persistence and sets the path to \a path. + If \a path is empty, the user-specific data location specified by + \l{QDesktopServices::DataLocation}{DataLocation} will be used instead. + + This method will simultaneously set and enable the iconDatabasePath(), + localStoragePath(), offlineStoragePath() and offlineWebApplicationCachePath(). + \sa localStoragePath() */ void QWebSettings::enablePersistentStorage(const QString& path) diff --git a/src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.h b/src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.h index d2e536e..3592e2c 100644 --- a/src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.h +++ b/src/3rdparty/webkit/WebKit/qt/Api/qwebsettings.h @@ -63,7 +63,7 @@ public: OfflineStorageDatabaseEnabled, OfflineWebApplicationCacheEnabled, LocalStorageEnabled, -#ifdef QT_DEPRECATED +#if defined(QT_DEPRECATED) || defined(qdoc) LocalStorageDatabaseEnabled = LocalStorageEnabled, #endif LocalContentCanAccessRemoteUrls, diff --git a/src/3rdparty/webkit/WebKit/qt/ChangeLog b/src/3rdparty/webkit/WebKit/qt/ChangeLog index 6ddaa2b..efc8f27 100644 --- a/src/3rdparty/webkit/WebKit/qt/ChangeLog +++ b/src/3rdparty/webkit/WebKit/qt/ChangeLog @@ -1,3 +1,102 @@ +2010-05-14 Kent Hansen , Jocelyn Turcotte , Tor Arne Vestbø , Henry Haverinen , Jedrzej Nowacki , Andreas Kling + + Reviewed by Simon Hausmann. + + [Qt] Merge overhaul of the QtWebKit API documentation + + Numerous improvements in wording, qdoc warning fixes and + clarifications, done in a team work effort. + + No functional changes. + + * Api/qwebdatabase.cpp: + * Api/qwebelement.cpp: + * Api/qwebframe.cpp: + * Api/qwebhistoryinterface.cpp: + * Api/qwebinspector.cpp: + * Api/qwebkitversion.cpp: + * Api/qwebpage.cpp: + * Api/qwebpluginfactory.cpp: + * Api/qwebsecurityorigin.cpp: + * Api/qwebsettings.cpp: + * Api/qwebsettings.h: + * docs/qtwebkit.qdoc: + * docs/webkitsnippets/qtwebkit_qwebinspector_snippet.cpp: + (wrapInFunction): + * docs/webkitsnippets/webelement/main.cpp: + (findButtonAndClick): + (autocomplete1): + (autocomplete2): + (main): + +2010-05-14 Martin Smith + + Reviewed by Simon Hausmann. + + Documentation: Fix overview grouping. + + * docs/qtwebkit.qdoc: + +2010-05-14 Benjamin Poulain + + Reviewed by Laszlo Gombos. + + [QT] Update the Symbian version for the user agent + https://bugs.webkit.org/show_bug.cgi?id=38389 + + Update the user agent for Symbian^2 to Symbian^4 + + * Api/qwebpage.cpp: + (QWebPage::userAgentForUrl): + +2010-05-11 Antonio Gomes + + Reviewed by Kenneth Christiansen. + + [Qt] emit initialLayoutCompleted signal from FrameLoaderClientQt::dispatchDidFirstVisuallyNonEmptyLayout + https://bugs.webkit.org/show_bug.cgi?id=38921 + + Emit initialLayoutCompleted signal from FrameLoaderClientQt::dispatchDidFirstVisuallyNonEmptyLayout + instead of FrameLoaderClientQt::dispatchDidFirstLayout , because the former ensures that a + visual content layed out on the frame. + + It matches to QWebFrame::initialLayoutCompleted signal documentation at: + + "... This is the first time you will see contents displayed on the frame ..." + + * WebCoreSupport/FrameLoaderClientQt.cpp: + (WebCore::FrameLoaderClientQt::dispatchDidFirstLayout): + (WebCore::FrameLoaderClientQt::dispatchDidFirstVisuallyNonEmptyLayout): + +2010-05-11 Kenneth Rohde Christiansen + + Reviewed by Laszlo Gombos. + + [Qt] REGRESSION(r58497) tst_QGraphicsWebView::crashOnViewlessWebPages() is failing + https://bugs.webkit.org/show_bug.cgi?id=38655 + + Fix double free by moving the connect till after the resize. + + The bug is causes by the fact that a resize of an empty page causes a + layout, thus deleting the qgraphicswebview before setHtml is called, + which then deletes it again, causing a double free. + + * tests/qgraphicswebview/tst_qgraphicswebview.cpp: + (tst_QGraphicsWebView::crashOnViewlessWebPages): + +2010-05-11 Diego Gonzalez + + Reviewed by Kenneth Rohde Christiansen. + + [Qt] tst_QWebPage::inputMethods failing on Maemo5 + https://bugs.webkit.org/show_bug.cgi?id=38685 + + Check if the SIP (Software Input Panel) is triggered, which normally + happens on mobile platforms, when a user input form receives a mouse click. + + * tests/qwebpage/tst_qwebpage.cpp: + (tst_QWebPage::inputMethods): + 2010-05-09 Noam Rosenthal Reviewed by Kenneth Rohde Christiansen. diff --git a/src/3rdparty/webkit/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp b/src/3rdparty/webkit/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp index 4ad008b..686bfcc 100644 --- a/src/3rdparty/webkit/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp +++ b/src/3rdparty/webkit/WebKit/qt/WebCoreSupport/FrameLoaderClientQt.cpp @@ -414,13 +414,13 @@ void FrameLoaderClientQt::dispatchDidFinishLoad() void FrameLoaderClientQt::dispatchDidFirstLayout() { - if (m_webFrame) - emit m_webFrame->initialLayoutCompleted(); + notImplemented(); } void FrameLoaderClientQt::dispatchDidFirstVisuallyNonEmptyLayout() { - notImplemented(); + if (m_webFrame) + emit m_webFrame->initialLayoutCompleted(); } void FrameLoaderClientQt::dispatchShow() diff --git a/src/3rdparty/webkit/WebKit/qt/docs/qtwebkit.qdoc b/src/3rdparty/webkit/WebKit/qt/docs/qtwebkit.qdoc index 96eb16e..c6dd550 100644 --- a/src/3rdparty/webkit/WebKit/qt/docs/qtwebkit.qdoc +++ b/src/3rdparty/webkit/WebKit/qt/docs/qtwebkit.qdoc @@ -7,63 +7,9 @@ \ingroup modules \ingroup technology-apis - \brief The QtWebKit module provides a web browser engine and + \brief The QtWebKit module provides a web browser engine as well as classes to render and interact with web content. - To include the definitions of the module's classes, use the - following directive: - - \snippet webkitsnippets/qtwebkit_build_snippet.qdoc 1 - - To link against the module, add this line to your \l qmake \c - .pro file: - - \snippet webkitsnippets/qtwebkit_build_snippet.qdoc 0 - - \section1 License Information - - This is a snapshot of the Qt port of WebKit. The exact version information - can be found in the \c{src/3rdparty/webkit/VERSION} file supplied with Qt. - - Qt Commercial Edition licensees that wish to distribute applications that - use the QtWebKit module need to be aware of their obligations under the - GNU Library General Public License (LGPL). - - Developers using the Open Source Edition can choose to redistribute - the module under the appropriate version of the GNU LGPL. - - \legalese - WebKit is licensed under the GNU Library General Public License. - Individual contributor names and copyright dates can be found - inline in the code. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library 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 - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - \endlegalese -*/ - -/*! - \page webintegration.html - \title Integrating Web Content with QtWebKit - \since 4.4 - - \ingroup frameworks-technologies - - \keyword Browser - \keyword Web Browser - QtWebKit provides a Web browser engine that makes it easy to embed content from the World Wide Web into your Qt application. At the same time Web content can be enhanced with native controls. @@ -85,6 +31,20 @@ QtWebKit is based on the Open Source WebKit engine. More information about WebKit itself can be found on the \l{WebKit Open Source Project} Web site. + \section1 Including In Your Project + + To include the definitions of the module's classes, use the + following directive: + + \snippet webkitsnippets/qtwebkit_build_snippet.qdoc 1 + + To link against the module, add this line to your \l qmake \c + .pro file: + + \snippet webkitsnippets/qtwebkit_build_snippet.qdoc 0 + + \section1 Notes + \note Building the QtWebKit module with debugging symbols is problematic on many platforms due to the size of the WebKit engine. We recommend building the module only in release mode for embedded platforms. @@ -99,10 +59,6 @@ Embedded Linux systems. See the \l{Qt for Embedded Linux Requirements} document for more information. - Topics: - - \tableofcontents - \section1 Architecture The easiest way to render content is through the QWebView class. As a @@ -195,4 +151,39 @@ \o The system \c{/Library/Internet Plug-Ins} directory \endlist \endtable + + + \section1 License Information + + This is a snapshot of the Qt port of WebKit. The exact version information + can be found in the \c{src/3rdparty/webkit/VERSION} file supplied with Qt. + + Qt Commercial Edition licensees that wish to distribute applications that + use the QtWebKit module need to be aware of their obligations under the + GNU Library General Public License (LGPL). + + Developers using the Open Source Edition can choose to redistribute + the module under the appropriate version of the GNU LGPL. + + \legalese + WebKit is licensed under the GNU Library General Public License. + Individual contributor names and copyright dates can be found + inline in the code. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + \endlegalese */ + diff --git a/src/3rdparty/webkit/WebKit/qt/docs/webkitsnippets/qtwebkit_qwebinspector_snippet.cpp b/src/3rdparty/webkit/WebKit/qt/docs/webkitsnippets/qtwebkit_qwebinspector_snippet.cpp index a6b6620..07f1d45 100644 --- a/src/3rdparty/webkit/WebKit/qt/docs/webkitsnippets/qtwebkit_qwebinspector_snippet.cpp +++ b/src/3rdparty/webkit/WebKit/qt/docs/webkitsnippets/qtwebkit_qwebinspector_snippet.cpp @@ -9,8 +9,6 @@ void wrapInFunction() QWebInspector *inspector = new QWebInspector; inspector->setPage(page); - - connect(page, SIGNAL(webInspectorTriggered(QWebElement)), inspector, SLOT(show())); //! [0] } diff --git a/src/3rdparty/webkit/WebKit/qt/docs/webkitsnippets/webelement/main.cpp b/src/3rdparty/webkit/WebKit/qt/docs/webkitsnippets/webelement/main.cpp index 822b61c..b1781a6 100644 --- a/src/3rdparty/webkit/WebKit/qt/docs/webkitsnippets/webelement/main.cpp +++ b/src/3rdparty/webkit/WebKit/qt/docs/webkitsnippets/webelement/main.cpp @@ -36,6 +36,59 @@ static void traverse() //! [Traversing with QWebElement] } +static void findButtonAndClick() +{ + + frame->setHtml("
" + "" + "" + "
"); + +//! [Calling a DOM element method] + + QWebElement document = frame->documentElement(); + /* Assume that the document has the following structure: + +
+ + +
+ + */ + + QWebElement button = document.findFirst("input[type=submit]"); + button.evaluateJavaScript("click()"); + +//! [Calling a DOM element method] + + } + +static void autocomplete1() +{ + QWebElement document = frame->documentElement(); + +//! [autocomplete1] + QWebElement firstTextInput = document.findFirst("input[type=text]"); + QString storedText = firstTextInput.attribute("value"); +//! [autocomplete1] + +} + + +static void autocomplete2() +{ + + QWebElement document = frame->documentElement(); + QString storedText = "text"; + +//! [autocomplete2] + QWebElement firstTextInput = document.findFirst("input[type=text]"); + textInput.setAttribute("value", storedText); +//! [autocomplete2] + +} + + static void findAll() { //! [FindAll] @@ -65,5 +118,8 @@ int main(int argc, char *argv[]) frame = view->page()->mainFrame(); traverse(); findAll(); + findButtonAndClick(); + autocomplete1(); + autocomplete2(); return 0; } diff --git a/src/3rdparty/webkit/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.cpp b/src/3rdparty/webkit/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.cpp index 14f5820..ebe847d 100644 --- a/src/3rdparty/webkit/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.cpp +++ b/src/3rdparty/webkit/WebKit/qt/tests/qgraphicswebview/tst_qgraphicswebview.cpp @@ -84,16 +84,19 @@ void tst_QGraphicsWebView::crashOnViewlessWebPages() WebPage* page = new WebPage; webView->setPage(page); page->webView = webView; - connect(page->mainFrame(), SIGNAL(initialLayoutCompleted()), page, SLOT(aborting())); - scene.addItem(webView); view.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); view.resize(600, 480); webView->resize(view.geometry().size()); + QCoreApplication::processEvents(); view.show(); + // Resizing the page will resize and layout the empty "about:blank" + // page, so we first connect the signal afterward. + connect(page->mainFrame(), SIGNAL(initialLayoutCompleted()), page, SLOT(aborting())); + page->mainFrame()->setHtml(QString("data:text/html," "" "" @@ -101,6 +104,7 @@ void tst_QGraphicsWebView::crashOnViewlessWebPages() "")); QVERIFY(waitForSignal(page, SIGNAL(loadFinished(bool)))); + delete page; } void tst_QGraphicsWebView::microFocusCoordinates() diff --git a/src/3rdparty/webkit/WebKit/qt/tests/qwebpage/tst_qwebpage.cpp b/src/3rdparty/webkit/WebKit/qt/tests/qwebpage/tst_qwebpage.cpp index 834a394..12fb9b3 100644 --- a/src/3rdparty/webkit/WebKit/qt/tests/qwebpage/tst_qwebpage.cpp +++ b/src/3rdparty/webkit/WebKit/qt/tests/qwebpage/tst_qwebpage.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1367,7 +1368,27 @@ void tst_QWebPage::inputMethods() page->event(&evrel); #if QT_VERSION >= QT_VERSION_CHECK(4, 6, 0) - QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); + // This part of the test checks if the SIP (Software Input Panel) is triggered, + // which normally happens on mobile platforms, when a user input form receives + // a mouse click. + int inputPanel = 0; + if (viewType == "QWebView") { + if (QWebView* wv = qobject_cast(view)) + inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel); + } else if (viewType == "QGraphicsWebView") { + if (QGraphicsWebView* wv = qobject_cast(view)) + inputPanel = wv->style()->styleHint(QStyle::SH_RequestSoftwareInputPanel); + } + + // For non-mobile platforms RequestSoftwareInputPanel event is not called + // because there is no SIP (Software Input Panel) triggered. In the case of a + // mobile platform, an input panel, e.g. virtual keyboard, is usually invoked + // and the RequestSoftwareInputPanel event is called. For these two situations + // this part of the test can verified as the checks below. + if (inputPanel) + QVERIFY(viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); + else + QVERIFY(!viewEventSpy.contains(QEvent::RequestSoftwareInputPanel)); #endif viewEventSpy.clear(); -- cgit v0.12 f='#n2230'>2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661
#!/usr/bin/env python3
"""Generate Python documentation in HTML or text for interactive use.

At the Python interactive prompt, calling help(thing) on a Python object
documents the object, and calling help() starts up an interactive
help session.

Or, at the shell command line outside of Python:

Run "pydoc <name>" to show documentation on something.  <name> may be
the name of a function, module, package, or a dotted reference to a
class or function within a module or module in a package.  If the
argument contains a path segment delimiter (e.g. slash on Unix,
backslash on Windows) it is treated as the path to a Python source file.

Run "pydoc -k <keyword>" to search for a keyword in the synopsis lines
of all available modules.

Run "pydoc -p <port>" to start an HTTP server on the given port on the
local machine.  Port number 0 can be used to get an arbitrary unused port.

Run "pydoc -b" to start an HTTP server on an arbitrary unused port and
open a Web browser to interactively browse documentation.  The -p option
can be used with the -b option to explicitly specify the server port.

Run "pydoc -w <name>" to write out the HTML documentation for a module
to a file named "<name>.html".

Module docs for core modules are assumed to be in

    http://docs.python.org/X.Y/library/

This can be overridden by setting the PYTHONDOCS environment variable
to a different URL or to a local directory containing the Library
Reference Manual pages.
"""
__all__ = ['help']
__author__ = "Ka-Ping Yee <ping@lfw.org>"
__date__ = "26 February 2001"

__credits__ = """Guido van Rossum, for an excellent programming language.
Tommy Burnette, the original creator of manpy.
Paul Prescod, for all his work on onlinehelp.
Richard Chamberlain, for the first implementation of textdoc.
"""

# Known bugs that can't be fixed here:
#   - synopsis() cannot be prevented from clobbering existing
#     loaded modules.
#   - If the __file__ attribute on a module is a relative path and
#     the current directory is changed with os.chdir(), an incorrect
#     path will be displayed.

import builtins
import importlib._bootstrap
import importlib._bootstrap_external
import importlib.machinery
import importlib.util
import inspect
import io
import os
import pkgutil
import platform
import re
import sys
import time
import tokenize
import urllib.parse
import warnings
from collections import deque
from reprlib import Repr
from traceback import format_exception_only


# --------------------------------------------------------- common routines

def pathdirs():
    """Convert sys.path into a list of absolute, existing, unique paths."""
    dirs = []
    normdirs = []
    for dir in sys.path:
        dir = os.path.abspath(dir or '.')
        normdir = os.path.normcase(dir)
        if normdir not in normdirs and os.path.isdir(dir):
            dirs.append(dir)
            normdirs.append(normdir)
    return dirs

def getdoc(object):
    """Get the doc string or comments for an object."""
    result = inspect.getdoc(object) or inspect.getcomments(object)
    return result and re.sub('^ *\n', '', result.rstrip()) or ''

def splitdoc(doc):
    """Split a doc string into a synopsis line (if any) and the rest."""
    lines = doc.strip().split('\n')
    if len(lines) == 1:
        return lines[0], ''
    elif len(lines) >= 2 and not lines[1].rstrip():
        return lines[0], '\n'.join(lines[2:])
    return '', '\n'.join(lines)

def classname(object, modname):
    """Get a class name and qualify it with a module name if necessary."""
    name = object.__name__
    if object.__module__ != modname:
        name = object.__module__ + '.' + name
    return name

def isdata(object):
    """Check if an object is of a type that probably means it's data."""
    return not (inspect.ismodule(object) or inspect.isclass(object) or
                inspect.isroutine(object) or inspect.isframe(object) or
                inspect.istraceback(object) or inspect.iscode(object))

def replace(text, *pairs):
    """Do a series of global replacements on a string."""
    while pairs:
        text = pairs[1].join(text.split(pairs[0]))
        pairs = pairs[2:]
    return text

def cram(text, maxlen):
    """Omit part of a string if needed to make it fit in a maximum length."""
    if len(text) > maxlen:
        pre = max(0, (maxlen-3)//2)
        post = max(0, maxlen-3-pre)
        return text[:pre] + '...' + text[len(text)-post:]
    return text

_re_stripid = re.compile(r' at 0x[0-9a-f]{6,16}(>+)$', re.IGNORECASE)
def stripid(text):
    """Remove the hexadecimal id from a Python object representation."""
    # The behaviour of %p is implementation-dependent in terms of case.
    return _re_stripid.sub(r'\1', text)

def _is_some_method(obj):
    return (inspect.isfunction(obj) or
            inspect.ismethod(obj) or
            inspect.isbuiltin(obj) or
            inspect.ismethoddescriptor(obj))

def _is_bound_method(fn):
    """
    Returns True if fn is a bound method, regardless of whether
    fn was implemented in Python or in C.
    """
    if inspect.ismethod(fn):
        return True
    if inspect.isbuiltin(fn):
        self = getattr(fn, '__self__', None)
        return not (inspect.ismodule(self) or (self is None))
    return False


def allmethods(cl):
    methods = {}
    for key, value in inspect.getmembers(cl, _is_some_method):
        methods[key] = 1
    for base in cl.__bases__:
        methods.update(allmethods(base)) # all your base are belong to us
    for key in methods.keys():
        methods[key] = getattr(cl, key)
    return methods

def _split_list(s, predicate):
    """Split sequence s via predicate, and return pair ([true], [false]).

    The return value is a 2-tuple of lists,
        ([x for x in s if predicate(x)],
         [x for x in s if not predicate(x)])
    """

    yes = []
    no = []
    for x in s:
        if predicate(x):
            yes.append(x)
        else:
            no.append(x)
    return yes, no

def visiblename(name, all=None, obj=None):
    """Decide whether to show documentation on a variable."""
    # Certain special names are redundant or internal.
    # XXX Remove __initializing__?
    if name in {'__author__', '__builtins__', '__cached__', '__credits__',
                '__date__', '__doc__', '__file__', '__spec__',
                '__loader__', '__module__', '__name__', '__package__',
                '__path__', '__qualname__', '__slots__', '__version__'}:
        return 0
    # Private names are hidden, but special names are displayed.
    if name.startswith('__') and name.endswith('__'): return 1
    # Namedtuples have public fields and methods with a single leading underscore
    if name.startswith('_') and hasattr(obj, '_fields'):
        return True
    if all is not None:
        # only document that which the programmer exported in __all__
        return name in all
    else:
        return not name.startswith('_')

def classify_class_attrs(object):
    """Wrap inspect.classify_class_attrs, with fixup for data descriptors."""
    results = []
    for (name, kind, cls, value) in inspect.classify_class_attrs(object):
        if inspect.isdatadescriptor(value):
            kind = 'data descriptor'
        results.append((name, kind, cls, value))
    return results

def sort_attributes(attrs, object):
    'Sort the attrs list in-place by _fields and then alphabetically by name'
    # This allows data descriptors to be ordered according
    # to a _fields attribute if present.
    fields = getattr(object, '_fields', [])
    try:
        field_order = {name : i-len(fields) for (i, name) in enumerate(fields)}
    except TypeError:
        field_order = {}
    keyfunc = lambda attr: (field_order.get(attr[0], 0), attr[0])
    attrs.sort(key=keyfunc)

# ----------------------------------------------------- module manipulation

def ispackage(path):
    """Guess whether a path refers to a package directory."""
    if os.path.isdir(path):
        for ext in ('.py', '.pyc'):
            if os.path.isfile(os.path.join(path, '__init__' + ext)):
                return True
    return False

def source_synopsis(file):
    line = file.readline()
    while line[:1] == '#' or not line.strip():
        line = file.readline()
        if not line: break
    line = line.strip()
    if line[:4] == 'r"""': line = line[1:]
    if line[:3] == '"""':
        line = line[3:]
        if line[-1:] == '\\': line = line[:-1]
        while not line.strip():
            line = file.readline()
            if not line: break
        result = line.split('"""')[0].strip()
    else: result = None
    return result

def synopsis(filename, cache={}):
    """Get the one-line summary out of a module file."""
    mtime = os.stat(filename).st_mtime
    lastupdate, result = cache.get(filename, (None, None))
    if lastupdate is None or lastupdate < mtime:
        # Look for binary suffixes first, falling back to source.
        if filename.endswith(tuple(importlib.machinery.BYTECODE_SUFFIXES)):
            loader_cls = importlib.machinery.SourcelessFileLoader
        elif filename.endswith(tuple(importlib.machinery.EXTENSION_SUFFIXES)):
            loader_cls = importlib.machinery.ExtensionFileLoader
        else:
            loader_cls = None
        # Now handle the choice.
        if loader_cls is None:
            # Must be a source file.
            try:
                file = tokenize.open(filename)
            except OSError:
                # module can't be opened, so skip it
                return None
            # text modules can be directly examined
            with file:
                result = source_synopsis(file)
        else:
            # Must be a binary module, which has to be imported.
            loader = loader_cls('__temp__', filename)
            # XXX We probably don't need to pass in the loader here.
            spec = importlib.util.spec_from_file_location('__temp__', filename,
                                                          loader=loader)
            try:
                module = importlib._bootstrap._load(spec)
            except:
                return None
            del sys.modules['__temp__']
            result = module.__doc__.splitlines()[0] if module.__doc__ else None
        # Cache the result.
        cache[filename] = (mtime, result)
    return result

class ErrorDuringImport(Exception):
    """Errors that occurred while trying to import something to document it."""
    def __init__(self, filename, exc_info):
        self.filename = filename
        self.exc, self.value, self.tb = exc_info

    def __str__(self):
        exc = self.exc.__name__
        return 'problem in %s - %s: %s' % (self.filename, exc, self.value)

def importfile(path):
    """Import a Python source file or compiled file given its path."""
    magic = importlib.util.MAGIC_NUMBER
    with open(path, 'rb') as file:
        is_bytecode = magic == file.read(len(magic))
    filename = os.path.basename(path)
    name, ext = os.path.splitext(filename)
    if is_bytecode:
        loader = importlib._bootstrap_external.SourcelessFileLoader(name, path)
    else:
        loader = importlib._bootstrap_external.SourceFileLoader(name, path)
    # XXX We probably don't need to pass in the loader here.
    spec = importlib.util.spec_from_file_location(name, path, loader=loader)
    try:
        return importlib._bootstrap._load(spec)
    except:
        raise ErrorDuringImport(path, sys.exc_info())

def safeimport(path, forceload=0, cache={}):
    """Import a module; handle errors; return None if the module isn't found.

    If the module *is* found but an exception occurs, it's wrapped in an
    ErrorDuringImport exception and reraised.  Unlike __import__, if a
    package path is specified, the module at the end of the path is returned,
    not the package at the beginning.  If the optional 'forceload' argument
    is 1, we reload the module from disk (unless it's a dynamic extension)."""
    try:
        # If forceload is 1 and the module has been previously loaded from
        # disk, we always have to reload the module.  Checking the file's
        # mtime isn't good enough (e.g. the module could contain a class
        # that inherits from another module that has changed).
        if forceload and path in sys.modules:
            if path not in sys.builtin_module_names:
                # Remove the module from sys.modules and re-import to try
                # and avoid problems with partially loaded modules.
                # Also remove any submodules because they won't appear
                # in the newly loaded module's namespace if they're already
                # in sys.modules.
                subs = [m for m in sys.modules if m.startswith(path + '.')]
                for key in [path] + subs:
                    # Prevent garbage collection.
                    cache[key] = sys.modules[key]
                    del sys.modules[key]
        module = __import__(path)
    except:
        # Did the error occur before or after the module was found?
        (exc, value, tb) = info = sys.exc_info()
        if path in sys.modules:
            # An error occurred while executing the imported module.
            raise ErrorDuringImport(sys.modules[path].__file__, info)
        elif exc is SyntaxError:
            # A SyntaxError occurred before we could execute the module.
            raise ErrorDuringImport(value.filename, info)
        elif exc is ImportError and value.name == path:
            # No such module in the path.
            return None
        else:
            # Some other error occurred during the importing process.
            raise ErrorDuringImport(path, sys.exc_info())
    for part in path.split('.')[1:]:
        try: module = getattr(module, part)
        except AttributeError: return None
    return module

# ---------------------------------------------------- formatter base class

class Doc:

    PYTHONDOCS = os.environ.get("PYTHONDOCS",
                                "https://docs.python.org/%d.%d/library"
                                % sys.version_info[:2])

    def document(self, object, name=None, *args):
        """Generate documentation for an object."""
        args = (object, name) + args
        # 'try' clause is to attempt to handle the possibility that inspect
        # identifies something in a way that pydoc itself has issues handling;
        # think 'super' and how it is a descriptor (which raises the exception
        # by lacking a __name__ attribute) and an instance.
        if inspect.isgetsetdescriptor(object): return self.docdata(*args)
        if inspect.ismemberdescriptor(object): return self.docdata(*args)
        try:
            if inspect.ismodule(object): return self.docmodule(*args)
            if inspect.isclass(object): return self.docclass(*args)
            if inspect.isroutine(object): return self.docroutine(*args)
        except AttributeError:
            pass
        if isinstance(object, property): return self.docproperty(*args)
        return self.docother(*args)

    def fail(self, object, name=None, *args):
        """Raise an exception for unimplemented types."""
        message = "don't know how to document object%s of type %s" % (
            name and ' ' + repr(name), type(object).__name__)
        raise TypeError(message)

    docmodule = docclass = docroutine = docother = docproperty = docdata = fail

    def getdocloc(self, object,
                  basedir=os.path.join(sys.base_exec_prefix, "lib",
                                       "python%d.%d" %  sys.version_info[:2])):
        """Return the location of module docs or None"""

        try:
            file = inspect.getabsfile(object)
        except TypeError:
            file = '(built-in)'

        docloc = os.environ.get("PYTHONDOCS", self.PYTHONDOCS)

        if (isinstance(object, type(os)) and
            (object.__name__ in ('errno', 'exceptions', 'gc', 'imp',
                                 'marshal', 'posix', 'signal', 'sys',
                                 '_thread', 'zipimport') or
             (file.startswith(basedir) and
              not file.startswith(os.path.join(basedir, 'site-packages')))) and
            object.__name__ not in ('xml.etree', 'test.pydoc_mod')):
            if docloc.startswith("http://"):
                docloc = "%s/%s" % (docloc.rstrip("/"), object.__name__.lower())
            else:
                docloc = os.path.join(docloc, object.__name__.lower() + ".html")
        else:
            docloc = None
        return docloc

# -------------------------------------------- HTML documentation generator

class HTMLRepr(Repr):
    """Class for safely making an HTML representation of a Python object."""
    def __init__(self):
        Repr.__init__(self)
        self.maxlist = self.maxtuple = 20
        self.maxdict = 10
        self.maxstring = self.maxother = 100

    def escape(self, text):
        return replace(text, '&', '&amp;', '<', '&lt;', '>', '&gt;')

    def repr(self, object):
        return Repr.repr(self, object)

    def repr1(self, x, level):
        if hasattr(type(x), '__name__'):
            methodname = 'repr_' + '_'.join(type(x).__name__.split())
            if hasattr(self, methodname):
                return getattr(self, methodname)(x, level)
        return self.escape(cram(stripid(repr(x)), self.maxother))

    def repr_string(self, x, level):
        test = cram(x, self.maxstring)
        testrepr = repr(test)
        if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
            # Backslashes are only literal in the string and are never
            # needed to make any special characters, so show a raw string.
            return 'r' + testrepr[0] + self.escape(test) + testrepr[0]
        return re.sub(r'((\\[\\abfnrtv\'"]|\\[0-9]..|\\x..|\\u....)+)',
                      r'<font color="#c040c0">\1</font>',
                      self.escape(testrepr))

    repr_str = repr_string

    def repr_instance(self, x, level):
        try:
            return self.escape(cram(stripid(repr(x)), self.maxstring))
        except:
            return self.escape('<%s instance>' % x.__class__.__name__)

    repr_unicode = repr_string

class HTMLDoc(Doc):
    """Formatter class for HTML documentation."""

    # ------------------------------------------- HTML formatting utilities

    _repr_instance = HTMLRepr()
    repr = _repr_instance.repr
    escape = _repr_instance.escape

    def page(self, title, contents):
        """Format an HTML page."""
        return '''\
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: %s</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
%s
</body></html>''' % (title, contents)

    def heading(self, title, fgcol, bgcol, extras=''):
        """Format a page heading."""
        return '''
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="%s">
<td valign=bottom>&nbsp;<br>
<font color="%s" face="helvetica, arial">&nbsp;<br>%s</font></td
><td align=right valign=bottom
><font color="%s" face="helvetica, arial">%s</font></td></tr></table>
    ''' % (bgcol, fgcol, title, fgcol, extras or '&nbsp;')

    def section(self, title, fgcol, bgcol, contents, width=6,
                prelude='', marginalia=None, gap='&nbsp;'):
        """Format a section with a heading."""
        if marginalia is None:
            marginalia = '<tt>' + '&nbsp;' * width + '</tt>'
        result = '''<p>
<table width="100%%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="%s">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="%s" face="helvetica, arial">%s</font></td></tr>
    ''' % (bgcol, fgcol, title)
        if prelude:
            result = result + '''
<tr bgcolor="%s"><td rowspan=2>%s</td>
<td colspan=2>%s</td></tr>
<tr><td>%s</td>''' % (bgcol, marginalia, prelude, gap)
        else:
            result = result + '''
<tr><td bgcolor="%s">%s</td><td>%s</td>''' % (bgcol, marginalia, gap)

        return result + '\n<td width="100%%">%s</td></tr></table>' % contents

    def bigsection(self, title, *args):
        """Format a section with a big heading."""
        title = '<big><strong>%s</strong></big>' % title
        return self.section(title, *args)

    def preformat(self, text):
        """Format literal preformatted text."""
        text = self.escape(text.expandtabs())
        return replace(text, '\n\n', '\n \n', '\n\n', '\n \n',
                             ' ', '&nbsp;', '\n', '<br>\n')

    def multicolumn(self, list, format, cols=4):
        """Format a list of items into a multi-column list."""
        result = ''
        rows = (len(list)+cols-1)//cols
        for col in range(cols):
            result = result + '<td width="%d%%" valign=top>' % (100//cols)
            for i in range(rows*col, rows*col+rows):
                if i < len(list):
                    result = result + format(list[i]) + '<br>\n'
            result = result + '</td>'
        return '<table width="100%%" summary="list"><tr>%s</tr></table>' % result

    def grey(self, text): return '<font color="#909090">%s</font>' % text

    def namelink(self, name, *dicts):
        """Make a link for an identifier, given name-to-URL mappings."""
        for dict in dicts:
            if name in dict:
                return '<a href="%s">%s</a>' % (dict[name], name)
        return name

    def classlink(self, object, modname):
        """Make a link for a class."""
        name, module = object.__name__, sys.modules.get(object.__module__)
        if hasattr(module, name) and getattr(module, name) is object:
            return '<a href="%s.html#%s">%s</a>' % (
                module.__name__, name, classname(object, modname))
        return classname(object, modname)

    def modulelink(self, object):
        """Make a link for a module."""
        return '<a href="%s.html">%s</a>' % (object.__name__, object.__name__)

    def modpkglink(self, modpkginfo):
        """Make a link for a module or package to display in an index."""
        name, path, ispackage, shadowed = modpkginfo
        if shadowed:
            return self.grey(name)
        if path:
            url = '%s.%s.html' % (path, name)
        else:
            url = '%s.html' % name
        if ispackage:
            text = '<strong>%s</strong>&nbsp;(package)' % name
        else:
            text = name
        return '<a href="%s">%s</a>' % (url, text)

    def filelink(self, url, path):
        """Make a link to source file."""
        return '<a href="file:%s">%s</a>' % (url, path)

    def markup(self, text, escape=None, funcs={}, classes={}, methods={}):
        """Mark up some plain text, given a context of symbols to look for.
        Each context dictionary maps object names to anchor names."""
        escape = escape or self.escape
        results = []
        here = 0
        pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|'
                                r'RFC[- ]?(\d+)|'
                                r'PEP[- ]?(\d+)|'
                                r'(self\.)?(\w+))')
        while True:
            match = pattern.search(text, here)
            if not match: break
            start, end = match.span()
            results.append(escape(text[here:start]))

            all, scheme, rfc, pep, selfdot, name = match.groups()
            if scheme:
                url = escape(all).replace('"', '&quot;')
                results.append('<a href="%s">%s</a>' % (url, url))
            elif rfc:
                url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc)
                results.append('<a href="%s">%s</a>' % (url, escape(all)))
            elif pep:
                url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep)
                results.append('<a href="%s">%s</a>' % (url, escape(all)))
            elif selfdot:
                # Create a link for methods like 'self.method(...)'
                # and use <strong> for attributes like 'self.attr'
                if text[end:end+1] == '(':
                    results.append('self.' + self.namelink(name, methods))
                else:
                    results.append('self.<strong>%s</strong>' % name)
            elif text[end:end+1] == '(':
                results.append(self.namelink(name, methods, funcs, classes))
            else:
                results.append(self.namelink(name, classes))
            here = end
        results.append(escape(text[here:]))
        return ''.join(results)

    # ---------------------------------------------- type-specific routines

    def formattree(self, tree, modname, parent=None):
        """Produce HTML for a class tree as given by inspect.getclasstree()."""
        result = ''
        for entry in tree:
            if type(entry) is type(()):
                c, bases = entry
                result = result + '<dt><font face="helvetica, arial">'
                result = result + self.classlink(c, modname)
                if bases and bases != (parent,):
                    parents = []
                    for base in bases:
                        parents.append(self.classlink(base, modname))
                    result = result + '(' + ', '.join(parents) + ')'
                result = result + '\n</font></dt>'
            elif type(entry) is type([]):
                result = result + '<dd>\n%s</dd>\n' % self.formattree(
                    entry, modname, c)
        return '<dl>\n%s</dl>\n' % result

    def docmodule(self, object, name=None, mod=None, *ignored):
        """Produce HTML documentation for a module object."""
        name = object.__name__ # ignore the passed-in name
        try:
            all = object.__all__
        except AttributeError:
            all = None
        parts = name.split('.')
        links = []
        for i in range(len(parts)-1):
            links.append(
                '<a href="%s.html"><font color="#ffffff">%s</font></a>' %
                ('.'.join(parts[:i+1]), parts[i]))
        linkedname = '.'.join(links + parts[-1:])
        head = '<big><big><strong>%s</strong></big></big>' % linkedname
        try:
            path = inspect.getabsfile(object)
            url = urllib.parse.quote(path)
            filelink = self.filelink(url, path)
        except TypeError:
            filelink = '(built-in)'
        info = []
        if hasattr(object, '__version__'):
            version = str(object.__version__)
            if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
                version = version[11:-1].strip()
            info.append('version %s' % self.escape(version))
        if hasattr(object, '__date__'):
            info.append(self.escape(str(object.__date__)))
        if info:
            head = head + ' (%s)' % ', '.join(info)
        docloc = self.getdocloc(object)
        if docloc is not None:
            docloc = '<br><a href="%(docloc)s">Module Reference</a>' % locals()
        else:
            docloc = ''
        result = self.heading(
            head, '#ffffff', '#7799ee',
            '<a href=".">index</a><br>' + filelink + docloc)

        modules = inspect.getmembers(object, inspect.ismodule)

        classes, cdict = [], {}
        for key, value in inspect.getmembers(object, inspect.isclass):
            # if __all__ exists, believe it.  Otherwise use old heuristic.
            if (all is not None or
                (inspect.getmodule(value) or object) is object):
                if visiblename(key, all, object):
                    classes.append((key, value))
                    cdict[key] = cdict[value] = '#' + key
        for key, value in classes:
            for base in value.__bases__:
                key, modname = base.__name__, base.__module__
                module = sys.modules.get(modname)
                if modname != name and module and hasattr(module, key):
                    if getattr(module, key) is base:
                        if not key in cdict:
                            cdict[key] = cdict[base] = modname + '.html#' + key
        funcs, fdict = [], {}
        for key, value in inspect.getmembers(object, inspect.isroutine):
            # if __all__ exists, believe it.  Otherwise use old heuristic.
            if (all is not None or
                inspect.isbuiltin(value) or inspect.getmodule(value) is object):
                if visiblename(key, all, object):
                    funcs.append((key, value))
                    fdict[key] = '#-' + key
                    if inspect.isfunction(value): fdict[value] = fdict[key]
        data = []
        for key, value in inspect.getmembers(object, isdata):
            if visiblename(key, all, object):
                data.append((key, value))

        doc = self.markup(getdoc(object), self.preformat, fdict, cdict)
        doc = doc and '<tt>%s</tt>' % doc
        result = result + '<p>%s</p>\n' % doc

        if hasattr(object, '__path__'):
            modpkgs = []
            for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
                modpkgs.append((modname, name, ispkg, 0))
            modpkgs.sort()
            contents = self.multicolumn(modpkgs, self.modpkglink)
            result = result + self.bigsection(
                'Package Contents', '#ffffff', '#aa55cc', contents)
        elif modules:
            contents = self.multicolumn(
                modules, lambda t: self.modulelink(t[1]))
            result = result + self.bigsection(
                'Modules', '#ffffff', '#aa55cc', contents)

        if classes:
            classlist = [value for (key, value) in classes]
            contents = [
                self.formattree(inspect.getclasstree(classlist, 1), name)]
            for key, value in classes:
                contents.append(self.document(value, key, name, fdict, cdict))
            result = result + self.bigsection(
                'Classes', '#ffffff', '#ee77aa', ' '.join(contents))
        if funcs:
            contents = []
            for key, value in funcs:
                contents.append(self.document(value, key, name, fdict, cdict))
            result = result + self.bigsection(
                'Functions', '#ffffff', '#eeaa77', ' '.join(contents))
        if data:
            contents = []
            for key, value in data:
                contents.append(self.document(value, key))
            result = result + self.bigsection(
                'Data', '#ffffff', '#55aa55', '<br>\n'.join(contents))
        if hasattr(object, '__author__'):
            contents = self.markup(str(object.__author__), self.preformat)
            result = result + self.bigsection(
                'Author', '#ffffff', '#7799ee', contents)
        if hasattr(object, '__credits__'):
            contents = self.markup(str(object.__credits__), self.preformat)
            result = result + self.bigsection(
                'Credits', '#ffffff', '#7799ee', contents)

        return result

    def docclass(self, object, name=None, mod=None, funcs={}, classes={},
                 *ignored):
        """Produce HTML documentation for a class object."""
        realname = object.__name__
        name = name or realname
        bases = object.__bases__

        contents = []
        push = contents.append

        # Cute little class to pump out a horizontal rule between sections.
        class HorizontalRule:
            def __init__(self):
                self.needone = 0
            def maybe(self):
                if self.needone:
                    push('<hr>\n')
                self.needone = 1
        hr = HorizontalRule()

        # List the mro, if non-trivial.
        mro = deque(inspect.getmro(object))
        if len(mro) > 2:
            hr.maybe()
            push('<dl><dt>Method resolution order:</dt>\n')
            for base in mro:
                push('<dd>%s</dd>\n' % self.classlink(base,
                                                      object.__module__))
            push('</dl>\n')

        def spill(msg, attrs, predicate):
            ok, attrs = _split_list(attrs, predicate)
            if ok:
                hr.maybe()
                push(msg)
                for name, kind, homecls, value in ok:
                    try:
                        value = getattr(object, name)
                    except Exception:
                        # Some descriptors may meet a failure in their __get__.
                        # (bug #1785)
                        push(self._docdescriptor(name, value, mod))
                    else:
                        push(self.document(value, name, mod,
                                        funcs, classes, mdict, object))
                    push('\n')
            return attrs

        def spilldescriptors(msg, attrs, predicate):
            ok, attrs = _split_list(attrs, predicate)
            if ok:
                hr.maybe()
                push(msg)
                for name, kind, homecls, value in ok:
                    push(self._docdescriptor(name, value, mod))
            return attrs

        def spilldata(msg, attrs, predicate):
            ok, attrs = _split_list(attrs, predicate)
            if ok:
                hr.maybe()
                push(msg)
                for name, kind, homecls, value in ok:
                    base = self.docother(getattr(object, name), name, mod)
                    if callable(value) or inspect.isdatadescriptor(value):
                        doc = getattr(value, "__doc__", None)
                    else:
                        doc = None
                    if doc is None:
                        push('<dl><dt>%s</dl>\n' % base)
                    else:
                        doc = self.markup(getdoc(value), self.preformat,
                                          funcs, classes, mdict)
                        doc = '<dd><tt>%s</tt>' % doc
                        push('<dl><dt>%s%s</dl>\n' % (base, doc))
                    push('\n')
            return attrs

        attrs = [(name, kind, cls, value)
                 for name, kind, cls, value in classify_class_attrs(object)
                 if visiblename(name, obj=object)]

        mdict = {}
        for key, kind, homecls, value in attrs:
            mdict[key] = anchor = '#' + name + '-' + key
            try:
                value = getattr(object, name)
            except Exception:
                # Some descriptors may meet a failure in their __get__.
                # (bug #1785)
                pass
            try:
                # The value may not be hashable (e.g., a data attr with
                # a dict or list value).
                mdict[value] = anchor
            except TypeError:
                pass

        while attrs:
            if mro:
                thisclass = mro.popleft()
            else:
                thisclass = attrs[0][2]
            attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)

            if thisclass is builtins.object:
                attrs = inherited
                continue
            elif thisclass is object:
                tag = 'defined here'
            else:
                tag = 'inherited from %s' % self.classlink(thisclass,
                                                           object.__module__)
            tag += ':<br>\n'

            sort_attributes(attrs, object)

            # Pump out the attrs, segregated by kind.
            attrs = spill('Methods %s' % tag, attrs,
                          lambda t: t[1] == 'method')
            attrs = spill('Class methods %s' % tag, attrs,
                          lambda t: t[1] == 'class method')
            attrs = spill('Static methods %s' % tag, attrs,
                          lambda t: t[1] == 'static method')
            attrs = spilldescriptors('Data descriptors %s' % tag, attrs,
                                     lambda t: t[1] == 'data descriptor')
            attrs = spilldata('Data and other attributes %s' % tag, attrs,
                              lambda t: t[1] == 'data')
            assert attrs == []
            attrs = inherited

        contents = ''.join(contents)

        if name == realname:
            title = '<a name="%s">class <strong>%s</strong></a>' % (
                name, realname)
        else:
            title = '<strong>%s</strong> = <a name="%s">class %s</a>' % (
                name, name, realname)
        if bases:
            parents = []
            for base in bases:
                parents.append(self.classlink(base, object.__module__))
            title = title + '(%s)' % ', '.join(parents)
        doc = self.markup(getdoc(object), self.preformat, funcs, classes, mdict)
        doc = doc and '<tt>%s<br>&nbsp;</tt>' % doc

        return self.section(title, '#000000', '#ffc8d8', contents, 3, doc)

    def formatvalue(self, object):
        """Format an argument default value as text."""
        return self.grey('=' + self.repr(object))

    def docroutine(self, object, name=None, mod=None,
                   funcs={}, classes={}, methods={}, cl=None):
        """Produce HTML documentation for a function or method object."""
        realname = object.__name__
        name = name or realname
        anchor = (cl and cl.__name__ or '') + '-' + name
        note = ''
        skipdocs = 0
        if _is_bound_method(object):
            imclass = object.__self__.__class__
            if cl:
                if imclass is not cl:
                    note = ' from ' + self.classlink(imclass, mod)
            else:
                if object.__self__ is not None:
                    note = ' method of %s instance' % self.classlink(
                        object.__self__.__class__, mod)
                else:
                    note = ' unbound %s method' % self.classlink(imclass,mod)

        if name == realname:
            title = '<a name="%s"><strong>%s</strong></a>' % (anchor, realname)
        else:
            if (cl and realname in cl.__dict__ and
                cl.__dict__[realname] is object):
                reallink = '<a href="#%s">%s</a>' % (
                    cl.__name__ + '-' + realname, realname)
                skipdocs = 1
            else:
                reallink = realname
            title = '<a name="%s"><strong>%s</strong></a> = %s' % (
                anchor, name, reallink)
        argspec = None
        if inspect.isroutine(object):
            try:
                signature = inspect.signature(object)
            except (ValueError, TypeError):
                signature = None
            if signature:
                argspec = str(signature)
                if realname == '<lambda>':
                    title = '<strong>%s</strong> <em>lambda</em> ' % name
                    # XXX lambda's won't usually have func_annotations['return']
                    # since the syntax doesn't support but it is possible.
                    # So removing parentheses isn't truly safe.
                    argspec = argspec[1:-1] # remove parentheses
        if not argspec:
            argspec = '(...)'

        decl = title + self.escape(argspec) + (note and self.grey(
               '<font face="helvetica, arial">%s</font>' % note))

        if skipdocs:
            return '<dl><dt>%s</dt></dl>\n' % decl
        else:
            doc = self.markup(
                getdoc(object), self.preformat, funcs, classes, methods)
            doc = doc and '<dd><tt>%s</tt></dd>' % doc
            return '<dl><dt>%s</dt>%s</dl>\n' % (decl, doc)

    def _docdescriptor(self, name, value, mod):
        results = []
        push = results.append

        if name:
            push('<dl><dt><strong>%s</strong></dt>\n' % name)
        if value.__doc__ is not None:
            doc = self.markup(getdoc(value), self.preformat)
            push('<dd><tt>%s</tt></dd>\n' % doc)
        push('</dl>\n')

        return ''.join(results)

    def docproperty(self, object, name=None, mod=None, cl=None):
        """Produce html documentation for a property."""
        return self._docdescriptor(name, object, mod)

    def docother(self, object, name=None, mod=None, *ignored):
        """Produce HTML documentation for a data object."""
        lhs = name and '<strong>%s</strong> = ' % name or ''
        return lhs + self.repr(object)

    def docdata(self, object, name=None, mod=None, cl=None):
        """Produce html documentation for a data descriptor."""
        return self._docdescriptor(name, object, mod)

    def index(self, dir, shadowed=None):
        """Generate an HTML index for a directory of modules."""
        modpkgs = []
        if shadowed is None: shadowed = {}
        for importer, name, ispkg in pkgutil.iter_modules([dir]):
            if any((0xD800 <= ord(ch) <= 0xDFFF) for ch in name):
                # ignore a module if its name contains a surrogate character
                continue
            modpkgs.append((name, '', ispkg, name in shadowed))
            shadowed[name] = 1

        modpkgs.sort()
        contents = self.multicolumn(modpkgs, self.modpkglink)
        return self.bigsection(dir, '#ffffff', '#ee77aa', contents)

# -------------------------------------------- text documentation generator

class TextRepr(Repr):
    """Class for safely making a text representation of a Python object."""
    def __init__(self):
        Repr.__init__(self)
        self.maxlist = self.maxtuple = 20
        self.maxdict = 10
        self.maxstring = self.maxother = 100

    def repr1(self, x, level):
        if hasattr(type(x), '__name__'):
            methodname = 'repr_' + '_'.join(type(x).__name__.split())
            if hasattr(self, methodname):
                return getattr(self, methodname)(x, level)
        return cram(stripid(repr(x)), self.maxother)

    def repr_string(self, x, level):
        test = cram(x, self.maxstring)
        testrepr = repr(test)
        if '\\' in test and '\\' not in replace(testrepr, r'\\', ''):
            # Backslashes are only literal in the string and are never
            # needed to make any special characters, so show a raw string.
            return 'r' + testrepr[0] + test + testrepr[0]
        return testrepr

    repr_str = repr_string

    def repr_instance(self, x, level):
        try:
            return cram(stripid(repr(x)), self.maxstring)
        except:
            return '<%s instance>' % x.__class__.__name__

class TextDoc(Doc):
    """Formatter class for text documentation."""

    # ------------------------------------------- text formatting utilities

    _repr_instance = TextRepr()
    repr = _repr_instance.repr

    def bold(self, text):
        """Format a string in bold by overstriking."""
        return ''.join(ch + '\b' + ch for ch in text)

    def indent(self, text, prefix='    '):
        """Indent text by prepending a given prefix to each line."""
        if not text: return ''
        lines = [prefix + line for line in text.split('\n')]
        if lines: lines[-1] = lines[-1].rstrip()
        return '\n'.join(lines)

    def section(self, title, contents):
        """Format a section with a given heading."""
        clean_contents = self.indent(contents).rstrip()
        return self.bold(title) + '\n' + clean_contents + '\n\n'

    # ---------------------------------------------- type-specific routines

    def formattree(self, tree, modname, parent=None, prefix=''):
        """Render in text a class tree as returned by inspect.getclasstree()."""
        result = ''
        for entry in tree:
            if type(entry) is type(()):
                c, bases = entry
                result = result + prefix + classname(c, modname)
                if bases and bases != (parent,):
                    parents = (classname(c, modname) for c in bases)
                    result = result + '(%s)' % ', '.join(parents)
                result = result + '\n'
            elif type(entry) is type([]):
                result = result + self.formattree(
                    entry, modname, c, prefix + '    ')
        return result

    def docmodule(self, object, name=None, mod=None):
        """Produce text documentation for a given module object."""
        name = object.__name__ # ignore the passed-in name
        synop, desc = splitdoc(getdoc(object))
        result = self.section('NAME', name + (synop and ' - ' + synop))
        all = getattr(object, '__all__', None)
        docloc = self.getdocloc(object)
        if docloc is not None:
            result = result + self.section('MODULE REFERENCE', docloc + """

The following documentation is automatically generated from the Python
source files.  It may be incomplete, incorrect or include features that
are considered implementation detail and may vary between Python
implementations.  When in doubt, consult the module reference at the
location listed above.
""")

        if desc:
            result = result + self.section('DESCRIPTION', desc)

        classes = []
        for key, value in inspect.getmembers(object, inspect.isclass):
            # if __all__ exists, believe it.  Otherwise use old heuristic.
            if (all is not None
                or (inspect.getmodule(value) or object) is object):
                if visiblename(key, all, object):
                    classes.append((key, value))
        funcs = []
        for key, value in inspect.getmembers(object, inspect.isroutine):
            # if __all__ exists, believe it.  Otherwise use old heuristic.
            if (all is not None or
                inspect.isbuiltin(value) or inspect.getmodule(value) is object):
                if visiblename(key, all, object):
                    funcs.append((key, value))
        data = []
        for key, value in inspect.getmembers(object, isdata):
            if visiblename(key, all, object):
                data.append((key, value))

        modpkgs = []
        modpkgs_names = set()
        if hasattr(object, '__path__'):
            for importer, modname, ispkg in pkgutil.iter_modules(object.__path__):
                modpkgs_names.add(modname)
                if ispkg:
                    modpkgs.append(modname + ' (package)')
                else:
                    modpkgs.append(modname)

            modpkgs.sort()
            result = result + self.section(
                'PACKAGE CONTENTS', '\n'.join(modpkgs))

        # Detect submodules as sometimes created by C extensions
        submodules = []
        for key, value in inspect.getmembers(object, inspect.ismodule):
            if value.__name__.startswith(name + '.') and key not in modpkgs_names:
                submodules.append(key)
        if submodules:
            submodules.sort()
            result = result + self.section(
                'SUBMODULES', '\n'.join(submodules))

        if classes:
            classlist = [value for key, value in classes]
            contents = [self.formattree(
                inspect.getclasstree(classlist, 1), name)]
            for key, value in classes:
                contents.append(self.document(value, key, name))
            result = result + self.section('CLASSES', '\n'.join(contents))

        if funcs:
            contents = []
            for key, value in funcs:
                contents.append(self.document(value, key, name))
            result = result + self.section('FUNCTIONS', '\n'.join(contents))

        if data:
            contents = []
            for key, value in data:
                contents.append(self.docother(value, key, name, maxlen=70))
            result = result + self.section('DATA', '\n'.join(contents))

        if hasattr(object, '__version__'):
            version = str(object.__version__)
            if version[:11] == '$' + 'Revision: ' and version[-1:] == '$':
                version = version[11:-1].strip()
            result = result + self.section('VERSION', version)
        if hasattr(object, '__date__'):
            result = result + self.section('DATE', str(object.__date__))
        if hasattr(object, '__author__'):
            result = result + self.section('AUTHOR', str(object.__author__))
        if hasattr(object, '__credits__'):
            result = result + self.section('CREDITS', str(object.__credits__))
        try:
            file = inspect.getabsfile(object)
        except TypeError:
            file = '(built-in)'
        result = result + self.section('FILE', file)
        return result

    def docclass(self, object, name=None, mod=None, *ignored):
        """Produce text documentation for a given class object."""
        realname = object.__name__
        name = name or realname
        bases = object.__bases__

        def makename(c, m=object.__module__):
            return classname(c, m)

        if name == realname:
            title = 'class ' + self.bold(realname)
        else:
            title = self.bold(name) + ' = class ' + realname
        if bases:
            parents = map(makename, bases)
            title = title + '(%s)' % ', '.join(parents)

        doc = getdoc(object)
        contents = doc and [doc + '\n'] or []
        push = contents.append

        # List the mro, if non-trivial.
        mro = deque(inspect.getmro(object))
        if len(mro) > 2:
            push("Method resolution order:")
            for base in mro:
                push('    ' + makename(base))
            push('')

        # Cute little class to pump out a horizontal rule between sections.
        class HorizontalRule:
            def __init__(self):
                self.needone = 0
            def maybe(self):
                if self.needone:
                    push('-' * 70)
                self.needone = 1
        hr = HorizontalRule()

        def spill(msg, attrs, predicate):
            ok, attrs = _split_list(attrs, predicate)
            if ok:
                hr.maybe()
                push(msg)
                for name, kind, homecls, value in ok:
                    try:
                        value = getattr(object, name)
                    except Exception:
                        # Some descriptors may meet a failure in their __get__.
                        # (bug #1785)
                        push(self._docdescriptor(name, value, mod))
                    else:
                        push(self.document(value,
                                        name, mod, object))
            return attrs

        def spilldescriptors(msg, attrs, predicate):
            ok, attrs = _split_list(attrs, predicate)
            if ok:
                hr.maybe()
                push(msg)
                for name, kind, homecls, value in ok:
                    push(self._docdescriptor(name, value, mod))
            return attrs

        def spilldata(msg, attrs, predicate):
            ok, attrs = _split_list(attrs, predicate)
            if ok:
                hr.maybe()
                push(msg)
                for name, kind, homecls, value in ok:
                    if callable(value) or inspect.isdatadescriptor(value):
                        doc = getdoc(value)
                    else:
                        doc = None
                    try:
                        obj = getattr(object, name)
                    except AttributeError:
                        obj = homecls.__dict__[name]
                    push(self.docother(obj, name, mod, maxlen=70, doc=doc) +
                         '\n')
            return attrs

        attrs = [(name, kind, cls, value)
                 for name, kind, cls, value in classify_class_attrs(object)
                 if visiblename(name, obj=object)]

        while attrs:
            if mro:
                thisclass = mro.popleft()
            else:
                thisclass = attrs[0][2]
            attrs, inherited = _split_list(attrs, lambda t: t[2] is thisclass)

            if thisclass is builtins.object:
                attrs = inherited
                continue
            elif thisclass is object:
                tag = "defined here"
            else:
                tag = "inherited from %s" % classname(thisclass,
                                                      object.__module__)

            sort_attributes(attrs, object)

            # Pump out the attrs, segregated by kind.
            attrs = spill("Methods %s:\n" % tag, attrs,
                          lambda t: t[1] == 'method')
            attrs = spill("Class methods %s:\n" % tag, attrs,
                          lambda t: t[1] == 'class method')
            attrs = spill("Static methods %s:\n" % tag, attrs,
                          lambda t: t[1] == 'static method')
            attrs = spilldescriptors("Data descriptors %s:\n" % tag, attrs,
                                     lambda t: t[1] == 'data descriptor')
            attrs = spilldata("Data and other attributes %s:\n" % tag, attrs,
                              lambda t: t[1] == 'data')

            assert attrs == []
            attrs = inherited

        contents = '\n'.join(contents)
        if not contents:
            return title + '\n'
        return title + '\n' + self.indent(contents.rstrip(), ' |  ') + '\n'

    def formatvalue(self, object):
        """Format an argument default value as text."""
        return '=' + self.repr(object)

    def docroutine(self, object, name=None, mod=None, cl=None):
        """Produce text documentation for a function or method object."""
        realname = object.__name__
        name = name or realname
        note = ''
        skipdocs = 0
        if _is_bound_method(object):
            imclass = object.__self__.__class__
            if cl:
                if imclass is not cl:
                    note = ' from ' + classname(imclass, mod)
            else:
                if object.__self__ is not None:
                    note = ' method of %s instance' % classname(
                        object.__self__.__class__, mod)
                else:
                    note = ' unbound %s method' % classname(imclass,mod)

        if name == realname:
            title = self.bold(realname)
        else:
            if (cl and realname in cl.__dict__ and
                cl.__dict__[realname] is object):
                skipdocs = 1
            title = self.bold(name) + ' = ' + realname
        argspec = None

        if inspect.isroutine(object):
            try:
                signature = inspect.signature(object)
            except (ValueError, TypeError):
                signature = None
            if signature:
                argspec = str(signature)
                if realname == '<lambda>':
                    title = self.bold(name) + ' lambda '
                    # XXX lambda's won't usually have func_annotations['return']
                    # since the syntax doesn't support but it is possible.
                    # So removing parentheses isn't truly safe.
                    argspec = argspec[1:-1] # remove parentheses
        if not argspec:
            argspec = '(...)'
        decl = title + argspec + note

        if skipdocs:
            return decl + '\n'
        else:
            doc = getdoc(object) or ''
            return decl + '\n' + (doc and self.indent(doc).rstrip() + '\n')

    def _docdescriptor(self, name, value, mod):
        results = []
        push = results.append

        if name:
            push(self.bold(name))
            push('\n')
        doc = getdoc(value) or ''
        if doc:
            push(self.indent(doc))
            push('\n')
        return ''.join(results)

    def docproperty(self, object, name=None, mod=None, cl=None):
        """Produce text documentation for a property."""
        return self._docdescriptor(name, object, mod)

    def docdata(self, object, name=None, mod=None, cl=None):
        """Produce text documentation for a data descriptor."""
        return self._docdescriptor(name, object, mod)

    def docother(self, object, name=None, mod=None, parent=None, maxlen=None, doc=None):
        """Produce text documentation for a data object."""
        repr = self.repr(object)
        if maxlen:
            line = (name and name + ' = ' or '') + repr
            chop = maxlen - len(line)
            if chop < 0: repr = repr[:chop] + '...'
        line = (name and self.bold(name) + ' = ' or '') + repr
        if doc is not None:
            line += '\n' + self.indent(str(doc))
        return line

class _PlainTextDoc(TextDoc):
    """Subclass of TextDoc which overrides string styling"""
    def bold(self, text):
        return text

# --------------------------------------------------------- user interfaces

def pager(text):
    """The first time this is called, determine what kind of pager to use."""
    global pager
    pager = getpager()
    pager(text)

def getpager():
    """Decide what method to use for paging through text."""
    if not hasattr(sys.stdin, "isatty"):
        return plainpager
    if not hasattr(sys.stdout, "isatty"):
        return plainpager
    if not sys.stdin.isatty() or not sys.stdout.isatty():
        return plainpager
    if 'PAGER' in os.environ:
        if sys.platform == 'win32': # pipes completely broken in Windows
            return lambda text: tempfilepager(plain(text), os.environ['PAGER'])
        elif os.environ.get('TERM') in ('dumb', 'emacs'):
            return lambda text: pipepager(plain(text), os.environ['PAGER'])
        else:
            return lambda text: pipepager(text, os.environ['PAGER'])
    if os.environ.get('TERM') in ('dumb', 'emacs'):
        return plainpager
    if sys.platform == 'win32':
        return lambda text: tempfilepager(plain(text), 'more <')
    if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
        return lambda text: pipepager(text, 'less')

    import tempfile
    (fd, filename) = tempfile.mkstemp()
    os.close(fd)
    try:
        if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
            return lambda text: pipepager(text, 'more')
        else:
            return ttypager
    finally:
        os.unlink(filename)

def plain(text):
    """Remove boldface formatting from text."""
    return re.sub('.\b', '', text)

def pipepager(text, cmd):
    """Page through text by feeding it to another program."""
    import subprocess
    proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE)
    try:
        with io.TextIOWrapper(proc.stdin, errors='backslashreplace') as pipe:
            try:
                pipe.write(text)
            except KeyboardInterrupt:
                # We've hereby abandoned whatever text hasn't been written,
                # but the pager is still in control of the terminal.
                pass
    except OSError:
        pass # Ignore broken pipes caused by quitting the pager program.
    while True:
        try:
            proc.wait()
            break
        except KeyboardInterrupt:
            # Ignore ctl-c like the pager itself does.  Otherwise the pager is
            # left running and the terminal is in raw mode and unusable.
            pass

def tempfilepager(text, cmd):
    """Page through text by invoking a program on a temporary file."""
    import tempfile
    filename = tempfile.mktemp()
    with open(filename, 'w', errors='backslashreplace') as file:
        file.write(text)
    try:
        os.system(cmd + ' "' + filename + '"')
    finally:
        os.unlink(filename)

def _escape_stdout(text):
    # Escape non-encodable characters to avoid encoding errors later
    encoding = getattr(sys.stdout, 'encoding', None) or 'utf-8'
    return text.encode(encoding, 'backslashreplace').decode(encoding)

def ttypager(text):
    """Page through text on a text terminal."""
    lines = plain(_escape_stdout(text)).split('\n')
    try:
        import tty
        fd = sys.stdin.fileno()
        old = tty.tcgetattr(fd)
        tty.setcbreak(fd)
        getchar = lambda: sys.stdin.read(1)
    except (ImportError, AttributeError, io.UnsupportedOperation):
        tty = None
        getchar = lambda: sys.stdin.readline()[:-1][:1]

    try:
        try:
            h = int(os.environ.get('LINES', 0))
        except ValueError:
            h = 0
        if h <= 1:
            h = 25
        r = inc = h - 1
        sys.stdout.write('\n'.join(lines[:inc]) + '\n')
        while lines[r:]:
            sys.stdout.write('-- more --')
            sys.stdout.flush()
            c = getchar()

            if c in ('q', 'Q'):
                sys.stdout.write('\r          \r')
                break
            elif c in ('\r', '\n'):
                sys.stdout.write('\r          \r' + lines[r] + '\n')
                r = r + 1
                continue
            if c in ('b', 'B', '\x1b'):
                r = r - inc - inc
                if r < 0: r = 0
            sys.stdout.write('\n' + '\n'.join(lines[r:r+inc]) + '\n')
            r = r + inc

    finally:
        if tty:
            tty.tcsetattr(fd, tty.TCSAFLUSH, old)

def plainpager(text):
    """Simply print unformatted text.  This is the ultimate fallback."""
    sys.stdout.write(plain(_escape_stdout(text)))

def describe(thing):
    """Produce a short description of the given thing."""
    if inspect.ismodule(thing):
        if thing.__name__ in sys.builtin_module_names:
            return 'built-in module ' + thing.__name__
        if hasattr(thing, '__path__'):
            return 'package ' + thing.__name__
        else:
            return 'module ' + thing.__name__
    if inspect.isbuiltin(thing):
        return 'built-in function ' + thing.__name__
    if inspect.isgetsetdescriptor(thing):
        return 'getset descriptor %s.%s.%s' % (
            thing.__objclass__.__module__, thing.__objclass__.__name__,
            thing.__name__)
    if inspect.ismemberdescriptor(thing):
        return 'member descriptor %s.%s.%s' % (
            thing.__objclass__.__module__, thing.__objclass__.__name__,
            thing.__name__)
    if inspect.isclass(thing):
        return 'class ' + thing.__name__
    if inspect.isfunction(thing):
        return 'function ' + thing.__name__
    if inspect.ismethod(thing):
        return 'method ' + thing.__name__
    return type(thing).__name__

def locate(path, forceload=0):
    """Locate an object by name or dotted path, importing as necessary."""
    parts = [part for part in path.split('.') if part]
    module, n = None, 0
    while n < len(parts):
        nextmodule = safeimport('.'.join(parts[:n+1]), forceload)
        if nextmodule: module, n = nextmodule, n + 1
        else: break
    if module:
        object = module
    else:
        object = builtins
    for part in parts[n:]:
        try:
            object = getattr(object, part)
        except AttributeError:
            return None
    return object

# --------------------------------------- interactive interpreter interface

text = TextDoc()
plaintext = _PlainTextDoc()
html = HTMLDoc()

def resolve(thing, forceload=0):
    """Given an object or a path to an object, get the object and its name."""
    if isinstance(thing, str):
        object = locate(thing, forceload)
        if object is None:
            raise ImportError('''\
No Python documentation found for %r.
Use help() to get the interactive help utility.
Use help(str) for help on the str class.''' % thing)
        return object, thing
    else:
        name = getattr(thing, '__name__', None)
        return thing, name if isinstance(name, str) else None

def render_doc(thing, title='Python Library Documentation: %s', forceload=0,
        renderer=None):
    """Render text documentation, given an object or a path to an object."""
    if renderer is None:
        renderer = text
    object, name = resolve(thing, forceload)
    desc = describe(object)
    module = inspect.getmodule(object)
    if name and '.' in name:
        desc += ' in ' + name[:name.rfind('.')]
    elif module and module is not object:
        desc += ' in module ' + module.__name__

    if not (inspect.ismodule(object) or
              inspect.isclass(object) or
              inspect.isroutine(object) or
              inspect.isgetsetdescriptor(object) or
              inspect.ismemberdescriptor(object) or
              isinstance(object, property)):
        # If the passed object is a piece of data or an instance,
        # document its available methods instead of its value.
        object = type(object)
        desc += ' object'
    return title % desc + '\n\n' + renderer.document(object, name)

def doc(thing, title='Python Library Documentation: %s', forceload=0,
        output=None):
    """Display text documentation, given an object or a path to an object."""
    try:
        if output is None:
            pager(render_doc(thing, title, forceload))
        else:
            output.write(render_doc(thing, title, forceload, plaintext))
    except (ImportError, ErrorDuringImport) as value:
        print(value)

def writedoc(thing, forceload=0):
    """Write HTML documentation to a file in the current directory."""
    try:
        object, name = resolve(thing, forceload)
        page = html.page(describe(object), html.document(object, name))
        with open(name + '.html', 'w', encoding='utf-8') as file:
            file.write(page)
        print('wrote', name + '.html')
    except (ImportError, ErrorDuringImport) as value:
        print(value)

def writedocs(dir, pkgpath='', done=None):
    """Write out HTML documentation for all modules in a directory tree."""
    if done is None: done = {}
    for importer, modname, ispkg in pkgutil.walk_packages([dir], pkgpath):
        writedoc(modname)
    return

class Helper:

    # These dictionaries map a topic name to either an alias, or a tuple
    # (label, seealso-items).  The "label" is the label of the corresponding
    # section in the .rst file under Doc/ and an index into the dictionary
    # in pydoc_data/topics.py.
    #
    # CAUTION: if you change one of these dictionaries, be sure to adapt the
    #          list of needed labels in Doc/tools/pyspecific.py and
    #          regenerate the pydoc_data/topics.py file by running
    #              make pydoc-topics
    #          in Doc/ and copying the output file into the Lib/ directory.

    keywords = {
        'False': '',
        'None': '',
        'True': '',
        'and': 'BOOLEAN',
        'as': 'with',
        'assert': ('assert', ''),
        'break': ('break', 'while for'),
        'class': ('class', 'CLASSES SPECIALMETHODS'),
        'continue': ('continue', 'while for'),
        'def': ('function', ''),
        'del': ('del', 'BASICMETHODS'),
        'elif': 'if',
        'else': ('else', 'while for'),
        'except': 'try',
        'finally': 'try',
        'for': ('for', 'break continue while'),
        'from': 'import',
        'global': ('global', 'nonlocal NAMESPACES'),
        'if': ('if', 'TRUTHVALUE'),
        'import': ('import', 'MODULES'),
        'in': ('in', 'SEQUENCEMETHODS'),
        'is': 'COMPARISON',
        'lambda': ('lambda', 'FUNCTIONS'),
        'nonlocal': ('nonlocal', 'global NAMESPACES'),
        'not': 'BOOLEAN',
        'or': 'BOOLEAN',
        'pass': ('pass', ''),
        'raise': ('raise', 'EXCEPTIONS'),
        'return': ('return', 'FUNCTIONS'),
        'try': ('try', 'EXCEPTIONS'),
        'while': ('while', 'break continue if TRUTHVALUE'),
        'with': ('with', 'CONTEXTMANAGERS EXCEPTIONS yield'),
        'yield': ('yield', ''),
    }
    # Either add symbols to this dictionary or to the symbols dictionary
    # directly: Whichever is easier. They are merged later.
    _symbols_inverse = {
        'STRINGS' : ("'", "'''", "r'", "b'", '"""', '"', 'r"', 'b"'),
        'OPERATORS' : ('+', '-', '*', '**', '/', '//', '%', '<<', '>>', '&',
                       '|', '^', '~', '<', '>', '<=', '>=', '==', '!=', '<>'),
        'COMPARISON' : ('<', '>', '<=', '>=', '==', '!=', '<>'),
        'UNARY' : ('-', '~'),
        'AUGMENTEDASSIGNMENT' : ('+=', '-=', '*=', '/=', '%=', '&=', '|=',
                                '^=', '<<=', '>>=', '**=', '//='),
        'BITWISE' : ('<<', '>>', '&', '|', '^', '~'),
        'COMPLEX' : ('j', 'J')
    }
    symbols = {
        '%': 'OPERATORS FORMATTING',
        '**': 'POWER',
        ',': 'TUPLES LISTS FUNCTIONS',
        '.': 'ATTRIBUTES FLOAT MODULES OBJECTS',
        '...': 'ELLIPSIS',
        ':': 'SLICINGS DICTIONARYLITERALS',
        '@': 'def class',
        '\\': 'STRINGS',
        '_': 'PRIVATENAMES',
        '__': 'PRIVATENAMES SPECIALMETHODS',
        '`': 'BACKQUOTES',
        '(': 'TUPLES FUNCTIONS CALLS',
        ')': 'TUPLES FUNCTIONS CALLS',
        '[': 'LISTS SUBSCRIPTS SLICINGS',
        ']': 'LISTS SUBSCRIPTS SLICINGS'
    }
    for topic, symbols_ in _symbols_inverse.items():
        for symbol in symbols_:
            topics = symbols.get(symbol, topic)
            if topic not in topics:
                topics = topics + ' ' + topic
            symbols[symbol] = topics

    topics = {
        'TYPES': ('types', 'STRINGS UNICODE NUMBERS SEQUENCES MAPPINGS '
                  'FUNCTIONS CLASSES MODULES FILES inspect'),
        'STRINGS': ('strings', 'str UNICODE SEQUENCES STRINGMETHODS '
                    'FORMATTING TYPES'),
        'STRINGMETHODS': ('string-methods', 'STRINGS FORMATTING'),
        'FORMATTING': ('formatstrings', 'OPERATORS'),
        'UNICODE': ('strings', 'encodings unicode SEQUENCES STRINGMETHODS '
                    'FORMATTING TYPES'),
        'NUMBERS': ('numbers', 'INTEGER FLOAT COMPLEX TYPES'),
        'INTEGER': ('integers', 'int range'),
        'FLOAT': ('floating', 'float math'),
        'COMPLEX': ('imaginary', 'complex cmath'),
        'SEQUENCES': ('typesseq', 'STRINGMETHODS FORMATTING range LISTS'),
        'MAPPINGS': 'DICTIONARIES',
        'FUNCTIONS': ('typesfunctions', 'def TYPES'),
        'METHODS': ('typesmethods', 'class def CLASSES TYPES'),
        'CODEOBJECTS': ('bltin-code-objects', 'compile FUNCTIONS TYPES'),
        'TYPEOBJECTS': ('bltin-type-objects', 'types TYPES'),
        'FRAMEOBJECTS': 'TYPES',
        'TRACEBACKS': 'TYPES',
        'NONE': ('bltin-null-object', ''),
        'ELLIPSIS': ('bltin-ellipsis-object', 'SLICINGS'),
        'SPECIALATTRIBUTES': ('specialattrs', ''),
        'CLASSES': ('types', 'class SPECIALMETHODS PRIVATENAMES'),
        'MODULES': ('typesmodules', 'import'),
        'PACKAGES': 'import',
        'EXPRESSIONS': ('operator-summary', 'lambda or and not in is BOOLEAN '
                        'COMPARISON BITWISE SHIFTING BINARY FORMATTING POWER '
                        'UNARY ATTRIBUTES SUBSCRIPTS SLICINGS CALLS TUPLES '
                        'LISTS DICTIONARIES'),
        'OPERATORS': 'EXPRESSIONS',
        'PRECEDENCE': 'EXPRESSIONS',
        'OBJECTS': ('objects', 'TYPES'),
        'SPECIALMETHODS': ('specialnames', 'BASICMETHODS ATTRIBUTEMETHODS '
                           'CALLABLEMETHODS SEQUENCEMETHODS MAPPINGMETHODS '
                           'NUMBERMETHODS CLASSES'),
        'BASICMETHODS': ('customization', 'hash repr str SPECIALMETHODS'),
        'ATTRIBUTEMETHODS': ('attribute-access', 'ATTRIBUTES SPECIALMETHODS'),
        'CALLABLEMETHODS': ('callable-types', 'CALLS SPECIALMETHODS'),
        'SEQUENCEMETHODS': ('sequence-types', 'SEQUENCES SEQUENCEMETHODS '
                             'SPECIALMETHODS'),
        'MAPPINGMETHODS': ('sequence-types', 'MAPPINGS SPECIALMETHODS'),
        'NUMBERMETHODS': ('numeric-types', 'NUMBERS AUGMENTEDASSIGNMENT '
                          'SPECIALMETHODS'),
        'EXECUTION': ('execmodel', 'NAMESPACES DYNAMICFEATURES EXCEPTIONS'),
        'NAMESPACES': ('naming', 'global nonlocal ASSIGNMENT DELETION DYNAMICFEATURES'),
        'DYNAMICFEATURES': ('dynamic-features', ''),
        'SCOPING': 'NAMESPACES',
        'FRAMES': 'NAMESPACES',
        'EXCEPTIONS': ('exceptions', 'try except finally raise'),
        'CONVERSIONS': ('conversions', ''),
        'IDENTIFIERS': ('identifiers', 'keywords SPECIALIDENTIFIERS'),
        'SPECIALIDENTIFIERS': ('id-classes', ''),
        'PRIVATENAMES': ('atom-identifiers', ''),
        'LITERALS': ('atom-literals', 'STRINGS NUMBERS TUPLELITERALS '
                     'LISTLITERALS DICTIONARYLITERALS'),
        'TUPLES': 'SEQUENCES',
        'TUPLELITERALS': ('exprlists', 'TUPLES LITERALS'),
        'LISTS': ('typesseq-mutable', 'LISTLITERALS'),
        'LISTLITERALS': ('lists', 'LISTS LITERALS'),
        'DICTIONARIES': ('typesmapping', 'DICTIONARYLITERALS'),
        'DICTIONARYLITERALS': ('dict', 'DICTIONARIES LITERALS'),
        'ATTRIBUTES': ('attribute-references', 'getattr hasattr setattr ATTRIBUTEMETHODS'),
        'SUBSCRIPTS': ('subscriptions', 'SEQUENCEMETHODS'),
        'SLICINGS': ('slicings', 'SEQUENCEMETHODS'),
        'CALLS': ('calls', 'EXPRESSIONS'),
        'POWER': ('power', 'EXPRESSIONS'),
        'UNARY': ('unary', 'EXPRESSIONS'),
        'BINARY': ('binary', 'EXPRESSIONS'),
        'SHIFTING': ('shifting', 'EXPRESSIONS'),
        'BITWISE': ('bitwise', 'EXPRESSIONS'),
        'COMPARISON': ('comparisons', 'EXPRESSIONS BASICMETHODS'),
        'BOOLEAN': ('booleans', 'EXPRESSIONS TRUTHVALUE'),
        'ASSERTION': 'assert',
        'ASSIGNMENT': ('assignment', 'AUGMENTEDASSIGNMENT'),
        'AUGMENTEDASSIGNMENT': ('augassign', 'NUMBERMETHODS'),
        'DELETION': 'del',
        'RETURNING': 'return',
        'IMPORTING': 'import',
        'CONDITIONAL': 'if',
        'LOOPING': ('compound', 'for while break continue'),
        'TRUTHVALUE': ('truth', 'if while and or not BASICMETHODS'),
        'DEBUGGING': ('debugger', 'pdb'),
        'CONTEXTMANAGERS': ('context-managers', 'with'),
    }

    def __init__(self, input=None, output=None):
        self._input = input
        self._output = output

    input  = property(lambda self: self._input or sys.stdin)
    output = property(lambda self: self._output or sys.stdout)

    def __repr__(self):
        if inspect.stack()[1][3] == '?':
            self()
            return ''
        return '<%s.%s instance>' % (self.__class__.__module__,
                                     self.__class__.__qualname__)

    _GoInteractive = object()
    def __call__(self, request=_GoInteractive):
        if request is not self._GoInteractive:
            self.help(request)
        else:
            self.intro()
            self.interact()
            self.output.write('''
You are now leaving help and returning to the Python interpreter.
If you want to ask for help on a particular object directly from the
interpreter, you can type "help(object)".  Executing "help('string')"
has the same effect as typing a particular string at the help> prompt.
''')

    def interact(self):
        self.output.write('\n')
        while True:
            try:
                request = self.getline('help> ')
                if not request: break
            except (KeyboardInterrupt, EOFError):
                break
            request = replace(request, '"', '', "'", '').strip()
            if request.lower() in ('q', 'quit'): break
            if request == 'help':
                self.intro()
            else:
                self.help(request)

    def getline(self, prompt):
        """Read one line, using input() when appropriate."""
        if self.input is sys.stdin:
            return input(prompt)
        else:
            self.output.write(prompt)
            self.output.flush()
            return self.input.readline()

    def help(self, request):
        if type(request) is type(''):
            request = request.strip()
            if request == 'keywords': self.listkeywords()
            elif request == 'symbols': self.listsymbols()
            elif request == 'topics': self.listtopics()
            elif request == 'modules': self.listmodules()
            elif request[:8] == 'modules ':
                self.listmodules(request.split()[1])
            elif request in self.symbols: self.showsymbol(request)
            elif request in ['True', 'False', 'None']:
                # special case these keywords since they are objects too
                doc(eval(request), 'Help on %s:')
            elif request in self.keywords: self.showtopic(request)
            elif request in self.topics: self.showtopic(request)
            elif request: doc(request, 'Help on %s:', output=self._output)
            else: doc(str, 'Help on %s:', output=self._output)
        elif isinstance(request, Helper): self()
        else: doc(request, 'Help on %s:', output=self._output)
        self.output.write('\n')

    def intro(self):
        self.output.write('''
Welcome to Python {0}'s help utility!

If this is your first time using Python, you should definitely check out
the tutorial on the Internet at http://docs.python.org/{0}/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules.  To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, symbols, or topics, type
"modules", "keywords", "symbols", or "topics".  Each module also comes
with a one-line summary of what it does; to list the modules whose name
or summary contain a given string such as "spam", type "modules spam".
'''.format('%d.%d' % sys.version_info[:2]))

    def list(self, items, columns=4, width=80):
        items = list(sorted(items))
        colw = width // columns
        rows = (len(items) + columns - 1) // columns
        for row in range(rows):
            for col in range(columns):
                i = col * rows + row
                if i < len(items):
                    self.output.write(items[i])
                    if col < columns - 1:
                        self.output.write(' ' + ' ' * (colw - 1 - len(items[i])))
            self.output.write('\n')

    def listkeywords(self):
        self.output.write('''
Here is a list of the Python keywords.  Enter any keyword to get more help.

''')
        self.list(self.keywords.keys())

    def listsymbols(self):
        self.output.write('''
Here is a list of the punctuation symbols which Python assigns special meaning
to. Enter any symbol to get more help.

''')
        self.list(self.symbols.keys())

    def listtopics(self):
        self.output.write('''
Here is a list of available topics.  Enter any topic name to get more help.

''')
        self.list(self.topics.keys())

    def showtopic(self, topic, more_xrefs=''):
        try:
            import pydoc_data.topics
        except ImportError:
            self.output.write('''
Sorry, topic and keyword documentation is not available because the
module "pydoc_data.topics" could not be found.
''')
            return
        target = self.topics.get(topic, self.keywords.get(topic))
        if not target:
            self.output.write('no documentation found for %s\n' % repr(topic))
            return
        if type(target) is type(''):
            return self.showtopic(target, more_xrefs)

        label, xrefs = target
        try:
            doc = pydoc_data.topics.topics[label]
        except KeyError:
            self.output.write('no documentation found for %s\n' % repr(topic))
            return
        pager(doc.strip() + '\n')
        if more_xrefs:
            xrefs = (xrefs or '') + ' ' + more_xrefs
        if xrefs:
            import textwrap
            text = 'Related help topics: ' + ', '.join(xrefs.split()) + '\n'
            wrapped_text = textwrap.wrap(text, 72)
            self.output.write('\n%s\n' % ''.join(wrapped_text))

    def _gettopic(self, topic, more_xrefs=''):
        """Return unbuffered tuple of (topic, xrefs).

        If an error occurs here, the exception is caught and displayed by
        the url handler.

        This function duplicates the showtopic method but returns its
        result directly so it can be formatted for display in an html page.
        """
        try:
            import pydoc_data.topics
        except ImportError:
            return('''
Sorry, topic and keyword documentation is not available because the
module "pydoc_data.topics" could not be found.
''' , '')
        target = self.topics.get(topic, self.keywords.get(topic))
        if not target:
            raise ValueError('could not find topic')
        if isinstance(target, str):
            return self._gettopic(target, more_xrefs)
        label, xrefs = target
        doc = pydoc_data.topics.topics[label]
        if more_xrefs:
            xrefs = (xrefs or '') + ' ' + more_xrefs
        return doc, xrefs

    def showsymbol(self, symbol):
        target = self.symbols[symbol]
        topic, _, xrefs = target.partition(' ')
        self.showtopic(topic, xrefs)

    def listmodules(self, key=''):
        if key:
            self.output.write('''
Here is a list of modules whose name or summary contains '{}'.
If there are any, enter a module name to get more help.

'''.format(key))
            apropos(key)
        else:
            self.output.write('''
Please wait a moment while I gather a list of all available modules...

''')
            modules = {}
            def callback(path, modname, desc, modules=modules):
                if modname and modname[-9:] == '.__init__':
                    modname = modname[:-9] + ' (package)'
                if modname.find('.') < 0:
                    modules[modname] = 1
            def onerror(modname):
                callback(None, modname, None)
            ModuleScanner().run(callback, onerror=onerror)
            self.list(modules.keys())
            self.output.write('''
Enter any module name to get more help.  Or, type "modules spam" to search
for modules whose name or summary contain the string "spam".
''')

help = Helper()

class ModuleScanner:
    """An interruptible scanner that searches module synopses."""

    def run(self, callback, key=None, completer=None, onerror=None):
        if key: key = key.lower()
        self.quit = False
        seen = {}

        for modname in sys.builtin_module_names:
            if modname != '__main__':
                seen[modname] = 1
                if key is None:
                    callback(None, modname, '')
                else:
                    name = __import__(modname).__doc__ or ''
                    desc = name.split('\n')[0]
                    name = modname + ' - ' + desc
                    if name.lower().find(key) >= 0:
                        callback(None, modname, desc)

        for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror):
            if self.quit:
                break

            if key is None:
                callback(None, modname, '')
            else:
                try:
                    spec = pkgutil._get_spec(importer, modname)
                except SyntaxError:
                    # raised by tests for bad coding cookies or BOM
                    continue
                loader = spec.loader
                if hasattr(loader, 'get_source'):
                    try:
                        source = loader.get_source(modname)
                    except Exception:
                        if onerror:
                            onerror(modname)
                        continue
                    desc = source_synopsis(io.StringIO(source)) or ''
                    if hasattr(loader, 'get_filename'):
                        path = loader.get_filename(modname)
                    else:
                        path = None
                else:
                    try:
                        module = importlib._bootstrap._load(spec)
                    except ImportError:
                        if onerror:
                            onerror(modname)
                        continue
                    desc = module.__doc__.splitlines()[0] if module.__doc__ else ''
                    path = getattr(module,'__file__',None)
                name = modname + ' - ' + desc
                if name.lower().find(key) >= 0:
                    callback(path, modname, desc)

        if completer:
            completer()

def apropos(key):
    """Print all the one-line module summaries that contain a substring."""
    def callback(path, modname, desc):
        if modname[-9:] == '.__init__':
            modname = modname[:-9] + ' (package)'
        print(modname, desc and '- ' + desc)
    def onerror(modname):
        pass
    with warnings.catch_warnings():
        warnings.filterwarnings('ignore') # ignore problems during import
        ModuleScanner().run(callback, key, onerror=onerror)

# --------------------------------------- enhanced Web browser interface

def _start_server(urlhandler, port):
    """Start an HTTP server thread on a specific port.

    Start an HTML/text server thread, so HTML or text documents can be
    browsed dynamically and interactively with a Web browser.  Example use:

        >>> import time
        >>> import pydoc

        Define a URL handler.  To determine what the client is asking
        for, check the URL and content_type.

        Then get or generate some text or HTML code and return it.

        >>> def my_url_handler(url, content_type):
        ...     text = 'the URL sent was: (%s, %s)' % (url, content_type)
        ...     return text

        Start server thread on port 0.
        If you use port 0, the server will pick a random port number.
        You can then use serverthread.port to get the port number.

        >>> port = 0
        >>> serverthread = pydoc._start_server(my_url_handler, port)

        Check that the server is really started.  If it is, open browser
        and get first page.  Use serverthread.url as the starting page.

        >>> if serverthread.serving:
        ...    import webbrowser

        The next two lines are commented out so a browser doesn't open if
        doctest is run on this module.

        #...    webbrowser.open(serverthread.url)
        #True

        Let the server do its thing. We just need to monitor its status.
        Use time.sleep so the loop doesn't hog the CPU.

        >>> starttime = time.time()
        >>> timeout = 1                    #seconds

        This is a short timeout for testing purposes.

        >>> while serverthread.serving:
        ...     time.sleep(.01)
        ...     if serverthread.serving and time.time() - starttime > timeout:
        ...          serverthread.stop()
        ...          break

        Print any errors that may have occurred.

        >>> print(serverthread.error)
        None
   """
    import http.server
    import email.message
    import select
    import threading

    class DocHandler(http.server.BaseHTTPRequestHandler):

        def do_GET(self):
            """Process a request from an HTML browser.

            The URL received is in self.path.
            Get an HTML page from self.urlhandler and send it.
            """
            if self.path.endswith('.css'):
                content_type = 'text/css'
            else:
                content_type = 'text/html'
            self.send_response(200)
            self.send_header('Content-Type', '%s; charset=UTF-8' % content_type)
            self.end_headers()
            self.wfile.write(self.urlhandler(
                self.path, content_type).encode('utf-8'))

        def log_message(self, *args):
            # Don't log messages.
            pass

    class DocServer(http.server.HTTPServer):

        def __init__(self, port, callback):
            self.host = 'localhost'
            self.address = (self.host, port)
            self.callback = callback
            self.base.__init__(self, self.address, self.handler)
            self.quit = False

        def serve_until_quit(self):
            while not self.quit:
                rd, wr, ex = select.select([self.socket.fileno()], [], [], 1)
                if rd:
                    self.handle_request()
            self.server_close()

        def server_activate(self):
            self.base.server_activate(self)
            if self.callback:
                self.callback(self)

    class ServerThread(threading.Thread):

        def __init__(self, urlhandler, port):
            self.urlhandler = urlhandler
            self.port = int(port)
            threading.Thread.__init__(self)
            self.serving = False
            self.error = None

        def run(self):
            """Start the server."""
            try:
                DocServer.base = http.server.HTTPServer
                DocServer.handler = DocHandler
                DocHandler.MessageClass = email.message.Message
                DocHandler.urlhandler = staticmethod(self.urlhandler)
                docsvr = DocServer(self.port, self.ready)
                self.docserver = docsvr
                docsvr.serve_until_quit()
            except Exception as e:
                self.error = e

        def ready(self, server):
            self.serving = True
            self.host = server.host
            self.port = server.server_port
            self.url = 'http://%s:%d/' % (self.host, self.port)

        def stop(self):
            """Stop the server and this thread nicely"""
            self.docserver.quit = True
            self.serving = False
            self.url = None

    thread = ServerThread(urlhandler, port)
    thread.start()
    # Wait until thread.serving is True to make sure we are
    # really up before returning.
    while not thread.error and not thread.serving:
        time.sleep(.01)
    return thread


def _url_handler(url, content_type="text/html"):
    """The pydoc url handler for use with the pydoc server.

    If the content_type is 'text/css', the _pydoc.css style
    sheet is read and returned if it exits.

    If the content_type is 'text/html', then the result of
    get_html_page(url) is returned.
    """
    class _HTMLDoc(HTMLDoc):

        def page(self, title, contents):
            """Format an HTML page."""
            css_path = "pydoc_data/_pydoc.css"
            css_link = (
                '<link rel="stylesheet" type="text/css" href="%s">' %
                css_path)
            return '''\
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Pydoc: %s</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
%s</head><body bgcolor="#f0f0f8">%s<div style="clear:both;padding-top:.5em;">%s</div>
</body></html>''' % (title, css_link, html_navbar(), contents)

        def filelink(self, url, path):
            return '<a href="getfile?key=%s">%s</a>' % (url, path)


    html = _HTMLDoc()

    def html_navbar():
        version = html.escape("%s [%s, %s]" % (platform.python_version(),
                                               platform.python_build()[0],
                                               platform.python_compiler()))
        return """
            <div style='float:left'>
                Python %s<br>%s
            </div>
            <div style='float:right'>
                <div style='text-align:center'>
                  <a href="index.html">Module Index</a>
                  : <a href="topics.html">Topics</a>
                  : <a href="keywords.html">Keywords</a>
                </div>
                <div>
                    <form action="get" style='display:inline;'>
                      <input type=text name=key size=15>
                      <input type=submit value="Get">
                    </form>&nbsp;
                    <form action="search" style='display:inline;'>
                      <input type=text name=key size=15>
                      <input type=submit value="Search">
                    </form>
                </div>
            </div>
            """ % (version, html.escape(platform.platform(terse=True)))

    def html_index():
        """Module Index page."""

        def bltinlink(name):
            return '<a href="%s.html">%s</a>' % (name, name)

        heading = html.heading(
            '<big><big><strong>Index of Modules</strong></big></big>',
            '#ffffff', '#7799ee')
        names = [name for name in sys.builtin_module_names
                 if name != '__main__']
        contents = html.multicolumn(names, bltinlink)
        contents = [heading, '<p>' + html.bigsection(
            'Built-in Modules', '#ffffff', '#ee77aa', contents)]

        seen = {}
        for dir in sys.path:
            contents.append(html.index(dir, seen))

        contents.append(
            '<p align=right><font color="#909090" face="helvetica,'
            'arial"><strong>pydoc</strong> by Ka-Ping Yee'
            '&lt;ping@lfw.org&gt;</font>')
        return 'Index of Modules', ''.join(contents)

    def html_search(key):
        """Search results page."""
        # scan for modules
        search_result = []

        def callback(path, modname, desc):
            if modname[-9:] == '.__init__':
                modname = modname[:-9] + ' (package)'
            search_result.append((modname, desc and '- ' + desc))

        with warnings.catch_warnings():
            warnings.filterwarnings('ignore') # ignore problems during import
            def onerror(modname):
                pass
            ModuleScanner().run(callback, key, onerror=onerror)

        # format page
        def bltinlink(name):
            return '<a href="%s.html">%s</a>' % (name, name)

        results = []
        heading = html.heading(
            '<big><big><strong>Search Results</strong></big></big>',
            '#ffffff', '#7799ee')
        for name, desc in search_result:
            results.append(bltinlink(name) + desc)
        contents = heading + html.bigsection(
            'key = %s' % key, '#ffffff', '#ee77aa', '<br>'.join(results))
        return 'Search Results', contents

    def html_getfile(path):
        """Get and display a source file listing safely."""
        path = urllib.parse.unquote(path)
        with tokenize.open(path) as fp:
            lines = html.escape(fp.read())
        body = '<pre>%s</pre>' % lines
        heading = html.heading(
            '<big><big><strong>File Listing</strong></big></big>',
            '#ffffff', '#7799ee')
        contents = heading + html.bigsection(
            'File: %s' % path, '#ffffff', '#ee77aa', body)
        return 'getfile %s' % path, contents

    def html_topics():
        """Index of topic texts available."""

        def bltinlink(name):
            return '<a href="topic?key=%s">%s</a>' % (name, name)

        heading = html.heading(
            '<big><big><strong>INDEX</strong></big></big>',
            '#ffffff', '#7799ee')
        names = sorted(Helper.topics.keys())

        contents = html.multicolumn(names, bltinlink)
        contents = heading + html.bigsection(
            'Topics', '#ffffff', '#ee77aa', contents)
        return 'Topics', contents

    def html_keywords():
        """Index of keywords."""
        heading = html.heading(
            '<big><big><strong>INDEX</strong></big></big>',
            '#ffffff', '#7799ee')
        names = sorted(Helper.keywords.keys())

        def bltinlink(name):
            return '<a href="topic?key=%s">%s</a>' % (name, name)

        contents = html.multicolumn(names, bltinlink)
        contents = heading + html.bigsection(
            'Keywords', '#ffffff', '#ee77aa', contents)
        return 'Keywords', contents

    def html_topicpage(topic):
        """Topic or keyword help page."""
        buf = io.StringIO()
        htmlhelp = Helper(buf, buf)
        contents, xrefs = htmlhelp._gettopic(topic)
        if topic in htmlhelp.keywords:
            title = 'KEYWORD'
        else:
            title = 'TOPIC'
        heading = html.heading(
            '<big><big><strong>%s</strong></big></big>' % title,
            '#ffffff', '#7799ee')
        contents = '<pre>%s</pre>' % html.markup(contents)
        contents = html.bigsection(topic , '#ffffff','#ee77aa', contents)
        if xrefs:
            xrefs = sorted(xrefs.split())

            def bltinlink(name):
                return '<a href="topic?key=%s">%s</a>' % (name, name)

            xrefs = html.multicolumn(xrefs, bltinlink)
            xrefs = html.section('Related help topics: ',
                                 '#ffffff', '#ee77aa', xrefs)
        return ('%s %s' % (title, topic),
                ''.join((heading, contents, xrefs)))

    def html_getobj(url):
        obj = locate(url, forceload=1)
        if obj is None and url != 'None':
            raise ValueError('could not find object')
        title = describe(obj)
        content = html.document(obj, url)
        return title, content

    def html_error(url, exc):
        heading = html.heading(
            '<big><big><strong>Error</strong></big></big>',
            '#ffffff', '#7799ee')
        contents = '<br>'.join(html.escape(line) for line in
                               format_exception_only(type(exc), exc))
        contents = heading + html.bigsection(url, '#ffffff', '#bb0000',
                                             contents)
        return "Error - %s" % url, contents

    def get_html_page(url):
        """Generate an HTML page for url."""
        complete_url = url
        if url.endswith('.html'):
            url = url[:-5]
        try:
            if url in ("", "index"):
                title, content = html_index()
            elif url == "topics":
                title, content = html_topics()
            elif url == "keywords":
                title, content = html_keywords()
            elif '=' in url:
                op, _, url = url.partition('=')
                if op == "search?key":
                    title, content = html_search(url)
                elif op == "getfile?key":
                    title, content = html_getfile(url)
                elif op == "topic?key":
                    # try topics first, then objects.
                    try:
                        title, content = html_topicpage(url)
                    except ValueError:
                        title, content = html_getobj(url)
                elif op == "get?key":
                    # try objects first, then topics.
                    if url in ("", "index"):
                        title, content = html_index()
                    else:
                        try:
                            title, content = html_getobj(url)
                        except ValueError:
                            title, content = html_topicpage(url)
                else:
                    raise ValueError('bad pydoc url')
            else:
                title, content = html_getobj(url)
        except Exception as exc:
            # Catch any errors and display them in an error page.
            title, content = html_error(complete_url, exc)
        return html.page(title, content)

    if url.startswith('/'):
        url = url[1:]
    if content_type == 'text/css':
        path_here = os.path.dirname(os.path.realpath(__file__))
        css_path = os.path.join(path_here, url)
        with open(css_path) as fp:
            return ''.join(fp.readlines())
    elif content_type == 'text/html':
        return get_html_page(url)
    # Errors outside the url handler are caught by the server.
    raise TypeError('unknown content type %r for url %s' % (content_type, url))


def browse(port=0, *, open_browser=True):
    """Start the enhanced pydoc Web server and open a Web browser.

    Use port '0' to start the server on an arbitrary port.
    Set open_browser to False to suppress opening a browser.
    """
    import webbrowser
    serverthread = _start_server(_url_handler, port)
    if serverthread.error:
        print(serverthread.error)
        return
    if serverthread.serving:
        server_help_msg = 'Server commands: [b]rowser, [q]uit'
        if open_browser:
            webbrowser.open(serverthread.url)
        try:
            print('Server ready at', serverthread.url)
            print(server_help_msg)
            while serverthread.serving:
                cmd = input('server> ')
                cmd = cmd.lower()
                if cmd == 'q':
                    break
                elif cmd == 'b':
                    webbrowser.open(serverthread.url)
                else:
                    print(server_help_msg)
        except (KeyboardInterrupt, EOFError):
            print()
        finally:
            if serverthread.serving:
                serverthread.stop()
                print('Server stopped')


# -------------------------------------------------- command-line interface

def ispath(x):
    return isinstance(x, str) and x.find(os.sep) >= 0

def cli():
    """Command-line interface (looks at sys.argv to decide what to do)."""
    import getopt
    class BadUsage(Exception): pass

    # Scripts don't get the current directory in their path by default
    # unless they are run with the '-m' switch
    if '' not in sys.path:
        scriptdir = os.path.dirname(sys.argv[0])
        if scriptdir in sys.path:
            sys.path.remove(scriptdir)
        sys.path.insert(0, '.')

    try:
        opts, args = getopt.getopt(sys.argv[1:], 'bk:p:w')
        writing = False
        start_server = False
        open_browser = False
        port = None
        for opt, val in opts:
            if opt == '-b':
                start_server = True
                open_browser = True
            if opt == '-k':
                apropos(val)
                return
            if opt == '-p':
                start_server = True
                port = val
            if opt == '-w':
                writing = True

        if start_server:
            if port is None:
                port = 0
            browse(port, open_browser=open_browser)
            return

        if not args: raise BadUsage
        for arg in args:
            if ispath(arg) and not os.path.exists(arg):
                print('file %r does not exist' % arg)
                break
            try:
                if ispath(arg) and os.path.isfile(arg):
                    arg = importfile(arg)
                if writing:
                    if ispath(arg) and os.path.isdir(arg):
                        writedocs(arg)
                    else:
                        writedoc(arg)
                else:
                    help.help(arg)
            except ErrorDuringImport as value:
                print(value)

    except (getopt.error, BadUsage):
        cmd = os.path.splitext(os.path.basename(sys.argv[0]))[0]
        print("""pydoc - the Python documentation tool

{cmd} <name> ...
    Show text documentation on something.  <name> may be the name of a
    Python keyword, topic, function, module, or package, or a dotted
    reference to a class or function within a module or module in a
    package.  If <name> contains a '{sep}', it is used as the path to a
    Python source file to document. If name is 'keywords', 'topics',
    or 'modules', a listing of these things is displayed.

{cmd} -k <keyword>
    Search for a keyword in the synopsis lines of all available modules.

{cmd} -p <port>
    Start an HTTP server on the given port on the local machine.  Port
    number 0 can be used to get an arbitrary unused port.

{cmd} -b
    Start an HTTP server on an arbitrary unused port and open a Web browser
    to interactively browse documentation.  The -p option can be used with
    the -b option to explicitly specify the server port.

{cmd} -w <name> ...
    Write out the HTML documentation for a module to a file in the current
    directory.  If <name> contains a '{sep}', it is treated as a filename; if
    it names a directory, documentation is written for all the contents.
""".format(cmd=cmd, sep=os.sep))

if __name__ == '__main__':
    cli()