Add QML Extensions API
ClosedPublic

Authored by anmolgautam on Jul 31 2018, 2:51 PM.

Diff Detail

Repository
R875 Falkon
Branch
patch-qml-api-only
Lint
No Linters Available
Unit
No Unit Test Coverage
Build Status
Buildable 1716
Build 1734: arc lint + arc unit
There are a very large number of changes, so older changes are hidden. Show Older Changes
drosca added inline comments.Aug 7 2018, 5:05 PM
src/lib/history/history.h
72

There is already indexesFromTimeRange that you can use instead of adding new method. Also, timestamp is integer and not double.

src/lib/plugins/qml/api/cookies/qmlcookies.cpp
30

This is completely wrong, it can actually lead to invalid results now due to possibility of this reference always having the same address (because it is on stack).
Do you know what this code does now?

src/lib/plugins/qml/api/events/qmlkeyevent.h
73

Rename to clear()

src/lib/plugins/qml/api/events/qmlmouseevent.cpp
34

Don't use C-style casts, use static_cast<int> or int(value)

src/lib/plugins/qml/api/events/qmlqzobjects.h
20

This is not system include

src/lib/plugins/qml/api/history/qmlhistory.cpp
44

You can just declare result as const instead of using qAsConst.

src/lib/plugins/qml/api/menus/qmlaction.cpp
65

You should be able to get engine with qmlEngine(this) instead of needing to pass the plugin path manually.

This revision now requires changes to proceed.Aug 7 2018, 5:05 PM
anmolgautam added inline comments.Aug 8 2018, 7:35 AM
src/lib/history/history.h
72

Do you want it like this -
void QmlHistory::deleteRange(const QVariantMap &map)
{

if (!map.contains(QSL("startTime")) || !map.contains(QSL("endTime"))) {
    qWarning() << "Error:" << "wrong arguments passed to" << __FUNCTION__;
    return;
}
const qlonglong startTime = map.value(QSL("startTime")).toLongLong();
const qlonglong endTime = map.value(QSL("endTime")).toLongLong();

QSqlQuery query(SqlDatabase::instance()->database());
query.prepare(QSL("DELETE FROM history WHERE date BETWEEN ? AND ?"));
query.bindValue(0, startTime);
query.bindValue(1, endTime);
query.exec();

}

Here deleteRange is removed from history.h. Also if indexFromTimeRange is used then it wil have to execute query twice (once to get the list of ids to remove and other to remove the ids)

anmolgautam retitled this revision from Created QML Extensions API & Moved plugins info to SQL to Add QML Extensions API.Aug 8 2018, 9:24 AM
drosca added inline comments.Aug 8 2018, 9:51 AM
src/lib/history/history.h
72

Also if indexFromTimeRange is used then it wil have to execute query twice

That's fine, this won't be called often.

anmolgautam added inline comments.Aug 8 2018, 10:09 AM
src/lib/history/history.h
72

Sorry, but your comment is unclear to me, do you mean the snipped I posted is fine or to delete the ids by first using indexFromTimeRange?

drosca added inline comments.Aug 8 2018, 10:19 AM
src/lib/history/history.h
72

Delete the ids by first using indexFromTimeRange.

