Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App modules/WWAHost: use package functions to determine product name/version and which app module to load #9119

Merged
merged 17 commits into from Jul 6, 2020

Conversation

josephsl
Copy link
Collaborator

@josephsl josephsl commented Jan 3, 2019

Link to issue number:

Partly fixes #4259
Fixes #4569

Summary of the issue:

Adds ability to let NVDA load appropriate app modules for apps hosted by WWAHost.

Description of how this pull request fixes the issue:

Enhancements to WWAHost app module:

  • App module imports: uses kernel32.dll::GetApplicationUserModelId to fetch app model, returning the app name converted to lowercase.
  • Browse mode: focus mode will be entered no matter which app is in use.

Testing performed:

Tested on Windows 8.1 and 10 with various web apps hosted by WWAHost.exe, including Store app on Windows 8.1 and Twitter PWA (progressive web app) on Windows 10.

Known issues with pull request:

App modules wishing to support apps hosted by WWAHost must import contents of wwahost app module (at least the app module class) to take advantage of services implemented, including future additions and changes.

Change log entry:

Bug fixes:

Although note that the above is an optional bug fix entry, as the change is already part of NVDA since 2019.3.

Changes for developers:

Thanks.

…on. Re nvaccess#4259.

AppModule.productName/productVersion looks at product name/version fields for the executable itself. For WWAHost, this means a generic product name (Microsoft Windows operating system) and version (Windows major.minor.build.revision) will be returned, thus not giving an accurate picture as to which app is being hosted and the guest app version. Thankfully, there is a function (kernel32.dll::GetPackageFullName) that will return a serialized package info text, which does return the actual product name and version of the hosted app. The package info structure consits of name, version, publisher, architecture and other relevant information for a package given the process ID for the app container, and is joined via an underscore character (_), with the first two indecies being publisher.name and version, respectively.
Thus modified AppModule._setProductInfo for wwahost to fetch the app info via the function described above. This then allows the correct name and version of the hosted app to be returned. Note that in some cases, product version is 1.0.0.0 (especially Store app in Windows 8 and 8.1), but this can be ignored.
…4569.

Just like Javaw (hosted Java apps): different app modules for apps hosted by WWAHost are desirable for handling individual apps. Thus fetch which app module to load via a call to kernel32.dll::GetApplicationUserModelId. The app model text consists of app family name, an exclamation point, and the app name. Because Python can't import files with exclamation marks in the middle of their name, just return the lowercase version of app name (latter half of the app model text).
Note that app modules for WWAHost apps must import contents of wwahost app module in order to take advantage of package-specific product name/version field, as well as other potential services in the future.
@josephsl
Copy link
Collaborator Author

josephsl commented Jan 3, 2019

Hi,

To @jcsteh: although Mick was requested for review, I'd like to request a second look from you (if you have time) since you're the one who gave me advice on how to proceed with this.

Thanks.

