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

SubModules #37

Open
wants to merge 54 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
255a3e0
Bump addon_lastTestedNVDAVersion
JulienCochuyt Jul 19, 2024
c5b85f5
SubModules: Set addon feature version
JulienCochuyt Jul 19, 2024
2711cbf
ElementDescription: Add document URL
JulienCochuyt Jul 21, 2024
8443a8c
SubModules: GUI/JSON: Add a new `subModule` property to zones
JulienCochuyt Jul 21, 2024
a97d2df
SubModules: GUI/JSON: Add new `url` criterion
JulienCochuyt Jul 21, 2024
ad14036
Node search: Slight optimization of role and states search
JulienCochuyt Jul 21, 2024
96177c2
Node search: Slight optimization and rationalize `in` test
JulienCochuyt Jul 21, 2024
49d621a
SubModules: Node search: Implement `url` criterion
JulienCochuyt Jul 21, 2024
3c45d7f
Zones: Support rules matching multiple results
JulienCochuyt Jul 23, 2024
295d732
SubModules: Switch WebModules package to `webModulesSM` for easier te…
JulienCochuyt Jul 25, 2024
ef8b77a
Zone: Step towards zones not bound to a single node (#43)
JulienCochuyt Jul 25, 2024
e83a67f
Remove dead code
JulienCochuyt Aug 21, 2024
49e835c
Rename more legacy "WebApp" and "MarkerManager" leftovers
JulienCochuyt Aug 21, 2024
20c013a
GUI: Follow-up of NVDA #15121
JulienCochuyt Aug 25, 2024
5761d4c
Rules offline edition (#42)
JulienCochuyt Aug 26, 2024
e33f9ce
Support the "multiple" property for rules of type "parent"
JulienCochuyt Aug 26, 2024
b55ffe9
SubModules WIP - Very early alpha
JulienCochuyt Aug 26, 2024
e516776
SubModules: GUI: Editor: Offer a combo for SubModule selection rather…
JulienCochuyt Aug 29, 2024
0a42480
SubModules: GUI: Editor: Prompt for creation of a missing SubModule
JulienCochuyt Aug 30, 2024
4ad7177
WebAccessBmdti: Force re-analysis of the nodes when manually refreshi…
JulienCochuyt Aug 30, 2024
b46b3d7
GUI: Rules Manager: Widen the "filter" field and make it enlarge with…
JulienCochuyt Sep 1, 2024
0590dc1
GUI: Rules Manager: Group by Position: Fix rendering of nested zones
JulienCochuyt Sep 1, 2024
2d0a3ff
Nodes: Fix test for boundary check
JulienCochuyt Sep 1, 2024
c4b5e06
SubModules: Control Mutation
JulienCochuyt Sep 2, 2024
d315283
SubModules: RuleManager.iterResultsAtTextInfo: Include all WebModules…
JulienCochuyt Sep 2, 2024
13bbbf0
SubModules: Helper: Cache the WebModule retrieved by position for an …
JulienCochuyt Sep 2, 2024
5bb2625
SubModules: GUI: Rule Editor: Set a more explicit dialog title
JulienCochuyt Sep 2, 2024
0a0fd72
GUI: Context: Reword "Global" to "General" to avoid confusion with "G…
JulienCochuyt Sep 4, 2024
f864da5
Restore Element Description gesture
AAClause Sep 9, 2024
10328d5
GUI: Properties: Fix display value of custom autoAction
JulienCochuyt Sep 10, 2024
1d4a253
Control Mutation: Fix error handling
JulienCochuyt Sep 10, 2024
f85d07b
fix(rule/manager): resolve AttributeError in Dialog.onRuleDelete by u…
AAClause Sep 11, 2024
93b890b
SubModules: Try fix handling of custom event handlers
JulienCochuyt Sep 10, 2024
55b928c
Switch SubModules and Control Mutation indexing from id to (docHandle…
JulienCochuyt Sep 11, 2024
b68aaa5
GUI: Rules Manager: When grouping by position, mention which alternat…
JulienCochuyt Sep 12, 2024
aa2b0c9
GUI: Rules Manager: Add grouping by context
JulienCochuyt Sep 13, 2024
e06659b
RuleManager: Restore `script_notFound` fallback
JulienCochuyt Sep 17, 2024
e790e05
fix criteria not found by searchNode when text surounded with spaces
GatienBouyssou Sep 17, 2024
8ddb6f2
NodeManager: Refine detection of control ID doubles
JulienCochuyt Sep 18, 2024
5f7cf2e
SubModules: Fix loading the proper SubModule on a nested dialog
JulienCochuyt Sep 18, 2024
455b0e6
GUI: Editors: Remove autoAction from Actions panels
JulienCochuyt Sep 13, 2024
e28d902
Criteria Editor: testCriteria: Drop properties
AAClause Sep 19, 2024
e781420
Overlay: Add support for application root elements
JulienCochuyt Sep 26, 2024
82c1846
TableHandler integration: Move `createResult` from `Criteria` to `Rule`
JulienCochuyt Oct 15, 2024
fe24a80
WebAccessBmdti: Manual refresh is now synchronous
JulienCochuyt Oct 15, 2024
a2b0e54
NodeManager: Clean-up
JulienCochuyt Oct 15, 2024
c2abec9
Bump version to 2024.10.15-dev+subModules
JulienCochuyt Nov 6, 2024
73c329c
GUI: Input Gestures: Fix adding multiple gesture bindings in a row
JulienCochuyt Nov 14, 2024
e7f4fcd
GUI: Rule Summary: Display criteria set index as 1-based
JulienCochuyt Dec 3, 2024
b303e20
GUI: Editors: Do not attempt to save gesture bindings or properties n…
JulienCochuyt Dec 6, 2024
72de3de
Bump version to 2024.12.06-dev+subModules
JulienCochuyt Dec 9, 2024
fdb3099
GUI: Rule Editor: Initially select the alternative matched at caret
JulienCochuyt Dec 9, 2024
ff8c20e
Update fr translation
JulienCochuyt Dec 10, 2024
d63ba00
GUI: Summary: Condense alternatives
JulienCochuyt Dec 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
240 changes: 59 additions & 181 deletions addon/globalPlugins/webAccess/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,58 +56,48 @@

import os
import re
import core
import wx

from NVDAObjects.IAccessible import IAccessible
from NVDAObjects.IAccessible.MSHTML import MSHTML
from NVDAObjects.IAccessible.ia2Web import Ia2Web
from NVDAObjects.IAccessible.mozilla import Mozilla
from scriptHandler import script
import addonHandler
import api
import baseObject
from buildVersion import version_detailed as NVDA_VERSION
import controlTypes
import core
import eventHandler
import globalPluginHandler
import gui
from logHandler import log
import scriptHandler
import speech
from scriptHandler import script
import ui
import virtualBuffers

from . import nodeHandler
from . import overlay
from . import webAppLib
from .webAppLib import *
from . import overlay, webModuleHandler
from .webAppLib import playWebAccessSound, sleep
from .webAppScheduler import WebAppScheduler
from . import webModuleHandler


addonHandler.initTranslation()


TRACE = lambda *args, **kwargs: None # @UnusedVariable
#TRACE = log.info

SCRIPT_CATEGORY = "WebAccess"

#
# defines sound directory
#

SOUND_DIRECTORY = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "..", "sounds")
SUPPORTED_HOSTS = ['brave', 'firefox', 'chrome', 'java', 'iexplore', 'microsoftedgecp', 'msedge']
TRACE = lambda *args, **kwargs: None # @UnusedVariable
#TRACE = log.info


# Currently dead code, but will likely be revived for issue #17.
activeWebModule = None

supportedWebAppHosts = ['brave', 'firefox', 'chrome', 'java', 'iexplore', 'microsoftedgecp', 'msedge']

activeWebApp = None
webAccessEnabled = True
scheduler = None


class DefaultBrowserScripts(baseObject.ScriptableObject):

def __init__(self, warningMessage):
Expand All @@ -118,7 +108,7 @@ def __init__(self, warningMessage):
self.__class__.__gestures["kb:control+shift+%s" % character] = "notAssigned"

def script_notAssigned(self, gesture): # @UnusedVariable
playWebAppSound("keyError")
playWebAccessSound("keyError")
sleep(0.2)
ui.message(self.warningMessage)

Expand Down Expand Up @@ -202,6 +192,7 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList):
if cls in clsList
):
if obj.role in (
controlTypes.ROLE_APPLICATION,
controlTypes.ROLE_DIALOG,
controlTypes.ROLE_DOCUMENT,
):
Expand All @@ -220,27 +211,36 @@ def script_showWebAccessGui(self, gesture): # @UnusedVariable

