PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/IronPython_Main/Hosts/Silverlight/Microsoft.Scripting.SilverLight/DynamicScriptTags.cs

#
C# | 399 lines | 249 code | 50 blank | 100 comment | 96 complexity | 8b589024127f6ddb5ba97b987fcea712 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception, CPL-1.0, CC-BY-SA-3.0, BSD-3-Clause, ISC, AGPL-3.0, LGPL-2.1, Apache-2.0
  1. /* ****************************************************************************
  2. *
  3. * Copyright (c) Microsoft Corporation.
  4. *
  5. * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  6. * copy of the license can be found in the License.html file at the root of this distribution. If
  7. * you cannot locate the Apache License, Version 2.0, please send an email to
  8. * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  9. * by the terms of the Apache License, Version 2.0.
  10. *
  11. * You must not remove this notice, or any other, from this software.
  12. *
  13. *
  14. * ***************************************************************************/
  15. using System;
  16. using System.Collections.Generic;
  17. using System.Text;
  18. using System.Windows.Browser;
  19. using System.IO;
  20. using Microsoft.Scripting.Hosting;
  21. using Microsoft.Scripting.Utils;
  22. namespace Microsoft.Scripting.Silverlight {
  23. /// <summary>
  24. /// Manages script tags that hold DLR-based code.
  25. /// </summary>
  26. public class DynamicScriptTags {
  27. /// <summary>
  28. /// Inline strings and external URIs for script-tag registered
  29. /// to this control.
  30. /// </summary>
  31. public class ScriptCode {
  32. public string Language { get; private set; }
  33. public bool Defer { get; private set; }
  34. public string Inline { get; set; }
  35. public Uri External { get; set; }
  36. public ScriptCode(string lang, bool defer) {
  37. Language = lang;
  38. Defer = defer;
  39. }
  40. }
  41. /// <summary>
  42. /// Script code registered for this Silverlight control
  43. /// </summary>
  44. public List<ScriptCode> Code { get; private set; }
  45. public List<Uri> ZipPackages { get; private set; }
  46. /// <summary>
  47. /// Holds onto the language config
  48. /// </summary>
  49. private DynamicLanguageConfig _LangConfig;
  50. /// <summary>
  51. /// true while the script-tag is running, false otherwise
  52. /// </summary>
  53. internal bool RunningScriptTags;
  54. public DynamicScriptTags(DynamicLanguageConfig langConfig) {
  55. RunningScriptTags = false;
  56. _LangConfig = langConfig;
  57. Code = new List<ScriptCode>();
  58. ZipPackages = new List<Uri>();
  59. }
  60. public void DownloadHtmlPage(Action onComplete) {
  61. if (!HtmlPage.IsEnabled) {
  62. onComplete();
  63. } else {
  64. DownloadAndCache(new List<Uri>() { DynamicApplication.HtmlPageUri }, onComplete);
  65. }
  66. }
  67. /// <summary>
  68. /// Scrapes the HTML page and populates the "Code" structure.
  69. /// </summary>
  70. public void FetchScriptTags() {
  71. if (!HtmlPage.IsEnabled)
  72. return;
  73. var scriptTags = HtmlPage.Document.GetElementsByTagName("script");
  74. foreach (ScriptObject scriptTag in scriptTags) {
  75. var e = (HtmlElement)scriptTag;
  76. var type = (string)e.GetAttribute("type");
  77. var src = (string)e.GetAttribute("src");
  78. string language = null;
  79. // Find the language by either mime-type or script's file extension
  80. if (type != null)
  81. language = GetLanguageByType(type);
  82. else if (src != null)
  83. language = GetLanguageByExtension(Path.GetExtension(src));
  84. // Only move on if the language was found
  85. if (language != null) {
  86. var initParams = DynamicApplication.Current.InitParams;
  87. // Process this script-tag if ...
  88. if (
  89. // it's class is "*" ... OR
  90. (e.CssClass == "*") ||
  91. // the xamlid initparam is set and matches this tag's class ... OR
  92. (initParams.ContainsKey("xamlid") && initParams["xamlid"] != null &&
  93. e.CssClass != null && e.CssClass == initParams["xamlid"]) ||
  94. // the xamlid initparam is not set and this tag does not have a class
  95. (!initParams.ContainsKey("xamlid") && (e.CssClass == null || e.CssClass.Length == 0))
  96. ) {
  97. bool defer = (bool)e.GetProperty("defer");
  98. _LangConfig.LanguagesUsed[language] = true;
  99. var sc = new ScriptCode(language, defer);
  100. if (src != null) {
  101. sc.External = DynamicApplication.MakeUri(src);
  102. } else {
  103. var innerHtml = (string)e.GetProperty("innerHTML");
  104. if (innerHtml != null) {
  105. // IE BUG: inline script-tags have an extra newline at the front,
  106. // so remove it ...
  107. if (HtmlPage.BrowserInformation.Name == "Microsoft Internet Explorer" && innerHtml.IndexOf("\r\n") == 0) {
  108. innerHtml = innerHtml.Substring(2);
  109. }
  110. sc.Inline = innerHtml;
  111. }
  112. }
  113. Code.Add(sc);
  114. }
  115. // Lastly, check to see if this is a zip file
  116. } else if (src != null && ((type != null && type == "application/x-zip-compressed") || Path.GetExtension(src) == ".zip")) {
  117. ZipPackages.Add(DynamicApplication.MakeUri(src));
  118. }
  119. }
  120. }
  121. /// <summary>
  122. /// Gathers the set of external script URIs, and asks the
  123. /// HttpVirtualFilesystem to download and cache the URIs. Calls the
  124. /// delegate when all downloads are complete.
  125. /// </summary>
  126. public void DownloadExternalCode(Action onComplete) {
  127. var externalUris = new List<Uri>();
  128. foreach (var sc in Code)
  129. if (sc.External != null)
  130. externalUris.Add(sc.External);
  131. foreach (var zip in ZipPackages)
  132. externalUris.Add(zip);
  133. DownloadAndCache(externalUris, onComplete);
  134. }
  135. private void DownloadAndCache(List<Uri> uris, Action onComplete) {
  136. if (!HtmlPage.IsEnabled) {
  137. onComplete();
  138. } else {
  139. ((HttpVirtualFilesystem)HttpPAL.PAL.VirtualFilesystem).
  140. DownloadAndCache(uris, onComplete);
  141. }
  142. }
  143. /// <summary>
  144. /// Are there any registered script tags?
  145. /// </summary>
  146. public bool HasScriptTags {
  147. get {
  148. foreach (var i in Code)
  149. if (i.External != null && i.Inline != null)
  150. return true;
  151. return false;
  152. }
  153. }
  154. /// <summary>
  155. /// Runs the registered script tags against the DynamicEngine.
  156. /// </summary>
  157. public void Run(DynamicEngine engine) {
  158. var inlineScope = engine.CreateScope();
  159. foreach (var sc in Code) {
  160. engine.Engine = _LangConfig.GetEngine(sc.Language);
  161. if (engine.Engine != null && !sc.Defer) {
  162. ScriptSource sourceCode = null;
  163. ScriptScope scope = null;
  164. if(sc.External != null) {
  165. var code = BrowserPAL.PAL.VirtualFilesystem.GetFileContents(sc.External);
  166. if (code == null) continue;
  167. sourceCode =
  168. engine.Engine.CreateScriptSourceFromString(
  169. code,
  170. DynamicApplication.BaseUri.MakeRelativeUri(sc.External).ToString(),
  171. SourceCodeKind.File
  172. );
  173. scope = engine.CreateScope();
  174. } else if (sc.Inline != null) {
  175. Assert.NotNull(DynamicApplication.HtmlPageUri);
  176. var page = BrowserPAL.PAL.VirtualFilesystem.GetFileContents(DynamicApplication.HtmlPageUri);
  177. var code = AlignSourceLines(sc.Inline, page);
  178. sourceCode =
  179. engine.Engine.CreateScriptSourceFromString(
  180. RemoveMargin(code),
  181. DynamicApplication.HtmlPageUri.ToString(),
  182. SourceCodeKind.File
  183. );
  184. scope = inlineScope;
  185. }
  186. if (sourceCode != null && scope != null) {
  187. RunningScriptTags = true;
  188. sourceCode.Compile(new ErrorFormatter.Sink()).Execute(scope);
  189. RunningScriptTags = false;
  190. }
  191. }
  192. }
  193. }
  194. private string AlignSourceLines(string partialSource, string fullSource) {
  195. partialSource = partialSource.Replace("\r", "");
  196. fullSource = fullSource.Replace("\r", "");
  197. StringBuilder final = new StringBuilder();
  198. if (fullSource.Contains(partialSource)) {
  199. int offset = fullSource.IndexOf(partialSource);
  200. int partialOffset = offset + partialSource.Length;
  201. for (int i = 0; i < fullSource.Length; i++) {
  202. if ((i < offset || i >= partialOffset) ) {
  203. if (fullSource[i] == '\n') {
  204. final.Append('\n');
  205. }
  206. } else {
  207. final.Append(fullSource[i]);
  208. }
  209. }
  210. }
  211. return final.ToString();
  212. }
  213. /// <summary>
  214. /// Removes as much of a margin as possible from "text".
  215. /// </summary>
  216. public static string RemoveMargin(string text) {
  217. return RemoveMargin(text, -1, true, true);
  218. }
  219. /// <summary>
  220. /// Removes as much of a margin as possible from "text".
  221. /// </summary>
  222. /// <param name="text">"text" to remove margin from</param>
  223. /// <param name="firstLineMargin">What is the first line's margin. Set to "-1" to assume no margin.</param>
  224. /// <param name="firstLine">does the "firstLine" need to be processed?</param>
  225. /// <param name="keepLines">Should lines be kept (true) or removed (false)</param>
  226. /// <returns>the de-margined text</returns>
  227. private static string RemoveMargin(string text, int firstLineMargin, bool firstLine, bool keepLines) {
  228. var reader = new StringReader(text);
  229. var writer = new StringWriter();
  230. string line;
  231. var processedWriter = new StringWriter();
  232. while ((line = reader.ReadLine()) != null) {
  233. if (firstLine) {
  234. // skips all blank lines at the beginning of the string
  235. if (line.Length == 0) {
  236. if (keepLines) {
  237. writer.Write("\n");
  238. processedWriter.Write("\n");
  239. }
  240. continue;
  241. }
  242. // get the first real line's margin as a reference
  243. if(firstLineMargin == -1)
  244. firstLineMargin = GetMarginSize(line);
  245. firstLine = false;
  246. }
  247. if (line.Trim().Length != 0) {
  248. // if any line does not have any margin spaces to
  249. // remove, stop removing margins and reprocess previously
  250. // processed lines without removing margins as well; if any
  251. // line's margins are less than , then there is no
  252. // margin.
  253. var currentLineMargin = GetMarginSize(line);
  254. if (currentLineMargin < firstLineMargin) {
  255. var processedText = RemoveMargin(processedWriter.ToString(), currentLineMargin, true, keepLines);
  256. writer.Close();
  257. writer = null;
  258. writer = new StringWriter(new StringBuilder(processedText));
  259. firstLineMargin = currentLineMargin;
  260. }
  261. } else if (reader.Peek() == -1) {
  262. // skip if it's the last line
  263. continue;
  264. }
  265. var newLine = line;
  266. newLine = RemoveSpacesFromStart(firstLineMargin, line);
  267. writer.Write(newLine + "\n");
  268. processedWriter.Write(line + "\n");
  269. }
  270. string result = writer.ToString();
  271. reader.Close();
  272. writer.Close();
  273. return result;
  274. }
  275. /// <summary>
  276. /// returns the number of spaces in the beginning of "line"
  277. /// </summary>
  278. /// <param name="line">a string to find the number of spaces in</param>
  279. /// <returns>the number of spaces at the beginning of "line"</returns>
  280. private static int GetMarginSize(string line) {
  281. var count = 0;
  282. foreach(char c in line) {
  283. if(c == ' ') {
  284. count++;
  285. } else {
  286. return count;
  287. }
  288. }
  289. return count;
  290. }
  291. /// <summary>
  292. /// Removes "n" spaces from the start of "line". If not all those chars
  293. /// spaces, then "line" is returned in it's entirety.
  294. /// </summary>
  295. /// <param name="n">Number of spaces to remove from "line"</param>
  296. /// <param name="line">The string to remove spaces from</param>
  297. /// <returns>
  298. /// A string with "n" spaces removed, or a copy of "line" if there exists
  299. /// a non-space character in the first "n" spaces.
  300. /// </returns>
  301. private static string RemoveSpacesFromStart(int n, string line) {
  302. n = line.Length < n ? line.Length : n;
  303. for (int i = 0; i < n; i++)
  304. if (line[i] != ' ')
  305. return line;
  306. return line.Remove(0, n);
  307. }
  308. /// <summary>
  309. /// Get the language by name; if it exists get back the "main"
  310. /// language name, otherwise null
  311. /// </summary>
  312. public string GetLanguageByType(string mimeType) {
  313. return GetLanguage(GetLanguageNameFromType(mimeType), (dli) => { return dli.Names; });
  314. }
  315. /// <summary>
  316. /// Get the language by file extension; if it exists get back the
  317. /// "main" language name, otherwise null
  318. /// </summary>
  319. /// <param name="extension"></param>
  320. /// <returns></returns>
  321. public string GetLanguageByExtension(string extension) {
  322. return GetLanguage(extension, (dli) => { return dli.Extensions; });
  323. }
  324. private string GetLanguage(string token, Func<DynamicLanguageInfo, string[]> getProperty) {
  325. if (token == null) return null;
  326. foreach (var l in _LangConfig.Languages) {
  327. foreach (var n in getProperty(l)) {
  328. if (n.ToLower() == token.ToLower()) {
  329. return (l.Names.Length > 0 ? l.Names[0] : token).ToLower();
  330. }
  331. }
  332. }
  333. return null;
  334. }
  335. /// <summary>
  336. /// Given a mime-type, return the language name
  337. /// </summary>
  338. public string GetLanguageNameFromType(string type) {
  339. if (!type.StartsWith("application/x-") &&
  340. !type.StartsWith("text/") &&
  341. !type.StartsWith("application/")) return null;
  342. string lang = type.Substring(type.LastIndexOf('/') + 1);
  343. if (lang.StartsWith("x-")) {
  344. lang = lang.Substring(2);
  345. }
  346. return lang;
  347. }
  348. }
  349. }