diff --git a/Winium/Winium.Mobile.Connectivity/AppType.cs b/Winium/Winium.Mobile.Connectivity/AppType.cs new file mode 100644 index 0000000..6f58476 --- /dev/null +++ b/Winium/Winium.Mobile.Connectivity/AppType.cs @@ -0,0 +1,9 @@ +namespace Winium.Mobile.Connectivity +{ + public enum AppType + { + XAP = Microsoft.Phone.Tools.Deploy.TypeOfApp.XAP, + APPX = Microsoft.Phone.Tools.Deploy.TypeOfApp.APPX, + APPXBUNDLE = Microsoft.Phone.Tools.Deploy.TypeOfApp.APPXBUNDLE + } +} \ No newline at end of file diff --git a/Winium/Winium.Mobile.Connectivity/Deployer.cs b/Winium/Winium.Mobile.Connectivity/Deployer.cs index 5cfd723..a4cbd9c 100644 --- a/Winium/Winium.Mobile.Connectivity/Deployer.cs +++ b/Winium/Winium.Mobile.Connectivity/Deployer.cs @@ -85,10 +85,31 @@ public string DeviceName private IRemoteApplication RemoteApplication { get; set; } + public AppType AppType { get; private set; } + #endregion #region Public Methods and Operators + public static AppType DetermineAppType(string packagePath) + { + var extension = Path.GetExtension(packagePath); + if (!string.IsNullOrEmpty(extension)) + { + switch (extension.ToLower(CultureInfo.InvariantCulture)) + { + case ".appxbundle": + return AppType.APPXBUNDLE; + case ".appx": + return AppType.APPX; + case ".xap": + return AppType.XAP; + } + } + + throw new NotImplementedException("This file extension is not supported by the tool."); + } + public void Install(string appPath, List dependencies) { this.InstallDependencies(dependencies); @@ -99,6 +120,7 @@ public void UsePreInstalledApplication(string appPath) { var appManifest = Utils.ReadAppManifestInfoFromPackage(appPath); this.RemoteApplication = this.Device.GetApplication(appManifest.ProductId); + this.AppType = DetermineAppType(appPath); } public void Launch() @@ -136,7 +158,14 @@ public void SendFiles(List> files) public void Terminate() { - throw new NotImplementedException("Deployer.Terminate"); + if (this.AppType == AppType.XAP) + { + this.RemoteApplication.TerminateRunningInstances(); + } + else + { + throw new NotImplementedException("Deployer.Terminate"); + } } public void Uninstall() @@ -162,6 +191,7 @@ private void InstallApp(string appPath) var appManifestInfo = this.InstallApplicationPackage(appPath); this.installed = true; this.RemoteApplication = this.Device.GetApplication(appManifestInfo.ProductId); + this.AppType = DetermineAppType(appPath); } private IAppManifestInfo InstallApplicationPackage(string path) diff --git a/Winium/Winium.Mobile.Connectivity/IDeployer.cs b/Winium/Winium.Mobile.Connectivity/IDeployer.cs index 791cc21..98c4dd7 100644 --- a/Winium/Winium.Mobile.Connectivity/IDeployer.cs +++ b/Winium/Winium.Mobile.Connectivity/IDeployer.cs @@ -12,6 +12,8 @@ public interface IDeployer string DeviceName { get; } + AppType AppType { get; } + #endregion #region Public Methods and Operators diff --git a/Winium/Winium.Mobile.Connectivity/Winium.Mobile.Connectivity.csproj b/Winium/Winium.Mobile.Connectivity/Winium.Mobile.Connectivity.csproj index 8d2a959..3bbe3a8 100644 --- a/Winium/Winium.Mobile.Connectivity/Winium.Mobile.Connectivity.csproj +++ b/Winium/Winium.Mobile.Connectivity/Winium.Mobile.Connectivity.csproj @@ -52,6 +52,7 @@ + diff --git a/Winium/Winium.Mobile.Driver/CommandExecutorDispatchTable.cs b/Winium/Winium.Mobile.Driver/CommandExecutorDispatchTable.cs index e225d7f..1a23f00 100644 --- a/Winium/Winium.Mobile.Driver/CommandExecutorDispatchTable.cs +++ b/Winium/Winium.Mobile.Driver/CommandExecutorDispatchTable.cs @@ -47,10 +47,10 @@ public CommandExecutorBase GetExecutor(Command command) { var automator = Automator.Automator.InstanceForSession(command.SessionId); - if (automator.CurrentContext != DefaultContextNames.NativeAppContextName - && !DriverBoundCommands.Contains(command.Name)) + if (automator.CurrentContext != null && automator.CurrentContext != DefaultContextNames.NativeAppContextName) { - return new CommandExecutorWebContextForward(); + if (!DriverBoundCommands.Contains(command.Name)) + return new CommandExecutorWebContextForward(); } // TODO inject dependencies into command executor diff --git a/Winium/Winium.Mobile.Driver/CommandExecutors/CloseAppExecutor.cs b/Winium/Winium.Mobile.Driver/CommandExecutors/CloseAppExecutor.cs index ad7a573..ccea39e 100644 --- a/Winium/Winium.Mobile.Driver/CommandExecutors/CloseAppExecutor.cs +++ b/Winium/Winium.Mobile.Driver/CommandExecutors/CloseAppExecutor.cs @@ -1,5 +1,6 @@ namespace Winium.Mobile.Driver.CommandExecutors { + using Connectivity; using System; using System.Threading; @@ -12,9 +13,16 @@ internal class CloseAppExecutor : CommandExecutorBase public static void CloseApp(Automator automator) { - var remoteCommand = new Command(DriverCommand.CloseApp); - automator.CommandForwarder.ForwardCommand(remoteCommand); - Thread.Sleep(TimeSpan.FromMilliseconds(500)); + if (automator.Deployer.AppType == AppType.XAP) + { + automator.Deployer.Terminate(); + } + else + { + var remoteCommand = new Command(DriverCommand.CloseApp); + automator.CommandForwarder.ForwardCommand(remoteCommand); + Thread.Sleep(TimeSpan.FromMilliseconds(500)); + } } #endregion diff --git a/Winium/Winium.Silverlight.InnerServer/AutomationServer.cs b/Winium/Winium.Silverlight.InnerServer/AutomationServer.cs index 86b82ad..cb98673 100644 --- a/Winium/Winium.Silverlight.InnerServer/AutomationServer.cs +++ b/Winium/Winium.Silverlight.InnerServer/AutomationServer.cs @@ -12,6 +12,7 @@ using Newtonsoft.Json; using Winium.Mobile.Common; + using Microsoft.Phone.Controls; public class AutomationServer { @@ -60,6 +61,11 @@ public void SetAutomator(UIElement visualRoot) this.automator = new Automator(visualRoot); } + public void SetBrowser(WebBrowser browser) + { + this.automator.browser = browser; + } + public async void Start() { if (this.isServerActive) diff --git a/Winium/Winium.Silverlight.InnerServer/Automator.cs b/Winium/Winium.Silverlight.InnerServer/Automator.cs index a33eb99..ea2a64a 100644 --- a/Winium/Winium.Silverlight.InnerServer/Automator.cs +++ b/Winium/Winium.Silverlight.InnerServer/Automator.cs @@ -10,6 +10,9 @@ using Winium.Mobile.Common; using Winium.Silverlight.InnerServer.Commands; + using Web.Commands; + using Web; + using Microsoft.Phone.Controls; internal class Automator { @@ -19,6 +22,8 @@ public Automator(UIElement visualRoot) { this.VisualRoot = visualRoot; this.WebElements = new AutomatorElements(); + this.ContextsRegistry = new ContextsRegistry(); + this.CurrentContext = ContextsRegistry.NativeAppContext; } #endregion @@ -29,6 +34,13 @@ public Automator(UIElement visualRoot) public AutomatorElements WebElements { get; private set; } + public string CurrentContext { get; set; } + + public WebBrowser browser { get; set; } + + public ContextsRegistry ContextsRegistry { get; private set; } + + #endregion #region Public Methods and Operators @@ -51,7 +63,7 @@ public string ProcessCommand(string content) elementId = elementIdObject.ToString(); } - CommandBase commandToExecute; + CommandBase commandToExecute = null; if (command.Equals("ping")) { @@ -59,6 +71,82 @@ public string ProcessCommand(string content) return ""; } + if (command.Equals(DriverCommand.Contexts)) + { + commandToExecute = new GetWebContextsCommand(); + } + else if (command.Equals(DriverCommand.SetContext)) + { + commandToExecute = new SetContextCommand(); + } + + // TODO: Refactor similar to CommandExecutors in OuterDriver + // TODO: Refactor similar to CommandExecutors in Driver + if (commandToExecute == null) + { + commandToExecute = this.CurrentContext == ContextsRegistry.NativeAppContext + ? GetNativeAppCommandToExecute(command, elementId, parameters) + : this.GetWebCommandToExecute(requestData); + } + + + + JToken sessionIdObject; + commandToExecute.Session = parameters.TryGetValue("SESSIONID", out sessionIdObject) + ? sessionIdObject.ToString() + : string.Empty; + + // TODO: Replace passing Automator to command with passing some kind of configuration + commandToExecute.Automator = this; + commandToExecute.Parameters = parameters; + + var response = commandToExecute.Do(); + + return response; + } + + private CommandBase GetWebCommandToExecute(Command request) + { + var command = request.Name; + WebCommandHandler commandToExecute; + + var context = this.ContextsRegistry.GetContext(this.CurrentContext); + + if (command.Equals(DriverCommand.Get)) + { + commandToExecute = new GoToUrlCommandHandler(); + } + else if (command.Equals(DriverCommand.FindElement)) + { + commandToExecute = new FindElementCommandHandler(); + } + else if (command.Equals(DriverCommand.ClickElement)) + { + commandToExecute = new ClickElementCommandHandler(); + } + else if (command.Equals(DriverCommand.SendKeysToElement)) + { + commandToExecute = new SendKeysCommandHandler(); + } + else if (command.Equals(DriverCommand.GetPageSource)) + { + commandToExecute = new GetPageSourceCommandHandler(); + } + else + { + throw new NotImplementedException("Not implemented: " + command); + } + + commandToExecute.Atom = request.Atom; + commandToExecute.Context = context; + + return commandToExecute; + + } + + private CommandBase GetNativeAppCommandToExecute(string command, string elementId, IDictionary parameters) + { + CommandBase commandToExecute; // TODO: Refactor similar to CommandExecutors in Driver if (command.Equals(DriverCommand.GetAlertText)) { @@ -148,13 +236,7 @@ public string ProcessCommand(string content) throw new NotImplementedException("Not implemented: " + command); } - // TODO: Replace passing Automator to command with passing some kind of configuration - commandToExecute.Automator = this; - commandToExecute.Parameters = parameters; - - var response = commandToExecute.Do(); - - return response; + return commandToExecute; } #endregion diff --git a/Winium/Winium.Silverlight.InnerServer/Commands/GetContextsCommand.cs b/Winium/Winium.Silverlight.InnerServer/Commands/GetContextsCommand.cs new file mode 100644 index 0000000..c93d6b3 --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Commands/GetContextsCommand.cs @@ -0,0 +1,18 @@ +namespace Winium.Silverlight.InnerServer.Commands +{ + using System.Collections.Generic; + using System.Linq; + using Winium.Mobile.Common; + + internal class GetWebContextsCommand : CommandBase + { + private static readonly IEnumerable UnconditionalContexts = new[] { DefaultContextNames.NativeAppContextName }; + public override string DoImpl() + + { + var contexts = this.Automator.ContextsRegistry.GetAllContexts(this.Automator.browser); + + return this.JsonResponse(ResponseStatus.Success, UnconditionalContexts.Union(contexts)); + } + } +} \ No newline at end of file diff --git a/Winium/Winium.Silverlight.InnerServer/Commands/SetContextCommand.cs b/Winium/Winium.Silverlight.InnerServer/Commands/SetContextCommand.cs new file mode 100644 index 0000000..1e6086a --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Commands/SetContextCommand.cs @@ -0,0 +1,39 @@ +namespace Winium.Silverlight.InnerServer.Commands +{ + #region + + using Newtonsoft.Json.Linq; + + using Mobile.Common; + using Mobile.Common.Exceptions; + + #endregion + internal class SetContextCommand : CommandBase + { + public override string DoImpl() + { + JToken value; + if (!this.Parameters.TryGetValue("name", out value)) + { + throw new AutomationException("Bad argument: name", ResponseStatus.UnknownCommand); + } + + var name = value.ToObject(); + + if (string.IsNullOrWhiteSpace(name)) + { + name = DefaultContextNames.NativeAppContextName; + } + + if (!name.Equals(DefaultContextNames.NativeAppContextName)) + { + // check if context is still valid + this.Automator.ContextsRegistry.GetContext(name); + } + + this.Automator.CurrentContext = name; + + return this.JsonResponse(ResponseStatus.Success, this.Automator.CurrentContext); + } + } +} \ No newline at end of file diff --git a/Winium/Winium.Silverlight.InnerServer/ContextsRegistry.cs b/Winium/Winium.Silverlight.InnerServer/ContextsRegistry.cs new file mode 100644 index 0000000..625ed53 --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/ContextsRegistry.cs @@ -0,0 +1,82 @@ +namespace Winium.Silverlight.InnerServer +{ + using System; + using System.Collections.Generic; + using Microsoft.Phone.Controls; + using Mobile.Common; + using Mobile.Common.Exceptions; + using Web; + using System.Windows.Media; + using System.Windows; + using Commands.FindByHelpers; + using System.Linq; + using System.Threading; + using Commands; + using System.Globalization; + + internal class ContextsRegistry + { + private static int safeInstanceCount; + + private readonly Dictionary contexts; + + public const string NativeAppContext = "NATIVE_APP"; + + public WebBrowser browser { get; set; } + + public ContextsRegistry() + { + this.contexts = new Dictionary(); + } + + public IEnumerable GetAllContexts(WebBrowser browser) + { + this.browser = browser; + RegisterContext(this.browser); + return contexts.Keys; + } + + private void RegisterContext(DependencyObject element) + { + var existing = this.contexts.FirstOrDefault(x => Equals(x.Value, element)).Key; + + if (existing != null) + { + return; + } + + var id = GenerateId((FrameworkElement)element); + this.contexts.Add(id, new CommandEnvironment(this.browser)); + } + + public CommandEnvironment GetContext(string id) + { + if (this.contexts.Count == 1 ) + { + this.GetAllContexts(this.browser); + } + + CommandEnvironment context; + if (this.contexts.TryGetValue(id, out context)) + { + if (context != null) + { + return context; + } + } + throw new AutomationException("Stale element reference", ResponseStatus.StaleElementReference); + } + + private static string GenerateId(FrameworkElement element) + { + Interlocked.Increment(ref safeInstanceCount); + + var friendlyName = !string.IsNullOrWhiteSpace(element.AutomationId()) + ? element.AutomationId() + : safeInstanceCount.ToString(CultureInfo.InvariantCulture); + + return string.Format("WEBVIEW_{0}", friendlyName); + } + } + +} diff --git a/Winium/Winium.Silverlight.InnerServer/ProtocolValueJsonConverter.cs b/Winium/Winium.Silverlight.InnerServer/ProtocolValueJsonConverter.cs new file mode 100644 index 0000000..c20c53c --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/ProtocolValueJsonConverter.cs @@ -0,0 +1,113 @@ +// +// +// Copyright (c) 2014 Salesforce.com, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace Winium.Silverlight.InnerServer +{ + using System; + using System.Collections.Generic; + + using Newtonsoft.Json; + + /// + /// Converts the response to JSON + /// + internal class ProtocolValueJsonConverter : JsonConverter + { + /// + /// Checks if the object can be converted + /// + /// The object to be converted + /// True if it can be converted or false if can't be + public override bool CanConvert(Type objectType) + { + return true; + } + + /// + /// Process the reader to return an object from JSON + /// + /// A JSON reader + /// Type of the object + /// The existing value of the object + /// JSON Serializer + /// Object created from JSON + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return this.ProcessToken(reader); + } + + /// + /// Writes objects to JSON. Currently not implemented + /// + /// JSON Writer Object + /// Value to be written + /// JSON Serializer + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (serializer != null) + { + serializer.Serialize(writer, value); + } + } + + private object ProcessToken(JsonReader reader) + { + // Recursively processes a token. This is required for elements that next other elements. + object processedObject = null; + if (reader != null) + { + if (reader.TokenType == JsonToken.StartObject) + { + Dictionary dictionaryValue = new Dictionary(); + while (reader.Read() && reader.TokenType != JsonToken.EndObject) + { + string elementKey = reader.Value.ToString(); + reader.Read(); + dictionaryValue.Add(elementKey, this.ProcessToken(reader)); + } + + processedObject = dictionaryValue; + } + else if (reader.TokenType == JsonToken.StartArray) + { + List arrayValue = new List(); + while (reader.Read() && reader.TokenType != JsonToken.EndArray) + { + arrayValue.Add(this.ProcessToken(reader)); + } + + processedObject = arrayValue.ToArray(); + } + else + { + processedObject = reader.Value; + } + } + + return processedObject; + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Commandenvironment.cs b/Winium/Winium.Silverlight.InnerServer/Web/Commandenvironment.cs new file mode 100644 index 0000000..fa50fcb --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Commandenvironment.cs @@ -0,0 +1,195 @@ +namespace Winium.Silverlight.InnerServer.Web +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Phone.Controls; + + public class CommandEnvironment + { + /// + /// The key used to denote a window object. + /// + public const string WindowObjectKey = "WINDOW"; + + /// + /// The key used to denote an element object. + /// + public const string ElementObjectKey = "ELEMENT"; + + /// + /// The global window handle string used, since the driver only supports one window. + /// + public const string GlobalWindowHandle = "WPDriverWindowHandle"; + + private WebBrowser browser; + private string focusedFrame = string.Empty; + private Dictionary keyboardState = null; + private Dictionary mouseState = new Dictionary(); + + private int implicitWaitTimeout; + private int asyncScriptTimeout = -1; + private int pageLoadTimeout = -1; + private bool isBlocked; + private string alertText = string.Empty; + private string alertType = string.Empty; + + + public CommandEnvironment(WebBrowser browser) + { + this.browser = browser; + this.ClearCache(); + this.browser.ScriptNotify += this.BrowserScriptNotifyEventHandler; + this.browser.Navigating += this.BrowserNavigatingEventHandler; + + } + + /// + /// Gets or sets the keyboard state of the driver. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Values are typed correctly for JSON serialization/deserialization.")] + public Dictionary KeyboardState + { + get { return this.keyboardState; } + set { this.keyboardState = value; } + } + + /// + /// Gets or sets the mouse state of the driver. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly", Justification = "Values are typed correctly for JSON serialization/deserialization.")] + public Dictionary MouseState + { + get { return this.mouseState; } + set { this.mouseState = value; } + } + + /// + /// Gets the text of the active alert. + /// + public string AlertText + { + get { return this.alertText; } + } + + /// + /// Gets the type of the active alert. + /// + public string AlertType + { + get { return this.alertType; } + } + + /// + /// Gets the browser against which commands are run. + /// + public WebBrowser Browser + { + get { return this.browser; } + } + + /// + /// Gets a value indicating whether execution of the next command should be blocked. + /// + public bool IsBlocked + { + get { return this.isBlocked; } + } + + /// + /// Gets or sets the ID of the currently focused frame in the browser. + /// + public string FocusedFrame + { + get { return this.focusedFrame; } + set { this.focusedFrame = value; } + } + + /// + /// Gets or sets the implicit wait timeout in milliseconds. + /// + public int ImplicitWaitTimeout + { + get { return this.implicitWaitTimeout; } + set { this.implicitWaitTimeout = value; } + } + + /// + /// Gets or sets the asynchronous script timeout in milliseconds. + /// + public int AsyncScriptTimeout + { + get { return this.asyncScriptTimeout; } + set { this.asyncScriptTimeout = value; } + } + + /// + /// Gets or sets the page load timeout in milliseconds. + /// + public int PageLoadTimeout + { + get { return this.pageLoadTimeout; } + set { this.pageLoadTimeout = value; } + } + + /// + /// Creates a serializable object for the currently focused frame. + /// + /// A representing the currently focused + /// frame that can be serialized into a format the atoms will expect. + public Dictionary CreateFrameObject() + { + if (string.IsNullOrEmpty(this.focusedFrame)) + { + return null; + } + + Dictionary returnValue = new Dictionary(); + returnValue[WindowObjectKey] = this.focusedFrame; + return returnValue; + } + + /// + /// Clears the alert status of the driver. + /// + public void ClearAlertStatus() + { + this.isBlocked = false; + this.alertType = string.Empty; + this.alertText = string.Empty; + } + + private async void ClearCache() + { + await this.browser.ClearInternetCacheAsync(); + } + + private void BrowserNavigatingEventHandler(object sender, NavigatingEventArgs e) + { + this.mouseState.Clear(); + Dictionary clientXY = new Dictionary(); + clientXY["x"] = 0; + clientXY["y"] = 0; + this.mouseState["clientXY"] = clientXY; + this.mouseState["element"] = null; + } + + private void BrowserScriptNotifyEventHandler(object sender, NotifyEventArgs e) + { + if (!this.isBlocked) + { + string[] valueParts = e.Value.Split(new char[] { ':' }, 2); + this.alertType = valueParts[0]; + this.alertText = valueParts[1]; + this.isBlocked = true; + } + else + { + this.ClearAlertStatus(); + } + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Commands/ClearElementCommandHandler.cs b/Winium/Winium.Silverlight.InnerServer/Web/Commands/ClearElementCommandHandler.cs new file mode 100644 index 0000000..6895645 --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Commands/ClearElementCommandHandler.cs @@ -0,0 +1,62 @@ +// +// +// Copyright (c) 2014 Salesforce.com, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace Winium.Silverlight.InnerServer.Web.Commands +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + internal class ClearElementCommandHandler : WebCommandHandler + { + /// + /// Executes the command. + /// + /// The to use in executing the command. + /// The containing the command parameters. + /// The JSON serialized string representing the command response. + public override Response Execute(CommandEnvironment environment, Dictionary parameters) + { + object element; + var clearAtom = this.Atom; + if (!parameters.TryGetValue("ID", out element)) + { + return Response.CreateMissingParametersResponse("ID"); + } + + this.SetAtomExecutionTimeout(TimeSpan.FromMilliseconds(100)); + string result = this.EvaluateAtom(environment, clearAtom, element, environment.CreateFrameObject()); + if (string.IsNullOrEmpty(result)) + { + return Response.CreateSuccessResponse(); + } + + return Response.FromJson(result); + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Commands/ClickElementCommandHandler.cs b/Winium/Winium.Silverlight.InnerServer/Web/Commands/ClickElementCommandHandler.cs new file mode 100644 index 0000000..8681845 --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Commands/ClickElementCommandHandler.cs @@ -0,0 +1,32 @@ + + +namespace Winium.Silverlight.InnerServer.Web.Commands +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + internal class ClickElementCommandHandler : WebCommandHandler + { + public override Response Execute(CommandEnvironment environment, Dictionary parameters) + { + object element; + var clickAtom = this.Atom; + if (!parameters.TryGetValue("ID", out element)) + { + return Response.CreateMissingParametersResponse("ID"); + } + + this.SetAtomExecutionTimeout(TimeSpan.FromMilliseconds(100)); + string result = this.EvaluateAtom(environment, clickAtom, element, environment.CreateFrameObject()); + if (string.IsNullOrEmpty(result)) + { + return Response.CreateSuccessResponse(); + } + + return Response.FromJson(result); + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Commands/FindElementCommandHandler.cs b/Winium/Winium.Silverlight.InnerServer/Web/Commands/FindElementCommandHandler.cs new file mode 100644 index 0000000..f721582 --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Commands/FindElementCommandHandler.cs @@ -0,0 +1,97 @@ +// +// +// Copyright (c) 2014 Salesforce.com, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace Winium.Silverlight.InnerServer.Web.Commands +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using Newtonsoft.Json; + using Mobile.Common; + + /// + /// Provides handling for the find element command. + /// + internal class FindElementCommandHandler : WebCommandHandler + { + /// + /// Executes the command. + /// + /// The to use in executing the command. + /// The containing the command parameters. + /// The JSON serialized string representing the command response. + public override Response Execute(CommandEnvironment environment, Dictionary parameters) + { + object mechanism; + var findElementAtom = this.Atom; + if (!parameters.TryGetValue("using", out mechanism)) + { + return Response.CreateMissingParametersResponse("using"); + } + + object criteria; + if (!parameters.TryGetValue("value", out criteria)) + { + return Response.CreateMissingParametersResponse("value"); + } + + Response response; + DateTime timeout = DateTime.Now.AddMilliseconds(environment.ImplicitWaitTimeout); + do + { + string result = this.EvaluateAtom(environment, findElementAtom, mechanism, criteria, null, environment.CreateFrameObject()); + response = Response.FromJson(result); + if (response.Status == ResponseStatus.Success) + { + // Return early for success + Dictionary foundElement = response.Value as Dictionary; + if (foundElement != null && foundElement.ContainsKey(CommandEnvironment.ElementObjectKey)) + { + return response; + } + } + else if (response.Status != ResponseStatus.NoSuchElement) + { + if (mechanism.ToString().ToUpperInvariant() != "XPATH" && response.Status == ResponseStatus.InvalidSelector) + { + continue; + } + + // Also return early for response of not NoSuchElement. + return response; + } + } + while (DateTime.Now < timeout); + + string errorMessage = string.Format(CultureInfo.InvariantCulture, "No element found for {0} == '{1}'", mechanism.ToString(), criteria.ToString()); + response = Response.CreateErrorResponse(ResponseStatus.NoSuchElement, errorMessage); + return response; + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Commands/GetPageSourceCommandHandler.cs b/Winium/Winium.Silverlight.InnerServer/Web/Commands/GetPageSourceCommandHandler.cs new file mode 100644 index 0000000..c156d9c --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Commands/GetPageSourceCommandHandler.cs @@ -0,0 +1,46 @@ +namespace Winium.Silverlight.InnerServer.Web.Commands +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using System.Windows; + + class GetPageSourceCommandHandler : WebCommandHandler + { + /// + /// Executes the command. + /// + /// The to use in executing the command. + /// The containing the command parameters. + /// The JSON serialized string representing the command response. + public override Response Execute(CommandEnvironment environment, Dictionary parameters) + { + string pageSource = string.Empty; + var executeScriptAtom = this.Atom; + if (string.IsNullOrEmpty(environment.FocusedFrame)) + { + //ManualResetEvent synchronizer = new ManualResetEvent(false); + //Deployment.Current.Dispatcher.BeginInvoke(() => + //{ + // pageSource = environment.Browser.SaveToString(); + // synchronizer.Set(); + //}); + + //synchronizer.WaitOne(); + + pageSource = environment.Browser.SaveToString(); + } + else + { + string script = "return document.documentElement.outerHTML;"; + string result = this.EvaluateAtom(environment, executeScriptAtom, script, new object[] { }, environment.CreateFrameObject()); + return Response.FromJson(result); + } + + return Response.CreateSuccessResponse(pageSource); + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Commands/GoToUrlCommandHandler.cs b/Winium/Winium.Silverlight.InnerServer/Web/Commands/GoToUrlCommandHandler.cs new file mode 100644 index 0000000..febe2fa --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Commands/GoToUrlCommandHandler.cs @@ -0,0 +1,100 @@ +namespace Winium.Silverlight.InnerServer.Web.Commands +{ + using Mobile.Common; + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + internal class GoToUrlCommandHandler : WebCommandHandler + { + private const string AlertHandler = @" +function() { + window.__wd_alert = window.alert; + window.alert = function(text) { + window.external.notify('alert:' + text); + window.__wd_alert(text); + window.external.notify('clear'); + }; + window.__wd_confirm = window.confirm; + window.confirm = function(text) { + window.external.notify('confirm:' + text); + var confirmValue = window.__wd_confirm(text); + window.external.notify('clear'); + return confirmValue; + }; +}"; + + private int retryCount = 3; + private bool handleAlerts = false; + + /// + /// Executes the command. + /// + /// The to use in executing the command. + /// The containing the command parameters. + /// The JSON serialized string representing the command response. + public override Response Execute(CommandEnvironment environment, Dictionary parameters) + { + object url; + if (!parameters.TryGetValue("url", out url)) + { + return Response.CreateMissingParametersResponse("url"); + } + + Uri targetUri = null; + if (!Uri.TryCreate(url.ToString(), UriKind.Absolute, out targetUri)) + { + return Response.CreateErrorResponse(ResponseStatus.UnknownError, string.Format(CultureInfo.InvariantCulture, "Could not create valie URL from {0}", url.ToString())); + } + + int timeoutInMilliseconds = environment.PageLoadTimeout; + if (timeoutInMilliseconds < 0) + { + timeoutInMilliseconds = 15000; + } + else + { + // If a page load timeout has been set, don't retry the page load. + this.retryCount = 1; + } + + WebBrowserNavigationMonitor monitor = new WebBrowserNavigationMonitor(environment); + for (int retries = 0; retries < this.retryCount; retries++) + { + monitor.MonitorNavigation(() => + { + environment.Browser.Dispatcher.BeginInvoke(() => + { + environment.Browser.Navigate(targetUri); + }); + }); + + if ((monitor.IsSuccessfullyNavigated && monitor.IsLoadCompleted) || monitor.IsNavigationError) + { + break; + } + } + + if (monitor.IsNavigationTimedOut) + { + return Response.CreateErrorResponse(ResponseStatus.Timeout, "Timed out loading page"); + } + + if (!monitor.IsNavigationError) + { + environment.FocusedFrame = string.Empty; + } + + if (this.handleAlerts) + { + this.EvaluateAtom(environment, AlertHandler); + } + + return Response.CreateSuccessResponse(); + } + } + +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Commands/SendKeysCommandHandler.cs b/Winium/Winium.Silverlight.InnerServer/Web/Commands/SendKeysCommandHandler.cs new file mode 100644 index 0000000..0351aa0 --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Commands/SendKeysCommandHandler.cs @@ -0,0 +1,42 @@ +namespace Winium.Silverlight.InnerServer.Web.Commands +{ + using Newtonsoft.Json.Linq; + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + internal class SendKeysCommandHandler : WebCommandHandler + { + public override Response Execute(CommandEnvironment environment, Dictionary parameters) + { + object element; + if (!parameters.TryGetValue("ID", out element)) + { + return Response.CreateMissingParametersResponse("ID"); + } + + object keys; + if (!parameters.TryGetValue("value", out keys)) + { + return Response.CreateMissingParametersResponse("value"); + } + + var keysAsString = string.Empty; + //var keysAsArray = keys as object[]; + var keysAsArray = keys as JArray; + if (keysAsArray != null) + { + keysAsString = keysAsArray.Cast().Aggregate(keysAsString, (current, key) => current + key.ToString()); + } + + var typeAtom = this.Atom; + + // Normalize line endings to single line feed, as that's what the atom expects. + keysAsString = keysAsString.Replace("\r\n", "\n"); + string result = this.EvaluateAtom(environment, typeAtom, element, keysAsString, environment.CreateFrameObject()); + return Response.FromJson(result); + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Commands/WebCommandHandler.cs b/Winium/Winium.Silverlight.InnerServer/Web/Commands/WebCommandHandler.cs new file mode 100644 index 0000000..e453cca --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Commands/WebCommandHandler.cs @@ -0,0 +1,105 @@ +namespace Winium.Silverlight.InnerServer.Web.Commands +{ + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Text; + using System.Threading; + using System.Threading.Tasks; + using Microsoft.Phone.Controls; + using Newtonsoft.Json; + + using Winium.Mobile.Common; + using InnerServer.Commands; + using Mobile.Common.Exceptions; + + internal abstract class WebCommandHandler : CommandBase + { + private TimeSpan atomExecutionTimeout = TimeSpan.FromMilliseconds(-1); + + public CommandEnvironment Context { get; set; } + + public string Atom { get; set; } + + public abstract Response Execute(CommandEnvironment environment, Dictionary parameters); + + public override string DoImpl() + { + var parameters = this.Parameters.ToDictionary(x => x.Key, x => x.Value as object); + if (this.Parameters.ContainsKey("ID")) + { + parameters["ID"] = new Dictionary { { "ELEMENT", this.Parameters["ID"].ToObject() } }; + } + var rv = this.Execute(this.Context, parameters); + + return this.JsonResponse(rv.Status, rv.Value); + } + + protected static string CreateArgumentString(object[] args) + { + StringBuilder builder = new StringBuilder(); + foreach (object arg in args) + { + if (builder.Length > 0) + { + builder.Append(","); + } + + string argAsString = JsonConvert.SerializeObject(arg); + builder.Append(argAsString); + } + + return builder.ToString(); + } + + /// + /// Evaluates a JavaScript atom in the . + /// + /// The in which to evaluate the JavaScript atom. + /// The JavaScript atom to evaluate. + /// An array of arguments to the JavaScript atom. + /// The string result of the atom execution. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Catching general exception type is expressly permitted here to allow proper reporting via JSON-serialized result.")] + protected string EvaluateAtom(CommandEnvironment environment, string atom, params object[] args) + { + WebBrowser browser = environment.Browser; + string argumentString = CreateArgumentString(args); + string script = "window.top.__wd_fn_result = (" + atom + ")(" + argumentString + ");"; + string result = string.Empty; + ManualResetEvent synchronizer = new ManualResetEvent(false); + try + { + browser.InvokeScript("eval", script); + result = browser.InvokeScript("eval", "window.top.__wd_fn_result").ToString(); + } + + catch (Exception ex) + { + throw new AutomationException(string.Format( + CultureInfo.InvariantCulture, + "Unexpected exception {0}: {1}", + ex.GetType(), + ex.Message), ResponseStatus.UnknownError); + } + + finally + { + synchronizer.Set(); + } + + synchronizer.WaitOne(this.atomExecutionTimeout); + + return result; + } + + /// + /// Sets the timeout for execution of an atom. + /// + /// The representing the timeout for the atom execution. + protected void SetAtomExecutionTimeout(TimeSpan timeout) + { + this.atomExecutionTimeout = timeout; + } + } +} \ No newline at end of file diff --git a/Winium/Winium.Silverlight.InnerServer/Web/Response.cs b/Winium/Winium.Silverlight.InnerServer/Web/Response.cs new file mode 100644 index 0000000..bdd547b --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/Response.cs @@ -0,0 +1,98 @@ +namespace Winium.Silverlight.InnerServer.Web +{ + + using System.Collections.Generic; + using Newtonsoft.Json; + using Winium.Mobile.Common; + + class Response + { + /// + /// Gets or sets the status of the response. + /// + [JsonProperty(PropertyName = "status")] + public ResponseStatus Status { get; set; } + + /// + /// Gets or sets the session ID of the command. + /// + [JsonProperty("sessionId", NullValueHandling = NullValueHandling.Ignore)] + public string SessionId { get; set; } + + /// + /// Gets or sets the value of the response. + /// + [JsonProperty(PropertyName = "value", NullValueHandling = NullValueHandling.Include)] + [JsonConverter(typeof(ProtocolValueJsonConverter))] + public object Value { get; set; } + + /// + /// Creates a response from a serialized JSON string. + /// + /// The serialized JSON string containing the response. + /// The response object. + public static Response FromJson(string jsonResponse) + { + Response response = JsonConvert.DeserializeObject(jsonResponse); + return response; + } + + /// + /// Creates a success response. + /// + /// The value contained by the response. + /// The response indicating a successful command execution. + public static Response CreateSuccessResponse(object responseValue = null) + { + Response response = new Response(); + if (responseValue == null) + { + responseValue = new Dictionary(); + } + else + { + response.Value = responseValue; + } + + return response; + } + + /// + /// Creates an error response with the specified error code and message. + /// + /// The error code of the response. + /// The error message containing information about the error. + /// The response indicating an unsuccessful command execution. + public static Response CreateErrorResponse(ResponseStatus errorCode, string errorMessage) + { + Response response = new Response(); + response.Status = errorCode; + Dictionary errorDetails = new Dictionary(); + errorDetails["message"] = errorMessage; + response.Value = errorDetails; + return response; + } + + /// + /// Creates an error response when a command is missing values for its parameters. + /// + /// The list of missing parameters. + /// The response indicating an unsuccessful command execution. + public static Response CreateMissingParametersResponse(string missingParameters) + { + Response response = new Response(); + response.Status = ResponseStatus.MissingParameters; + response.Value = missingParameters; + return response; + } + + /// + /// Serializes this to JSON. + /// + /// The JSON-serialized response. + public string ToJson() + { + return JsonConvert.SerializeObject(this); + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Web/WebBrowserNavigationMonitor.cs b/Winium/Winium.Silverlight.InnerServer/Web/WebBrowserNavigationMonitor.cs new file mode 100644 index 0000000..bd38400 --- /dev/null +++ b/Winium/Winium.Silverlight.InnerServer/Web/WebBrowserNavigationMonitor.cs @@ -0,0 +1,151 @@ +// +// +// Copyright (c) 2014 Salesforce.com, Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +// following conditions are met: +// +// Redistributions of source code must retain the above copyright notice, this list of conditions and the following +// disclaimer. +// +// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the +// following disclaimer in the documentation and/or other materials provided with the distribution. +// +// Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE +// USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +namespace Winium.Silverlight.InnerServer.Web +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using System.Windows.Navigation; + using Microsoft.Phone.Controls; + + /// + /// Monitors navigation within the driver's browser. + /// + + internal class WebBrowserNavigationMonitor + { + private CommandEnvironment environment; + private bool isNavigated = false; + private bool isLoadCompleted = false; + private bool isNavigationError = false; + private bool isNavigationTimedOut = false; + + /// + /// Initializes a new instance of the class. + /// + /// The in which the navigation should be monitored. + public WebBrowserNavigationMonitor(CommandEnvironment environment) + { + this.environment = environment; + } + + /// + /// Gets a value indicating whether the navigation was successfully completed. + /// + public bool IsSuccessfullyNavigated + { + get { return this.isNavigated; } + } + + /// + /// Gets a value indicating whether the page load was successfully completed. + /// + public bool IsLoadCompleted + { + get { return this.isLoadCompleted; } + } + + /// + /// Gets a value indicating whether an error was encountered during the navigation. + /// + public bool IsNavigationError + { + get { return this.isNavigationError; } + } + + /// + /// Gets a value indicating whether an the navigation timed out. + /// + public bool IsNavigationTimedOut + { + get { return this.isNavigationTimedOut; } + } + + /// + /// Monitors a navigation event. + /// + /// The causing the navigation. + public void MonitorNavigation(Action navigationAction) + { + bool ignoreTimeout = this.environment.PageLoadTimeout < 0; + bool timedOut = true; + TimeSpan timeout = TimeSpan.FromMilliseconds(this.environment.PageLoadTimeout); + DateTime endTime = DateTime.Now.Add(timeout); + this.isLoadCompleted = false; + this.isNavigated = false; + this.isNavigationError = false; + this.environment.Browser.Navigated += this.BrowserNavigatedEventHandler; + this.environment.Browser.LoadCompleted += this.BrowserLoadCompletedEventHandler; + this.environment.Browser.NavigationFailed += this.BrowserNavigationFailedEventHandler; + navigationAction(); + + while (ignoreTimeout || DateTime.Now < endTime) + { + if ((this.isNavigated && this.isLoadCompleted) || this.isNavigationError) + { + timedOut = false; + break; + } + + System.Threading.Thread.Sleep(100); + } + + this.environment.Browser.LoadCompleted -= this.BrowserLoadCompletedEventHandler; + this.environment.Browser.Navigated -= this.BrowserNavigatedEventHandler; + this.environment.Browser.NavigationFailed -= this.BrowserNavigationFailedEventHandler; + + this.isNavigationTimedOut = timedOut; + } + + /// + /// Sets the values if the navigation is successful. + /// + public void SetNavigationSuccessful() + { + this.isNavigated = true; + this.isLoadCompleted = true; + this.isNavigationError = false; + } + + private void BrowserNavigationFailedEventHandler(object sender, NavigationFailedEventArgs e) + { + this.isNavigationError = true; + } + + private void BrowserNavigatedEventHandler(object sender, NavigationEventArgs e) + { + this.isNavigated = true; + } + + private void BrowserLoadCompletedEventHandler(object sender, NavigationEventArgs e) + { + this.isLoadCompleted = true; + } + } +} diff --git a/Winium/Winium.Silverlight.InnerServer/Winium.Silverlight.InnerServer.csproj b/Winium/Winium.Silverlight.InnerServer/Winium.Silverlight.InnerServer.csproj index dc2f04c..2a32535 100644 --- a/Winium/Winium.Silverlight.InnerServer/Winium.Silverlight.InnerServer.csproj +++ b/Winium/Winium.Silverlight.InnerServer/Winium.Silverlight.InnerServer.csproj @@ -102,6 +102,9 @@ + + + @@ -124,12 +127,27 @@ + + + + + + + + + + + {a4248ec1-20ad-47b9-956f-a036e4d7425b} Winium.Mobile.Common + + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707} + Winium.Mobile.Logging + diff --git a/Winium/Winium.sln b/Winium/Winium.sln index 878e06c..5c5ddf7 100644 --- a/Winium/Winium.sln +++ b/Winium/Winium.sln @@ -56,6 +56,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp.Silverli EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebViewApp", "WebViewApp\WebViewApp.csproj", "{D305A69D-1852-4CE9-B0A6-90B610A66E4A}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebViewBrowser", "WebViewBrowser\WebViewBrowser.csproj", "{B5657616-E813-4E10-85BE-942BA7B56126}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution TestApp.StoreApps\TestApp.Shared\TestApp.Shared.projitems*{10a0b15d-ca68-4a4f-83bc-639a62b5ef1e}*SharedItemsImports = 4 @@ -64,73 +66,223 @@ Global EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|ARM = Debug|ARM + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU + Release|ARM = Release|ARM + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B6BE579B-B008-45B2-9740-12F0E6223661}.Debug|Any CPU.ActiveCfg = Debug|x86 {B6BE579B-B008-45B2-9740-12F0E6223661}.Debug|Any CPU.Build.0 = Debug|x86 + {B6BE579B-B008-45B2-9740-12F0E6223661}.Debug|ARM.ActiveCfg = Debug|x86 + {B6BE579B-B008-45B2-9740-12F0E6223661}.Debug|x86.ActiveCfg = Debug|x86 + {B6BE579B-B008-45B2-9740-12F0E6223661}.Debug|x86.Build.0 = Debug|x86 {B6BE579B-B008-45B2-9740-12F0E6223661}.Release|Any CPU.ActiveCfg = Release|x86 {B6BE579B-B008-45B2-9740-12F0E6223661}.Release|Any CPU.Build.0 = Release|x86 + {B6BE579B-B008-45B2-9740-12F0E6223661}.Release|ARM.ActiveCfg = Release|x86 + {B6BE579B-B008-45B2-9740-12F0E6223661}.Release|x86.ActiveCfg = Release|x86 + {B6BE579B-B008-45B2-9740-12F0E6223661}.Release|x86.Build.0 = Release|x86 {E8683025-E703-4293-AA7E-3A9A3555BC40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E8683025-E703-4293-AA7E-3A9A3555BC40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8683025-E703-4293-AA7E-3A9A3555BC40}.Debug|ARM.ActiveCfg = Debug|Any CPU + {E8683025-E703-4293-AA7E-3A9A3555BC40}.Debug|ARM.Build.0 = Debug|Any CPU + {E8683025-E703-4293-AA7E-3A9A3555BC40}.Debug|x86.ActiveCfg = Debug|Any CPU + {E8683025-E703-4293-AA7E-3A9A3555BC40}.Debug|x86.Build.0 = Debug|Any CPU {E8683025-E703-4293-AA7E-3A9A3555BC40}.Release|Any CPU.ActiveCfg = Release|Any CPU {E8683025-E703-4293-AA7E-3A9A3555BC40}.Release|Any CPU.Build.0 = Release|Any CPU + {E8683025-E703-4293-AA7E-3A9A3555BC40}.Release|ARM.ActiveCfg = Release|Any CPU + {E8683025-E703-4293-AA7E-3A9A3555BC40}.Release|ARM.Build.0 = Release|Any CPU + {E8683025-E703-4293-AA7E-3A9A3555BC40}.Release|x86.ActiveCfg = Release|Any CPU + {E8683025-E703-4293-AA7E-3A9A3555BC40}.Release|x86.Build.0 = Release|Any CPU {89E0F534-8622-42DD-9846-5514E390694D}.Debug|Any CPU.ActiveCfg = Debug|x86 + {89E0F534-8622-42DD-9846-5514E390694D}.Debug|ARM.ActiveCfg = Debug|x86 + {89E0F534-8622-42DD-9846-5514E390694D}.Debug|x86.ActiveCfg = Debug|x86 + {89E0F534-8622-42DD-9846-5514E390694D}.Debug|x86.Build.0 = Debug|x86 + {89E0F534-8622-42DD-9846-5514E390694D}.Debug|x86.Deploy.0 = Debug|x86 {89E0F534-8622-42DD-9846-5514E390694D}.Release|Any CPU.ActiveCfg = Release|x86 + {89E0F534-8622-42DD-9846-5514E390694D}.Release|ARM.ActiveCfg = Release|x86 + {89E0F534-8622-42DD-9846-5514E390694D}.Release|x86.ActiveCfg = Release|x86 + {89E0F534-8622-42DD-9846-5514E390694D}.Release|x86.Build.0 = Release|x86 + {89E0F534-8622-42DD-9846-5514E390694D}.Release|x86.Deploy.0 = Release|x86 {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|Any CPU.Build.0 = Debug|Any CPU {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|ARM.ActiveCfg = Debug|ARM + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|ARM.Build.0 = Debug|ARM + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|ARM.Deploy.0 = Debug|ARM + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|x86.ActiveCfg = Debug|x86 + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|x86.Build.0 = Debug|x86 + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Debug|x86.Deploy.0 = Debug|x86 {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|Any CPU.Build.0 = Release|Any CPU {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|Any CPU.Deploy.0 = Release|Any CPU + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|ARM.ActiveCfg = Release|ARM + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|ARM.Build.0 = Release|ARM + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|ARM.Deploy.0 = Release|ARM + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|x86.ActiveCfg = Release|x86 + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|x86.Build.0 = Release|x86 + {10A0B15D-CA68-4A4F-83BC-639A62B5EF1E}.Release|x86.Deploy.0 = Release|x86 {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Debug|ARM.ActiveCfg = Debug|Any CPU + {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Debug|ARM.Build.0 = Debug|Any CPU + {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Debug|x86.ActiveCfg = Debug|Any CPU + {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Debug|x86.Build.0 = Debug|Any CPU {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Release|Any CPU.ActiveCfg = Release|Any CPU {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Release|Any CPU.Build.0 = Release|Any CPU + {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Release|ARM.ActiveCfg = Release|Any CPU + {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Release|ARM.Build.0 = Release|Any CPU + {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Release|x86.ActiveCfg = Release|Any CPU + {C5261C5C-B5FB-43AC-A5BA-B67FF0634B3E}.Release|x86.Build.0 = Release|Any CPU {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Debug|ARM.ActiveCfg = Debug|Any CPU + {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Debug|ARM.Build.0 = Debug|Any CPU + {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Debug|x86.ActiveCfg = Debug|Any CPU + {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Debug|x86.Build.0 = Debug|Any CPU {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Release|Any CPU.Build.0 = Release|Any CPU + {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Release|ARM.ActiveCfg = Release|Any CPU + {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Release|ARM.Build.0 = Release|Any CPU + {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Release|x86.ActiveCfg = Release|Any CPU + {2D005293-6FF8-4FE0-B6DD-73CA0A2C71F5}.Release|x86.Build.0 = Release|Any CPU {DA38085E-F3DB-46EF-8889-D89283924917}.Debug|Any CPU.ActiveCfg = Debug|x86 {DA38085E-F3DB-46EF-8889-D89283924917}.Debug|Any CPU.Build.0 = Debug|x86 + {DA38085E-F3DB-46EF-8889-D89283924917}.Debug|ARM.ActiveCfg = Debug|x86 + {DA38085E-F3DB-46EF-8889-D89283924917}.Debug|x86.ActiveCfg = Debug|x86 + {DA38085E-F3DB-46EF-8889-D89283924917}.Debug|x86.Build.0 = Debug|x86 {DA38085E-F3DB-46EF-8889-D89283924917}.Release|Any CPU.ActiveCfg = Release|x86 {DA38085E-F3DB-46EF-8889-D89283924917}.Release|Any CPU.Build.0 = Release|x86 + {DA38085E-F3DB-46EF-8889-D89283924917}.Release|ARM.ActiveCfg = Release|x86 + {DA38085E-F3DB-46EF-8889-D89283924917}.Release|x86.ActiveCfg = Release|x86 + {DA38085E-F3DB-46EF-8889-D89283924917}.Release|x86.Build.0 = Release|x86 {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Debug|ARM.ActiveCfg = Debug|Any CPU + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Debug|ARM.Build.0 = Debug|Any CPU + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Debug|x86.ActiveCfg = Debug|Any CPU + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Debug|x86.Build.0 = Debug|Any CPU {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Release|Any CPU.ActiveCfg = Release|Any CPU {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Release|Any CPU.Build.0 = Release|Any CPU + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Release|ARM.ActiveCfg = Release|Any CPU + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Release|ARM.Build.0 = Release|Any CPU + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Release|x86.ActiveCfg = Release|Any CPU + {DAC0EAFE-2FE0-4F33-B173-736C1BAFD707}.Release|x86.Build.0 = Release|Any CPU {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Debug|Any CPU.ActiveCfg = Debug|x86 {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Debug|Any CPU.Build.0 = Debug|x86 + {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Debug|ARM.ActiveCfg = Debug|x86 + {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Debug|x86.ActiveCfg = Debug|x86 + {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Debug|x86.Build.0 = Debug|x86 {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Release|Any CPU.ActiveCfg = Release|x86 {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Release|Any CPU.Build.0 = Release|x86 + {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Release|ARM.ActiveCfg = Release|x86 + {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Release|x86.ActiveCfg = Release|x86 + {13B4DF5C-E542-4A7A-8F00-929D423B522C}.Release|x86.Build.0 = Release|x86 {07168ED9-C12A-43F5-898F-84AB113A9441}.Debug|Any CPU.ActiveCfg = Debug|x86 {07168ED9-C12A-43F5-898F-84AB113A9441}.Debug|Any CPU.Build.0 = Debug|x86 + {07168ED9-C12A-43F5-898F-84AB113A9441}.Debug|ARM.ActiveCfg = Debug|x86 + {07168ED9-C12A-43F5-898F-84AB113A9441}.Debug|x86.ActiveCfg = Debug|x86 + {07168ED9-C12A-43F5-898F-84AB113A9441}.Debug|x86.Build.0 = Debug|x86 {07168ED9-C12A-43F5-898F-84AB113A9441}.Release|Any CPU.ActiveCfg = Release|x86 {07168ED9-C12A-43F5-898F-84AB113A9441}.Release|Any CPU.Build.0 = Release|x86 + {07168ED9-C12A-43F5-898F-84AB113A9441}.Release|ARM.ActiveCfg = Release|x86 + {07168ED9-C12A-43F5-898F-84AB113A9441}.Release|x86.ActiveCfg = Release|x86 + {07168ED9-C12A-43F5-898F-84AB113A9441}.Release|x86.Build.0 = Release|x86 {E5DEE502-3D2D-42BA-9505-632F6C304380}.Debug|Any CPU.ActiveCfg = Debug|x86 {E5DEE502-3D2D-42BA-9505-632F6C304380}.Debug|Any CPU.Build.0 = Debug|x86 + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Debug|ARM.ActiveCfg = Debug|ARM + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Debug|ARM.Build.0 = Debug|ARM + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Debug|ARM.Deploy.0 = Debug|ARM + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Debug|x86.ActiveCfg = Debug|x86 + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Debug|x86.Build.0 = Debug|x86 + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Debug|x86.Deploy.0 = Debug|x86 {E5DEE502-3D2D-42BA-9505-632F6C304380}.Release|Any CPU.ActiveCfg = Release|x86 {E5DEE502-3D2D-42BA-9505-632F6C304380}.Release|Any CPU.Build.0 = Release|x86 + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Release|ARM.ActiveCfg = Release|ARM + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Release|ARM.Build.0 = Release|ARM + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Release|ARM.Deploy.0 = Release|ARM + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Release|x86.ActiveCfg = Release|x86 + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Release|x86.Build.0 = Release|x86 + {E5DEE502-3D2D-42BA-9505-632F6C304380}.Release|x86.Deploy.0 = Release|x86 {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Debug|ARM.ActiveCfg = Debug|Any CPU + {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Debug|ARM.Build.0 = Debug|Any CPU + {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Debug|x86.ActiveCfg = Debug|Any CPU + {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Debug|x86.Build.0 = Debug|Any CPU {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Release|Any CPU.Build.0 = Release|Any CPU + {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Release|ARM.ActiveCfg = Release|Any CPU + {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Release|ARM.Build.0 = Release|Any CPU + {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Release|x86.ActiveCfg = Release|Any CPU + {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Release|x86.Build.0 = Release|Any CPU {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Debug|ARM.ActiveCfg = Debug|ARM + {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Debug|ARM.Build.0 = Debug|ARM + {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Debug|x86.ActiveCfg = Debug|x86 + {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Debug|x86.Build.0 = Debug|x86 {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Release|Any CPU.Build.0 = Release|Any CPU + {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Release|ARM.ActiveCfg = Release|ARM + {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Release|ARM.Build.0 = Release|ARM + {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Release|x86.ActiveCfg = Release|x86 + {BC2B2946-0F51-4C34-B8EB-1331BF076A70}.Release|x86.Build.0 = Release|x86 {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|Any CPU.Build.0 = Debug|Any CPU {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|ARM.ActiveCfg = Debug|ARM + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|ARM.Build.0 = Debug|ARM + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|ARM.Deploy.0 = Debug|ARM + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|x86.ActiveCfg = Debug|x86 + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|x86.Build.0 = Debug|x86 + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Debug|x86.Deploy.0 = Debug|x86 {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|Any CPU.ActiveCfg = Release|Any CPU {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|Any CPU.Build.0 = Release|Any CPU {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|Any CPU.Deploy.0 = Release|Any CPU + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|ARM.ActiveCfg = Release|ARM + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|ARM.Build.0 = Release|ARM + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|ARM.Deploy.0 = Release|ARM + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|x86.ActiveCfg = Release|x86 + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|x86.Build.0 = Release|x86 + {8A202F60-6343-4FEF-B838-FB7C668BA29D}.Release|x86.Deploy.0 = Release|x86 {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|Any CPU.Build.0 = Debug|Any CPU {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|ARM.ActiveCfg = Debug|ARM + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|ARM.Build.0 = Debug|ARM + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|ARM.Deploy.0 = Debug|ARM + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|x86.ActiveCfg = Debug|x86 + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|x86.Build.0 = Debug|x86 + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Debug|x86.Deploy.0 = Debug|x86 {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|Any CPU.ActiveCfg = Release|Any CPU {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|Any CPU.Build.0 = Release|Any CPU {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|Any CPU.Deploy.0 = Release|Any CPU + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|ARM.ActiveCfg = Release|ARM + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|ARM.Build.0 = Release|ARM + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|ARM.Deploy.0 = Release|ARM + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|x86.ActiveCfg = Release|x86 + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|x86.Build.0 = Release|x86 + {D305A69D-1852-4CE9-B0A6-90B610A66E4A}.Release|x86.Deploy.0 = Release|x86 + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|Any CPU.Deploy.0 = Debug|Any CPU + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|ARM.ActiveCfg = Debug|ARM + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|ARM.Build.0 = Debug|ARM + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|ARM.Deploy.0 = Debug|ARM + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|x86.ActiveCfg = Debug|x86 + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|x86.Build.0 = Debug|x86 + {B5657616-E813-4E10-85BE-942BA7B56126}.Debug|x86.Deploy.0 = Debug|x86 + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|Any CPU.Build.0 = Release|Any CPU + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|Any CPU.Deploy.0 = Release|Any CPU + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|ARM.ActiveCfg = Release|ARM + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|ARM.Build.0 = Release|ARM + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|ARM.Deploy.0 = Release|ARM + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|x86.ActiveCfg = Release|x86 + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|x86.Build.0 = Release|x86 + {B5657616-E813-4E10-85BE-942BA7B56126}.Release|x86.Deploy.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE