private static void OnSourceRootElementChanged (IAutomationSource source) { if (inSourceRootElementChanged) { pendingRootChangeSources.Enqueue (source); return; } lock (RawViewWalker.directChildrenLock) { inSourceRootElementChanged = true; List<AutomationElement> rootElements = new List<AutomationElement> (); var pidElementMapping = new Dictionary<int, IElement> (); foreach (AutomationElement element in RawViewWalker.directChildren) { if (element.SourceElement.AutomationSource != source) { try { pidElementMapping [element.Current.ProcessId] = element.SourceElement; } catch { continue; } // TODO: ElementNotAvailableException rootElements.Add (element); } } // We don't handle the cleanup of AutomationElements here, they're // handled by each AutomationSource. // "Clean up" includes removing event handlers etc. AddUniqueRootElements (rootElements, source, pidElementMapping); RawViewWalker.directChildren = rootElements; inSourceRootElementChanged = false; } Log.Debug ("Root elements are refreshed, Count: {0}", RawViewWalker.directChildren.Count); while (pendingRootChangeSources.Count > 0) OnSourceRootElementChanged (pendingRootChangeSources.Dequeue ()); }
private static void OnSourceRootElementChanged(IAutomationSource source) { if (inSourceRootElementChanged) { pendingRootChangeSources.Enqueue(source); return; } lock (RawViewWalker.directChildrenLock) { inSourceRootElementChanged = true; List <AutomationElement> rootElements = new List <AutomationElement> (); var pidElementMapping = new Dictionary <int, IElement> (); foreach (AutomationElement element in RawViewWalker.directChildren) { if (element.SourceElement.AutomationSource != source) { try { pidElementMapping [element.Current.ProcessId] = element.SourceElement; } catch { continue; } // TODO: ElementNotAvailableException rootElements.Add(element); } } // We don't handle the cleanup of AutomationElements here, they're // handled by each AutomationSource. // "Clean up" includes removing event handlers etc. AddUniqueRootElements(rootElements, source, pidElementMapping); RawViewWalker.directChildren = rootElements; inSourceRootElementChanged = false; } Log.Debug("Root elements are refreshed, Count: {0}", RawViewWalker.directChildren.Count); while (pendingRootChangeSources.Count > 0) { OnSourceRootElementChanged(pendingRootChangeSources.Dequeue()); } }
// TODO: This approach will completely break when the // MoonUiaDbusBridge work is committed. This code // will be replaced with proper source tree merging, // which requires updates to gtk-sharp. private static void AddUniqueRootElements(List <AutomationElement> rootElements, IAutomationSource source, Dictionary <int, IElement> pidElementMapping) { const string at_spi = "at-spi"; foreach (IElement sourceElement in source.GetRootElements()) { int pid; string sourceFid; try { pid = sourceElement.ProcessId; sourceFid = sourceElement.FrameworkId; } catch { continue; } // TODO: ElementNotAvailableException // NOTE: This is not a complete mapping, since // one process could generate multiple root // elements. But it's sufficient to catch // that at least one element exists for // a given PID. if (pidElementMapping.TryGetValue(pid, out IElement found)) { string foundFid; try { foundFid = found.FrameworkId; } catch { continue; } // TODO: ElementNotAvailableException // Ignore at-spi elements when elements // for this process from a preferred // framework exist if (sourceFid == at_spi && foundFid != at_spi) { continue; } // Remove at-spi elements when elements // for this process from a preferred // framework exist else if (sourceFid != at_spi && foundFid == at_spi) { // TODO: When we fix ElementNotAvailableException, // we'll need to mark these // elements as unavailable. rootElements.RemoveAll(e => e.Current.ProcessId == pid && e.Current.FrameworkId == at_spi); } } rootElements.Add(SourceManager.GetOrCreateAutomationElement(sourceElement)); pidElementMapping [pid] = sourceElement; } }
private static IAutomationSource GetAutomationSource(string sourceAssemblyName) { Assembly sourceAssembly = null; try { sourceAssembly = Assembly.Load(sourceAssemblyName); } catch (Exception e) { Log.Warn(string.Format("Error loading UIA source ({0}): {1}", sourceAssemblyName, e)); return(null); } Type sourceType = null; // Quickie inefficent implementation Type sourceInterfaceType = typeof(IAutomationSource); foreach (Type type in sourceAssembly.GetTypes()) { if (sourceInterfaceType.IsAssignableFrom(type)) { sourceType = type; break; } } if (sourceType == null) { Log.Error("No UIA source found in assembly: " + sourceAssemblyName); return(null); } try { IAutomationSource source = (IAutomationSource)Activator.CreateInstance(sourceType); if (!source.IsAccessibilityEnabled) { return(null); } source.Initialize(); return(source); } catch (Exception e) { Log.Error("Failed to load UIA source: " + e); return(null); } }
internal static IAutomationSource [] GetAutomationSources() { if (sources == null) { lock (sourcesLock) { if (sources == null) { var sourcesList = new List <IAutomationSource> (); // Let MONO_UIA_SOURCE env var override default source string sourceAssemblyNames = Environment.GetEnvironmentVariable("MONO_UIA_SOURCE"); if (string.IsNullOrEmpty(sourceAssemblyNames)) { sourceAssemblyNames = UiaDbusSourceAssembly + ";" + AtspiUiaSourceAssembly; } foreach (string sourceAssembly in sourceAssemblyNames.Split(';')) { if (string.IsNullOrEmpty(sourceAssembly)) { continue; } IAutomationSource source = GetAutomationSource(sourceAssembly); if (source != null) { sourcesList.Add(source); } } sourcesList.Add(ClientAutomationSource.Instance); sources = sourcesList.ToArray(); } } } return(sources); }
// TODO: This approach will completely break when the // MoonUiaDbusBridge work is committed. This code // will be replaced with proper source tree merging, // which requires updates to gtk-sharp. private static void AddUniqueRootElements (List<AutomationElement> rootElements, IAutomationSource source, Dictionary<int, IElement> pidElementMapping) { const string at_spi = "at-spi"; foreach (IElement sourceElement in source.GetRootElements ()) { int pid; try { pid = sourceElement.ProcessId; } catch { continue; } // TODO: ElementNotAvailableException IElement found; // NOTE: This is not a complete mapping, since // one process could generate multiple root // elements. But it's sufficient to catch // that at least one element exists for // a given PID. if (pidElementMapping.TryGetValue (pid, out found)) { var sourceFid = sourceElement.FrameworkId; var foundFid = found.FrameworkId; // Ignore at-spi elements when elements // for this process from a preferred // framework exist if (sourceFid == at_spi && foundFid != at_spi) continue; // Remove at-spi elements when elements // for this process from a preferred // framework exist else if (sourceFid != at_spi && foundFid == at_spi) { // TODO: When we fix ElementNotAvailableException, // we'll need to mark these // elements as unavailable. rootElements.RemoveAll (e => e.Current.ProcessId == pid && e.Current.FrameworkId == at_spi); } } rootElements.Add ( SourceManager.GetOrCreateAutomationElement (sourceElement)); pidElementMapping [pid] = sourceElement; } }