def showWebAccessGui(self):
obj = api.getFocusObject()
if obj is None or obj.appModule is None:
# Translators: Error message when attempting to show the Web Access GUI.
ui.message(_("The current object does not support Web Access."))
return
if not supportWebApp(obj):
if not canHaveWebAccessSupport(obj):
# Translators: Error message when attempting to show the Web Access GUI.
ui.message(_("You must be in a web browser to use Web Access."))
return
if obj.treeInterceptor is None or not isinstance(obj, overlay.WebAccessObject):
if not isinstance(obj.treeInterceptor, overlay.WebAccessBmdti):
# Translators: Error message when attempting to show the Web Access GUI.
ui.message(_("You must be on the web page to use Web Access."))
return

from .gui import menu
context = {}
context["webAccess"] = self
context["focusObject"] = obj
webModule = obj.webAccess.webModule
if webModule is not None:
context = {
"webAccess": self,
"focusObject": obj,
}
# Use the helper of the TreeInterceptor to get the WebModule at caret
# rather than the one where the focus is stuck.
webModule = obj.treeInterceptor.webAccess.webModule
if webModule:
context["webModule"] = webModule
context["pageTitle"] = webModule.pageTitle
mgr = webModule.ruleManager
context["result"] = mgr.getResultAtCaret()
stack = []
while True:
stack.append(webModule)
try:
webModule = webModule.ruleManager.parentRuleManager.webModule
except AttributeError:
break
if len(stack) > 1:
context["webModuleStackAtCaret"] = stack
menu.show(context)

@script(
Expand All @@ -262,125 +262,6 @@ def script_showWebAccessSettings(self, gesture): # @UnusedVariable
# Now part of the public API as of NVDA PR #15121
gui.mainFrame.popupSettingsDialog(WebAccessSettingsDialog)

@script(
# Translators: Input help mode message for a command.
description=_("Toggle debug mode."),
category=SCRIPT_CATEGORY,
gesture="kb:nvda+control+shift+w"
)
def script_debugWebModule(self, gesture): # @UnusedVariable
global activeWebApp
focus = api.getFocusObject()
if \
activeWebApp is None \
and not hasattr(focus, "_webApp") \
and not hasattr(focus, "treeInterceptor") \
and not hasattr(focus.treeInterceptor, "_webApp") \
and not hasattr(focus.treeInterceptor, "nodeManager"):
ui.message("Pas de WebModule actif")
return

diverged = False
focusModule = None
treeModule = None
msg = "Divergence :"
msg += os.linesep
msg += "activeWebApp = {webModule}".format(
webModule=activeWebApp.storeRef
if hasattr(activeWebApp, "storeRef")
else activeWebApp
)
if activeWebApp is not None:
msg += " ({id})".format(id=id(activeWebApp))
if not hasattr(focus, "_webApp"):
msg += os.linesep
msg += "focus._webApp absent"
else:
focusModule = focus._webApp
if activeWebApp is not focusModule:
diverged = True
msg += os.linesep
msg += "focus._webApp = {webModule}".format(
webModule=
focusModule.storeRef
if hasattr(focusModule, "storeRef")
else focusModule
)
if focusModule is not None:
msg += " ({id})".format(id=id(focusModule))
if not hasattr(focus, "treeInterceptor"):
diverged = True
msg += os.linesep
msg += "focus.treeInterceptor absent"
else:
if focus.treeInterceptor is None:
diverged = True
msg += os.linesep
msg += "focus.treeInterceptor None"
# if not hasattr(focusModule, "treeInterceptor"):
# diverged = True
# msg += os.linesep
# msg += u"focus._webApp.treeInterceptor absent"
# elif focusModule.treeInterceptor is None:
# diverged = True
# msg += os.linesep
# msg += u"focus._webApp.treeInterceptor None"
# elif focus.treeInterceptor is not focusModule.treeInterceptor:
# diverged = True
# msg += os.linesep
# msg += u"TreeInterceptors différents"
if hasattr(focus.treeInterceptor, "_webApp"):
treeModule = focus.treeInterceptor._webApp
if \
treeModule is not focusModule \
or treeModule is not activeWebApp:
diverged = True
msg += os.linesep
msg += "treeInterceptor._webApp = {webModule}".format(
webModule=
treeModule.storeRef
if hasattr(treeModule, "storeRef")
else treeModule
)
if treeModule is not None:
msg += " ({id})".format(id=id(treeModule))
if hasattr(focus.treeInterceptor, "nodeManager"):
if focusModule is None:
diverged = True
msg += "treeInterceptor.nodeManager "
if focus.treeInterceptor.nodeManager is None:
msg += "est None"
else:
msg += "n'est pas None"
elif \
focusModule.ruleManager.nodeManager is not \
focus.treeInterceptor.nodeManager:
diverged = True
msg += os.linesep
msg += "NodeManagers différents"
elif focusModule.ruleManager.nodeManager is None:
msg += os.linesep
msg += "NodeManagers None"


allMsg = ""

if not diverged:
try:
from six import text_type
except ImportError:
# NVDA version < 2018.3
text_type = str
msg = text_type(focusModule.storeRef)
speech.speakMessage(msg)
allMsg += msg + os.linesep

treeInterceptor = html.getTreeInterceptor()
msg = "nodeManager %d caractères, %s, %s" % (treeInterceptor.nodeManager.treeInterceptorSize, treeInterceptor.nodeManager.isReady, treeInterceptor.nodeManager.mainNode is not None)
speech.speakMessage(msg)
allMsg += msg + os.linesep
api.copyToClip(allMsg)

@script(
# Translators: Input help mode message for show Web Access menu command.
description=_("Show the element description."),
Expand Down Expand Up @@ -417,23 +298,23 @@ def script_toggleWebAccessSupport(self, gesture): # @UnusedVariable
ui.message(_("Web Access support enabled.")) # FR: u"Support Web Access activé."


def getActiveWebApp():
global activeWebApp
return activeWebApp
def getActiveWebModule():
global activeWebModule
return activeWebModule


def webAppLoseFocus(obj):
global activeWebApp
if activeWebApp is not None:
sendWebAppEvent('webApp_loseFocus', obj, activeWebApp)
activeWebApp = None
#log.info("Losing webApp focus for object:\n%s\n" % ("\n".join(obj.devInfo)))
def webModuleLoseFocus(obj):
global activeWebModule
if activeWebModule is not None:
sendWebModuleEvent('webModule_loseFocus', obj, activeWebModule)
activeWebModule = None
#log.info("Losing webModule focus for object:\n%s\n" % ("\n".join(obj.devInfo)))


def supportWebApp(obj):
def canHaveWebAccessSupport(obj):
if obj is None or obj.appModule is None:
return None
return obj.appModule.appName in supportedWebAppHosts
return False
return obj.appModule.appName in SUPPORTED_HOSTS


def VirtualBuffer_changeNotify(cls, rootDocHandle, rootID):
Expand All @@ -448,10 +329,10 @@ def virtualBuffer_loadBufferDone(self, success=True):
virtualBuffer_loadBufferDone.super.__get__(self)(success=success)


def sendWebAppEvent(eventName, obj, webApp=None):
if webApp is None:
def sendWebModuleEvent(eventName, obj, webModule=None):
if webModule is None:
return
scheduler.send(eventName="webApp", name=eventName, obj=obj, webApp=webApp)
scheduler.send(eventName="webModule", name=eventName, obj=obj, webModule=webModule)


def eventExecuter_gen(self, eventName, obj):
Expand All @@ -465,21 +346,18 @@ def eventExecuter_gen(self, eventName, obj):
if func:
yield func, (obj, self.next)

# webApp level
if not supportWebApp(obj) and eventName in ["gainFocus"] and activeWebApp is not None:
# log.info("Received event %s on a non-hosted object" % eventName)
webAppLoseFocus(obj)
# WebModule level.
webModule = obj.webAccess.webModule if isinstance(obj, overlay.WebAccessObject) else None
if webModule is None:
# Currently dead code, but will likely be revived for issue #17.
if activeWebModule is not None and obj.hasFocus:
#log.info("Disabling active webApp event %s" % eventName)
webAppLoseFocus(obj)
else:
webApp = obj.webAccess.webModule if isinstance(obj, overlay.WebAccessObject) else None
if webApp is None:
if activeWebApp is not None and obj.hasFocus:
#log.info("Disabling active webApp event %s" % eventName)
webAppLoseFocus(obj)
else:
# log.info("Getting method %s -> %s" %(webApp.name, funcName))
func = getattr(webApp, funcName, None)
if func:
yield func,(obj, self.next)
# log.info("Getting method %s -> %s" %(webApp.name, funcName))
func = getattr(webModule, funcName, None)
if func:
yield func, (obj, self.next)

# App module level.
app = obj.appModule
Expand Down
Loading