diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..91e72f3
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,165 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+
+[Dd]ebug/
+[Rr]elease/
+x64/
+!build/
+/*/build
+[Bb]in/
+[Oo]bj/
+
+# Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
+!packages/*/build/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+_NCrunch_*
+.*crunch*.local.xml
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.Publish.xml
+
+# NuGet Packages Directory
+## TODO: If you have NuGet Package Restore enabled, uncomment the next line
+packages/
+
+# Windows Azure Build Output
+csx
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.[Pp]ublish.xml
+*.pfx
+*.publishsettings
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# =========================
+# Windows detritus
+# =========================
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Mac crap
+.DS_Store
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e87a115
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,363 @@
+Mozilla Public License, version 2.0
+
+1. Definitions
+
+1.1. "Contributor"
+
+ means each individual or legal entity that creates, contributes to the
+ creation of, or owns Covered Software.
+
+1.2. "Contributor Version"
+
+ means the combination of the Contributions of others (if any) used by a
+ Contributor and that particular Contributor's Contribution.
+
+1.3. "Contribution"
+
+ means Covered Software of a particular Contributor.
+
+1.4. "Covered Software"
+
+ means Source Code Form to which the initial Contributor has attached the
+ notice in Exhibit A, the Executable Form of such Source Code Form, and
+ Modifications of such Source Code Form, in each case including portions
+ thereof.
+
+1.5. "Incompatible With Secondary Licenses"
+ means
+
+ a. that the initial Contributor has attached the notice described in
+ Exhibit B to the Covered Software; or
+
+ b. that the Covered Software was made available under the terms of
+ version 1.1 or earlier of the License, but not also under the terms of
+ a Secondary License.
+
+1.6. "Executable Form"
+
+ means any form of the work other than Source Code Form.
+
+1.7. "Larger Work"
+
+ means a work that combines Covered Software with other material, in a
+ separate file or files, that is not Covered Software.
+
+1.8. "License"
+
+ means this document.
+
+1.9. "Licensable"
+
+ means having the right to grant, to the maximum extent possible, whether
+ at the time of the initial grant or subsequently, any and all of the
+ rights conveyed by this License.
+
+1.10. "Modifications"
+
+ means any of the following:
+
+ a. any file in Source Code Form that results from an addition to,
+ deletion from, or modification of the contents of Covered Software; or
+
+ b. any new file in Source Code Form that contains any Covered Software.
+
+1.11. "Patent Claims" of a Contributor
+
+ means any patent claim(s), including without limitation, method,
+ process, and apparatus claims, in any patent Licensable by such
+ Contributor that would be infringed, but for the grant of the License,
+ by the making, using, selling, offering for sale, having made, import,
+ or transfer of either its Contributions or its Contributor Version.
+
+1.12. "Secondary License"
+
+ means either the GNU General Public License, Version 2.0, the GNU Lesser
+ General Public License, Version 2.1, the GNU Affero General Public
+ License, Version 3.0, or any later versions of those licenses.
+
+1.13. "Source Code Form"
+
+ means the form of the work preferred for making modifications.
+
+1.14. "You" (or "Your")
+
+ means an individual or a legal entity exercising rights under this
+ License. For legal entities, "You" includes any entity that controls, is
+ controlled by, or is under common control with You. For purposes of this
+ definition, "control" means (a) the power, direct or indirect, to cause
+ the direction or management of such entity, whether by contract or
+ otherwise, or (b) ownership of more than fifty percent (50%) of the
+ outstanding shares or beneficial ownership of such entity.
+
+
+2. License Grants and Conditions
+
+2.1. Grants
+
+ Each Contributor hereby grants You a world-wide, royalty-free,
+ non-exclusive license:
+
+ a. under intellectual property rights (other than patent or trademark)
+ Licensable by such Contributor to use, reproduce, make available,
+ modify, display, perform, distribute, and otherwise exploit its
+ Contributions, either on an unmodified basis, with Modifications, or
+ as part of a Larger Work; and
+
+ b. under Patent Claims of such Contributor to make, use, sell, offer for
+ sale, have made, import, and otherwise transfer either its
+ Contributions or its Contributor Version.
+
+2.2. Effective Date
+
+ The licenses granted in Section 2.1 with respect to any Contribution
+ become effective for each Contribution on the date the Contributor first
+ distributes such Contribution.
+
+2.3. Limitations on Grant Scope
+
+ The licenses granted in this Section 2 are the only rights granted under
+ this License. No additional rights or licenses will be implied from the
+ distribution or licensing of Covered Software under this License.
+ Notwithstanding Section 2.1(b) above, no patent license is granted by a
+ Contributor:
+
+ a. for any code that a Contributor has removed from Covered Software; or
+
+ b. for infringements caused by: (i) Your and any other third party's
+ modifications of Covered Software, or (ii) the combination of its
+ Contributions with other software (except as part of its Contributor
+ Version); or
+
+ c. under Patent Claims infringed by Covered Software in the absence of
+ its Contributions.
+
+ This License does not grant any rights in the trademarks, service marks,
+ or logos of any Contributor (except as may be necessary to comply with
+ the notice requirements in Section 3.4).
+
+2.4. Subsequent Licenses
+
+ No Contributor makes additional grants as a result of Your choice to
+ distribute the Covered Software under a subsequent version of this
+ License (see Section 10.2) or under the terms of a Secondary License (if
+ permitted under the terms of Section 3.3).
+
+2.5. Representation
+
+ Each Contributor represents that the Contributor believes its
+ Contributions are its original creation(s) or it has sufficient rights to
+ grant the rights to its Contributions conveyed by this License.
+
+2.6. Fair Use
+
+ This License is not intended to limit any rights You have under
+ applicable copyright doctrines of fair use, fair dealing, or other
+ equivalents.
+
+2.7. Conditions
+
+ Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
+ Section 2.1.
+
+
+3. Responsibilities
+
+3.1. Distribution of Source Form
+
+ All distribution of Covered Software in Source Code Form, including any
+ Modifications that You create or to which You contribute, must be under
+ the terms of this License. You must inform recipients that the Source
+ Code Form of the Covered Software is governed by the terms of this
+ License, and how they can obtain a copy of this License. You may not
+ attempt to alter or restrict the recipients' rights in the Source Code
+ Form.
+
+3.2. Distribution of Executable Form
+
+ If You distribute Covered Software in Executable Form then:
+
+ a. such Covered Software must also be made available in Source Code Form,
+ as described in Section 3.1, and You must inform recipients of the
+ Executable Form how they can obtain a copy of such Source Code Form by
+ reasonable means in a timely manner, at a charge no more than the cost
+ of distribution to the recipient; and
+
+ b. You may distribute such Executable Form under the terms of this
+ License, or sublicense it under different terms, provided that the
+ license for the Executable Form does not attempt to limit or alter the
+ recipients' rights in the Source Code Form under this License.
+
+3.3. Distribution of a Larger Work
+
+ You may create and distribute a Larger Work under terms of Your choice,
+ provided that You also comply with the requirements of this License for
+ the Covered Software. If the Larger Work is a combination of Covered
+ Software with a work governed by one or more Secondary Licenses, and the
+ Covered Software is not Incompatible With Secondary Licenses, this
+ License permits You to additionally distribute such Covered Software
+ under the terms of such Secondary License(s), so that the recipient of
+ the Larger Work may, at their option, further distribute the Covered
+ Software under the terms of either this License or such Secondary
+ License(s).
+
+3.4. Notices
+
+ You may not remove or alter the substance of any license notices
+ (including copyright notices, patent notices, disclaimers of warranty, or
+ limitations of liability) contained within the Source Code Form of the
+ Covered Software, except that You may alter any license notices to the
+ extent required to remedy known factual inaccuracies.
+
+3.5. Application of Additional Terms
+
+ You may choose to offer, and to charge a fee for, warranty, support,
+ indemnity or liability obligations to one or more recipients of Covered
+ Software. However, You may do so only on Your own behalf, and not on
+ behalf of any Contributor. You must make it absolutely clear that any
+ such warranty, support, indemnity, or liability obligation is offered by
+ You alone, and You hereby agree to indemnify every Contributor for any
+ liability incurred by such Contributor as a result of warranty, support,
+ indemnity or liability terms You offer. You may include additional
+ disclaimers of warranty and limitations of liability specific to any
+ jurisdiction.
+
+4. Inability to Comply Due to Statute or Regulation
+
+ If it is impossible for You to comply with any of the terms of this License
+ with respect to some or all of the Covered Software due to statute,
+ judicial order, or regulation then You must: (a) comply with the terms of
+ this License to the maximum extent possible; and (b) describe the
+ limitations and the code they affect. Such description must be placed in a
+ text file included with all distributions of the Covered Software under
+ this License. Except to the extent prohibited by statute or regulation,
+ such description must be sufficiently detailed for a recipient of ordinary
+ skill to be able to understand it.
+
+5. Termination
+
+5.1. The rights granted under this License will terminate automatically if You
+ fail to comply with any of its terms. However, if You become compliant,
+ then the rights granted under this License from a particular Contributor
+ are reinstated (a) provisionally, unless and until such Contributor
+ explicitly and finally terminates Your grants, and (b) on an ongoing
+ basis, if such Contributor fails to notify You of the non-compliance by
+ some reasonable means prior to 60 days after You have come back into
+ compliance. Moreover, Your grants from a particular Contributor are
+ reinstated on an ongoing basis if such Contributor notifies You of the
+ non-compliance by some reasonable means, this is the first time You have
+ received notice of non-compliance with this License from such
+ Contributor, and You become compliant prior to 30 days after Your receipt
+ of the notice.
+
+5.2. If You initiate litigation against any entity by asserting a patent
+ infringement claim (excluding declaratory judgment actions,
+ counter-claims, and cross-claims) alleging that a Contributor Version
+ directly or indirectly infringes any patent, then the rights granted to
+ You by any and all Contributors for the Covered Software under Section
+ 2.1 of this License shall terminate.
+
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
+ license agreements (excluding distributors and resellers) which have been
+ validly granted by You or Your distributors under this License prior to
+ termination shall survive termination.
+
+6. Disclaimer of Warranty
+
+ Covered Software is provided under this License on an "as is" basis,
+ without warranty of any kind, either expressed, implied, or statutory,
+ including, without limitation, warranties that the Covered Software is free
+ of defects, merchantable, fit for a particular purpose or non-infringing.
+ The entire risk as to the quality and performance of the Covered Software
+ is with You. Should any Covered Software prove defective in any respect,
+ You (not any Contributor) assume the cost of any necessary servicing,
+ repair, or correction. This disclaimer of warranty constitutes an essential
+ part of this License. No use of any Covered Software is authorized under
+ this License except under this disclaimer.
+
+7. Limitation of Liability
+
+ Under no circumstances and under no legal theory, whether tort (including
+ negligence), contract, or otherwise, shall any Contributor, or anyone who
+ distributes Covered Software as permitted above, be liable to You for any
+ direct, indirect, special, incidental, or consequential damages of any
+ character including, without limitation, damages for lost profits, loss of
+ goodwill, work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses, even if such party shall have been
+ informed of the possibility of such damages. This limitation of liability
+ shall not apply to liability for death or personal injury resulting from
+ such party's negligence to the extent applicable law prohibits such
+ limitation. Some jurisdictions do not allow the exclusion or limitation of
+ incidental or consequential damages, so this exclusion and limitation may
+ not apply to You.
+
+8. Litigation
+
+ Any litigation relating to this License may be brought only in the courts
+ of a jurisdiction where the defendant maintains its principal place of
+ business and such litigation shall be governed by laws of that
+ jurisdiction, without reference to its conflict-of-law provisions. Nothing
+ in this Section shall prevent a party's ability to bring cross-claims or
+ counter-claims.
+
+9. Miscellaneous
+
+ This License represents the complete agreement concerning the subject
+ matter hereof. If any provision of this License is held to be
+ unenforceable, such provision shall be reformed only to the extent
+ necessary to make it enforceable. Any law or regulation which provides that
+ the language of a contract shall be construed against the drafter shall not
+ be used to construe this License against a Contributor.
+
+
+10. Versions of the License
+
+10.1. New Versions
+
+ Mozilla Foundation is the license steward. Except as provided in Section
+ 10.3, no one other than the license steward has the right to modify or
+ publish new versions of this License. Each version will be given a
+ distinguishing version number.
+
+10.2. Effect of New Versions
+
+ You may distribute the Covered Software under the terms of the version
+ of the License under which You originally received the Covered Software,
+ or under the terms of any subsequent version published by the license
+ steward.
+
+10.3. Modified Versions
+
+ If you create software not governed by this License, and you want to
+ create a new license for such software, you may create and use a
+ modified version of this License if you rename the license and remove
+ any references to the name of the license steward (except to note that
+ such modified license differs from this License).
+
+10.4. Distributing Source Code Form that is Incompatible With Secondary
+ Licenses If You choose to distribute Source Code Form that is
+ Incompatible With Secondary Licenses under the terms of this version of
+ the License, the notice described in Exhibit B of this License must be
+ attached.
+
+Exhibit A - Source Code Form License Notice
+
+ This Source Code Form is subject to the
+ terms of the Mozilla Public License, v.
+ 2.0. If a copy of the MPL was not
+ distributed with this file, You can
+ obtain one at
+ http://mozilla.org/MPL/2.0/.
+
+If it is not possible or desirable to put the notice in a particular file,
+then You may include the notice in a location (such as a LICENSE file in a
+relevant directory) where a recipient would be likely to look for such a
+notice.
+
+You may add additional accurate notices of copyright ownership.
+
+Exhibit B - "Incompatible With Secondary Licenses" Notice
+
+ This Source Code Form is "Incompatible
+ With Secondary Licenses", as defined by
+ the Mozilla Public License, v. 2.0.
+
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..38f1684
--- /dev/null
+++ b/README.md
@@ -0,0 +1,37 @@
+# Winium for Desktop
+[![GitHub license](https://img.shields.io/badge/license-MPL 2.0-blue.svg?style=flat-square)](LICENSE)
+
+
+
+
+
+Winium.Desktop is Selenium Remote WebDriver implementation for automated testing of Windows application based on WinFroms and WPF platforms.
+
+## Supported Platforms
+- WinForms
+- WPF
+
+For Windows Phone 8.1 test automation tool see [Windows Phone Driver](https://github.com/2gis/Winium.StoreApps).
+For Windows Phone 8 Silverlight test automation tool see [Windows Phone Driver](https://github.com/2gis/winphonedriver).
+
+## Why Winium?
+You have Selenium WebDriver for testing of web apps, Appium for testing of iOS and Android apps. And now you have Selenium-based tools for testing of Windows apps too. What are some of the benefits? As said by Appium:
+> - You can write tests with your favorite dev tools using any WebDriver-compatible language such as Java, Objective-C, JavaScript with Node.js (in promise, callback or generator flavors), PHP, Python, Ruby, C#, Clojure, or Perl with the Selenium WebDriver API and language-specific client libraries.
+> - You can use any testing framework.
+
+## Contributing
+
+Contributions are welcome!
+
+1. Check for open issues or open a fresh issue to start a discussion around a feature idea or a bug.
+2. Fork the repository to start making your changes to the master branch (or branch off of it).
+3. We recommend to write a test which shows that the bug was fixed or that the feature works as expected.
+4. Send a pull request and bug the maintainer until it gets merged and published. :smiley:
+
+## Contact
+
+Have some questions? Found a bug? Create [new issue](https://github.com/2gis/Winium.Desktop/issues/new) or contact us at g.golovin@2gis.ru
+
+## License
+
+Winium is released under the MPL 2.0 license. See [LICENSE](LICENSE) for details.
diff --git a/src/.nuget/NuGet.Config b/src/.nuget/NuGet.Config
new file mode 100644
index 0000000..67f8ea0
--- /dev/null
+++ b/src/.nuget/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.nuget/NuGet.targets b/src/.nuget/NuGet.targets
new file mode 100644
index 0000000..55c01f2
--- /dev/null
+++ b/src/.nuget/NuGet.targets
@@ -0,0 +1,144 @@
+
+
+
+ $(MSBuildProjectDirectory)\..\
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+ $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
+
+
+
+
+ $(SolutionDir).nuget
+
+
+
+ $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName.Replace(' ', '_')).config
+ $(MSBuildProjectDirectory)\packages.$(MSBuildProjectName).config
+
+
+
+ $(MSBuildProjectDirectory)\packages.config
+ $(PackagesProjectConfig)
+
+
+
+
+ $(NuGetToolsPath)\NuGet.exe
+ @(PackageSource)
+
+ "$(NuGetExePath)"
+ mono --runtime=v4.0.30319 "$(NuGetExePath)"
+
+ $(TargetDir.Trim('\\'))
+
+ -RequireConsent
+ -NonInteractive
+
+ "$(SolutionDir) "
+ "$(SolutionDir)"
+
+
+ $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
+ $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
+
+
+
+ RestorePackages;
+ $(BuildDependsOn);
+
+
+
+
+ $(BuildDependsOn);
+ BuildPackage;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Settings.StyleCop b/src/Settings.StyleCop
new file mode 100644
index 0000000..62855cc
--- /dev/null
+++ b/src/Settings.StyleCop
@@ -0,0 +1,221 @@
+
+
+
+
+ False
+
+
+
+
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+ False
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Winium.Desktop.Driver/App.config b/src/Winium.Desktop.Driver/App.config
new file mode 100644
index 0000000..d0feca6
--- /dev/null
+++ b/src/Winium.Desktop.Driver/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/Winium.Desktop.Driver/Automator/Automator.cs b/src/Winium.Desktop.Driver/Automator/Automator.cs
new file mode 100644
index 0000000..fbbce3a
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Automator/Automator.cs
@@ -0,0 +1,77 @@
+namespace Winium.Desktop.Driver.Automator
+{
+ #region using
+
+ using System.Collections.Generic;
+
+ using Winium.Cruciatus;
+
+ #endregion
+
+ internal class Automator
+ {
+ #region Static Fields
+
+ private static readonly object LockObject = new object();
+
+ private static volatile Automator instance;
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public Automator(string session)
+ {
+ this.Session = session;
+ this.Elements = new ElementStorage();
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public Capabilities ActualCapabilities { get; set; }
+
+ public Application Application { get; set; }
+
+ public ElementStorage Elements { get; private set; }
+
+ public string Session { get; private set; }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public static T GetValue(IReadOnlyDictionary parameters, string key) where T : class
+ {
+ object valueObject;
+ parameters.TryGetValue(key, out valueObject);
+
+ return valueObject as T;
+ }
+
+ public static Automator InstanceForSession(string sessionId)
+ {
+ if (instance == null)
+ {
+ lock (LockObject)
+ {
+ if (instance == null)
+ {
+ if (sessionId == null)
+ {
+ sessionId = "AwesomeSession";
+ }
+
+ // TODO: Add actual support for sessions. Temporary return single Automator for any season
+ instance = new Automator(sessionId);
+ }
+ }
+ }
+
+ return instance;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/Automator/Capabilities.cs b/src/Winium.Desktop.Driver/Automator/Capabilities.cs
new file mode 100644
index 0000000..a81c07f
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Automator/Capabilities.cs
@@ -0,0 +1,65 @@
+namespace Winium.Desktop.Driver.Automator
+{
+ #region using
+
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Serialization;
+
+ #endregion
+
+ internal class Capabilities
+ {
+ #region Constructors and Destructors
+
+ internal Capabilities()
+ {
+ this.App = string.Empty;
+ this.LaunchDelay = 0;
+ this.DebugConnectToRunningApp = false;
+ this.InnerPort = 9998;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ [JsonProperty("app")]
+ public string App { get; set; }
+
+ [JsonProperty("debugConnectToRunningApp")]
+ public bool DebugConnectToRunningApp { get; set; }
+
+ [JsonProperty("innerPort")]
+ public int InnerPort { get; set; }
+
+ [JsonProperty("launchDelay")]
+ public int LaunchDelay { get; set; }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public static Capabilities CapabilitiesFromJsonString(string jsonString)
+ {
+ var capabilities = JsonConvert.DeserializeObject(
+ jsonString,
+ new JsonSerializerSettings
+ {
+ Error =
+ delegate(object sender, ErrorEventArgs args)
+ {
+ args.ErrorContext.Handled = true;
+ }
+ });
+
+ return capabilities;
+ }
+
+ public string CapabilitiesToJsonString()
+ {
+ return JsonConvert.SerializeObject(this);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandExecutorDispatchTable.cs b/src/Winium.Desktop.Driver/CommandExecutorDispatchTable.cs
new file mode 100644
index 0000000..76013a8
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandExecutorDispatchTable.cs
@@ -0,0 +1,78 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+
+ using Winium.Desktop.Driver.CommandExecutors;
+ using Winium.StoreApps.Common;
+
+ #endregion
+
+ internal class CommandExecutorDispatchTable
+ {
+ #region Fields
+
+ private Dictionary commandExecutorsRepository;
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public CommandExecutorDispatchTable()
+ {
+ this.ConstructDispatcherTable();
+ }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public CommandExecutorBase GetExecutor(string commandName)
+ {
+ Type executorType;
+ if (this.commandExecutorsRepository.TryGetValue(commandName, out executorType))
+ {
+ }
+ else
+ {
+ executorType = typeof(NotImplementedExecutor);
+ }
+
+ return (CommandExecutorBase)Activator.CreateInstance(executorType);
+ }
+
+ #endregion
+
+ #region Methods
+
+ private void ConstructDispatcherTable()
+ {
+ this.commandExecutorsRepository = new Dictionary();
+
+ // TODO: bad const
+ const string ExecutorsNamespace = "Winium.Desktop.Driver.CommandExecutors";
+
+ var q =
+ (from t in Assembly.GetExecutingAssembly().GetTypes()
+ where t.IsClass && t.Namespace == ExecutorsNamespace
+ select t).ToArray();
+
+ var fields = typeof(DriverCommand).GetFields(BindingFlags.Public | BindingFlags.Static);
+ foreach (var field in fields)
+ {
+ var localField = field;
+ var executor = q.FirstOrDefault(x => x.Name.Equals(localField.Name + "Executor"));
+ if (executor != null)
+ {
+ this.commandExecutorsRepository.Add(localField.GetValue(null).ToString(), executor);
+ }
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandExecutors/ClickElementExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/ClickElementExecutor.cs
new file mode 100644
index 0000000..0f0175d
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandExecutors/ClickElementExecutor.cs
@@ -0,0 +1,17 @@
+namespace Winium.Desktop.Driver.CommandExecutors
+{
+ internal class ClickElementExecutor : CommandExecutorBase
+ {
+ #region Methods
+
+ protected override string DoImpl()
+ {
+ var registeredKey = this.ExecutedCommand.Parameters["ID"].ToString();
+ this.Automator.Elements.GetRegisteredElement(registeredKey).Click();
+
+ return this.JsonResponse();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandExecutors/CloseExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/CloseExecutor.cs
new file mode 100644
index 0000000..1c471e9
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandExecutors/CloseExecutor.cs
@@ -0,0 +1,27 @@
+namespace Winium.Desktop.Driver.CommandExecutors
+{
+ #region using
+
+ using Winium.StoreApps.Common;
+
+ #endregion
+
+ internal class CloseExecutor : CommandExecutorBase
+ {
+ #region Methods
+
+ protected override string DoImpl()
+ {
+ if (!this.Automator.Application.Close())
+ {
+ this.Automator.Application.Kill();
+ }
+
+ this.Automator.Elements.Clear();
+
+ return this.JsonResponse(ResponseStatus.Success, null);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandExecutors/CommandExecutorBase.cs b/src/Winium.Desktop.Driver/CommandExecutors/CommandExecutorBase.cs
new file mode 100644
index 0000000..42077e5
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandExecutors/CommandExecutorBase.cs
@@ -0,0 +1,84 @@
+namespace Winium.Desktop.Driver.CommandExecutors
+{
+ #region using
+
+ using System;
+ using System.Net;
+
+ using Newtonsoft.Json;
+
+ using Winium.Desktop.Driver.Automator;
+ using Winium.StoreApps.Common;
+ using Winium.StoreApps.Common.Exceptions;
+
+ #endregion
+
+ internal abstract class CommandExecutorBase
+ {
+ #region Public Properties
+
+ public Command ExecutedCommand { get; set; }
+
+ #endregion
+
+ #region Properties
+
+ protected Automator Automator { get; set; }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public CommandResponse Do()
+ {
+ if (this.ExecutedCommand == null)
+ {
+ throw new NullReferenceException("ExecutedCommand property must be set before calling Do");
+ }
+
+ try
+ {
+ var session = this.ExecutedCommand.SessionId;
+ this.Automator = Automator.InstanceForSession(session);
+ return CommandResponse.Create(HttpStatusCode.OK, this.DoImpl());
+ }
+ catch (AutomationException ex)
+ {
+ return CommandResponse.Create(HttpStatusCode.OK, this.JsonResponse(ex.Status, ex.Message));
+ }
+ catch (NotImplementedException exception)
+ {
+ return CommandResponse.Create(
+ HttpStatusCode.NotImplemented,
+ this.JsonResponse(ResponseStatus.UnknownCommand, exception.Message));
+ }
+ catch (Exception exception)
+ {
+ return CommandResponse.Create(
+ HttpStatusCode.OK,
+ this.JsonResponse(ResponseStatus.UnknownError, "Unknown error: " + exception.Message));
+ }
+ }
+
+ #endregion
+
+ #region Methods
+
+ protected abstract string DoImpl();
+
+ ///
+ /// The JsonResponse with SUCCESS status and NULL value.
+ ///
+ protected string JsonResponse()
+ {
+ return this.JsonResponse(ResponseStatus.Success, null);
+ }
+
+ protected string JsonResponse(ResponseStatus status, object value)
+ {
+ return JsonConvert.SerializeObject(new JsonResponse(this.Automator.Session, status, value));
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandExecutors/FindChildElementExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/FindChildElementExecutor.cs
new file mode 100644
index 0000000..4608df8
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandExecutors/FindChildElementExecutor.cs
@@ -0,0 +1,26 @@
+namespace Winium.Desktop.Driver.CommandExecutors
+{
+ #region using
+
+ using Winium.StoreApps.Common;
+
+ #endregion
+
+ internal class FindChildElementExecutor : CommandExecutorBase
+ {
+ #region Methods
+
+ protected override string DoImpl()
+ {
+ var registeredKey = this.ExecutedCommand.Parameters["ID"].ToString();
+ var searchValue = this.ExecutedCommand.Parameters["value"].ToString();
+ var searchStrategy = this.ExecutedCommand.Parameters["using"].ToString();
+
+ var elementId = this.Automator.Elements.FindElement(registeredKey, searchStrategy, searchValue);
+ var webElement = new JsonWebElementContent(elementId);
+ return this.JsonResponse(ResponseStatus.Success, webElement);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandExecutors/FindElementExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/FindElementExecutor.cs
new file mode 100644
index 0000000..8e3e192
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandExecutors/FindElementExecutor.cs
@@ -0,0 +1,26 @@
+namespace Winium.Desktop.Driver.CommandExecutors
+{
+ #region using
+
+ using Winium.Cruciatus;
+ using Winium.StoreApps.Common;
+
+ #endregion
+
+ internal class FindElementExecutor : CommandExecutorBase
+ {
+ #region Methods
+
+ protected override string DoImpl()
+ {
+ var searchValue = this.ExecutedCommand.Parameters["value"].ToString();
+ var searchStrategy = this.ExecutedCommand.Parameters["using"].ToString();
+
+ var elementId = this.Automator.Elements.FindElement(CruciatusFactory.Root, searchStrategy, searchValue);
+ var webElement = new JsonWebElementContent(elementId);
+ return this.JsonResponse(ResponseStatus.Success, webElement);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs
new file mode 100644
index 0000000..ac79ff8
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandExecutors/NewSessionExecutor.cs
@@ -0,0 +1,47 @@
+namespace Winium.Desktop.Driver.CommandExecutors
+{
+ #region using
+
+ using System.Threading;
+
+ using Newtonsoft.Json;
+
+ using Winium.Cruciatus;
+ using Winium.Desktop.Driver.Automator;
+ using Winium.StoreApps.Common;
+
+ #endregion
+
+ internal class NewSessionExecutor : CommandExecutorBase
+ {
+ #region Methods
+
+ protected override string DoImpl()
+ {
+ // It is easier to reparse desired capabilities as JSON instead of re-mapping keys to attributes and calling type conversions,
+ // so we will take possible one time performance hit by serializing Dictionary and deserializing it as Capabilities object
+ var serializedCapability =
+ JsonConvert.SerializeObject(this.ExecutedCommand.Parameters["desiredCapabilities"]);
+ this.Automator.ActualCapabilities = Capabilities.CapabilitiesFromJsonString(serializedCapability);
+
+ this.InitializeApplication(this.Automator.ActualCapabilities.DebugConnectToRunningApp);
+
+ // Gives sometime to load visuals (needed only in case of slow emulation)
+ Thread.Sleep(this.Automator.ActualCapabilities.LaunchDelay);
+
+ return this.JsonResponse(ResponseStatus.Success, this.Automator.ActualCapabilities);
+ }
+
+ private void InitializeApplication(bool debugDoNotDeploy = false)
+ {
+ var appPath = this.Automator.ActualCapabilities.App;
+ this.Automator.Application = new Application(appPath);
+ if (!debugDoNotDeploy)
+ {
+ this.Automator.Application.Start();
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandExecutors/NotImplementedExecutor.cs b/src/Winium.Desktop.Driver/CommandExecutors/NotImplementedExecutor.cs
new file mode 100644
index 0000000..7c14ee3
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandExecutors/NotImplementedExecutor.cs
@@ -0,0 +1,21 @@
+namespace Winium.Desktop.Driver.CommandExecutors
+{
+ #region using
+
+ using System;
+
+ #endregion
+
+ internal class NotImplementedExecutor : CommandExecutorBase
+ {
+ #region Methods
+
+ protected override string DoImpl()
+ {
+ var msg = string.Format("'{0}' is not valid or implemented command.", this.ExecutedCommand.Name);
+ throw new NotImplementedException(msg);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/CommandLineOptions.cs b/src/Winium.Desktop.Driver/CommandLineOptions.cs
new file mode 100644
index 0000000..5f28695
--- /dev/null
+++ b/src/Winium.Desktop.Driver/CommandLineOptions.cs
@@ -0,0 +1,39 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using CommandLine;
+ using CommandLine.Text;
+
+ #endregion
+
+ internal class CommandLineOptions
+ {
+ #region Public Properties
+
+ [Option("log-path", Required = false,
+ HelpText = "write server log to file instead of stdout, increases log level to INFO")]
+ public string LogPath { get; set; }
+
+ [Option("port", Required = false, HelpText = "port to listen on")]
+ public int? Port { get; set; }
+
+ [Option("url-base", Required = false, HelpText = "base URL path prefix for commands, e.g. wd/url")]
+ public string UrlBase { get; set; }
+
+ [Option("verbose", Required = false, HelpText = "log verbosely")]
+ public bool Verbose { get; set; }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ [HelpOption]
+ public string GetUsage()
+ {
+ return HelpText.AutoBuild(this, current => HelpText.DefaultParsingErrorsHandler(this, current));
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/ElementStorage.cs b/src/Winium.Desktop.Driver/ElementStorage.cs
new file mode 100644
index 0000000..769b474
--- /dev/null
+++ b/src/Winium.Desktop.Driver/ElementStorage.cs
@@ -0,0 +1,103 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Threading;
+
+ using Winium.Cruciatus.Elements;
+ using Winium.Desktop.Driver.Extensions;
+ using Winium.StoreApps.Common;
+ using Winium.StoreApps.Common.Exceptions;
+
+ #endregion
+
+ internal class ElementStorage
+ {
+ #region Static Fields
+
+ private static int safeInstanceCount;
+
+ #endregion
+
+ #region Fields
+
+ private readonly Dictionary registeredElements;
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public ElementStorage()
+ {
+ this.registeredElements = new Dictionary();
+ }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public void Clear()
+ {
+ this.registeredElements.Clear();
+ }
+
+ public string FindElement(CruciatusElement parent, string searchStrategy, string searchValue)
+ {
+ var strategy = ByHelper.GetStrategy(searchStrategy, searchValue);
+ var element = parent.FindElement(strategy);
+ if (element == null)
+ {
+ throw new AutomationException("Element cannot be found", ResponseStatus.NoSuchElement);
+ }
+
+ return this.RegisterElement(element);
+ }
+
+ public string FindElement(string parentRegisteredKey, string searchStrategy, string searchValue)
+ {
+ var parent = this.GetRegisteredElement(parentRegisteredKey);
+ return this.FindElement(parent, searchStrategy, searchValue);
+ }
+
+ ///
+ /// Returns FrameworkElement registered with specified key if any exists. Throws if no element is found.
+ ///
+ ///
+ /// Registered element is not found or element has been garbage collected.
+ ///
+ public CruciatusElement GetRegisteredElement(string registeredKey)
+ {
+ CruciatusElement element;
+ if (this.registeredElements.TryGetValue(registeredKey, out element))
+ {
+ if (element != null)
+ {
+ return element;
+ }
+ }
+
+ throw new AutomationException("Stale element reference", ResponseStatus.StaleElementReference);
+ }
+
+ public string RegisterElement(CruciatusElement element)
+ {
+ var registeredKey = this.registeredElements.FirstOrDefault(x => x.Value == element).Key;
+
+ if (registeredKey == null)
+ {
+ Interlocked.Increment(ref safeInstanceCount);
+
+ registeredKey = element.GetHashCode() + "-"
+ + safeInstanceCount.ToString(string.Empty, CultureInfo.InvariantCulture);
+ this.registeredElements.Add(registeredKey, element);
+ }
+
+ return registeredKey;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/Extensions/ByHelper.cs b/src/Winium.Desktop.Driver/Extensions/ByHelper.cs
new file mode 100644
index 0000000..5de3750
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Extensions/ByHelper.cs
@@ -0,0 +1,34 @@
+namespace Winium.Desktop.Driver.Extensions
+{
+ #region using
+
+ using System;
+ using System.Windows.Automation;
+
+ using Winium.Cruciatus.Core;
+
+ #endregion
+
+ public static class ByHelper
+ {
+ #region Public Methods and Operators
+
+ public static By GetStrategy(string strategy, string value)
+ {
+ switch (strategy)
+ {
+ case "id":
+ return By.Uid(value);
+ case "name":
+ return By.Name(value);
+ case "class name":
+ return By.AutomationProperty(AutomationElementIdentifiers.ClassNameProperty, value);
+ default:
+ throw new NotImplementedException(
+ string.Format("'{0}' is not valid or implemented searching strategy.", strategy));
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/HttpRequest.cs b/src/Winium.Desktop.Driver/HttpRequest.cs
new file mode 100644
index 0000000..5c9aa2f
--- /dev/null
+++ b/src/Winium.Desktop.Driver/HttpRequest.cs
@@ -0,0 +1,81 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Linq;
+
+ #endregion
+
+ public class HttpRequest
+ {
+ #region Public Properties
+
+ public Dictionary Headers { get; set; }
+
+ public string MessageBody { get; private set; }
+
+ public string StartingLine { get; private set; }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public static HttpRequest ReadFromStreamWithoutClosing(Stream stream)
+ {
+ var request = new HttpRequest();
+ var streamReader = new StreamReader(stream);
+
+ request.StartingLine = streamReader.ReadLine();
+
+ request.Headers = ReadHeaders(streamReader);
+
+ var contentLength = GetContentLength(request.Headers);
+ request.MessageBody = contentLength != 0 ? ReadContent(streamReader, contentLength) : string.Empty;
+
+ return request;
+ }
+
+ #endregion
+
+ #region Methods
+
+ private static int GetContentLength(IReadOnlyDictionary headers)
+ {
+ var contentLength = 0;
+ string contentLengthString;
+ if (headers.TryGetValue("Content-Length", out contentLengthString))
+ {
+ contentLength = Convert.ToInt32(contentLengthString, CultureInfo.InvariantCulture);
+ }
+
+ return contentLength;
+ }
+
+ // reads the content of a request depending on its length
+ private static string ReadContent(TextReader textReader, int contentLength)
+ {
+ var readBuffer = new char[contentLength];
+ textReader.Read(readBuffer, 0, readBuffer.Length);
+ return readBuffer.Aggregate(string.Empty, (current, ch) => current + ch);
+ }
+
+ private static Dictionary ReadHeaders(TextReader textReader)
+ {
+ var headers = new Dictionary();
+ string header;
+ while (!string.IsNullOrEmpty(header = textReader.ReadLine()))
+ {
+ var splitHeader = header.Split(':');
+ headers.Add(splitHeader[0], splitHeader[1].Trim(' '));
+ }
+
+ return headers;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/Listener.cs b/src/Winium.Desktop.Driver/Listener.cs
new file mode 100644
index 0000000..d9884ac
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Listener.cs
@@ -0,0 +1,183 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using System;
+ using System.Globalization;
+ using System.IO;
+ using System.Net;
+ using System.Net.Sockets;
+
+ using Winium.StoreApps.Common;
+
+ #endregion
+
+ public class Listener
+ {
+ #region Static Fields
+
+ private static string urnPrefix;
+
+ #endregion
+
+ #region Fields
+
+ private UriDispatchTables dispatcher;
+
+ private CommandExecutorDispatchTable executorDispatcher;
+
+ private TcpListener listener;
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public Listener(int listenerPort)
+ {
+ this.Port = listenerPort;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public static string UrnPrefix
+ {
+ get
+ {
+ return urnPrefix;
+ }
+
+ set
+ {
+ if (!string.IsNullOrEmpty(value))
+ {
+ // Normalize prefix
+ urnPrefix = "/" + value.Trim('/');
+ }
+ }
+ }
+
+ public int Port { get; private set; }
+
+ public Uri Prefix { get; private set; }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public void StartListening()
+ {
+ try
+ {
+ this.listener = new TcpListener(IPAddress.Any, this.Port);
+
+ this.Prefix = new Uri(string.Format(CultureInfo.InvariantCulture, "http://localhost:{0}", this.Port));
+ this.dispatcher = new UriDispatchTables(new Uri(this.Prefix, UrnPrefix));
+ this.executorDispatcher = new CommandExecutorDispatchTable();
+
+ // Start listening for client requests.
+ this.listener.Start();
+
+ // Enter the listening loop
+ while (true)
+ {
+ Logger.Debug("Waiting for a connection...");
+
+ // Perform a blocking call to accept requests.
+ var client = this.listener.AcceptTcpClient();
+
+ // Get a stream object for reading and writing
+ using (var stream = client.GetStream())
+ {
+ var acceptedRequest = HttpRequest.ReadFromStreamWithoutClosing(stream);
+ Logger.Debug("ACCEPTED REQUEST {0}", acceptedRequest.StartingLine);
+
+ var response = this.HandleRequest(acceptedRequest);
+ using (var writer = new StreamWriter(stream))
+ {
+ try
+ {
+ writer.Write(response);
+ writer.Flush();
+ }
+ catch (IOException ex)
+ {
+ Logger.Error("Error occured while writing response: {0}", ex);
+ }
+ }
+
+ // Shutdown and end connection
+ }
+
+ client.Close();
+
+ Logger.Debug("Client closed\n");
+ }
+ }
+ catch (SocketException ex)
+ {
+ Logger.Error("SocketException occurred while trying to start listner: {0}", ex);
+ throw;
+ }
+ catch (ArgumentException ex)
+ {
+ Logger.Error("ArgumentException occurred while trying to start listner: {0}", ex);
+ throw;
+ }
+ finally
+ {
+ // Stop listening for new clients.
+ this.listener.Stop();
+ }
+ }
+
+ public void StopListening()
+ {
+ this.listener.Stop();
+ }
+
+ #endregion
+
+ #region Methods
+
+ private string HandleRequest(HttpRequest acceptedRequest)
+ {
+ var firstHeaderTokens = acceptedRequest.StartingLine.Split(' ');
+ var method = firstHeaderTokens[0];
+ var resourcePath = firstHeaderTokens[1];
+
+ var uriToMatch = new Uri(this.Prefix, resourcePath);
+ var matched = this.dispatcher.Match(method, uriToMatch);
+
+ if (matched == null)
+ {
+ Logger.Warn("Unknown command recived: {0}", uriToMatch);
+ return HttpResponseHelper.ResponseString(HttpStatusCode.NotFound, "Unknown command " + uriToMatch);
+ }
+
+ var commandName = matched.Data.ToString();
+ var commandToExecute = new Command(commandName, acceptedRequest.MessageBody);
+ foreach (string variableName in matched.BoundVariables.Keys)
+ {
+ commandToExecute.Parameters[variableName] = matched.BoundVariables[variableName];
+ }
+
+ var commandResponse = this.ProcessCommand(commandToExecute);
+ return HttpResponseHelper.ResponseString(commandResponse.HttpStatusCode, commandResponse.Content);
+ }
+
+ private CommandResponse ProcessCommand(Command command)
+ {
+ Logger.Info("COMMAND {0}\r\n{1}", command.Name, command.Parameters.ToString());
+ var executor = this.executorDispatcher.GetExecutor(command.Name);
+ executor.ExecutedCommand = command;
+ var respnose = executor.Do();
+ Logger.Debug("RESPONSE:\r\n{0}", respnose);
+
+ return respnose;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/Logger.cs b/src/Winium.Desktop.Driver/Logger.cs
new file mode 100644
index 0000000..f20b855
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Logger.cs
@@ -0,0 +1,76 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using System.ComponentModel;
+
+ using NLog;
+ using NLog.Targets;
+
+ #endregion
+
+ internal static class Logger
+ {
+ #region Constants
+
+ private const string LayoutFormat = "${date:format=HH\\:MM\\:ss} [${level:uppercase=true}] ${message}";
+
+ #endregion
+
+ #region Static Fields
+
+ private static readonly NLog.Logger Log = LogManager.GetLogger("outerdriver");
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public static void Debug([Localizable(false)] string message, params object[] args)
+ {
+ Log.Debug(message, args);
+ }
+
+ public static void Error([Localizable(false)] string message, params object[] args)
+ {
+ Log.Error(message, args);
+ }
+
+ public static void Fatal([Localizable(false)] string message, params object[] args)
+ {
+ Log.Fatal(message, args);
+ }
+
+ public static void Info([Localizable(false)] string message, params object[] args)
+ {
+ Log.Info(message, args);
+ }
+
+ public static void TargetConsole(bool verbose)
+ {
+ var target = new ConsoleTarget { Layout = LayoutFormat };
+
+ NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, verbose ? LogLevel.Debug : LogLevel.Fatal);
+ LogManager.ReconfigExistingLoggers();
+ }
+
+ public static void TargetFile(string fileName, bool verbose)
+ {
+ var target = new FileTarget { Layout = LayoutFormat, FileName = fileName };
+
+ NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, verbose ? LogLevel.Debug : LogLevel.Info);
+ LogManager.ReconfigExistingLoggers();
+ }
+
+ public static void Trace([Localizable(false)] string message, params object[] args)
+ {
+ Log.Trace(message, args);
+ }
+
+ public static void Warn([Localizable(false)] string message, params object[] args)
+ {
+ Log.Warn(message, args);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/Program.cs b/src/Winium.Desktop.Driver/Program.cs
new file mode 100644
index 0000000..07b1faa
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Program.cs
@@ -0,0 +1,54 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using System;
+
+ #endregion
+
+ internal class Program
+ {
+ #region Methods
+
+ [STAThread]
+ private static void Main(string[] args)
+ {
+ var listeningPort = 9999;
+
+ var options = new CommandLineOptions();
+ if (CommandLine.Parser.Default.ParseArguments(args, options))
+ {
+ if (options.Port.HasValue)
+ {
+ listeningPort = options.Port.Value;
+ }
+ }
+
+ if (options.LogPath != null)
+ {
+ Logger.TargetFile(options.LogPath, options.Verbose);
+ }
+ else
+ {
+ Logger.TargetConsole(options.Verbose);
+ }
+
+ try
+ {
+ var listener = new Listener(listeningPort);
+ Listener.UrnPrefix = options.UrlBase;
+
+ Console.WriteLine("Starting Windows Desktop Driver on port {0}\n", listeningPort);
+
+ listener.StartListening();
+ }
+ catch (Exception ex)
+ {
+ Logger.Fatal("Failed to start driver: {0}", ex);
+ throw;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/Properties/AssemblyInfo.cs b/src/Winium.Desktop.Driver/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..8c6227a
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+#region using
+
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+#endregion
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Winium.Desktop.Driver")]
+[assembly: AssemblyDescription("Selenium Remote WebDriver implementation for test automation of Windows application based on WinFroms and WPF platforms.")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Winium.Desktop.Driver")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("2878a69b-16cd-455a-8f46-6323e9109635")]
+
+// Version information for an assembly consists of the following four values:
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Winium.Desktop.Driver/Requester.cs b/src/Winium.Desktop.Driver/Requester.cs
new file mode 100644
index 0000000..890b0a4
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Requester.cs
@@ -0,0 +1,147 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.IO;
+ using System.Net;
+
+ using Newtonsoft.Json;
+
+ using Winium.StoreApps.Common;
+ using Winium.StoreApps.Common.Exceptions;
+
+ #endregion
+
+ internal class Requester
+ {
+ #region Fields
+
+ private readonly string ip;
+
+ private readonly int port;
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public Requester(string ip, int port)
+ {
+ this.ip = ip;
+ this.port = port;
+ }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public string ForwardCommand(Command commandToForward, bool verbose = true, int timeout = 0)
+ {
+ var serializedCommand = JsonConvert.SerializeObject(commandToForward);
+
+ var response = this.SendRequest(serializedCommand, verbose, timeout);
+ if (response.Key == HttpStatusCode.OK)
+ {
+ return response.Value;
+ }
+
+ throw new InnerDriverRequestException(response.Value, response.Key);
+ }
+
+ public KeyValuePair SendRequest(string requestContent, bool verbose, int timeout)
+ {
+ var result = string.Empty;
+ StreamReader reader = null;
+ HttpWebResponse response = null;
+ var status = HttpStatusCode.OK;
+ try
+ {
+ // create the request
+ var uri = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", this.ip, this.port);
+ var request = CreateWebRequest(uri, requestContent);
+ if (timeout != 0)
+ {
+ request.Timeout = timeout;
+ }
+
+ if (verbose)
+ {
+ Logger.Debug("Sending request to inner driver: {0}", uri);
+ }
+
+ // send the request and get the response
+ try
+ {
+ response = request.GetResponse() as HttpWebResponse;
+ }
+ catch (WebException ex)
+ {
+ response = ex.Response as HttpWebResponse;
+ }
+
+ if (response != null)
+ {
+ status = response.StatusCode;
+ var stream = response.GetResponseStream();
+ if (stream == null)
+ {
+ throw new NullReferenceException("No response stream.");
+ }
+
+ // read and return the response
+ reader = new StreamReader(stream);
+ result = reader.ReadToEnd();
+ }
+ }
+ catch (Exception ex)
+ {
+ if (verbose)
+ {
+ // No need to log exceptions raised when sending service commands like ping.
+ Logger.Error("Error occurred while trying to send request to inner driver: {0}", ex);
+ }
+ }
+ finally
+ {
+ if (response != null)
+ {
+ response.Close();
+ }
+
+ if (reader != null)
+ {
+ reader.Close();
+ }
+ }
+
+ return new KeyValuePair(status, result);
+ }
+
+ #endregion
+
+ #region Methods
+
+ private static HttpWebRequest CreateWebRequest(string uri, string content)
+ {
+ // create request
+ var request = (HttpWebRequest)WebRequest.Create(uri);
+ request.ContentType = "application/json";
+ request.Method = "POST";
+ request.KeepAlive = false;
+
+ // write request body
+ if (!string.IsNullOrEmpty(content))
+ {
+ var writer = new StreamWriter(request.GetRequestStream());
+ writer.Write(content);
+ writer.Close();
+ }
+
+ return request;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/UriDispatchTables.cs b/src/Winium.Desktop.Driver/UriDispatchTables.cs
new file mode 100644
index 0000000..5cdaaed
--- /dev/null
+++ b/src/Winium.Desktop.Driver/UriDispatchTables.cs
@@ -0,0 +1,293 @@
+namespace Winium.Desktop.Driver
+{
+ #region using
+
+ using System;
+ using System.Collections.Generic;
+ using System.Reflection;
+
+ using Winium.StoreApps.Common;
+
+ #endregion
+
+ internal class UriDispatchTables
+ {
+ #region Fields
+
+ private readonly Dictionary commandDictionary = new Dictionary();
+
+ private UriTemplateTable deleteDispatcherTable;
+
+ private UriTemplateTable getDispatcherTable;
+
+ private UriTemplateTable postDispatcherTable;
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public UriDispatchTables(Uri prefix)
+ {
+ this.InitializeCommandDictionary();
+ this.ConstructDispatcherTables(prefix);
+ }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public UriTemplateMatch Match(string httpMethod, Uri uriToMatch)
+ {
+ var table = this.FindDispatcherTable(httpMethod);
+ return table != null ? table.MatchSingle(uriToMatch) : null;
+ }
+
+ #endregion
+
+ #region Methods
+
+ internal UriTemplateTable FindDispatcherTable(string httpMethod)
+ {
+ UriTemplateTable tableToReturn = null;
+ switch (httpMethod)
+ {
+ case CommandInfo.GetCommand:
+ tableToReturn = this.getDispatcherTable;
+ break;
+
+ case CommandInfo.PostCommand:
+ tableToReturn = this.postDispatcherTable;
+ break;
+
+ case CommandInfo.DeleteCommand:
+ tableToReturn = this.deleteDispatcherTable;
+ break;
+ }
+
+ return tableToReturn;
+ }
+
+ private void ConstructDispatcherTables(Uri prefix)
+ {
+ this.getDispatcherTable = new UriTemplateTable(prefix);
+ this.postDispatcherTable = new UriTemplateTable(prefix);
+ this.deleteDispatcherTable = new UriTemplateTable(prefix);
+
+ var fields = typeof(DriverCommand).GetFields(BindingFlags.Public | BindingFlags.Static);
+ foreach (var field in fields)
+ {
+ var commandName = field.GetValue(null).ToString();
+ var commandInformation = this.commandDictionary[commandName];
+ var commandUriTemplate = new UriTemplate(commandInformation.ResourcePath);
+ var templateTable = this.FindDispatcherTable(commandInformation.Method);
+ templateTable.KeyValuePairs.Add(new KeyValuePair(commandUriTemplate, commandName));
+ }
+
+ this.getDispatcherTable.MakeReadOnly(false);
+ this.postDispatcherTable.MakeReadOnly(false);
+ this.deleteDispatcherTable.MakeReadOnly(false);
+ }
+
+ private void InitializeCommandDictionary()
+ {
+ this.commandDictionary.Add(DriverCommand.DefineDriverMapping, new CommandInfo("POST", "/config/drivers"));
+ this.commandDictionary.Add(DriverCommand.Status, new CommandInfo("GET", "/status"));
+ this.commandDictionary.Add(DriverCommand.NewSession, new CommandInfo("POST", "/session"));
+ this.commandDictionary.Add(DriverCommand.GetSessionList, new CommandInfo("GET", "/sessions"));
+ this.commandDictionary.Add(
+ DriverCommand.GetSessionCapabilities,
+ new CommandInfo("GET", "/session/{sessionId}"));
+ this.commandDictionary.Add(DriverCommand.Quit, new CommandInfo("DELETE", "/session/{sessionId}"));
+ this.commandDictionary.Add(
+ DriverCommand.GetCurrentWindowHandle,
+ new CommandInfo("GET", "/session/{sessionId}/window_handle"));
+ this.commandDictionary.Add(
+ DriverCommand.GetWindowHandles,
+ new CommandInfo("GET", "/session/{sessionId}/window_handles"));
+ this.commandDictionary.Add(DriverCommand.GetCurrentUrl, new CommandInfo("GET", "/session/{sessionId}/url"));
+ this.commandDictionary.Add(DriverCommand.Get, new CommandInfo("POST", "/session/{sessionId}/url"));
+ this.commandDictionary.Add(DriverCommand.GoForward, new CommandInfo("POST", "/session/{sessionId}/forward"));
+ this.commandDictionary.Add(DriverCommand.GoBack, new CommandInfo("POST", "/session/{sessionId}/back"));
+ this.commandDictionary.Add(DriverCommand.Refresh, new CommandInfo("POST", "/session/{sessionId}/refresh"));
+ this.commandDictionary.Add(
+ DriverCommand.ExecuteScript,
+ new CommandInfo("POST", "/session/{sessionId}/execute"));
+ this.commandDictionary.Add(
+ DriverCommand.ExecuteAsyncScript,
+ new CommandInfo("POST", "/session/{sessionId}/execute_async"));
+ this.commandDictionary.Add(
+ DriverCommand.Screenshot,
+ new CommandInfo("GET", "/session/{sessionId}/screenshot"));
+ this.commandDictionary.Add(
+ DriverCommand.SwitchToFrame,
+ new CommandInfo("POST", "/session/{sessionId}/frame"));
+ this.commandDictionary.Add(
+ DriverCommand.SwitchToParentFrame,
+ new CommandInfo("POST", "/session/{sessionId}/frame/parent"));
+ this.commandDictionary.Add(
+ DriverCommand.SwitchToWindow,
+ new CommandInfo("POST", "/session/{sessionId}/window"));
+ this.commandDictionary.Add(
+ DriverCommand.GetAllCookies,
+ new CommandInfo("GET", "/session/{sessionId}/cookie"));
+ this.commandDictionary.Add(DriverCommand.AddCookie, new CommandInfo("POST", "/session/{sessionId}/cookie"));
+ this.commandDictionary.Add(
+ DriverCommand.DeleteAllCookies,
+ new CommandInfo("DELETE", "/session/{sessionId}/cookie"));
+ this.commandDictionary.Add(
+ DriverCommand.DeleteCookie,
+ new CommandInfo("DELETE", "/session/{sessionId}/cookie/{name}"));
+ this.commandDictionary.Add(
+ DriverCommand.GetPageSource,
+ new CommandInfo("GET", "/session/{sessionId}/source"));
+ this.commandDictionary.Add(DriverCommand.GetTitle, new CommandInfo("GET", "/session/{sessionId}/title"));
+ this.commandDictionary.Add(
+ DriverCommand.FindElement,
+ new CommandInfo("POST", "/session/{sessionId}/element"));
+ this.commandDictionary.Add(
+ DriverCommand.FindElements,
+ new CommandInfo("POST", "/session/{sessionId}/elements"));
+ this.commandDictionary.Add(
+ DriverCommand.GetActiveElement,
+ new CommandInfo("POST", "/session/{sessionId}/element/active"));
+ this.commandDictionary.Add(
+ DriverCommand.FindChildElement,
+ new CommandInfo("POST", "/session/{sessionId}/element/{id}/element"));
+ this.commandDictionary.Add(
+ DriverCommand.FindChildElements,
+ new CommandInfo("POST", "/session/{sessionId}/element/{id}/elements"));
+ this.commandDictionary.Add(
+ DriverCommand.DescribeElement,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}"));
+ this.commandDictionary.Add(
+ DriverCommand.ClickElement,
+ new CommandInfo("POST", "/session/{sessionId}/element/{id}/click"));
+ this.commandDictionary.Add(
+ DriverCommand.GetElementText,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/text"));
+ this.commandDictionary.Add(
+ DriverCommand.SubmitElement,
+ new CommandInfo("POST", "/session/{sessionId}/element/{id}/submit"));
+ this.commandDictionary.Add(
+ DriverCommand.SendKeysToElement,
+ new CommandInfo("POST", "/session/{sessionId}/element/{id}/value"));
+ this.commandDictionary.Add(
+ DriverCommand.GetElementTagName,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/name"));
+ this.commandDictionary.Add(
+ DriverCommand.ClearElement,
+ new CommandInfo("POST", "/session/{sessionId}/element/{id}/clear"));
+ this.commandDictionary.Add(
+ DriverCommand.IsElementSelected,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/selected"));
+ this.commandDictionary.Add(
+ DriverCommand.IsElementEnabled,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/enabled"));
+ this.commandDictionary.Add(
+ DriverCommand.IsElementDisplayed,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/displayed"));
+ this.commandDictionary.Add(
+ DriverCommand.GetElementLocation,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/location"));
+ this.commandDictionary.Add(
+ DriverCommand.GetElementLocationOnceScrolledIntoView,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/location_in_view"));
+ this.commandDictionary.Add(
+ DriverCommand.GetElementSize,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/size"));
+ this.commandDictionary.Add(
+ DriverCommand.GetElementValueOfCssProperty,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/css/{propertyName}"));
+ this.commandDictionary.Add(
+ DriverCommand.GetElementAttribute,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/attribute/{name}"));
+ this.commandDictionary.Add(
+ DriverCommand.ElementEquals,
+ new CommandInfo("GET", "/session/{sessionId}/element/{id}/equals/{other}"));
+ this.commandDictionary.Add(DriverCommand.Close, new CommandInfo("DELETE", "/session/{sessionId}/window"));
+ this.commandDictionary.Add(
+ DriverCommand.GetWindowSize,
+ new CommandInfo("GET", "/session/{sessionId}/window/{windowHandle}/size"));
+ this.commandDictionary.Add(
+ DriverCommand.SetWindowSize,
+ new CommandInfo("POST", "/session/{sessionId}/window/{windowHandle}/size"));
+ this.commandDictionary.Add(
+ DriverCommand.GetWindowPosition,
+ new CommandInfo("GET", "/session/{sessionId}/window/{windowHandle}/position"));
+ this.commandDictionary.Add(
+ DriverCommand.SetWindowPosition,
+ new CommandInfo("POST", "/session/{sessionId}/window/{windowHandle}/position"));
+ this.commandDictionary.Add(
+ DriverCommand.MaximizeWindow,
+ new CommandInfo("POST", "/session/{sessionId}/window/{windowHandle}/maximize"));
+ this.commandDictionary.Add(
+ DriverCommand.GetOrientation,
+ new CommandInfo("GET", "/session/{sessionId}/orientation"));
+ this.commandDictionary.Add(
+ DriverCommand.SetOrientation,
+ new CommandInfo("POST", "/session/{sessionId}/orientation"));
+ this.commandDictionary.Add(
+ DriverCommand.DismissAlert,
+ new CommandInfo("POST", "/session/{sessionId}/dismiss_alert"));
+ this.commandDictionary.Add(
+ DriverCommand.AcceptAlert,
+ new CommandInfo("POST", "/session/{sessionId}/accept_alert"));
+ this.commandDictionary.Add(
+ DriverCommand.GetAlertText,
+ new CommandInfo("GET", "/session/{sessionId}/alert_text"));
+ this.commandDictionary.Add(
+ DriverCommand.SetAlertValue,
+ new CommandInfo("POST", "/session/{sessionId}/alert_text"));
+ this.commandDictionary.Add(
+ DriverCommand.SetTimeout,
+ new CommandInfo("POST", "/session/{sessionId}/timeouts"));
+ this.commandDictionary.Add(
+ DriverCommand.ImplicitlyWait,
+ new CommandInfo("POST", "/session/{sessionId}/timeouts/implicit_wait"));
+ this.commandDictionary.Add(
+ DriverCommand.SetAsyncScriptTimeout,
+ new CommandInfo("POST", "/session/{sessionId}/timeouts/async_script"));
+ this.commandDictionary.Add(DriverCommand.MouseClick, new CommandInfo("POST", "/session/{sessionId}/click"));
+ this.commandDictionary.Add(
+ DriverCommand.MouseDoubleClick,
+ new CommandInfo("POST", "/session/{sessionId}/doubleclick"));
+ this.commandDictionary.Add(
+ DriverCommand.MouseDown,
+ new CommandInfo("POST", "/session/{sessionId}/buttondown"));
+ this.commandDictionary.Add(DriverCommand.MouseUp, new CommandInfo("POST", "/session/{sessionId}/buttonup"));
+ this.commandDictionary.Add(
+ DriverCommand.MouseMoveTo,
+ new CommandInfo("POST", "/session/{sessionId}/moveto"));
+ this.commandDictionary.Add(
+ DriverCommand.SendKeysToActiveElement,
+ new CommandInfo("POST", "/session/{sessionId}/keys"));
+ this.commandDictionary.Add(
+ DriverCommand.TouchSingleTap,
+ new CommandInfo("POST", "/session/{sessionId}/touch/click"));
+ this.commandDictionary.Add(
+ DriverCommand.TouchPress,
+ new CommandInfo("POST", "/session/{sessionId}/touch/down"));
+ this.commandDictionary.Add(
+ DriverCommand.TouchRelease,
+ new CommandInfo("POST", "/session/{sessionId}/touch/up"));
+ this.commandDictionary.Add(
+ DriverCommand.TouchMove,
+ new CommandInfo("POST", "/session/{sessionId}/touch/move"));
+ this.commandDictionary.Add(
+ DriverCommand.TouchScroll,
+ new CommandInfo("POST", "/session/{sessionId}/touch/scroll"));
+ this.commandDictionary.Add(
+ DriverCommand.TouchDoubleTap,
+ new CommandInfo("POST", "/session/{sessionId}/touch/doubleclick"));
+ this.commandDictionary.Add(
+ DriverCommand.TouchLongPress,
+ new CommandInfo("POST", "/session/{sessionId}/touch/longclick"));
+ this.commandDictionary.Add(
+ DriverCommand.TouchFlick,
+ new CommandInfo("POST", "/session/{sessionId}/touch/flick"));
+ this.commandDictionary.Add(DriverCommand.UploadFile, new CommandInfo("POST", "/session/{sessionId}/file"));
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj
new file mode 100644
index 0000000..9f523bb
--- /dev/null
+++ b/src/Winium.Desktop.Driver/Winium.Desktop.Driver.csproj
@@ -0,0 +1,107 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {B214C2BA-43FA-486F-AD0B-D4890C1748C0}
+ Exe
+ Properties
+ Winium.Desktop.Driver
+ Winium.Desktop.Driver
+ v4.5.1
+ 512
+ ..\
+ true
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll
+
+
+ ..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll
+
+
+ False
+ ..\packages\NLog.3.1.0.0\lib\net45\NLog.dll
+
+
+
+
+
+
+ ..\packages\InputSimulator.1.0.4.0\lib\net20\WindowsInput.dll
+
+
+ False
+ ..\packages\Winium.Cruciatus.2.0.0\lib\net45\Winium.Cruciatus.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {3c8d0b9c-576b-4778-97b1-6839aa944aee}
+ Winium.StoreApps.Common
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Winium.Desktop.Driver/packages.config b/src/Winium.Desktop.Driver/packages.config
new file mode 100644
index 0000000..e74c3f3
--- /dev/null
+++ b/src/Winium.Desktop.Driver/packages.config
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Winium.StoreApps.Common/Command.cs b/src/Winium.StoreApps.Common/Command.cs
new file mode 100644
index 0000000..82333a2
--- /dev/null
+++ b/src/Winium.StoreApps.Common/Command.cs
@@ -0,0 +1,80 @@
+namespace Winium.StoreApps.Common
+{
+ #region
+
+ using System.Collections.Generic;
+
+ using Newtonsoft.Json;
+ using Newtonsoft.Json.Linq;
+
+ #endregion
+
+ public class Command
+ {
+ #region Fields
+
+ private IDictionary commandParameters = new JObject();
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public Command(string name, IDictionary parameters)
+ {
+ this.Name = name;
+ if (parameters != null)
+ {
+ this.Parameters = parameters;
+ }
+ }
+
+ public Command(string name, string jsonParameters)
+ : this(name, string.IsNullOrEmpty(jsonParameters) ? null : JObject.Parse(jsonParameters))
+ {
+ }
+
+ public Command(string name)
+ {
+ this.Name = name;
+ }
+
+ public Command()
+ {
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ ///
+ /// Gets the command name
+ ///
+ [JsonProperty("name")]
+ public string Name { get; set; }
+
+ ///
+ /// Gets the parameters of the command
+ ///
+ [JsonProperty("parameters")]
+ public IDictionary Parameters
+ {
+ get
+ {
+ return this.commandParameters;
+ }
+
+ set
+ {
+ this.commandParameters = value;
+ }
+ }
+
+ ///
+ /// Gets the SessionID of the command
+ ///
+ [JsonProperty("sessionId")]
+ public string SessionId { get; set; }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.StoreApps.Common/CommandInfo.cs b/src/Winium.StoreApps.Common/CommandInfo.cs
new file mode 100644
index 0000000..e85b3a3
--- /dev/null
+++ b/src/Winium.StoreApps.Common/CommandInfo.cs
@@ -0,0 +1,33 @@
+namespace Winium.StoreApps.Common
+{
+ public class CommandInfo
+ {
+ #region Constants
+
+ public const string DeleteCommand = "DELETE";
+
+ public const string GetCommand = "GET";
+
+ public const string PostCommand = "POST";
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public CommandInfo(string method, string resourcePath)
+ {
+ this.ResourcePath = resourcePath;
+ this.Method = method;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public string Method { get; set; }
+
+ public string ResourcePath { get; set; }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.StoreApps.Common/CommandResponse.cs b/src/Winium.StoreApps.Common/CommandResponse.cs
new file mode 100644
index 0000000..a2b6e7d
--- /dev/null
+++ b/src/Winium.StoreApps.Common/CommandResponse.cs
@@ -0,0 +1,33 @@
+namespace Winium.StoreApps.Common
+{
+ #region
+
+ using System.Net;
+
+ #endregion
+
+ public class CommandResponse
+ {
+ #region Public Properties
+
+ public string Content { get; set; }
+
+ public HttpStatusCode HttpStatusCode { get; set; }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public static CommandResponse Create(HttpStatusCode code, string content)
+ {
+ return new CommandResponse { HttpStatusCode = code, Content = content };
+ }
+
+ public override string ToString()
+ {
+ return string.Format("{0}: {1}", this.HttpStatusCode, this.Content);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.StoreApps.Common/DriverCommand.cs b/src/Winium.StoreApps.Common/DriverCommand.cs
new file mode 100644
index 0000000..40391f6
--- /dev/null
+++ b/src/Winium.StoreApps.Common/DriverCommand.cs
@@ -0,0 +1,470 @@
+// Copied from OpenQA
+namespace Winium.StoreApps.Common
+{
+ ///
+ /// Values describing the list of commands understood by a remote server using the JSON wire protocol.
+ ///
+ ///
+ public static class DriverCommand
+ {
+ #region Static Fields
+
+ ///
+ /// Represents the AcceptAlert command
+ ///
+ ///
+ public static readonly string AcceptAlert = "acceptAlert";
+
+ ///
+ /// Represents adding a cookie command
+ ///
+ ///
+ public static readonly string AddCookie = "addCookie";
+
+ ///
+ /// Represents ClearElement command
+ ///
+ ///
+ public static readonly string ClearElement = "clearElement";
+
+ ///
+ /// Represents ClickElement command
+ ///
+ ///
+ public static readonly string ClickElement = "clickElement";
+
+ ///
+ /// Represents a Browser close command
+ ///
+ ///
+ public static readonly string Close = "close";
+
+ ///
+ /// Represents the Define Driver Mapping command
+ ///
+ ///
+ public static readonly string DefineDriverMapping = "defineDriverMapping";
+
+ ///
+ /// Represents Deleting all cookies command
+ ///
+ ///
+ public static readonly string DeleteAllCookies = "deleteAllCookies";
+
+ ///
+ /// Represents deleting a cookie command
+ ///
+ ///
+ public static readonly string DeleteCookie = "deleteCookie";
+
+ ///
+ /// Describes an element
+ ///
+ ///
+ public static readonly string DescribeElement = "describeElement";
+
+ ///
+ /// Represents the DismissAlert command
+ ///
+ ///
+ public static readonly string DismissAlert = "dismissAlert";
+
+ ///
+ /// Represents ElementEquals command
+ ///
+ ///
+ public static readonly string ElementEquals = "elementEquals";
+
+ ///
+ /// Represents ExecuteAsyncScript command
+ ///
+ ///
+ public static readonly string ExecuteAsyncScript = "executeAsyncScript";
+
+ ///
+ /// Represents ExecuteScript command
+ ///
+ ///
+ public static readonly string ExecuteScript = "executeScript";
+
+ ///
+ /// Represents FindChildElement command
+ ///
+ ///
+ public static readonly string FindChildElement = "findChildElement";
+
+ ///
+ /// Represents FindChildElements command
+ ///
+ ///
+ public static readonly string FindChildElements = "findChildElements";
+
+ ///
+ /// Represents FindElement command
+ ///
+ ///
+ public static readonly string FindElement = "findElement";
+
+ ///
+ /// Represents FindElements command
+ ///
+ ///
+ public static readonly string FindElements = "findElements";
+
+ ///
+ /// Represents a GET command
+ ///
+ ///
+ public static readonly string Get = "get";
+
+ ///
+ /// Represents GetActiveElement command
+ ///
+ ///
+ public static readonly string GetActiveElement = "getActiveElement";
+
+ ///
+ /// Represents the GetAlertText command
+ ///
+ ///
+ public static readonly string GetAlertText = "getAlertText";
+
+ ///
+ /// Represents getting all cookies command
+ ///
+ ///
+ public static readonly string GetAllCookies = "getCookies";
+
+ ///
+ /// Represents GetCurrentUrl command
+ ///
+ ///
+ public static readonly string GetCurrentUrl = "getCurrentUrl";
+
+ ///
+ /// Represents GetCurrentWindowHandle command
+ ///
+ ///
+ public static readonly string GetCurrentWindowHandle = "getCurrentWindowHandle";
+
+ ///
+ /// Represents GetElementAttribute command
+ ///
+ ///
+ public static readonly string GetElementAttribute = "getElementAttribute";
+
+ ///
+ /// Represents GetElementLocation command
+ ///
+ ///
+ public static readonly string GetElementLocation = "getElementLocation";
+
+ ///
+ /// Represents GetElementLocationOnceScrolledIntoView command
+ ///
+ ///
+ public static readonly string GetElementLocationOnceScrolledIntoView = "getElementLocationOnceScrolledIntoView";
+
+ ///
+ /// Represents GetElementSize command
+ ///
+ ///
+ public static readonly string GetElementSize = "getElementSize";
+
+ ///
+ /// Represents GetElementTagName command
+ ///
+ ///
+ public static readonly string GetElementTagName = "getElementTagName";
+
+ ///
+ /// Represents GetElementText command
+ ///
+ ///
+ public static readonly string GetElementText = "getElementText";
+
+ ///
+ /// Represents GetElementValueOfCSSProperty command
+ ///
+ ///
+ public static readonly string GetElementValueOfCssProperty = "getElementValueOfCssProperty";
+
+ ///
+ /// Represents GetOrientation command
+ ///
+ ///
+ public static readonly string GetOrientation = "getOrientation";
+
+ ///
+ /// Represents GetPageSource command
+ ///
+ ///
+ public static readonly string GetPageSource = "getPageSource";
+
+ ///
+ /// Represents the Get Session Capabilities command
+ ///
+ ///
+ public static readonly string GetSessionCapabilities = "getSessionCapabilities";
+
+ ///
+ /// Represents the Get Session List command
+ ///
+ ///
+ public static readonly string GetSessionList = "getSessionList";
+
+ ///
+ /// Represents GetTitle command
+ ///
+ ///
+ public static readonly string GetTitle = "getTitle";
+
+ ///
+ /// Represents GetWindowHandles command
+ ///
+ ///
+ public static readonly string GetWindowHandles = "getWindowHandles";
+
+ ///
+ /// Represents GetWindowPosition command
+ ///
+ ///
+ public static readonly string GetWindowPosition = "getWindowPosition";
+
+ ///
+ /// Represents GetWindowSize command
+ ///
+ ///
+ public static readonly string GetWindowSize = "getWindowSize";
+
+ ///
+ /// Represents a Browser going back command
+ ///
+ ///
+ public static readonly string GoBack = "goBack";
+
+ ///
+ /// Represents a Browser going forward command
+ ///
+ ///
+ public static readonly string GoForward = "goForward";
+
+ ///
+ /// Represents the ImplicitlyWait command
+ ///
+ ///
+ public static readonly string ImplicitlyWait = "implicitlyWait";
+
+ ///
+ /// Represents IsElementDisplayed command
+ ///
+ ///
+ public static readonly string IsElementDisplayed = "isElementDisplayed";
+
+ ///
+ /// Represents IsElementEnabled command
+ ///
+ ///
+ public static readonly string IsElementEnabled = "isElementEnabled";
+
+ ///
+ /// Represents IsElementSelected command
+ ///
+ ///
+ public static readonly string IsElementSelected = "isElementSelected";
+
+ ///
+ /// Represents MaximizeWindow command
+ ///
+ ///
+ public static readonly string MaximizeWindow = "maximizeWindow";
+
+ ///
+ /// Represents the MouseClick command.
+ ///
+ ///
+ public static readonly string MouseClick = "mouseClick";
+
+ ///
+ /// Represents the MouseDoubleClick command.
+ ///
+ ///
+ public static readonly string MouseDoubleClick = "mouseDoubleClick";
+
+ ///
+ /// Represents the MouseDown command.
+ ///
+ ///
+ public static readonly string MouseDown = "mouseDown";
+
+ ///
+ /// Represents the MouseMoveTo command.
+ ///
+ ///
+ public static readonly string MouseMoveTo = "mouseMoveTo";
+
+ ///
+ /// Represents the MouseUp command.
+ ///
+ ///
+ public static readonly string MouseUp = "mouseUp";
+
+ ///
+ /// Represents a New Session command
+ ///
+ ///
+ public static readonly string NewSession = "newSession";
+
+ ///
+ /// Represents a browser quit command
+ ///
+ ///
+ public static readonly string Quit = "quit";
+
+ ///
+ /// Represents a Browser refreshing command
+ ///
+ ///
+ public static readonly string Refresh = "refresh";
+
+ ///
+ /// Represents Screenshot command
+ ///
+ ///
+ public static readonly string Screenshot = "screenshot";
+
+ ///
+ /// Represents the SendKeysToActiveElement command.
+ ///
+ ///
+ public static readonly string SendKeysToActiveElement = "sendKeysToActiveElement";
+
+ ///
+ /// Represents SendKeysToElements command
+ ///
+ ///
+ public static readonly string SendKeysToElement = "sendKeysToElement";
+
+ ///
+ /// Represents the SetAlertValue command
+ ///
+ ///
+ public static readonly string SetAlertValue = "setAlertValue";
+
+ ///
+ /// Represents the SetAsyncScriptTimeout command
+ ///
+ ///
+ public static readonly string SetAsyncScriptTimeout = "setScriptTimeout";
+
+ ///
+ /// Represents SetOrientation command
+ ///
+ ///
+ public static readonly string SetOrientation = "setOrientation";
+
+ ///
+ /// Represents the SetTimeout command
+ ///
+ ///
+ public static readonly string SetTimeout = "setTimeout";
+
+ ///
+ /// Represents SetWindowPosition command
+ ///
+ ///
+ public static readonly string SetWindowPosition = "setWindowPosition";
+
+ ///
+ /// Represents SetWindowSize command
+ ///
+ ///
+ public static readonly string SetWindowSize = "setWindowSize";
+
+ ///
+ /// Represents the Status command.
+ ///
+ ///
+ public static readonly string Status = "status";
+
+ ///
+ /// Represents SubmitElement command
+ ///
+ ///
+ public static readonly string SubmitElement = "submitElement";
+
+ ///
+ /// Represents SwitchToFrame command
+ ///
+ ///
+ public static readonly string SwitchToFrame = "switchToFrame";
+
+ ///
+ /// Represents SwitchToParentFrame command
+ ///
+ ///
+ public static readonly string SwitchToParentFrame = "switchToParentFrame";
+
+ ///
+ /// Represents SwitchToWindow command
+ ///
+ ///
+ public static readonly string SwitchToWindow = "switchToWindow";
+
+ ///
+ /// Represents the TouchDoubleTap command.
+ ///
+ ///
+ public static readonly string TouchDoubleTap = "touchDoubleTap";
+
+ ///
+ /// Represents the TouchFlick command.
+ ///
+ ///
+ public static readonly string TouchFlick = "touchFlick";
+
+ ///
+ /// Represents the TouchLongPress command.
+ ///
+ ///
+ public static readonly string TouchLongPress = "touchLongPress";
+
+ ///
+ /// Represents the TouchMove command.
+ ///
+ ///
+ public static readonly string TouchMove = "touchMove";
+
+ ///
+ /// Represents the TouchPress command.
+ ///
+ ///
+ public static readonly string TouchPress = "touchDown";
+
+ ///
+ /// Represents the TouchRelease command.
+ ///
+ ///
+ public static readonly string TouchRelease = "touchUp";
+
+ ///
+ /// Represents the TouchScroll command.
+ ///
+ ///
+ public static readonly string TouchScroll = "touchScroll";
+
+ ///
+ /// Represents the TouchSingleTap command.
+ ///
+ ///
+ public static readonly string TouchSingleTap = "touchSingleTap";
+
+ ///
+ /// Represents the UploadFile command.
+ ///
+ ///
+ public static readonly string UploadFile = "uploadFile";
+
+ #endregion
+ }
+}
diff --git a/src/Winium.StoreApps.Common/Exceptions/AutomationException.cs b/src/Winium.StoreApps.Common/Exceptions/AutomationException.cs
new file mode 100644
index 0000000..58909c2
--- /dev/null
+++ b/src/Winium.StoreApps.Common/Exceptions/AutomationException.cs
@@ -0,0 +1,58 @@
+namespace Winium.StoreApps.Common.Exceptions
+{
+ #region
+
+ using System;
+
+ #endregion
+
+ public class AutomationException : Exception
+ {
+ #region Fields
+
+ private ResponseStatus responseStatus = ResponseStatus.UnknownError;
+
+ #endregion
+
+ #region Constructors and Destructors
+
+ public AutomationException()
+ {
+ }
+
+ public AutomationException(string message, ResponseStatus status)
+ : base(message)
+ {
+ this.Status = status;
+ }
+
+ public AutomationException(string message, params object[] args)
+ : base(string.Format(message, args))
+ {
+ }
+
+ public AutomationException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public ResponseStatus Status
+ {
+ get
+ {
+ return this.responseStatus;
+ }
+
+ set
+ {
+ this.responseStatus = value;
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.StoreApps.Common/Exceptions/InnerDriverRequestException.cs b/src/Winium.StoreApps.Common/Exceptions/InnerDriverRequestException.cs
new file mode 100644
index 0000000..dca7762
--- /dev/null
+++ b/src/Winium.StoreApps.Common/Exceptions/InnerDriverRequestException.cs
@@ -0,0 +1,42 @@
+namespace Winium.StoreApps.Common.Exceptions
+{
+ #region
+
+ using System;
+ using System.Net;
+
+ #endregion
+
+ public class InnerDriverRequestException : Exception
+ {
+ #region Constructors and Destructors
+
+ public InnerDriverRequestException()
+ {
+ }
+
+ public InnerDriverRequestException(string message, HttpStatusCode statusCode)
+ : base(message)
+ {
+ this.StatusCode = statusCode;
+ }
+
+ public InnerDriverRequestException(string message, params object[] args)
+ : base(string.Format(message, args))
+ {
+ }
+
+ public InnerDriverRequestException(string message, Exception innerException)
+ : base(message, innerException)
+ {
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ public HttpStatusCode StatusCode { get; set; }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.StoreApps.Common/HttpResponseHelper.cs b/src/Winium.StoreApps.Common/HttpResponseHelper.cs
new file mode 100644
index 0000000..4496168
--- /dev/null
+++ b/src/Winium.StoreApps.Common/HttpResponseHelper.cs
@@ -0,0 +1,73 @@
+namespace Winium.StoreApps.Common
+{
+ #region
+
+ using System.Collections.Generic;
+ using System.Net;
+ using System.Text;
+
+ #endregion
+
+ public static class HttpResponseHelper
+ {
+ #region Constants
+
+ private const string JsonContentType = "application/json;charset=UTF-8";
+
+ private const string PlainTextContentType = "text/plain";
+
+ #endregion
+
+ #region Static Fields
+
+ private static Dictionary statusCodeDescriptors;
+
+ #endregion
+
+ #region Public Properties
+
+ public static Dictionary StatusCodeDescriptors
+ {
+ get
+ {
+ return statusCodeDescriptors
+ ?? (statusCodeDescriptors =
+ new Dictionary
+ {
+ { HttpStatusCode.OK, "OK" },
+ { HttpStatusCode.BadRequest, "Bad Request" },
+ { HttpStatusCode.NotFound, "Not Found" },
+ { HttpStatusCode.NotImplemented, "Not Implemented" }
+ });
+ }
+ }
+
+ #endregion
+
+ #region Public Methods and Operators
+
+ public static bool IsClientError(int code)
+ {
+ return code >= 400 && code < 500;
+ }
+
+ public static string ResponseString(HttpStatusCode statusCode, string content)
+ {
+ var contentType = IsClientError((int)statusCode) ? PlainTextContentType : JsonContentType;
+
+ string statusDescription;
+ StatusCodeDescriptors.TryGetValue(statusCode, out statusDescription);
+
+ var responseString = new StringBuilder();
+ responseString.AppendLine(string.Format("HTTP/1.1 {0} {1}", (int)statusCode, statusDescription));
+ responseString.AppendLine(string.Format("Content-Type: {0}", contentType));
+ responseString.AppendLine("Connection: close");
+ responseString.AppendLine(string.Empty);
+ responseString.AppendLine(content);
+
+ return responseString.ToString();
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.StoreApps.Common/JsonWireClasses.cs b/src/Winium.StoreApps.Common/JsonWireClasses.cs
new file mode 100644
index 0000000..49e3ab0
--- /dev/null
+++ b/src/Winium.StoreApps.Common/JsonWireClasses.cs
@@ -0,0 +1,55 @@
+//
+namespace Winium.StoreApps.Common
+{
+ #region
+
+ using Newtonsoft.Json;
+
+ #endregion
+
+ public class JsonWebElementContent
+ {
+ #region Constructors and Destructors
+
+ public JsonWebElementContent(string element)
+ {
+ this.Element = element;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ [JsonProperty("ELEMENT")]
+ public string Element { get; set; }
+
+ #endregion
+ }
+
+ public class JsonResponse
+ {
+ #region Constructors and Destructors
+
+ public JsonResponse(string sessionId, ResponseStatus responseCode, object value)
+ {
+ this.SessionId = sessionId;
+ this.Status = responseCode;
+ this.Value = value;
+ }
+
+ #endregion
+
+ #region Public Properties
+
+ [JsonProperty("sessionId")]
+ public string SessionId { get; set; }
+
+ [JsonProperty("status")]
+ public ResponseStatus Status { get; set; }
+
+ [JsonProperty("value")]
+ public object Value { get; set; }
+
+ #endregion
+ }
+}
diff --git a/src/Winium.StoreApps.Common/Properties/AssemblyInfo.cs b/src/Winium.StoreApps.Common/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..b075b41
--- /dev/null
+++ b/src/Winium.StoreApps.Common/Properties/AssemblyInfo.cs
@@ -0,0 +1,29 @@
+#region
+
+using System.Reflection;
+using System.Resources;
+
+#endregion
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Winium.StoreApps.Common")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Winium.StoreApps.Common")]
+[assembly: AssemblyCopyright("Copyright © 2015")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+[assembly: NeutralResourcesLanguage("en")]
+
+// Version information for an assembly consists of the following four values:
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.*")]
diff --git a/src/Winium.StoreApps.Common/ResponseStatus.cs b/src/Winium.StoreApps.Common/ResponseStatus.cs
new file mode 100644
index 0000000..ab8cb47
--- /dev/null
+++ b/src/Winium.StoreApps.Common/ResponseStatus.cs
@@ -0,0 +1,55 @@
+namespace Winium.StoreApps.Common
+{
+ public enum ResponseStatus
+ {
+ Success = 0,
+
+ NoSuchDriver = 6,
+
+ NoSuchElement = 7,
+
+ NoSuchFrame = 8,
+
+ UnknownCommand = 9,
+
+ StaleElementReference = 10,
+
+ ElementNotVisible = 11,
+
+ InvalidElementState = 12,
+
+ UnknownError = 13,
+
+ ElementIsNotSelectable = 15,
+
+ JavaScriptError = 17,
+
+ XPathLookupError = 19,
+
+ Timeout = 21,
+
+ NoSuchWindow = 23,
+
+ InvalidCookieDomain = 24,
+
+ UnableToSetCookie = 25,
+
+ UnexpectedAlertOpen = 26,
+
+ NoAlertOpenError = 27,
+
+ ScriptTimeout = 28,
+
+ InvalidElementCoordinates = 29,
+
+ ImeNotAvailable = 30,
+
+ ImeEngineActivationFailed = 31,
+
+ InvalidSelector = 32,
+
+ SessionNotCreatedException = 33,
+
+ MoveTargetOutOfBounds = 34
+ }
+}
diff --git a/src/Winium.StoreApps.Common/Winium.StoreApps.Common.csproj b/src/Winium.StoreApps.Common/Winium.StoreApps.Common.csproj
new file mode 100644
index 0000000..1144dd8
--- /dev/null
+++ b/src/Winium.StoreApps.Common/Winium.StoreApps.Common.csproj
@@ -0,0 +1,73 @@
+
+
+
+
+ 12.0
+ Debug
+ AnyCPU
+ {3C8D0B9C-576B-4778-97B1-6839AA944AEE}
+ Library
+ Properties
+ Winium.StoreApps.Common
+ Winium.StoreApps.Common
+ en-US
+ 512
+ {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ Profile151
+ v4.6
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\packages\Newtonsoft.Json.6.0.8\lib\portable-net40+sl5+wp80+win8+wpa81\Newtonsoft.Json.dll
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Winium.StoreApps.Common/packages.config b/src/Winium.StoreApps.Common/packages.config
new file mode 100644
index 0000000..fa6b7f1
--- /dev/null
+++ b/src/Winium.StoreApps.Common/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Winium.sln b/src/Winium.sln
new file mode 100644
index 0000000..acddaa3
--- /dev/null
+++ b/src/Winium.sln
@@ -0,0 +1,40 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.31101.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Winium.Desktop.Driver", "Winium.Desktop.Driver\Winium.Desktop.Driver.csproj", "{B214C2BA-43FA-486F-AD0B-D4890C1748C0}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{56A74D50-0B52-4130-BF77-C5BD2B3441A6}"
+ ProjectSection(SolutionItems) = preProject
+ Settings.StyleCop = Settings.StyleCop
+ Winium.sln.DotSettings = Winium.sln.DotSettings
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Winium.StoreApps.Common", "Winium.StoreApps.Common\Winium.StoreApps.Common.csproj", "{3C8D0B9C-576B-4778-97B1-6839AA944AEE}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{7F1DBF6F-6390-429F-BAD0-08D85DE19E0C}"
+ ProjectSection(SolutionItems) = preProject
+ .nuget\NuGet.Config = .nuget\NuGet.Config
+ .nuget\NuGet.targets = .nuget\NuGet.targets
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {B214C2BA-43FA-486F-AD0B-D4890C1748C0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B214C2BA-43FA-486F-AD0B-D4890C1748C0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B214C2BA-43FA-486F-AD0B-D4890C1748C0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B214C2BA-43FA-486F-AD0B-D4890C1748C0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {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}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3C8D0B9C-576B-4778-97B1-6839AA944AEE}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/Winium.sln.DotSettings b/src/Winium.sln.DotSettings
new file mode 100644
index 0000000..7308043
--- /dev/null
+++ b/src/Winium.sln.DotSettings
@@ -0,0 +1,444 @@
+
+ <?xml version="1.0" encoding="utf-16"?><Profile name="StyleCop"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>True</EmbraceInRegion><RegionName>using</RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><StyleCop.Documentation><SA1600ElementsMustBeDocumented>False</SA1600ElementsMustBeDocumented><SA1604ElementDocumentationMustHaveSummary>False</SA1604ElementDocumentationMustHaveSummary><SA1609PropertyDocumentationMustHaveValueDocumented>False</SA1609PropertyDocumentationMustHaveValueDocumented><SA1611ElementParametersMustBeDocumented>False</SA1611ElementParametersMustBeDocumented><SA1615ElementReturnValueMustBeDocumented>False</SA1615ElementReturnValueMustBeDocumented><SA1617VoidReturnValueMustNotBeDocumented>False</SA1617VoidReturnValueMustNotBeDocumented><SA1618GenericTypeParametersMustBeDocumented>False</SA1618GenericTypeParametersMustBeDocumented><SA1626SingleLineCommentsMustNotUseDocumentationStyleSlashes>False</SA1626SingleLineCommentsMustNotUseDocumentationStyleSlashes><SA1628DocumentationTextMustBeginWithACapitalLetter>False</SA1628DocumentationTextMustBeginWithACapitalLetter><SA1629DocumentationTextMustEndWithAPeriod>False</SA1629DocumentationTextMustEndWithAPeriod><SA1633SA1641UpdateFileHeader>Ignore</SA1633SA1641UpdateFileHeader><SA1639FileHeaderMustHaveSummary>False</SA1639FileHeaderMustHaveSummary><SA1642ConstructorSummaryDocumentationMustBeginWithStandardText>False</SA1642ConstructorSummaryDocumentationMustBeginWithStandardText><SA1643DestructorSummaryDocumentationMustBeginWithStandardText>False</SA1643DestructorSummaryDocumentationMustBeginWithStandardText><SA1644DocumentationHeadersMustNotContainBlankLines>False</SA1644DocumentationHeadersMustNotContainBlankLines></StyleCop.Documentation><StyleCop.Layout><SA1500CurlyBracketsForMultiLineStatementsMustNotShareLine>True</SA1500CurlyBracketsForMultiLineStatementsMustNotShareLine><SA1509OpeningCurlyBracketsMustNotBePrecededByBlankLine>True</SA1509OpeningCurlyBracketsMustNotBePrecededByBlankLine><SA1510ChainedStatementBlocksMustNotBePrecededByBlankLine>True</SA1510ChainedStatementBlocksMustNotBePrecededByBlankLine><SA1511WhileDoFooterMustNotBePrecededByBlankLine>True</SA1511WhileDoFooterMustNotBePrecededByBlankLine><SA1512SingleLineCommentsMustNotBeFollowedByBlankLine>True</SA1512SingleLineCommentsMustNotBeFollowedByBlankLine><SA1513ClosingCurlyBracketMustBeFollowedByBlankLine>True</SA1513ClosingCurlyBracketMustBeFollowedByBlankLine><SA1514ElementDocumentationHeaderMustBePrecededByBlankLine>True</SA1514ElementDocumentationHeaderMustBePrecededByBlankLine><SA1515SingleLineCommentMustBeProceededByBlankLine>True</SA1515SingleLineCommentMustBeProceededByBlankLine></StyleCop.Layout><StyleCop.Maintainability><SA1119StatementMustNotUseUnnecessaryParenthesis>True</SA1119StatementMustNotUseUnnecessaryParenthesis></StyleCop.Maintainability><StyleCop.Ordering><AlphabeticalUsingDirectives>Alphabetical</AlphabeticalUsingDirectives><ExpandUsingDirectives>FullyQualify</ExpandUsingDirectives><SA1212PropertyAccessorsMustFollowOrder>True</SA1212PropertyAccessorsMustFollowOrder><SA1213EventAccessorsMustFollowOrder>True</SA1213EventAccessorsMustFollowOrder></StyleCop.Ordering><StyleCop.Readability><SA1100DoNotPrefixCallsWithBaseUnlessLocalImplementationExists>True</SA1100DoNotPrefixCallsWithBaseUnlessLocalImplementationExists><SA1106CodeMustNotContainEmptyStatements>True</SA1106CodeMustNotContainEmptyStatements><SA1108BlockStatementsMustNotContainEmbeddedComments>True</SA1108BlockStatementsMustNotContainEmbeddedComments><SA1109BlockStatementsMustNotContainEmbeddedRegions>True</SA1109BlockStatementsMustNotContainEmbeddedRegions><SA1120CommentsMustContainText>True</SA1120CommentsMustContainText><SA1121UseBuiltInTypeAlias>True</SA1121UseBuiltInTypeAlias><SA1122UseStringEmptyForEmptyStrings>True</SA1122UseStringEmptyForEmptyStrings><SA1123DoNotPlaceRegionsWithinElements>True</SA1123DoNotPlaceRegionsWithinElements><SA1124CodeMustNotContainEmptyRegions>True</SA1124CodeMustNotContainEmptyRegions></StyleCop.Readability><StyleCop.Spacing><SA1001CommasMustBeSpacedCorrectly>True</SA1001CommasMustBeSpacedCorrectly><SA1005SingleLineCommentsMustBeginWithSingleSpace>True</SA1005SingleLineCommentsMustBeginWithSingleSpace><SA1006PreprocessorKeywordsMustNotBePrecededBySpace>True</SA1006PreprocessorKeywordsMustNotBePrecededBySpace><SA1021NegativeSignsMustBeSpacedCorrectly>True</SA1021NegativeSignsMustBeSpacedCorrectly><SA1022PositiveSignsMustBeSpacedCorrectly>True</SA1022PositiveSignsMustBeSpacedCorrectly><SA1025CodeMustNotContainMultipleWhitespaceInARow>True</SA1025CodeMustNotContainMultipleWhitespaceInARow></StyleCop.Spacing></Profile>
+ StyleCop
+ StyleCop
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ True
+ NEXT_LINE_SHIFTED_2
+ 1
+ 1
+ 1
+ 1
+ 1
+ NEXT_LINE_SHIFTED_2
+ SEPARATE
+ ALWAYS_ADD
+ ALWAYS_ADD
+ ALWAYS_ADD
+ ALWAYS_ADD
+ ALWAYS_ADD
+ NEXT_LINE_SHIFTED_2
+ 1
+ 1
+ False
+ True
+ public protected internal private static new abstract virtual override sealed readonly extern unsafe volatile async
+ False
+ False
+ False
+ False
+ True
+ ALWAYS_USE
+ ON_SINGLE_LINE
+ False
+ True
+ False
+ False
+ True
+ False
+ True
+ True
+ CHOP_IF_LONG
+ True
+ True
+ CHOP_IF_LONG
+ CHOP_IF_LONG
+ <?xml version="1.0" encoding="utf-8"?>
+<!-- Last updated 15.05.2012 -->
+<Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns">
+
+ <!-- Do not reorder COM interfaces -->
+ <Pattern>
+ <Match>
+ <And Weight="2000">
+ <Kind Is="interface"/>
+ <Or>
+ <HasAttribute CLRName="System.Runtime.InteropServices.InterfaceTypeAttribute"/>
+ <HasAttribute CLRName="System.Runtime.InteropServices.ComImport"/>
+ </Or>
+ </And>
+ </Match>
+ </Pattern>
+
+ <!-- Do not reorder P/Invoke structs -->
+ <Pattern>
+ <Match>
+ <And Weight="2000">
+ <Or>
+ <Kind Is="struct"/>
+ <Kind Is="class"/>
+ </Or>
+ <HasAttribute CLRName="System.Runtime.InteropServices.StructLayoutAttribute"/>
+ </And>
+ </Match>
+ </Pattern>
+
+ <!-- Do not reorder P/Invoke classes (called xxxNativeMethods) -->
+ <Pattern>
+ <Match>
+ <And Weight="2000">
+ <Kind Is="class"/>
+ <Name Is=".*NativeMethods" />
+ </And>
+ </Match>
+ </Pattern>
+
+ <!-- StyleCop pattern -->
+ <Pattern RemoveAllRegions="true">
+ <Match>
+ <Or Weight="1000" >
+ <Kind Is="class" />
+ <Kind Is="struct" />
+ <Kind Is="interface"/>
+ </Or>
+ </Match>
+
+ <!-- Constants -->
+ <Entry>
+ <Match>
+ <Kind Is="constant"/>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private"/>
+ <Name/>
+ </Sort>
+ <Group Region="Constants"/>
+ </Entry>
+
+ <!-- Static fields -->
+ <Entry>
+ <Match>
+ <And>
+ <Kind Is="field"/>
+ <Static />
+ </And>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private"/>
+ <Readonly/>
+ <Name/>
+ </Sort>
+ <Group Region="Static Fields"/>
+ </Entry>
+
+ <!-- Fields -->
+ <Entry>
+ <Match>
+ <Kind Is="field"/>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private"/>
+ <Readonly/>
+ <Name/>
+ </Sort>
+ <Group Region="Fields"/>
+ </Entry>
+
+ <!-- constructors and destructors -->
+ <Entry>
+ <Match>
+ <Or Weight="200">
+ <Kind Is="constructor"/>
+ <Kind Is="destructor"/>
+ </Or>
+ </Match>
+ <Sort>
+ <Static/>
+ <Kind Order="constructor destructor"/>
+ <Access Order="public internal protected-internal protected private"/>
+ </Sort>
+ <Group Region="Constructors and Destructors"/>
+ </Entry>
+
+ <!-- delegates -->
+ <Entry>
+ <Match>
+ <Kind Is="delegate"/>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private" />
+ <Static />
+ <Name/>
+ </Sort>
+ <Group Region="Delegates"/>
+ </Entry>
+
+ <!-- public events -->
+ <Entry>
+ <Match>
+ <And>
+ <Kind Is="event"/>
+ <Access Is="public"/>
+ </And>
+ </Match>
+ <Sort>
+ <Access Order="public" />
+ <Static />
+ <Name/>
+ </Sort>
+ <Group Region="Public Events"/>
+ </Entry>
+
+ <!-- interface events -->
+ <Entry>
+ <Match>
+ <And>
+ <Kind Is="event"/>
+ <ImplementsInterface/>
+ </And>
+ </Match>
+ <Sort>
+ <ImplementsInterface Immediate="true"/>
+ <Name/>
+ </Sort>
+ <Group Region="Explicit Interface Events" />
+ </Entry>
+
+ <!-- other events -->
+ <Entry>
+ <Match>
+ <Kind Is="event"/>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private" />
+ <Static />
+ <Name/>
+ </Sort>
+ <Group Region="Events"/>
+ </Entry>
+
+ <!-- enum -->
+ <Entry>
+ <Match>
+ <Kind Is="enum"/>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private" />
+ <Name/>
+ </Sort>
+ <Group Region="Enums"/>
+ </Entry>
+
+ <!-- interfaces -->
+ <Entry>
+ <Match>
+ <Kind Is="interface" />
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private" />
+ <Name/>
+ </Sort>
+ <Group Region="Interfaces"/>
+ </Entry>
+
+ <!-- public properties -->
+ <Entry>
+ <Match>
+ <And>
+ <Kind Is="property"/>
+ <Access Is="public"/>
+ </And>
+ </Match>
+ <Sort>
+ <Access Order="public"/>
+ <Static/>
+ <Name/>
+ </Sort>
+ <Group Region="Public Properties"/>
+ </Entry>
+
+ <!-- interface properties -->
+ <Entry>
+ <Match>
+ <And>
+ <Kind Is="property"/>
+ <ImplementsInterface/>
+ </And>
+ </Match>
+ <Sort>
+ <ImplementsInterface Immediate="true"/>
+ <Name/>
+ </Sort>
+ <Group Region="Explicit Interface Properties" />
+ </Entry>
+
+ <!-- other properties -->
+ <Entry>
+ <Match>
+ <Kind Is="property"/>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private"/>
+ <Static/>
+ <Name/>
+ </Sort>
+ <Group Region="Properties"/>
+ </Entry>
+
+ <!-- public indexers -->
+ <Entry>
+ <Match>
+ <And>
+ <Kind Is="indexer" Weight="1000" />
+ <Access Is="public"/>
+ </And>
+ </Match>
+ <Sort>
+ <Access Order="public" />
+ <Static/>
+ <Name/>
+ </Sort>
+ <Group Region="Public Indexers"/>
+ </Entry>
+
+ <!-- interface indexers -->
+ <Entry>
+ <Match>
+ <And>
+ <Kind Is="indexer" Weight="1000"/>
+ <ImplementsInterface/>
+ </And>
+ </Match>
+ <Sort>
+ <ImplementsInterface Immediate="true"/>
+ <Name/>
+ </Sort>
+ <Group Region="Explicit Interface Indexers" />
+ </Entry>
+
+ <!-- other indexers -->
+ <Entry>
+ <Match>
+ <Kind Is="indexer" Weight="1000" />
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private" />
+ <Static/>
+ <Name/>
+ </Sort>
+ <Group Region="Indexers"/>
+ </Entry>
+
+ <!-- public methods (includes operators) -->
+ <Entry>
+ <Match>
+ <And>
+ <Or>
+ <Kind Is="method"/>
+ <Kind Is="operator"/>
+ </Or>
+ <Access Is="public"/>
+ </And>
+ </Match>
+ <Sort>
+ <Access Order="public"/>
+ <Static/>
+ <Name/>
+ </Sort>
+ <Group Region="Public Methods and Operators"/>
+ </Entry>
+
+ <!-- interface methods -->
+ <Entry>
+ <Match>
+ <And>
+ <Kind Is="method"/>
+ <ImplementsInterface/>
+ </And>
+ </Match>
+ <Sort>
+ <ImplementsInterface Immediate="true"/>
+ <Name/>
+ </Sort>
+ <Group Region="Explicit Interface Methods" />
+ </Entry>
+
+ <!-- other methods -->
+ <Entry>
+ <Match>
+ <Kind Is="method"/>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private"/>
+ <Static/>
+ <Name/>
+ </Sort>
+ <Group Region="Methods"/>
+ </Entry>
+
+ <!-- operators -->
+ <Entry>
+ <Match>
+ <Kind Is="operator"/>
+ </Match>
+ <Sort>
+ <Access Order="public internal protected-internal protected private" />
+ <Static/>
+ <Name/>
+ </Sort>
+ <Group Region="Operators"/>
+ </Entry>
+
+ <!-- Nested structs -->
+ <Entry>
+ <Match>
+ <Kind Is="struct"
+ Weight="600" />
+ </Match>
+ <Sort>
+ <Static />
+ <Access Order="public internal protected-internal protected private" />
+ <Name/>
+ </Sort>
+ </Entry>
+
+ <!-- Nested classes -->
+ <Entry>
+ <Match>
+ <Kind Is="class"
+ Weight="700" />
+ </Match>
+ <Sort>
+ <Static />
+ <Access Order="public internal protected-internal protected private" />
+ <Name/>
+ </Sort>
+ </Entry>
+
+ <!-- all other members -->
+ <Entry/>
+
+ </Pattern>
+</Patterns>
+ CustomLayout
+ True
+ True
+ $object$_On$event$
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="T" Suffix="" Style="AaBb" />
+ <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" />
+ True
+ True
+ CSharpOtherPage
+ OverriddenFalse
+ OverriddenFalse
\ No newline at end of file