diff options
25 files changed, 1254 insertions, 281 deletions
diff --git a/demos/declarative/flickr/flickr-mobile.qml b/demos/declarative/flickr/flickr-mobile.qml index 643298d..c497e4e 100644 --- a/demos/declarative/flickr/flickr-mobile.qml +++ b/demos/declarative/flickr/flickr-mobile.qml @@ -31,9 +31,18 @@ Item { id: PhotoListView; model: RssModel; delegate: ListDelegate width: parent.width; height: parent.height; x: -(parent.width * 1.5); cacheBuffer: 100; } + states: State { + name: "ListView"; when: Screen.inListView == true + PropertyChanges { target: PhotoListView; x: 0 } + PropertyChanges { target: PhotoGridView; x: -(parent.width * 1.5) } + } + + transitions: Transition { + NumberAnimation { properties: "x"; duration: 500; easing: "easeInOutQuad" } + } } - Common.ImageDetails { id: ImageDetails; width: parent.width; x: parent.width; height: parent.height } + Common.ImageDetails { id: ImageDetails; width: parent.width; anchors.left: Views.right; height: parent.height } Mobile.TitleBar { id: TitleBar; width: parent.width; height: 40; opacity: 0.9 } Mobile.ToolBar { @@ -45,9 +54,10 @@ Item { } states: State { - name: "ListView"; when: Screen.inListView == true - PropertyChanges { target: PhotoListView; x: 0 } - PropertyChanges { target: PhotoGridView; x: -(parent.width * 1.5) } + name: "DetailedView" + PropertyChanges { target: Views; x: -parent.width } + PropertyChanges { target: ToolBar; button2Label: "Back" } + PropertyChanges { target: ToolBar; onButton2Clicked: { } } } transitions: Transition { diff --git a/demos/declarative/flickr/mobile/Button.qml b/demos/declarative/flickr/mobile/Button.qml index 275408c..3a0b42e 100644 --- a/demos/declarative/flickr/mobile/Button.qml +++ b/demos/declarative/flickr/mobile/Button.qml @@ -9,13 +9,13 @@ Item { BorderImage { id: Image - source: "images/toolbutton2.sci" + source: "images/toolbutton.sci" width: Container.width; height: Container.height } BorderImage { id: Pressed opacity: 0 - source: "images/toolbutton2.sci" + source: "images/toolbutton.sci" width: Container.width; height: Container.height } MouseRegion { diff --git a/demos/declarative/flickr/mobile/GridDelegate.qml b/demos/declarative/flickr/mobile/GridDelegate.qml index 5ab4d3a..91e7c8d 100644 --- a/demos/declarative/flickr/mobile/GridDelegate.qml +++ b/demos/declarative/flickr/mobile/GridDelegate.qml @@ -48,23 +48,20 @@ name: "Details"; extend: "Show" ParentChange { target: Wrapper; parent: ImageDetails.frontContainer } PropertyChanges { target: Wrapper; x: 20; y: 60 } - PropertyChanges { target: ImageDetails; x: 0 } - PropertyChanges { target: Views; x: -parent.width } - PropertyChanges { target: ToolBar.button2; text: "Back" } - PropertyChanges { target: ToolBar; onButton2Clicked: { } } + PropertyChanges { target: Background; state: "DetailedView" } } ] transitions: [ Transition { from: "Show"; to: "Details" ParentAction { } - NumberAnimation { properties: "x,y,opacity,angle"; duration: 500; easing: "easeInOutQuad" } + NumberAnimation { properties: "x,y"; duration: 500; easing: "easeInOutQuad" } }, Transition { from: "Details"; to: "Show" SequentialAnimation { ParentAction { } - NumberAnimation { properties: "x,y,opacity,angle"; duration: 500; easing: "easeInOutQuad" } + NumberAnimation { properties: "x,y"; duration: 500; easing: "easeInOutQuad" } PropertyAction { target: Wrapper; properties: "z" } } } diff --git a/demos/declarative/flickr/mobile/TitleBar.qml b/demos/declarative/flickr/mobile/TitleBar.qml index b74e414..79c1326 100644 --- a/demos/declarative/flickr/mobile/TitleBar.qml +++ b/demos/declarative/flickr/mobile/TitleBar.qml @@ -5,7 +5,7 @@ Item { property string untaggedString: "Uploads from everyone" property string taggedString: "Recent uploads tagged " - BorderImage { source: "images/titlebar2.sci"; width: parent.width; height: parent.height + 14; y: -7 } + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } Item { id: Container @@ -13,8 +13,9 @@ Item { Script { function accept() { - RssModel.tags = Editor.text TitleBar.state = "" + Background.state = "" + RssModel.tags = Editor.text } } diff --git a/demos/declarative/flickr/mobile/ToolBar.qml b/demos/declarative/flickr/mobile/ToolBar.qml index 80897cc..cfdc8fe 100644 --- a/demos/declarative/flickr/mobile/ToolBar.qml +++ b/demos/declarative/flickr/mobile/ToolBar.qml @@ -3,20 +3,22 @@ import Qt 4.6 Item { id: Toolbar - property var button1Label - property var button2Label + property alias button1Label: Button1.text + property alias button2Label: Button2.text signal button1Clicked signal button2Clicked - BorderImage { source: "images/titlebar2.sci"; width: parent.width; height: parent.height + 14; y: -7 } + BorderImage { source: "images/titlebar.sci"; width: parent.width; height: parent.height + 14; y: -7 } Button { - anchors.left: parent.left; anchors.leftMargin: 5; y: 3; width: 140; height: 32; text: button1Label + id: Button1 + anchors.left: parent.left; anchors.leftMargin: 5; y: 3; width: 140; height: 32 onClicked: Toolbar.button1Clicked() } Button { - anchors.right: parent.right; anchors.rightMargin: 5; y: 3; width: 140; height: 32; text: button2Label + id: Button2 + anchors.right: parent.right; anchors.rightMargin: 5; y: 3; width: 140; height: 32 onClicked: Toolbar.button2Clicked() } } diff --git a/demos/declarative/flickr/mobile/images/titlebar2.png b/demos/declarative/flickr/mobile/images/titlebar.png Binary files differindex 51c9008..51c9008 100644 --- a/demos/declarative/flickr/mobile/images/titlebar2.png +++ b/demos/declarative/flickr/mobile/images/titlebar.png diff --git a/demos/declarative/flickr/mobile/images/titlebar.sci b/demos/declarative/flickr/mobile/images/titlebar.sci new file mode 100644 index 0000000..50444e1 --- /dev/null +++ b/demos/declarative/flickr/mobile/images/titlebar.sci @@ -0,0 +1,5 @@ +gridLeft: 10 +gridTop: 12 +gridBottom: 12 +gridRight: 10 +imageFile: titlebar.png diff --git a/demos/declarative/flickr/mobile/images/titlebar2.sci b/demos/declarative/flickr/mobile/images/titlebar2.sci deleted file mode 100644 index e8fc2d1..0000000 --- a/demos/declarative/flickr/mobile/images/titlebar2.sci +++ /dev/null @@ -1,5 +0,0 @@ -gridLeft: 22 -gridTop: 10 -gridBottom: 10 -gridRight: 22 -imageFile: titlebar2.png diff --git a/demos/declarative/flickr/mobile/images/toolbutton2.png b/demos/declarative/flickr/mobile/images/toolbutton.png Binary files differindex 8862898..8862898 100644 --- a/demos/declarative/flickr/mobile/images/toolbutton2.png +++ b/demos/declarative/flickr/mobile/images/toolbutton.png diff --git a/demos/declarative/flickr/mobile/images/toolbutton2.sci b/demos/declarative/flickr/mobile/images/toolbutton.sci index e3118b0..a0a885f 100644 --- a/demos/declarative/flickr/mobile/images/toolbutton2.sci +++ b/demos/declarative/flickr/mobile/images/toolbutton.sci @@ -2,4 +2,4 @@ gridLeft: 15 gridTop: 4 gridBottom: 4 gridRight: 15 -imageFile: toolbutton2.png +imageFile: toolbutton.png diff --git a/doc/src/declarative/extending.qdoc b/doc/src/declarative/extending.qdoc index 833a5db..3235435 100644 --- a/doc/src/declarative/extending.qdoc +++ b/doc/src/declarative/extending.qdoc @@ -818,7 +818,7 @@ Rectangle { \endcode \endtable -Components may be collected into \l {Modules of Components} that gives the +Components may be collected into \l {Modules} that gives the developer more freedom than just putting files in the same directory. \section2 Building reusable components diff --git a/doc/src/declarative/modules.qdoc b/doc/src/declarative/modules.qdoc index 60cf2a4..7c67f60 100644 --- a/doc/src/declarative/modules.qdoc +++ b/doc/src/declarative/modules.qdoc @@ -1,123 +1,105 @@ /*! -\page qmlmodules.html \target qmlmodules -\title Modules of Components - -A \bold module is a collection of \l Components. - -To use a module, include the following statement at the begining -of your QML: - -\code -import "path" -\endcode - -This allows all components defined in the directory \c path to be used in -the component where this statement appears. - -Currently, \c path may only be a directory relative to the directory containing -the component issuing the import. - -The import statement cannot be used by remote content. -*/ - -/* - -Ideas for full module support.... - -See QT-558. - -* Modularity within applications - -This is the currently-supported mechanism. +\page qmlmodules.html +\title Modules -By using the "import" statement, a subdirectory of types can be added to the -empty namespace. Alternatively, a type in a subdirectory can be referenced -explicitly. +A \bold module is a collection of QML types. -So, given these files: +To use types from a module it must be imported using the \c import statement. Successive +import statements override earlier import statements. - ./SubModule1/Type1.qml - ./SubModule2/Type1.qml +\section1 Importing Built-in Types -This is valid QML: +To use built-in types, you must import the module defining them. +For example, to use types from Qt, import it: - import "SubModule1" - Type1 { ... } - SubModule2.Type1 { ... } +\code +import Qt 4.6 +\endcode +This makes available all types in Qt that were available in Qt 4.6, regardless of the +actual version of Qt executing the QML. -* System-installed modules (dependencies) +Modules can be compiled-in (such as the Qt module), or they can be +defined in QML files. -To use system-installed modules, the dependency must be explicitly stated -using the "require" statement. Types in required modules must still be -explicitly qualified. Dependencies cannot be added to the empty namespace. +\section1 Importing QML Files - QMLPATH=/opt/Nokia/qml:/usr/share/lib/qml - /opt/Nokia/qml/Module1/Type1.qml - /usr/share/lib/qml/Module1/Type1.qml +To import types defined in QML files in directories relative to the file importing them, +a quoted import directory is used: - require "Module1" - Module1.Type1 { ... } +\code +import "path" +\endcode +This allows all components defined in the directory \c path to be used in +the component where this statement appears. -* Grouping of components within application modules +To import types defined in QML files that are installed somewhere on the system, +an unquoted URI is used: -Sub-sub directories allow further grouping of types. +\code +import com.nokia.CoolStuff 1.0 +\endcode - ./SubModule1/Group1/*.qml - ./SubModule1/Group2/*.qml +This will access file in the directory \c com/nokia/CoolStuff/, found in some +location determined outside QML. See QmlEngine::addImportPath() and the \c -L option +to the \l {qmlviewer}{viewer} application. - SubModule1.Group1.Type1 { ... } - SubModule1.Group1.Type2 { ... } - SubModule1.Group2.Type1 { ... } - SubModule1.Group2.Type2 { ... } +The directory of installed files must include a file \c qmldir which specifies the +mapping from all type names to versioned QML files. It is a list of lines of the form: - import "SubModule1/Group1" - Type1 { ... } +\code +# <Comment> +<TypeName> <VersionRange> <File> +\endcode - import "SubModule1" - Group1.Type1 { ... } +<TypeName> is the type being made available; <VersionRange> is either a single version +number like \c 4.0 or a range of minor versions like \c 4.0-2; <File> is the (relative) +file name of the QML file defining the type. +The same type can be provided by different files in different versions. +If a type is in multiple major versions, it should be listed on a separate line. +Installed files do not need to import the module of which they are a part, as they can refer +to the other QML files in the module as relative (local) files. -* Grouping of components within system-installed modules +Installed files \e must be referred to by version information described above, +local files \e may have it. -System-installed types may also be grouped into types. The hierarchy is a -global namespace, so such grouping is recommended to reduce clashes. +The versioning system ensures that a given QML file will work regardless of the version +of installed software, since a versioned import \e only imports types for that version, +leaving other identifiers available, even if the actual installed version might otherwise +use those identifiers. - /opt/Nokia/qml/Module1/Group1/*.qml - /opt/Nokia/qml/Module1/Group2/*.qml +\section1 Namespaces - Named Imports - require "Module1" - Module1.Group1.Type1 { ... } - Module1.Group1.Type2 { ... } - Module1.Group2.Type1 { ... } - Module1.Group2.Type2 { ... } +When importing content it by default imports types into the global namespace. +You may choose to import the module into another namespace, either to allow identically-named +types to be referenced, or purely for readability. - require "Module1/Group1" - Group1.Type1 { ... } +To import a module into a namespace: - // Alternative syntax - /opt/qml/com/nokia/qml/Module1/Group1/*.qml - require "com.nokia.qml.Module1.Group1" - Group1.Type1 { ... } +\code +import Qt 4.6 as TheQtLibrary +\endcode +Types from Qt 4.6 may then be used, but only by qualifying them with the namespace: -* Private sub-components +\code +TheQtLibrary.Rectangle { ... } +\endcode -Directories begining with _ cannot be referenced except by types in the -directory immediately containing it. +Multiple modules can be imported into the same namespace in the same way that multiple +modules can be imported into the global namespace: - /opt/Nokia/qml/Module1/_private/Type1.qml - ./SubModule1/_private/Type1.qml +\code +import Qt 4.6 as Nokia +import Ovi 1.0 as Nokia +\endcode +*/ - SubModule1._private.Type1 { ... } // Not allowed - import "SubModule1._private" // Not allowed - require "SubModule1._private" // Not allowed - require "SubModule1" - Module1._private.Type1 { ... } // Not allowed +/* - import "_private" // allowed - Type1 { ... } +See original requirement QT-558. */ diff --git a/examples/declarative/xmlhttprequest/test.qml b/examples/declarative/xmlhttprequest/test.qml new file mode 100644 index 0000000..56366d6 --- /dev/null +++ b/examples/declarative/xmlhttprequest/test.qml @@ -0,0 +1,26 @@ +import Qt 4.6 + +Rectangle { + width: 800; height: 600 + + MouseRegion { + anchors.fill: parent + onClicked: { + + var doc = new XMLHttpRequest(); + doc.onreadystatechange = function() { + if (doc.readyState == XMLHttpRequest.DONE) { + + var a = doc.responseXML.documentElement; + for (var ii = 0; ii < a.childNodes.length; ++ii) { + print (a.childNodes[ii].nodeName); + } + + } + } + + doc.open("GET", "test.xml"); + doc.send(); + } + } +} diff --git a/examples/declarative/xmlhttprequest/test.xml b/examples/declarative/xmlhttprequest/test.xml new file mode 100644 index 0000000..8b7f1e1 --- /dev/null +++ b/examples/declarative/xmlhttprequest/test.xml @@ -0,0 +1,5 @@ +<data> + <element1 /> + <element2 /> +</data> + diff --git a/src/declarative/fx/qfxwebview.h b/src/declarative/fx/qfxwebview.h index 3dd1b65..f136e2d 100644 --- a/src/declarative/fx/qfxwebview.h +++ b/src/declarative/fx/qfxwebview.h @@ -80,7 +80,6 @@ class QFxWebViewAttached; class QFxWebSettings; //### TODO: browser plugins -//### TODO: smart zooming using e.g. DIV class Q_DECLARATIVE_EXPORT QFxWebView : public QFxPaintedItem { diff --git a/src/declarative/qml/qmlcompositetypemanager.cpp b/src/declarative/qml/qmlcompositetypemanager.cpp index 1b1f67c..f64547c 100644 --- a/src/declarative/qml/qmlcompositetypemanager.cpp +++ b/src/declarative/qml/qmlcompositetypemanager.cpp @@ -329,10 +329,27 @@ void QmlCompositeTypeManager::compile(QmlCompositeTypeData *unit) QUrl url; int majorVersion; int minorVersion; - if (!QmlEnginePrivate::get(engine)->resolveType(unit->imports, typeName, &ref.type, &url, &majorVersion, &minorVersion, 0)) { + QmlEnginePrivate::ImportedNamespace *typeNamespace = 0; + if (!QmlEnginePrivate::get(engine)->resolveType(unit->imports, typeName, &ref.type, &url, &majorVersion, &minorVersion, &typeNamespace)) { // XXX could produce error message here. } + if (typeNamespace) { + QmlError error; + error.setUrl(unit->imports.baseUrl()); + error.setDescription(tr("Namespace %1 cannot be used as a type").arg(QLatin1String(typeName))); + if (!parserRef->refObjects.isEmpty()) { + QmlParser::Object *obj = parserRef->refObjects.first(); + error.setLine(obj->location.start.line); + error.setColumn(obj->location.start.column); + } + unit->status = QmlCompositeTypeData::Error; + unit->errorType = QmlCompositeTypeData::GeneralError; + unit->errors << error; + doComplete(unit); + return; + } + if (ref.type) { foreach (QmlParser::Object *obj, parserRef->refObjects) { // store namespace for DOM diff --git a/src/declarative/qml/qmlengine.cpp b/src/declarative/qml/qmlengine.cpp index 4b98d1b..d2d0590 100644 --- a/src/declarative/qml/qmlengine.cpp +++ b/src/declarative/qml/qmlengine.cpp @@ -109,8 +109,8 @@ QScriptValue desktopOpenUrl(QScriptContext *ctxt, QScriptEngine *e) QmlEnginePrivate::QmlEnginePrivate(QmlEngine *e) : rootContext(0), currentBindContext(0), currentExpression(0), isDebugging(false), contextClass(0), objectClass(0), valueTypeClass(0), - scriptEngine(this), rootComponent(0), networkAccessManager(0), typeManager(e), - uniqueId(1) + nodeListClass(0), namedNodeMapClass(0), scriptEngine(this), rootComponent(0), + networkAccessManager(0), typeManager(e), uniqueId(1) { QScriptValue qtObject = scriptEngine.newQMetaObject(StaticQtMetaObject::get()); @@ -139,6 +139,10 @@ QmlEnginePrivate::~QmlEnginePrivate() objectClass = 0; delete networkAccessManager; networkAccessManager = 0; + delete nodeListClass; + nodeListClass = 0; + delete namedNodeMapClass; + namedNodeMapClass = 0; for(int ii = 0; ii < bindValues.count(); ++ii) clear(bindValues[ii]); @@ -1138,65 +1142,71 @@ struct QmlEnginePrivate::ImportedNamespace { QList<int> majversions; QList<int> minversions; QList<bool> isLibrary; + QList<bool> isBuiltin; - QUrl find(const QString& type) const + bool find(const QByteArray& type, int *vmajor, int *vminor, QmlType** type_return, QUrl* url_return) const { for (int i=0; i<urls.count(); ++i) { - QUrl url = QUrl(urls.at(i) + QLatin1String("/") + type + QLatin1String(".qml")); int vmaj = majversions.at(i); int vmin = minversions.at(i); - if (vmaj || vmin) { - // Check version file - XXX cache these in QmlEngine! - QFile qmldir(QUrl(urls.at(i)+QLatin1String("/qmldir")).toLocalFile()); - if (qmldir.open(QIODevice::ReadOnly)) { - do { - QString line = QString::fromUtf8(qmldir.readLine()); - if (line.at(0) == QLatin1Char('#')) - continue; - int space1 = line.indexOf(QLatin1Char(' ')); - int space2 = space1 >=0 ? line.indexOf(QLatin1Char(' '),space1+1) : -1; - QStringRef maptype = line.leftRef(space1); - if (maptype==type) { - // eg. 1.2-5 - QString mapversions = line.mid(space1+1,space2<0?line.length()-space1-2:space2-space1-1); - int dot = mapversions.indexOf(QLatin1Char('.')); - int dash = mapversions.indexOf(QLatin1Char('-')); - int mapvmaj = mapversions.left(dot).toInt(); - if (mapvmaj==vmaj) { - int mapvmin_from = (dash <= 0 ? mapversions.mid(dot+1) : mapversions.mid(dot+1,dash-dot-1)).toInt(); - int mapvmin_to = dash <= 0 ? mapvmin_from : mapversions.mid(dash+1).toInt(); - if (vmin >= mapvmin_from && vmin <= mapvmin_to) { - QStringRef mapfile = space2<0 ? QStringRef() : line.midRef(space2+1,line.length()-space2-2); - return url.resolved(mapfile.toString()); + + if (isBuiltin.at(i)) { + QByteArray qt = urls.at(i).toLatin1(); + qt += "/"; + qt += type; + QmlType *t = QmlMetaType::qmlType(qt,vmaj,vmin); + if (vmajor) *vmajor = vmaj; + if (vminor) *vminor = vmin; + if (t) { + if (type_return) + *type_return = t; + return true; + } + } else { + QUrl url = QUrl(urls.at(i) + QLatin1String("/" + type + ".qml")); + if (vmaj || vmin) { + // Check version file - XXX cache these in QmlEngine! + QFile qmldir(QUrl(urls.at(i)+QLatin1String("/qmldir")).toLocalFile()); + if (qmldir.open(QIODevice::ReadOnly)) { + do { + QByteArray lineba = qmldir.readLine(); + if (lineba.at(0) == '#') + continue; + int space1 = lineba.indexOf(' '); + if (qstrncmp(lineba,type,space1)==0) { + // eg. 1.2-5 + QString line = QString::fromUtf8(lineba); + space1 = line.indexOf(QLatin1Char(' ')); // refind in Unicode + int space2 = space1 >=0 ? line.indexOf(QLatin1Char(' '),space1+1) : -1; + QString mapversions = line.mid(space1+1,space2<0?line.length()-space1-2:space2-space1-1); + int dot = mapversions.indexOf(QLatin1Char('.')); + int dash = mapversions.indexOf(QLatin1Char('-')); + int mapvmaj = mapversions.left(dot).toInt(); + if (mapvmaj==vmaj) { + int mapvmin_from = (dash <= 0 ? mapversions.mid(dot+1) : mapversions.mid(dot+1,dash-dot-1)).toInt(); + int mapvmin_to = dash <= 0 ? mapvmin_from : mapversions.mid(dash+1).toInt(); + if (vmin >= mapvmin_from && vmin <= mapvmin_to) { + QStringRef mapfile = space2<0 ? QStringRef() : line.midRef(space2+1,line.length()-space2-2); + if (url_return) + *url_return = url.resolved(mapfile.toString()); + return true; + } } } - } - } while (!qmldir.atEnd()); + } while (!qmldir.atEnd()); + } + } else { + // XXX search non-files too! (eg. zip files, see QT-524) + QFileInfo f(url.toLocalFile()); + if (f.exists()) { + if (url_return) + *url_return = url; + return true; + } } - } else { - // XXX search non-files too! (eg. zip files, see QT-524) - QFileInfo f(url.toLocalFile()); - if (f.exists()) - return url; // (unversioned) local import } } - return QUrl(); - } - - QmlType *findBuiltin(const QByteArray& type, int *vmajor, int *vminor) const - { - for (int i=0; i<urls.count(); ++i) { - int vmaj = majversions.at(i); - int vmin = minversions.at(i); - QByteArray qt = urls.at(i).toLatin1(); - qt += "/"; - qt += type; - QmlType *t = QmlMetaType::qmlType(qt,vmaj,vmin); - if (vmajor) *vmajor = vmaj; - if (vminor) *vminor = vmin; - if (t) return t; - } - return 0; + return false; } }; @@ -1216,22 +1226,14 @@ public: { QmlEnginePrivate::ImportedNamespace *s; if (prefix.isEmpty()) { - if (importType == QmlScriptParser::Import::Library && !vmaj && !vmin) { - // unversioned library imports are always qualified - if only by final URI component - int lastdot = uri.lastIndexOf(QLatin1Char('.')); - QString defaultprefix = uri.mid(lastdot+1); - s = set.value(defaultprefix); - if (!s) - set.insert(defaultprefix,(s=new QmlEnginePrivate::ImportedNamespace)); - } else { - s = &unqualifiedset; - } + s = &unqualifiedset; } else { s = set.value(prefix); if (!s) set.insert(prefix,(s=new QmlEnginePrivate::ImportedNamespace)); } QString url = uri; + bool isbuiltin = false; if (importType == QmlScriptParser::Import::Library) { url.replace(QLatin1Char('.'),QLatin1Char('/')); bool found = false; @@ -1245,42 +1247,20 @@ public: } if (!found) { // XXX assume it is a built-in type qualifier + isbuiltin = true; } } else { url = base.resolved(QUrl(url)).toString(); } - s->urls.append(url); - s->majversions.append(vmaj); - s->minversions.append(vmin); - s->isLibrary.append(importType == QmlScriptParser::Import::Library); + s->urls.prepend(url); + s->majversions.prepend(vmaj); + s->minversions.prepend(vmin); + s->isLibrary.prepend(importType == QmlScriptParser::Import::Library); + s->isBuiltin.prepend(isbuiltin); return true; } - QUrl find(const QString& type) const - { - const QmlEnginePrivate::ImportedNamespace *s = 0; - int slash = type.indexOf(QLatin1Char('/')); - if (slash >= 0) { - while (!s) { - s = set.value(type.left(slash)); - int nslash = type.indexOf(QLatin1Char('/'),slash+1); - if (nslash > 0) - slash = nslash; - else - break; - } - } else { - s = &unqualifiedset; - } - QString unqualifiedtype = type.mid(slash+1); - if (s) - return s->find(unqualifiedtype); - else - return QUrl(); - } - - - QmlType *findBuiltin(const QByteArray& type, int *vmajor, int *vminor) + bool find(const QByteArray& type, int *vmajor, int *vminor, QmlType** type_return, QUrl* url_return) { QmlEnginePrivate::ImportedNamespace *s = 0; int slash = type.indexOf('/'); @@ -1297,10 +1277,16 @@ public: s = &unqualifiedset; } QByteArray unqualifiedtype = slash < 0 ? type : type.mid(slash+1); // common-case opt (QString::mid works fine, but slower) - if (s) - return s->findBuiltin(unqualifiedtype,vmajor,vminor); - else - return 0; + if (s) { + if (s->find(unqualifiedtype,vmajor,vminor,type_return,url_return)) + return true; + } + if (url_return) { + *url_return = base.resolved(QUrl(QLatin1String(type + ".qml"))); + return true; + } else { + return false; + } } QmlEnginePrivate::ImportedNamespace *findNamespace(const QString& type) @@ -1412,40 +1398,27 @@ bool QmlEnginePrivate::addToImport(Imports* imports, const QString& uri, const Q */ bool QmlEnginePrivate::resolveType(const Imports& imports, const QByteArray& type, QmlType** type_return, QUrl* url_return, int *vmaj, int *vmin, ImportedNamespace** ns_return) const { - if (ns_return) { - *ns_return = imports.d->findNamespace(QLatin1String(type)); - if (*ns_return) - return true; - } - if (type_return) { - QmlType* t = imports.d->findBuiltin(type,vmaj,vmin); - if (!t) { - // XXX do we really still need this? - t = QmlMetaType::qmlType(type,0,0); // Try global namespace - if (vmin) *vmin = 0; - if (vmaj) *vmaj = 0; - } - if (t) { - if (type_return) *type_return = t; - if (qmlImportTrace()) - qDebug() << "QmlEngine::resolveType" << type << "= (builtin)"; - return true; - } + ImportedNamespace* ns = imports.d->findNamespace(QLatin1String(type)); + if (ns) { + if (qmlImportTrace()) + qDebug() << "QmlEngine::resolveType" << type << "is namespace for" << ns->urls; + if (ns_return) + *ns_return = ns; + return true; } - if (url_return) { - QUrl url = imports.d->find(QLatin1String(type)); - if (!url.isValid()) - url = imports.d->base.resolved(QUrl(QLatin1String(type + ".qml"))); - - if (url.isValid()) { - if (url_return) *url_return = url; - if (qmlImportTrace()) - qDebug() << "QmlEngine::resolveType" << type << "=" << url; + if (type_return || url_return) { + if (imports.d->find(type,vmaj,vmin,type_return,url_return)) { + if (qmlImportTrace()) { + if (type_return && *type_return) + qDebug() << "QmlEngine::resolveType" << type << "=" << (*type_return)->typeName(); + if (url_return) + qDebug() << "QmlEngine::resolveType" << type << "=" << *url_return; + } return true; } + if (qmlImportTrace()) + qDebug() << "QmlEngine::resolveType" << type << "not found"; } - if (qmlImportTrace()) - qDebug() << "QmlEngine::resolveType" << type << " not found"; return false; } @@ -1461,12 +1434,7 @@ bool QmlEnginePrivate::resolveType(const Imports& imports, const QByteArray& typ */ void QmlEnginePrivate::resolveTypeInNamespace(ImportedNamespace* ns, const QByteArray& type, QmlType** type_return, QUrl* url_return, int *vmaj, int *vmin ) const { - if (type_return) { - *type_return = ns->findBuiltin(type,vmaj,vmin); - } - if (url_return) { - *url_return = ns->find(QLatin1String(type)); - } + ns->find(type,vmaj,vmin,type_return,url_return); } QT_END_NAMESPACE diff --git a/src/declarative/qml/qmlengine_p.h b/src/declarative/qml/qmlengine_p.h index aeee355..b3c6279 100644 --- a/src/declarative/qml/qmlengine_p.h +++ b/src/declarative/qml/qmlengine_p.h @@ -114,7 +114,7 @@ public: QPODVector<CapturedProperty> capturedProperties; QmlContext *rootContext; - QmlContext *currentBindContext; + QmlContext *currentBindContext; // ### Remove me QmlExpression *currentExpression; bool isDebugging; #ifdef QT_SCRIPTTOOLS_LIB @@ -124,9 +124,12 @@ public: QmlContextScriptClass *contextClass; QmlObjectScriptClass *objectClass; QmlValueTypeScriptClass *valueTypeClass; + // Used by DOM Core 3 API + QScriptClass *nodeListClass; + QScriptClass *namedNodeMapClass; QmlContext *setCurrentBindContext(QmlContext *); - QStack<QmlContext *> activeContexts; + QStack<QmlContext *> activeContexts; // ### Remove me struct QmlScriptEngine : public QScriptEngine { diff --git a/src/declarative/qml/qmlxmlhttprequest.cpp b/src/declarative/qml/qmlxmlhttprequest.cpp index fb92c54..0cfd794 100644 --- a/src/declarative/qml/qmlxmlhttprequest.cpp +++ b/src/declarative/qml/qmlxmlhttprequest.cpp @@ -46,6 +46,10 @@ #include <QtScript/qscriptcontext.h> #include <QtScript/qscriptengine.h> #include <QtNetwork/qnetworkreply.h> +#include <QtCore/qxmlstream.h> +#include <private/qmlrefcount_p.h> +#include <private/qmlengine_p.h> +#include <QtCore/qstack.h> #include "qmlxmlhttprequest_p.h" #include <QtCore/qdebug.h> @@ -53,6 +57,840 @@ // ### Find real values #define INVALID_STATE_ERR ((QScriptContext::Error)15) +#define D(arg) (arg)->release() +#define A(arg) (arg)->addref() + +namespace { + +class NodeImpl : public QmlRefCount +{ +public: + NodeImpl() : type(Element), parent(0) {} + virtual ~NodeImpl() { + if (parent) D(parent); + for (int ii = 0; ii < children.count(); ++ii) + D(children.at(ii)); + for (int ii = 0; ii < attributes.count(); ++ii) + D(attributes.at(ii)); + } + + // These numbers are copied from the Node IDL definition + enum Type { + Attr = 2, + CDATA = 4, + Comment = 8, + Document = 9, + DocumentFragment = 11, + DocumentType = 10, + Element = 1, + Entity = 6, + EntityReference = 5, + Notation = 12, + ProcessingInstruction = 7, + Text = 3 + }; + Type type; + + QString namespaceUri; + QString name; + + QString data; + + NodeImpl *parent; + + QList<NodeImpl *> children; + QList<NodeImpl *> attributes; +}; + +class DocumentImpl : public QmlRefCount +{ +public: + DocumentImpl() : root(0) {} + virtual ~DocumentImpl() { + if (root) D(root); + } + + QString version; + QString encoding; + bool isStandalone; + + NodeImpl *root; +}; + +class NamedNodeMap +{ +public: + // JS API + static QScriptValue length(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); + static QScriptValue create(QScriptEngine *, NodeImpl *, QList<NodeImpl *> *); + + NamedNodeMap(); + NamedNodeMap(const NamedNodeMap &); + NamedNodeMap &operator=(const NamedNodeMap &); + ~NamedNodeMap(); + bool isNull(); + + NodeImpl *d; + QList<NodeImpl *> *list; +}; + +class NamedNodeMapClass : public QScriptClass +{ +public: + NamedNodeMapClass(QScriptEngine *engine) : QScriptClass(engine) {} + + virtual QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id); + virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); +}; + +class NodeList +{ +public: + // JS API + static QScriptValue length(QScriptContext *context, QScriptEngine *engine); + static QScriptValue item(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); + static QScriptValue create(QScriptEngine *, NodeImpl *); + + NodeList(); + NodeList(const NodeList &); + NodeList &operator=(const NodeList &); + ~NodeList(); + bool isNull(); + + NodeImpl *d; +}; + +class NodeListClass : public QScriptClass +{ +public: + NodeListClass(QScriptEngine *engine) : QScriptClass(engine) {} + virtual QueryFlags queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id); + virtual QScriptValue property(const QScriptValue &object, const QScriptString &name, uint id); +}; + +class Node +{ +public: + // JS API + static QScriptValue nodeName(QScriptContext *context, QScriptEngine *engine); + static QScriptValue nodeValue(QScriptContext *context, QScriptEngine *engine); + static QScriptValue nodeType(QScriptContext *context, QScriptEngine *engine); + + static QScriptValue parentNode(QScriptContext *context, QScriptEngine *engine); + static QScriptValue childNodes(QScriptContext *context, QScriptEngine *engine); + static QScriptValue firstChild(QScriptContext *context, QScriptEngine *engine); + static QScriptValue lastChild(QScriptContext *context, QScriptEngine *engine); + static QScriptValue previousSibling(QScriptContext *context, QScriptEngine *engine); + static QScriptValue nextSibling(QScriptContext *context, QScriptEngine *engine); + static QScriptValue attributes(QScriptContext *context, QScriptEngine *engine); + + //static QScriptValue ownerDocument(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue namespaceURI(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue prefix(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue localName(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue baseURI(QScriptContext *context, QScriptEngine *engine); + //static QScriptValue textContent(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); + static QScriptValue create(QScriptEngine *, NodeImpl *); + + Node(); + Node(const Node &o); + Node &operator=(const Node &); + ~Node(); + bool isNull() const; + + NodeImpl *d; +}; + +class Element : public Node +{ +public: + static QScriptValue prototype(QScriptEngine *); +}; + +class CharacterData : public Node +{ +public: + // JS API + static QScriptValue length(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); +}; + +class Text : public CharacterData +{ +public: + // JS API + static QScriptValue isElementContentWhitespace(QScriptContext *context, QScriptEngine *engine); + static QScriptValue wholeText(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); +}; + +class CDATA : public Text +{ +public: + // C++ API + static QScriptValue prototype(QScriptEngine *); +}; + +class Document : public Node +{ +public: + // JS API + static QScriptValue xmlVersion(QScriptContext *context, QScriptEngine *engine); + static QScriptValue xmlEncoding(QScriptContext *context, QScriptEngine *engine); + static QScriptValue xmlStandalone(QScriptContext *context, QScriptEngine *engine); + static QScriptValue documentElement(QScriptContext *context, QScriptEngine *engine); + + // C++ API + static QScriptValue prototype(QScriptEngine *); + static QScriptValue load(QScriptEngine *engine, const QString &data); + + Document(); + Document(const Document &); + Document &operator=(const Document &); + ~Document(); + bool isNull() const; + + DocumentImpl *d; +private: + Document(DocumentImpl *); +}; + +}; + +Q_DECLARE_METATYPE(Node); +Q_DECLARE_METATYPE(NodeList); +Q_DECLARE_METATYPE(NamedNodeMap); +Q_DECLARE_METATYPE(Document); + +QScriptValue Node::nodeName(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + return QScriptValue(node.d->name); +} + +QScriptValue Node::nodeValue(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return QScriptValue(node.d->data); +} + +QScriptValue Node::nodeType(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + return QScriptValue(node.d->type); +} + +QScriptValue Node::parentNode(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->parent) return Node::create(engine, node.d->parent); + else return engine->nullValue(); +} + +QScriptValue Node::childNodes(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return NodeList::create(engine, node.d); +} + +QScriptValue Node::firstChild(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->children.isEmpty()) return engine->nullValue(); + else return Node::create(engine, node.d->children.first()); +} + +QScriptValue Node::lastChild(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->children.isEmpty()) return engine->nullValue(); + else return Node::create(engine, node.d->children.last()); +} + +QScriptValue Node::previousSibling(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (!node.d->parent) return engine->nullValue(); + + for (int ii = 0; ii < node.d->parent->children.count(); ++ii) { + if (node.d->parent->children.at(ii) == node.d) { + if (ii == 0) return engine->nullValue(); + else return Node::create(engine, node.d->parent->children.at(ii - 1)); + } + } + + return engine->nullValue(); +} + +QScriptValue Node::nextSibling(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (!node.d->parent) return engine->nullValue(); + + for (int ii = 0; ii < node.d->parent->children.count(); ++ii) { + if (node.d->parent->children.at(ii) == node.d) { + if ((ii + 1) == node.d->parent->children.count()) return engine->nullValue(); + else return Node::create(engine, node.d->parent->children.at(ii + 1)); + } + } + + return engine->nullValue(); +} + +QScriptValue Node::attributes(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + if (node.d->type != NodeImpl::Element) + return engine->nullValue(); + else + return NamedNodeMap::create(engine, node.d, &node.d->attributes); +} + +QScriptValue Node::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + + proto.setProperty(QLatin1String("nodeName"), engine->newFunction(nodeName), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("nodeValue"), engine->newFunction(nodeValue), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("nodeType"), engine->newFunction(nodeType), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("parentNode"), engine->newFunction(parentNode), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("childNodes"), engine->newFunction(childNodes), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("firstChild"), engine->newFunction(firstChild), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("lastChild"), engine->newFunction(lastChild), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("previousSibling"), engine->newFunction(previousSibling), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("nextSibling"), engine->newFunction(nextSibling), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("attributes"), engine->newFunction(attributes), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue Node::create(QScriptEngine *engine, NodeImpl *data) +{ + QScriptValue instance = engine->newObject(); + + switch (data->type) { + case NodeImpl::Attr: + case NodeImpl::Comment: + case NodeImpl::Document: + case NodeImpl::DocumentFragment: + case NodeImpl::DocumentType: + case NodeImpl::Entity: + case NodeImpl::EntityReference: + case NodeImpl::Notation: + case NodeImpl::CDATA: + instance.setPrototype(CDATA::prototype(engine)); + break; + case NodeImpl::ProcessingInstruction: + instance.setPrototype(Node::prototype(engine)); + break; + case NodeImpl::Text: + instance.setPrototype(Text::prototype(engine)); + break; + case NodeImpl::Element: + instance.setPrototype(Element::prototype(engine)); + break; + } + + Node node; + node.d = data; + if (data) A(data); + + return engine->newVariant(instance, qVariantFromValue(node)); +} + +QScriptValue Element::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Node::prototype(engine)); + + proto.setProperty(QLatin1String("tagName"), engine->newFunction(nodeName), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue CharacterData::length(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + return QScriptValue(node.d->data.length()); +} + +QScriptValue CharacterData::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Node::prototype(engine)); + + proto.setProperty(QLatin1String("data"), engine->newFunction(nodeValue), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue Text::isElementContentWhitespace(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + // ### implement + return QScriptValue(false); +} + +QScriptValue Text::wholeText(QScriptContext *context, QScriptEngine *engine) +{ + Node node = qscriptvalue_cast<Node>(context->thisObject()); + if (node.isNull()) return engine->undefinedValue(); + + // ### implement + return QScriptValue(QString()); +} + +QScriptValue Text::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(CharacterData::prototype(engine)); + + proto.setProperty(QLatin1String("isElementContentWhitespace"), engine->newFunction(isElementContentWhitespace), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("wholeText"), engine->newFunction(wholeText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue CDATA::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Text::prototype(engine)); + return proto; +} + +QScriptValue Document::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + proto.setPrototype(Node::prototype(engine)); + + proto.setProperty(QLatin1String("xmlVersion"), engine->newFunction(xmlVersion), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("xmlEncoding"), engine->newFunction(xmlEncoding), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("xmlStandalone"), engine->newFunction(xmlStandalone), QScriptValue::ReadOnly | QScriptValue::PropertyGetter | QScriptValue::PropertySetter); + proto.setProperty(QLatin1String("documentElement"), engine->newFunction(documentElement), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue Document::load(QScriptEngine *engine, const QString &data) +{ + Q_ASSERT(engine); + + DocumentImpl *document = 0; + QStack<NodeImpl *> nodeStack; + + QXmlStreamReader reader(data); + + while (!reader.atEnd()) { + switch (reader.readNext()) { + case QXmlStreamReader::NoToken: + break; + case QXmlStreamReader::Invalid: + break; + case QXmlStreamReader::StartDocument: + Q_ASSERT(!document); + document = new DocumentImpl; + document->version = reader.documentVersion().toString(); + document->encoding = reader.documentEncoding().toString(); + document->isStandalone = reader.isStandaloneDocument(); + break; + case QXmlStreamReader::EndDocument: + break; + case QXmlStreamReader::StartElement: + { + Q_ASSERT(document); + NodeImpl *node = new NodeImpl; + node->namespaceUri = reader.namespaceUri().toString(); + node->name = reader.name().toString(); + if (nodeStack.isEmpty()) { + document->root = node; + } else { + node->parent = nodeStack.top(); + A(node->parent); + node->parent->children.append(node); + } + nodeStack.append(node); + + foreach (const QXmlStreamAttribute &a, reader.attributes()) { + NodeImpl *attr = new NodeImpl; + attr->type = NodeImpl::Attr; + attr->namespaceUri = a.namespaceUri().toString(); + attr->name = a.name().toString(); + attr->data = a.value().toString(); + node->attributes.append(attr); + } + } + break; + case QXmlStreamReader::EndElement: + nodeStack.pop(); + break; + case QXmlStreamReader::Characters: + { + NodeImpl *node = new NodeImpl; + node->type = reader.isCDATA()?NodeImpl::CDATA:NodeImpl::Text; + node->parent = nodeStack.top(); + A(node->parent); + node->parent->children.append(node); + node->data = reader.text().toString(); + } + break; + case QXmlStreamReader::Comment: + break; + case QXmlStreamReader::DTD: + break; + case QXmlStreamReader::EntityReference: + break; + case QXmlStreamReader::ProcessingInstruction: + break; + } + } + + if (!document || reader.hasError()) { + if (document) D(document); + return engine->nullValue(); + } + + QScriptValue instance = engine->newObject(); + instance.setPrototype(Document::prototype(engine)); + return engine->newVariant(instance, qVariantFromValue(Document(document))); +} + +Node::Node() +: d(0) +{ +} + +Node::Node(const Node &o) +: d(o.d) +{ + if (d) A(d); +} + +Node &Node::operator=(const Node &o) +{ + if (o.d) A(o.d); + if (d) D(d); + d = o.d; + return *this; +} + +Node::~Node() +{ + if (d) D(d); +} + +bool Node::isNull() const +{ + return d == 0; +} + +QScriptValue NamedNodeMap::length(QScriptContext *context, QScriptEngine *engine) +{ + NamedNodeMap map = qscriptvalue_cast<NamedNodeMap>(context->thisObject().data()); + if (map.isNull()) return engine->undefinedValue(); + + return QScriptValue(map.list->count()); +} + +QScriptValue NamedNodeMap::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + + proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + + return proto; +} + +QScriptValue NamedNodeMap::create(QScriptEngine *engine, NodeImpl *data, QList<NodeImpl *> *list) +{ + QScriptValue instance = engine->newObject(); + instance.setPrototype(NamedNodeMap::prototype(engine)); + + NamedNodeMap map; + map.d = data; + map.list = list; + if (data) A(data); + + instance.setData(engine->newVariant(qVariantFromValue(map))); + + if (!QmlEnginePrivate::get(engine)->namedNodeMapClass) + QmlEnginePrivate::get(engine)->namedNodeMapClass= new NamedNodeMapClass(engine); + + instance.setScriptClass(QmlEnginePrivate::get(engine)->namedNodeMapClass); + + return instance; +} + +NamedNodeMap::NamedNodeMap() +: d(0), list(0) +{ +} + +NamedNodeMap::NamedNodeMap(const NamedNodeMap &o) +: d(o.d), list(o.list) +{ + if (d) A(d); +} + +NamedNodeMap &NamedNodeMap::operator=(const NamedNodeMap &o) +{ + if (o.d) A(o.d); + if (d) D(d); + d = o.d; + list = o.list; + return *this; +} + +NamedNodeMap::~NamedNodeMap() +{ + if (d) D(d); +} + +bool NamedNodeMap::isNull() +{ + return d == 0; +} + +QScriptValue NodeList::item(QScriptContext *context, QScriptEngine *engine) +{ + NodeList list = qscriptvalue_cast<NodeList>(context->thisObject().data()); + if (list.isNull() || context->argumentCount() != 1) + return engine->undefinedValue(); + + qint32 index = context->argument(0).toInt32(); + + if (index >= list.d->children.count()) + return engine->undefinedValue(); // ### Exception + else + return Node::create(engine, list.d->children.at(index)); +} + +QScriptValue NodeList::length(QScriptContext *context, QScriptEngine *engine) +{ + NodeList list = qscriptvalue_cast<NodeList>(context->thisObject().data()); + if (list.isNull()) return engine->undefinedValue(); + + return QScriptValue(list.d->children.count()); +} + +QScriptValue NodeList::prototype(QScriptEngine *engine) +{ + QScriptValue proto = engine->newObject(); + + proto.setProperty(QLatin1String("length"), engine->newFunction(length), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + proto.setProperty(QLatin1String("item"), engine->newFunction(item, 1), QScriptValue::ReadOnly); + + return proto; +} + +QScriptValue NodeList::create(QScriptEngine *engine, NodeImpl *data) +{ + QScriptValue instance = engine->newObject(); + instance.setPrototype(NodeList::prototype(engine)); + + NodeList list; + list.d = data; + if (data) A(data); + + instance.setData(engine->newVariant(qVariantFromValue(list))); + + if (!QmlEnginePrivate::get(engine)->nodeListClass) + QmlEnginePrivate::get(engine)->nodeListClass= new NodeListClass(engine); + + instance.setScriptClass(QmlEnginePrivate::get(engine)->nodeListClass); + + return instance; +} + +NodeList::NodeList() +: d(0) +{ +} + +NodeList::NodeList(const NodeList &o) +: d(o.d) +{ + if (d) A(d); +} + +NodeList &NodeList::operator=(const NodeList &o) +{ + if (o.d) A(o.d); + if (d) D(d); + d = o.d; + return *this; +} + +NodeList::~NodeList() +{ + if (d) D(d); +} + +bool NodeList::isNull() +{ + return d == 0; +} + +NamedNodeMapClass::QueryFlags NamedNodeMapClass::queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id) +{ + if (!(flags & HandlesReadAccess)) + return 0; + + bool ok = false; + uint index = name.toString().toUInt(&ok); + if (!ok) + return 0; + + NamedNodeMap map = qscriptvalue_cast<NamedNodeMap>(object.data()); + if (map.isNull() || (uint)map.list->count() <= index) + return 0; // ### I think we're meant to raise an exception + + *id = index; + return HandlesReadAccess; +} + +QScriptValue NamedNodeMapClass::property(const QScriptValue &object, const QScriptString &, uint id) +{ + NamedNodeMap map = qscriptvalue_cast<NamedNodeMap>(object.data()); + return Node::create(engine(), map.list->at(id)); +} + +NodeListClass::QueryFlags NodeListClass::queryProperty(const QScriptValue &object, const QScriptString &name, QueryFlags flags, uint *id) +{ + if (!(flags & HandlesReadAccess)) + return 0; + + bool ok = false; + uint index = name.toString().toUInt(&ok); + if (!ok) + return 0; + + NodeList list = qscriptvalue_cast<NodeList>(object.data()); + if (list.isNull() || (uint)list.d->children.count() <= index) + return 0; // ### I think we're meant to raise an exception + + *id = index; + return HandlesReadAccess; +} + +QScriptValue NodeListClass::property(const QScriptValue &object, const QScriptString &, uint id) +{ + NodeList list = qscriptvalue_cast<NodeList>(object.data()); + return Node::create(engine(), list.d->children.at(id)); +} + +Document::Document() +: d(0) +{ +} + +Document::Document(DocumentImpl *data) +: d(data) +{ +} + +Document::Document(const Document &o) +: Node(o), d(o.d) +{ + if (d) A(d); +} + +Document &Document::operator=(const Document &o) +{ + if (o.d) A(o.d); + if (d) D(d); + d = o.d; + return *this; +} + +Document::~Document() +{ + if (d) D(d); +} + +bool Document::isNull() const +{ + return d == 0; +} + +QScriptValue Document::documentElement(QScriptContext *context, QScriptEngine *engine) +{ + Document document = qscriptvalue_cast<Document>(context->thisObject()); + if (document.isNull()) return engine->undefinedValue(); + + if (!document.d->root) return engine->nullValue(); + + return Node::create(engine, document.d->root); +} + +QScriptValue Document::xmlStandalone(QScriptContext *context, QScriptEngine *engine) +{ + Document document = qscriptvalue_cast<Document>(context->thisObject()); + if (document.isNull()) return engine->undefinedValue(); + + if (context->argumentCount()) + document.d->isStandalone = context->argument(0).toBool(); + + return QScriptValue(document.d->isStandalone); +} + +QScriptValue Document::xmlVersion(QScriptContext *context, QScriptEngine *engine) +{ + Document document = qscriptvalue_cast<Document>(context->thisObject()); + if (document.isNull()) return engine->undefinedValue(); + + if (context->argumentCount()) + document.d->version = context->argument(0).toString(); + + return QScriptValue(document.d->version); +} + +QScriptValue Document::xmlEncoding(QScriptContext *context, QScriptEngine *engine) +{ + Document document = qscriptvalue_cast<Document>(context->thisObject()); + if (document.isNull()) return engine->undefinedValue(); + + if (context->argumentCount()) + document.d->encoding = context->argument(0).toString(); + + return QScriptValue(document.d->encoding); +} + class QmlXMLHttpRequest : public QObject { Q_OBJECT @@ -311,8 +1149,13 @@ static QScriptValue qmlxmlhttprequest_open(QScriptContext *context, QScriptEngin // Argument 1 - URL QUrl url(context->argument(1).toString()); // ### Need to resolve correctly - if (url.isRelative()) // ### Fix me - return context->throwError(QScriptContext::SyntaxError, "Relative URLs not supported"); + if (url.isRelative()) { + QmlContext *ctxt = QmlEnginePrivate::get(engine)->currentExpression?QmlEnginePrivate::get(engine)->currentExpression->context():0; + if (ctxt) + url = ctxt->resolvedUrl(url); + else + return context->throwError(QScriptContext::SyntaxError, "Relative URLs not supported"); + } // Argument 2 - async (optional) if (context->argumentCount() > 2 && !context->argument(2).toBoolean()) @@ -480,6 +1323,18 @@ static QScriptValue qmlxmlhttprequest_responseText(QScriptContext *context, QScr return QScriptValue(request->responseBody()); } +static QScriptValue qmlxmlhttprequest_responseXML(QScriptContext *context, QScriptEngine *engine) +{ + QmlXMLHttpRequest *request = qobject_cast<QmlXMLHttpRequest *>(context->thisObject().data().toQObject()); + if (!request) return context->throwError(QScriptContext::ReferenceError, QLatin1String("Not an XMLHttpRequest object")); + + if (request->readyState() != QmlXMLHttpRequest::Loading && + request->readyState() != QmlXMLHttpRequest::Done) + return engine->nullValue(); + else + return Document::load(engine, request->responseBody()); +} + static QScriptValue qmlxmlhttprequest_onreadystatechange(QScriptContext *context, QScriptEngine *engine) { QmlXMLHttpRequest *request = qobject_cast<QmlXMLHttpRequest *>(context->thisObject().data().toQObject()); @@ -494,23 +1349,16 @@ static QScriptValue qmlxmlhttprequest_onreadystatechange(QScriptContext *context // Constructor static QScriptValue qmlxmlhttprequest_new(QScriptContext *context, QScriptEngine *engine) { - QScriptValue rv = engine->newObject(); - rv.setPrototype(context->callee().data()); - rv.setData(engine->newQObject(new QmlXMLHttpRequest(QmlEnginePrivate::getEngine(engine)), QScriptEngine::ScriptOwnership)); - return rv; + if (context->isCalledAsConstructor()) { + context->thisObject().setData(engine->newQObject(new QmlXMLHttpRequest(QmlEnginePrivate::getEngine(engine)), QScriptEngine::ScriptOwnership)); + } + return engine->undefinedValue(); } void qt_add_qmlxmlhttprequest(QScriptEngine *engine) { QScriptValue prototype = engine->newObject(); - // Constants - prototype.setProperty(QLatin1String("UNSENT"), 0, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); - prototype.setProperty(QLatin1String("OPENED"), 1, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); - prototype.setProperty(QLatin1String("HEADERS_RECEIVED"), 2, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); - prototype.setProperty(QLatin1String("LOADING"), 3, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); - prototype.setProperty(QLatin1String("DONE"), 4, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); - // Methods prototype.setProperty(QLatin1String("open"), engine->newFunction(qmlxmlhttprequest_open, 2)); prototype.setProperty(QLatin1String("setRequestHeader"), engine->newFunction(qmlxmlhttprequest_setRequestHeader, 2)); @@ -524,11 +1372,16 @@ void qt_add_qmlxmlhttprequest(QScriptEngine *engine) prototype.setProperty(QLatin1String("status"), engine->newFunction(qmlxmlhttprequest_status), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); prototype.setProperty(QLatin1String("statusText"), engine->newFunction(qmlxmlhttprequest_statusText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); prototype.setProperty(QLatin1String("responseText"), engine->newFunction(qmlxmlhttprequest_responseText), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); + prototype.setProperty(QLatin1String("responseXML"), engine->newFunction(qmlxmlhttprequest_responseXML), QScriptValue::ReadOnly | QScriptValue::PropertyGetter); prototype.setProperty(QLatin1String("onreadystatechange"), engine->newFunction(qmlxmlhttprequest_onreadystatechange), QScriptValue::PropertyGetter | QScriptValue::PropertySetter); // Constructor - QScriptValue constructor = engine->newFunction(qmlxmlhttprequest_new); - constructor.setData(prototype); + QScriptValue constructor = engine->newFunction(qmlxmlhttprequest_new, prototype); + constructor.setProperty(QLatin1String("UNSENT"), 0, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + constructor.setProperty(QLatin1String("OPENED"), 1, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + constructor.setProperty(QLatin1String("HEADERS_RECEIVED"), 2, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + constructor.setProperty(QLatin1String("LOADING"), 3, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); + constructor.setProperty(QLatin1String("DONE"), 4, QScriptValue::ReadOnly | QScriptValue::Undeletable | QScriptValue::SkipInEnumeration); engine->globalObject().setProperty(QLatin1String("XMLHttpRequest"), constructor); } diff --git a/tests/auto/declarative/qmlparser/failingComponent.errors.txt b/tests/auto/declarative/qmlparser/failingComponent.errors.txt index 190a649..0cf0ef3 100644 --- a/tests/auto/declarative/qmlparser/failingComponent.errors.txt +++ b/tests/auto/declarative/qmlparser/failingComponent.errors.txt @@ -1,2 +1 @@ -3:5:Unable to create type FailingComponent -4:5:Cannot assign to non-existant property "a" +3:5:Type FailingComponent unavailable diff --git a/tests/auto/declarative/qmlparser/importNamespaceConflict.errors.txt b/tests/auto/declarative/qmlparser/importNamespaceConflict.errors.txt new file mode 100644 index 0000000..231998d --- /dev/null +++ b/tests/auto/declarative/qmlparser/importNamespaceConflict.errors.txt @@ -0,0 +1 @@ +4:1:Namespace Rectangle cannot be used as a type diff --git a/tests/auto/declarative/qmlparser/importNamespaceConflict.qml b/tests/auto/declarative/qmlparser/importNamespaceConflict.qml new file mode 100644 index 0000000..cd112af --- /dev/null +++ b/tests/auto/declarative/qmlparser/importNamespaceConflict.qml @@ -0,0 +1,4 @@ +import Test 1.0 as Rectangle +import Qt 4.6 + +Rectangle { } diff --git a/tests/auto/declarative/qmlparser/lib/com/nokia/installedtest/qmldir b/tests/auto/declarative/qmlparser/lib/com/nokia/installedtest/qmldir index f22e179..ba0b42a 100644 --- a/tests/auto/declarative/qmlparser/lib/com/nokia/installedtest/qmldir +++ b/tests/auto/declarative/qmlparser/lib/com/nokia/installedtest/qmldir @@ -1,2 +1,3 @@ InstalledTest 1.0-3 InstalledTest.qml InstalledTest 1.4 InstalledTest2.qml +Rectangle 1.5 InstalledTest2.qml diff --git a/tests/auto/declarative/qmlparser/testtypes.h b/tests/auto/declarative/qmlparser/testtypes.h index ace7ef9..f700484 100644 --- a/tests/auto/declarative/qmlparser/testtypes.h +++ b/tests/auto/declarative/qmlparser/testtypes.h @@ -357,12 +357,13 @@ private: QML_DECLARE_TYPE(MyContainer); -class MyPropertyValueSource : public QmlPropertyValueSource +class MyPropertyValueSource : public QObject, public QmlPropertyValueSource { Q_OBJECT + Q_INTERFACES(QmlPropertyValueSource) public: MyPropertyValueSource() - : QmlPropertyValueSource(0) {} + : QmlPropertyValueSource() {} QmlMetaProperty prop; virtual void setTarget(const QmlMetaProperty &p) diff --git a/tests/auto/declarative/qmlparser/tst_qmlparser.cpp b/tests/auto/declarative/qmlparser/tst_qmlparser.cpp index 3047bb0..36471a4 100644 --- a/tests/auto/declarative/qmlparser/tst_qmlparser.cpp +++ b/tests/auto/declarative/qmlparser/tst_qmlparser.cpp @@ -48,14 +48,21 @@ private slots: void customVariantTypes(); void valueTypes(); - void imports_data(); - void imports(); + void importsBuiltin_data(); + void importsBuiltin(); + void importsLocal_data(); + void importsLocal(); + void importsInstalled_data(); + void importsInstalled(); + void importsOrder_data(); + void importsOrder(); // regression tests for crashes void crash1(); private: QmlEngine engine; + void testType(const QString& qml, const QString& type); }; #define VERIFY_ERRORS(errorfile) \ @@ -148,6 +155,8 @@ void tst_qmlparser::errors_data() QTest::newRow("failingComponent") << "failingComponentTest.qml" << "failingComponent.errors.txt" << false; QTest::newRow("missingSignal") << "missingSignal.qml" << "missingSignal.errors.txt" << false; QTest::newRow("finalOverride") << "finalOverride.qml" << "finalOverride.errors.txt" << false; + + QTest::newRow("importNamespaceConflict") << "importNamespaceConflict.qml" << "importNamespaceConflict.errors.txt" << false; } void tst_qmlparser::errors() @@ -420,6 +429,10 @@ void tst_qmlparser::autoComponentCreation() void tst_qmlparser::propertyValueSource() { + QVERIFY(false); + +/* Does not compile... + QmlComponent component(&engine, TEST_FILE("propertyValueSource.qml")); VERIFY_ERRORS(0); MyTypeObject *object = qobject_cast<MyTypeObject *>(component.create()); @@ -432,6 +445,7 @@ void tst_qmlparser::propertyValueSource() QVERIFY(valueSource != 0); QCOMPARE(valueSource->prop.object(), object); QCOMPARE(valueSource->prop.name(), QString(QLatin1String("intProperty"))); +*/ } void tst_qmlparser::attachedProperties() @@ -502,6 +516,21 @@ public: TestType2(QObject *p=0) : QObject(p) {} }; +// Check that first child of qml is of given type. Empty type insists on error. +void tst_qmlparser::testType(const QString& qml, const QString& type) +{ + QmlComponent component(&engine, qml.toUtf8(), TEST_FILE("empty.qml")); // just a file for relative local imports + + if (type.isEmpty()) { + QVERIFY(component.isError()); + } else { + VERIFY_ERRORS(0); + QObject *object = component.create(); + QVERIFY(object != 0); + QCOMPARE(QString(object->metaObject()->className()), type); + } +} + QML_DECLARE_TYPE(TestType) QML_DECLARE_TYPE(TestType2) @@ -511,7 +540,7 @@ QML_DEFINE_TYPE(com.nokia.Test, 1, 8, 9, Test, TestType2) QML_DEFINE_TYPE(com.nokia.Test, 1, 12, 13, Test, TestType2) QML_DEFINE_TYPE(com.nokia.Test, 1, 9, 11, OldTest, TestType) -void tst_qmlparser::imports_data() +void tst_qmlparser::importsBuiltin_data() { QTest::addColumn<QString>("qml"); QTest::addColumn<QString>("type"); @@ -605,6 +634,19 @@ void tst_qmlparser::imports_data() "import com.nokia.Test 1.10 as T10\n" "T10.Test {}" << ""; +} + +void tst_qmlparser::importsBuiltin() +{ + QFETCH(QString, qml); + QFETCH(QString, type); + testType(qml,type); +} + +void tst_qmlparser::importsLocal_data() +{ + QTest::addColumn<QString>("qml"); + QTest::addColumn<QString>("type"); // import locals QTest::newRow("local import") @@ -624,6 +666,19 @@ void tst_qmlparser::imports_data() "import com.nokia.Test 1.0\n" "Test {}" << "TestType"; +} + +void tst_qmlparser::importsLocal() +{ + QFETCH(QString, qml); + QFETCH(QString, type); + testType(qml,type); +} + +void tst_qmlparser::importsInstalled_data() +{ + QTest::addColumn<QString>("qml"); + QTest::addColumn<QString>("type"); // import installed QTest::newRow("installed import") @@ -636,21 +691,70 @@ void tst_qmlparser::imports_data() << "QFxText"; } -void tst_qmlparser::imports() +void tst_qmlparser::importsInstalled() { QFETCH(QString, qml); QFETCH(QString, type); + testType(qml,type); +} - QmlComponent component(&engine, qml.toUtf8(), TEST_FILE("empty.qml")); // just a file for relative local imports - if (type.isEmpty()) { - QVERIFY(component.isError()); - } else { - VERIFY_ERRORS(0); - QObject *object = component.create(); - QVERIFY(object != 0); - QCOMPARE(QString(object->metaObject()->className()), type); - } +void tst_qmlparser::importsOrder_data() +{ + QTest::addColumn<QString>("qml"); + QTest::addColumn<QString>("type"); + + QTest::newRow("installed import overrides 1") << + "import com.nokia.installedtest 1.0\n" + "import com.nokia.installedtest 1.4\n" + "InstalledTest {}" + << "QFxText"; + QTest::newRow("installed import overrides 2") << + "import com.nokia.installedtest 1.4\n" + "import com.nokia.installedtest 1.0\n" + "InstalledTest {}" + << "QFxRect"; + QTest::newRow("installed import re-overrides 1") << + "import com.nokia.installedtest 1.4\n" + "import com.nokia.installedtest 1.0\n" + "import com.nokia.installedtest 1.4\n" + "InstalledTest {}" + << "QFxText"; + QTest::newRow("installed import re-overrides 2") << + "import com.nokia.installedtest 1.4\n" + "import com.nokia.installedtest 1.0\n" + "import com.nokia.installedtest 1.4\n" + "import com.nokia.installedtest 1.0\n" + "InstalledTest {}" + << "QFxRect"; + + QTest::newRow("installed import versus builtin 1") << + "import com.nokia.installedtest 1.5\n" + "import Qt 4.6\n" + "Rectangle {}" + << "QFxRect"; + QTest::newRow("installed import versus builtin 2") << + "import Qt 4.6\n" + "import com.nokia.installedtest 1.5\n" + "Rectangle {}" + << "QFxText"; + QTest::newRow("namespaces cannot be overridden by types 1") << + "import Qt 4.6 as Rectangle\n" + "import com.nokia.installedtest 1.5\n" + "Rectangle {}" + << ""; + QTest::newRow("namespaces cannot be overridden by types 2") << + "import Qt 4.6 as Rectangle\n" + "import com.nokia.installedtest 1.5\n" + "Rectangle.Image {}" + << "QFxImage"; +} + +void tst_qmlparser::importsOrder() +{ + QFETCH(QString, qml); + QFETCH(QString, type); + testType(qml,type); } void tst_qmlparser::crash1() |