"""App module host for Windows 8.x and 10 web apps hosted by wwahost.exe.
In Windows 8, apps written in Javascript are executed inside WWAHost, including some WinRT apps.
In Windows 10, progressive web apps (PWA) and friends are hosted inside this process.
App modules wishing to support apps hosted inside this process must import contents of this app module.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean subclassing here? Also, what would such appModules look like? It would help to have some documentation for people to know how to create appModules like this.

if not appModel.value:
raise LookupError
# Convert this into lowercase to make the file name consistent with other NVDA app modules.
return appModel.value.split("!")[-1].lower()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the explanation mark mean here?

@josephsl
Copy link
Collaborator Author

josephsl commented Jan 4, 2019

Hi,

Subclassing: yep, I guess you're right in saying that app modules must be subclassed (will edit the documentation).

App modules for these: in Windows 10, they're really web apps, so they can utilize EdgeHTML code, along with adding keyboard shortcuts, fixing labels and such. In case of Windows 8.x, app modules for these can add support for native commands and workarounds.

Regarding exclamation point: this is part of the app model string and is used by Windows to communicate with the app in question. A typical app model text consists of family name and app name, joined together via an exclamation mark (familyName!appName). Because Python can't import files with an exclamation point in the middle of the name (another case of #5323), and to present a more friendly app name, app model text is split around the exclamation point.

To test this pull request, try using a progressive web app in Windows 10 such as Twitter app.

Thanks.

Reviewed by Leonard de Ruijter (Babbage): clarify docstring, and give an explanation as to how app model text is constructed and reasoning behind returning only a portion of it.
@@ -228,6 +229,44 @@ Events will be covered in greater detail later.

As with other examples in this guide, remember to delete the created app module when you are finished testing and then restart NVDA or reload plugins, so that original functionality is restored.

++ App modules for hosted apps ++
Some executables host various apps inside.
These include javaw.exe for running various java programs and wwahost.exe for web apps in Windows 8 and later.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to be more specific than just "web apps" here. I'm concerned people might think we're referring to web apps even on the web. Perhaps we could say "some web apps in the Windows Store"? Note "some" as well, because not all web apps in the Store are hosted in wwahost.


Note that wwahost.exe hosts web applications or apps behaving as such.
You should create an app module for apps hosted inside it if you believe the hosted app is not a true web application.
This is the case for built-in apps such as Mail and Calendar in Windows 8 and 8.1.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know what you're trying to say here, but I don't think this paragraph will be understood by most people. First, the base wwahost app module (which they are subclassing) already disables browse mode. Second, "true web application" is a pretty hard thing to define. Arguably, any app that uses web technologies is a true web app. The question is more whether users want to interact with the content primarily as a document or primarily using the app's own keyboard support. I would suggest you just remove this paragraph altogether and let devs make the decision on their own. If they feel the need to write an app module for a wwahost app and are reading this section, they've already made the decision; this guidance isn't helpful at that point and might just confuse them.


If an app runs inside a host executable, the name of the app module must be the name as defined by the host executable, which can be found through AppModule.appName property.
For example, an app module for a java app named "test" hosted inside javaw.exe must be named test.py.
For apps hosted inside wwahost, not only the app module name must be the name of the loaded app, but the app module must subclass the app module class found in wwahost.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "not only the app module name must be the name of the loaded app" -> "not only must the app module name be the name of the loaded app" (move the word "must")

processHandle = winKernel.openProcess(winKernel.SYNCHRONIZE|winKernel.PROCESS_QUERY_INFORMATION,False,processId)
length = ctypes.c_uint()
buf = winKernel.kernel32.GetApplicationUserModelId(processHandle, ctypes.byref(length), None)
appModel = ctypes.create_unicode_buffer(buf)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetApplicationUserModelId returns an error code, not a length. You should create a buffer using length.value. You can ignore the error code in this case. In any case, I don't recommend using the name buf to store a length; it's confusing.

raise RuntimeError("processHandle is 0")
length = ctypes.c_uint()
buf = winKernel.kernel32.GetPackageFullName(self.processHandle, ctypes.byref(length), None)
packageFullName = ctypes.create_unicode_buffer(buf)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above re length.

…via length.value. Re nvaccess#4259.

Comment from James Teh (Mozilla): try using length.value instead of a dedicated buffer variable, as the function used returns nonimportant error codes instead of actual length.
@josephsl
Copy link
Collaborator Author

Hi,

To @jcsteh: I can see why the explanatory paragraph can become confusing, as not all apps are hosted inside wwahost.exe.

I also tested length.value idea and it does work (thanks for the correction).

Note that if this PR comes into master, we would lose application role assignment workaround from Windows 8.x days, but it can be restored for individual apps if needed, resolving one or more bugs also.

Thanks.

@josephsl
Copy link
Collaborator Author

Hi,

I'll merge in latest work from master, including ability to tell NVDA that content hosted by WWAHost should not become a browse mode document, and taking care of linting issues that might arise as a result. Then I think it should be ready for a more general review.

Thanks.

…ult.

Although content appears to be a webpage, it is actually part of an app hosted inside WWAHost. This includes apps from Windows 8.x days, as well as progressive web apps (PWA's) that are not really browse mode documents. Apps requiring browse mode may override 'disable browse mode by default' flag to suit their needs.
Lint fixes include splitting long lines and comment style.
@josephsl
Copy link
Collaborator Author

josephsl commented Sep 8, 2019

Hi,

Change of plan: PR #10114 will deal with product name and version, as that PR also affects this one, too. This then simplifies pull request by adding a facility to let NVDA detect appropriate modules for ones hosted by WWAHost. The content of developer guide introduced with this PR won't change.

Thanks.

A more generic way of setting product name and version for hosted apps, including those hosted within WWAHost, will be used from base app module, thus this setter method is no longer needed.
@feerrenrut
Copy link
Contributor

Since this is related: I'd like to see a change to the way that we choose app modules. Rather than match the name of the appModule with the process name, I'd like a new function introduced on appModules that is given some details (or a way of getting details about the process) and returns True if it is a match. This could help to prevent having multiple appModules files for the same application, it could also give us more flexibility with supporting multiple versions of the same application.

@feerrenrut feerrenrut added Addon/API Changes to NVDA's API for addons to support addon development. audience/nvda-dev PR or issue is relevant to NVDA / Add-on developers enhancement labels Apr 8, 2020
@comanna

This comment has been minimized.

@josephsl
Copy link
Collaborator Author

josephsl commented Apr 8, 2020

Hi,

Before resolving merge conflicts, regarding app module specificiations for hosted apps:

Ad @LeonarddeR or osmeone pointed out, we might as well inspect app package data to see which app should be loaded based on the following:

  • App vendor
  • App name
  • App version
  • Architecture (if required)

This will work with Windows Runtime/UWP/WWA Host apps, not necessarily for Java GUI (javaw) and windowed Python (pyw) apps. Making matters more interesting is support for hosted apps in Windows 10 Version 2004 where an app can specify another app as host when run.

I think it might be best to do a separate PR for importing app modules based on specs - preferably from app hosts as they may have different ways to locate specs unless a unified approach can be taken.

Thanks.

@feerrenrut
Copy link
Contributor

I think it might be best to do a separate PR for importing app modules based on specs - preferably from app hosts as they may have different ways to locate specs unless a unified approach can be taken.

I agree it should be out of scope for this PR. I was thinking we could give the control to a module level function to decide whether it supports a given app. Perhaps the function would return the appropriate class (type or instance as appropriate) that supports the given process. I'm thinking about how we can encourage re-use in the appmodules but also enable supporting multiple versions of applications or the same version with multiple process names. But this should probably be discussed on an issue specific to that idea.

@josephsl
Copy link
Collaborator Author

josephsl commented Apr 8, 2020 via email

@jcsteh
Copy link
Contributor

jcsteh commented Apr 8, 2020

Since this is related: I'd like to see a change to the way that we choose app modules. Rather than match the name of the appModule with the process name, I'd like a new function introduced on appModules that is given some details (or a way of getting details about the process) and returns True if it is a match. This could help to prevent having multiple appModules files for the same application, it could also give us more flexibility with supporting multiple versions of the same application.

Perhaps I'm misunderstanding, but do you mean we'd no longer load module based on process name at all? That would mean we'd have to load all app modules (87 in core alone) when NVDA starts, and whenever an app starts, we'd have to run the check function in (at worst) every app module. That sounds pretty expensive to me (processing, i/o, memory) and is why we haven't done things like this in the past.

As an aside, my first attempt at braille display detection required all drivers to be imported to check whether they matched. I abandoned that (in favour of the centralised bdDetect approach we have now) because of this problem: loading all drivers wastes time and memory, especially if we're only going to use one of them.

@feerrenrut
Copy link
Contributor

That sounds pretty expensive to me (processing, i/o, memory) and is why we haven't done things like this in the past.

Certainly a concern that would need to be measured and mitigated. At this stage I'm just thinking about a "wish list" to make development and maintenance of appmodules easier.

@michaelDCurran
Copy link
Member

@josephsl Is there any further work required on this pr since pr #8644 was merged? or is it unrelated.

@josephsl
Copy link
Collaborator Author

josephsl commented Jul 6, 2020 via email

@josephsl
Copy link
Collaborator Author

josephsl commented Jul 6, 2020

Hi,

Actually, the change I was talking about above is already there (no change needed). And yes, this PR is different than #8633, so it can be merged.

Thanks.


class AppModule(appModuleHandler.AppModule):

# WWAHost app content is treated as part of an app, not a browse mode document.
disableBrowseModeByDefault = True

def event_NVDAObject_init(self,obj):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I understand correctly, all code from here to the end of the file can be removed. This code coerces the root of a Win8 metro app to ROLE_APPLICATION from ROLE_DOCUMENT. this was done simply to disable automatic browse mode for these apps. Since however browse mode is disabled above for all wwahost apps, this code is now completely redundant... Unless I'm missing something?

@josephsl
Copy link
Collaborator Author

josephsl commented Jul 6, 2020 via email

@michaelDCurran
Copy link
Member

michaelDCurran commented Jul 6, 2020 via email

… mode off flag.

Comment from Mick Curran (NV Access): in 2012, WWA Host app module was reponsible for turning what was essentially a web application into a proper 'application'. This was needed prior to introduction of browse mode flag for app modules. Now that this flag is present, there is no need for role coercion anymore (users can toggle browse mode instead).
@josephsl
Copy link
Collaborator Author

josephsl commented Jul 6, 2020

Hi,

Removed - I didn't see any third-party app modules being affected by this - haven't come across potential ones in add-ons community.

Thanks.

@michaelDCurran michaelDCurran merged commit 7b1d680 into nvaccess:master Jul 6, 2020
@nvaccessAuto nvaccessAuto added this to the 2020.3 milestone Jul 6, 2020
michaelDCurran added a commit that referenced this pull request Jul 6, 2020
@josephsl josephsl deleted the wwahostAppInfo branch July 19, 2020 22:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Addon/API Changes to NVDA's API for addons to support addon development. audience/nvda-dev PR or issue is relevant to NVDA / Add-on developers enhancement
Projects
None yet
7 participants