anmolgautam added inline comments.Aug 9 2018, 5:11 PM
src/lib/app/mainapplication.h
79 ↗(On Diff #38847)

I have thinked over it and I am unable to find a good solution to where to include it outside of mainapplication, since its used in both qmltabs and qmlwindows. Do you have some specific location in mind?

drosca added inline comments.Aug 10 2018, 7:55 AM
src/lib/app/mainapplication.h
79 ↗(On Diff #38847)

For example add new class that will hold all static data, including everything else from all *Data classes, so that way there is only one static data class.
If you make it QObject then you can just parent all data to this class and don't need to explicitly set CppOwnership.

  • updated revision
  • stored all data in static class
  • removed pluginsmanager modifications as they are in another revision
drosca added inline comments.Aug 10 2018, 3:53 PM
src/lib/plugins/qml/api/bookmarks/qmlbookmarks.cpp
234

Why did you change the for loop? Unless there is a reason, don't use for(int i = 0, ....)

src/lib/plugins/qml/api/browseraction/qmlbrowseraction.h
165

Missing initialization

src/lib/plugins/qml/api/cookies/qmlcookie.h
24

This should not be needed.

src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31

The question still stands? If it doesn't get to the QML engine, it will leak.

Example where it may happen (I did not test it myself, so plase check) is if you instantiate Falkon.ExtensionScheme { } item in QML but don't create the requestStarted handler (eg. no onRequestStarted code).

src/lib/plugins/qml/api/fileutils/qmlfileutils.cpp
28

?

src/lib/plugins/qml/api/menus/qmlaction.h
33

Why? You can get the QmlEngine pointer with qmlEngine(QObject*) function that is called on any QObject that was created by the engine.

src/lib/plugins/qml/api/settings/qmlsettings.cpp
32

Why not just create QSettings with correct parent?

src/lib/plugins/qml/api/sidebar/qmlsidebar.cpp
137

This seems like the same code for handling icon loading is pasted on multiple places, should be extracted to function.

src/lib/plugins/qml/api/sidebar/qmlsidebar.h
121

init

src/lib/plugins/qml/api/tabs/qmltabs.cpp
324

Again, use stl iterators. This creates temporary list only to iterate over it, that's not needed at all.

src/lib/plugins/qml/api/userscript/qmluserscripts.h
88

QList<QObject*>

src/lib/plugins/qml/qmlplugin.h
29

Just make it static variable in loadPlugin function as it is not used anywhere else.

src/lib/plugins/qml/qmlplugininterface.cpp
43

Why this at all? It should just use staticData->getTab!

src/lib/plugins/qml/qmlstaticdata.cpp
37

There should be no whitespace before/after increment, so it should be foo++.

src/lib/plugins/qml/qmlstaticdata.h
63

remove

67

It should be something like static QmlStaticData* instance() member function and not adding staticData to global namespace.

Again, if I say to change one thing there and it is also in multiple other places, please change it everywhere.

Also you marked as done some comments which were clearly not done at all, so in those cases please at least write reply why do you consider it done.

src/lib/plugins/qml/qmlplugins.cpp
67

Move the url to variable so it isn't copied thousand times.

96

Most (all?) these singletons should be shared with multiple engines (=multiple loaded qml extensions). I think it creates new instance for every engine right now.

anmolgautam added inline comments.Aug 10 2018, 4:28 PM
src/lib/plugins/qml/api/bookmarks/qmlbookmarks.cpp
234

Doesn't for(BookmarkItem *bookmarkitem : items) detaches the list? Should I use QListIterator<BookmarkItem *> it(items) ?

drosca added inline comments.Aug 10 2018, 4:30 PM
src/lib/plugins/qml/api/bookmarks/qmlbookmarks.cpp
234

No, because items is const. As long as the Qt container is const, you can use C++11 for.

anmolgautam added inline comments.Aug 10 2018, 4:59 PM
src/lib/plugins/qml/api/browseraction/qmlbrowseraction.h
165

Its not designed to be initialized explicitly, but checks must be added in setIcon where it can be null eq in auto qmlEngine = qobject_cast<QmlEngine*>(m_popup->creationContext()->engine());

drosca added inline comments.Aug 10 2018, 5:01 PM
src/lib/plugins/qml/api/browseraction/qmlbrowseraction.h
165

Sorry, that was meant for m_locations, one line above.

anmolgautam added inline comments.Aug 10 2018, 5:59 PM
src/lib/plugins/qml/api/i18n/qmli18n.cpp
37

Its is created per engine, so shouldn't it work?

drosca added inline comments.Aug 10 2018, 6:32 PM
src/lib/plugins/qml/api/i18n/qmli18n.cpp
37

Did you try?

anmolgautam added inline comments.Aug 10 2018, 6:37 PM
src/lib/plugins/qml/api/fileutils/qmlfileutils.cpp
28

Its not the extension root path which is known by QmlEngine, but the path of entry point of the qml plugin. The code here gets the plugin root path given the path of entry file.

drosca added inline comments.Aug 10 2018, 6:40 PM
src/lib/plugins/qml/api/fileutils/qmlfileutils.cpp
28

I don't understand. Please show me example how does the "extension root path" and "path of entry point" differs and why does it matter here?

anmolgautam added inline comments.Aug 10 2018, 6:54 PM
src/lib/plugins/qml/api/fileutils/qmlfileutils.cpp
28

Entry path and root path differs because in metadata.desktop we can specify entry file as - X-Falkon-EntryPoint=demo/main.qml, which will point to the main.qml file within the demo directory of plugin (root-dir-of-plugin/demo/main.qml) now the extensionpath set in the QmlEngine is the entry point path. Also you told previously that the paths must be relative to root irrespective of the qml file, so I added this. For example in this case -
FilePath & ExtensionPath is "/home/tarptaeya/.config/falkon/plugins/qml/TestPlugin/demo/main.qml"
m_path is "/home/tarptaeya/.config/falkon/plugins/qml/TestPlugin/"

drosca added inline comments.Aug 10 2018, 7:09 PM
src/lib/plugins/qml/api/fileutils/qmlfileutils.cpp
28
FilePath & ExtensionPath is "/home/tarptaeya/.config/falkon/plugins/qml/TestPlugin/demo/main.qml"

This is entry point and is only needed for creating the QML component, and is never used again.

m_path is "/home/tarptaeya/.config/falkon/plugins/qml/TestPlugin/"

Still I don't see the problem. This path is what you have stored in QmlEngine (and if you don't, you should!) and only this path is needed to resolve relative paths. That means this is "basePath" of the extension which will be used for resolving all relative paths (including what you get in X-Falkon-EntryPoint). This also means that the only path that needs to be absolute is this, everything else can be stored as relative path and resolved against it.

anmolgautam added inline comments.Aug 10 2018, 9:08 PM
src/lib/plugins/qml/api/menus/qmlaction.h
33

No, the menus and action are not created by the engine, but are created (menu) in qmlplugininterface (and action in menu). But it seem to me that they leak? Should I set the parent of the qmlMenu as qmlpluginInterface or store them in a list which will be deleted in the destructor of qmlplugininterface?

drosca added inline comments.Aug 11 2018, 10:45 AM
src/lib/plugins/qml/api/menus/qmlaction.h
33

You don't need to get the QmlEngine pointer in constructor, and later you call QmlEngine::newQObject on it, so after that you should get the correct pointer from qmlEngine(QObject*) I think.

  • fixed code
  • corrected i18n
drosca added inline comments.Aug 11 2018, 2:31 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31

Well, now it doesn't leak but it will only be released on exiting application, so it's effectively the same problem and you didn't fix anything.

src/lib/plugins/qml/api/fileutils/qmlfileutils.cpp
30

Why? This shouldn't be needed.
Always keep paths without trailing slash.

src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

What does this do?

src/lib/plugins/qml/api/tabs/qmltabs.cpp
275

If you know beforehand how big the resulting list will be, you should call reserve.

325

Please use STL iterators.

auto h = QmlStaticData::instance().windowIdHash();
for (auto it = h.constBegin(); it != h.constEnd(); ++it) {
}
/// or cbegin()/cend()
src/lib/plugins/qml/api/windows/qmlwindow.cpp
39

This is equivalent to

return QmlStaticData::instance().windowIdHash().value(m_window, -1);
src/lib/plugins/qml/qmlplugin.cpp
33

This is not member variable so it shouldn't have m_ prefix. It shouldn't have any prefix.

66

You are casting twice here, data.value<QmlPluginLoader*>() already gives you QmlPluginLoader*

src/lib/plugins/qml/qmlplugins.cpp
69

const

src/lib/plugins/qml/qmlstaticdata.cpp
141

Why it isn't created on heap and parented to QmlStaticData? That way you don't need to manually specify CppOwnership. Ideally you should only specify ownership explicitly only when there is no better way.

src/lib/plugins/qml/qmlstaticdata.h
52

static QmlStaticData &instance();

anmolgautam added inline comments.Aug 11 2018, 2:49 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31

Then how to fix this?

src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

If locale is an empty string, "", each part of the locale that should be modified is set according to the environment variables.

drosca added inline comments.Aug 11 2018, 3:17 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31

QmlEngine::newQObject as you already used in other places. But first test if it works as expected.

  • updated revision
drosca added inline comments.Aug 11 2018, 3:45 PM
src/lib/plugins/qml/api/events/qmlkeyevent.cpp
26

You already call newQObject on all events, which makes them JavaScriptOwnership, so it isn't needed here.

src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31 ↗(On Diff #39461)

You can still use the QmlWebEngineUrlRequestJob pointer in signal, not QJSValue.

src/lib/plugins/qml/api/fileutils/qmlfileutils.cpp
37 ↗(On Diff #39461)

Why does it use QUrl?

You can just do path + / + filePath and then QDir::cleanPath.

src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

And is that really needed?
I'd like to see explicitly choosing which language to load here.

anmolgautam added inline comments.Aug 11 2018, 4:05 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31 ↗(On Diff #39461)

Then it needs to make the jsvalue return of engine->newQObject as unused? something like -
QmlWebEngineUrlRequestJob *request = new QmlWebEngineUrlRequestJob(job);
auto jsval = qmlEngine(this)->newQObject(request);
emit requestStarted(request);
?

src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

You mean something like Falkon.I18n.setLocale() on qml side?

drosca added inline comments.Aug 11 2018, 4:08 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31 ↗(On Diff #39461)

No, just calling qmlEngine(this)->newQObject(request); should be enough.

Did you actually try it?

src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

No, on the cpp side so when/if there is a support for switching languages it is easy to implement.

Also translations functions should be added to global namespace, so they are accessible with just i18n/i18np without Falkon namespace.

anmolgautam added inline comments.Aug 11 2018, 4:11 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31 ↗(On Diff #39461)

Yes I tried. But qmlEngine(this)->newQObject(request); returns QJSValue - so its need to be changed to the signal paramater

drosca added inline comments.Aug 11 2018, 4:15 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31 ↗(On Diff #39461)

Tried how? Did you change the code so it just calls qmlEngine(this)->newQObject(request); and then checked if that object gets correctly garbage collected by engine (deleted)?

anmolgautam added inline comments.Aug 11 2018, 4:21 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31 ↗(On Diff #39461)
connect(m_schemeHandler, &QmlExtensionSchemeHandler::_requestStarted, this, [this](QWebEngineUrlRequestJob *job) {
        QmlWebEngineUrlRequestJob *request = new QmlWebEngineUrlRequestJob(job);
        emit requestStarted(qmlEngine(this)->newQObject(request));
    });

Or did you meant the snippet which I send in previous comment?

drosca added inline comments.Aug 11 2018, 4:27 PM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31 ↗(On Diff #39461)

I meant what I told you to do, just call qmlEngine(this)->newQObject(request);

connect(m_schemeHandler, &QmlExtensionSchemeHandler::_requestStarted, this, [this](QWebEngineUrlRequestJob *job) {
    QmlWebEngineUrlRequestJob *request = new QmlWebEngineUrlRequestJob(job);
    qmlEngine(this)->newQObject(request);
    emit requestStarted(request);
});
anmolgautam added inline comments.Aug 11 2018, 4:43 PM
src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

on the cpp side so when/if there is a support for switching languages it is easy to implement.

Sorry, I didn't understood this. Can you please elaborate.

Also translations functions should be added to global namespace, so they are accessible with just i18n/i18np without Falkon namespace.

using engine contextProperty? or is there some other way?

drosca added inline comments.Aug 11 2018, 4:56 PM
src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

Something like setlocale(QLocale::system().name())

Yes, contextProperty is fine if it works.

anmolgautam added inline comments.Aug 11 2018, 5:18 PM
src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

No, context property is not working. It seem like there is no way to make a method global (max is object.method ex i18n.i18n), is there some way? I couldn't found any in the documentation.

anmolgautam added inline comments.Aug 11 2018, 8:06 PM
src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

This can be similar to qsTr in Qt source, something like -
engine->globalObject().setProperty("i18n", engine->evaluate("function(string) { return i18n.i18n(string) }"));
Here i18n is the context property (QObject corresponding to QmlI18n).
But It has problems -

  1. i18n as contextProperty object
  2. It must be added to the engine at time of its creation i.e. remove singleton of i18n from QmlPlugins and move i18n to engine global object when the engine is created in QmlPluginLoader.

Should I change I18n so that i18n adds to global namespace or leave it now? Or is there some better solution

drosca added inline comments.Aug 12 2018, 6:15 AM
src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

i18n as contextProperty object

Is that an issue?

It must be added to the engine at time of its creation i.e. remove singleton of i18n from QmlPlugins and move i18n to engine global object when the engine is created in QmlPluginLoader.

I don't see any problem with this.

If it works, please do it this way.

  • updated revision
drosca added inline comments.Aug 12 2018, 9:34 AM
src/lib/plugins/qml/api/extensionscheme/qmlextensionscheme.cpp
31 ↗(On Diff #39461)

I asked you to test how it works, so what are your findings? I'm not myself sure if this is actually correct, as you may need to pass the QJSValue to QML (as you did) to not be garbage collected if the reference is held on QML side.
Eg. save the reference to the job on QML side and check if it is still valid after some time (with Timer).

src/lib/plugins/qml/api/i18n/qmli18n.cpp
27

I don't think it should call setlocale at all, that's QLocale job. Take a look how is this done in ki18n framework.

src/lib/plugins/qml/qmlpluginloader.cpp
73

QStringLiteral

This revision was not accepted when it landed; it landed in state Needs Review.Aug 12 2018, 1:43 PM
This revision was automatically updated to reflect the committed changes.