PageRenderTime 51ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/Hosts/Silverlight/Microsoft.Scripting.Silverlight/BrowserVirtualFilesystem.cs

http://github.com/IronLanguages/main
C# | 800 lines | 464 code | 104 blank | 232 comment | 97 complexity | 5a28cf784ad37212034888594978f4ce MD5 | raw file
Possible License(s): CPL-1.0, BSD-3-Clause, ISC, GPL-2.0, MPL-2.0-no-copyleft-exception
  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. #if CLR2
  16. using Microsoft.Scripting.Utils;
  17. #endif
  18. using System;
  19. using System.Collections.Generic;
  20. using System.Text;
  21. using System.IO;
  22. using System.Reflection;
  23. using System.Windows.Resources;
  24. using System.Windows;
  25. using System.Windows.Browser;
  26. using System.Net;
  27. using System.Windows.Threading;
  28. namespace Microsoft.Scripting.Silverlight {
  29. /// <summary>
  30. /// Interface for a browser virtual filesystem, as is expected
  31. /// by DLR-languages that run in Silverlight.
  32. /// </summary>
  33. public abstract class BrowserVirtualFilesystem {
  34. /// <summary>
  35. /// Defines the name of this filesystem.
  36. /// </summary>
  37. public abstract string Name();
  38. /// <summary>
  39. /// The current "storage-unit", which is left up to the concrete
  40. /// classes to decide it's meaning. For a XAP file, the storage-unit
  41. /// is which XAP file to get files out of. For a web-server, it's
  42. /// an absolute URI. Basically, it maps to a current-drive for a
  43. /// traditional filesystem.
  44. /// </summary>
  45. public object CurrentStorageUnit { get; set; }
  46. /// <summary>
  47. /// Switches the storage unit and executes the given delegate in that
  48. /// context.
  49. /// </summary>
  50. /// <param name="storageUnit">the storage unit to switch to</param>
  51. /// <param name="action">delegate to run in the context of the storage unit</param>
  52. public void UsingStorageUnit(object storageUnit, Action action) {
  53. var origStorageUnit = CurrentStorageUnit;
  54. CurrentStorageUnit = storageUnit;
  55. action.Invoke();
  56. CurrentStorageUnit = origStorageUnit;
  57. }
  58. /// <summary>
  59. /// Get a file based on a relative path, in the current storage unit
  60. /// </summary>
  61. /// <param name="relativePath"></param>
  62. /// <returns>the stream representing the file</returns>
  63. public Stream GetFile(string relativePath) {
  64. return GetFile(CurrentStorageUnit, relativePath);
  65. }
  66. /// <summary>
  67. /// Get a file based on a relative Uri, in the current storage unit
  68. /// </summary>
  69. /// <param name="relativePath"></param>
  70. /// <returns>the stream representing the file</returns>
  71. public Stream GetFile(Uri relativePath) {
  72. return GetFile(CurrentStorageUnit, relativePath);
  73. }
  74. /// <summary>
  75. /// Get a file based on a relative path, in the given storage unit
  76. /// </summary>
  77. /// <param name="storageUnit">Looks for the file in this</param>
  78. /// <param name="relativePath"></param>
  79. /// <returns>the stream representing the file</returns>
  80. public Stream GetFile(object storageUnit, string relativePath) {
  81. return GetFileInternal(storageUnit, relativePath);
  82. }
  83. /// <summary>
  84. /// Get a file based on a relative Uri, in the given storage unit
  85. /// </summary>
  86. /// <param name="storageUnit">Looks for the file in this</param>
  87. /// <param name="relativeUri"></param>
  88. /// <returns>the stream representing the file</returns>
  89. public Stream GetFile(object storageUnit, Uri relativeUri) {
  90. return GetFileInternal(storageUnit, relativeUri);
  91. }
  92. /// <summary>
  93. /// Get a file's contents based on a relative path, in the current
  94. /// storage-unit.
  95. /// </summary>
  96. /// <param name="relativeUri"></param>
  97. /// <returns>The file's contents as a string</returns>
  98. public string GetFileContents(string relativeUri) {
  99. return GetFileContents(CurrentStorageUnit, relativeUri);
  100. }
  101. /// <summary>
  102. /// Get a file's contents based on a relative Uri, in the current
  103. /// storage-unit.
  104. /// </summary>
  105. /// <param name="relativeUri"></param>
  106. /// <returns>The file's contents as a string</returns>
  107. public string GetFileContents(Uri relativeUri) {
  108. return GetFileContents(CurrentStorageUnit, relativeUri);
  109. }
  110. /// <summary>
  111. /// Get a file's contents based on a relative path, in the given
  112. /// storage-unit.
  113. /// </summary>
  114. /// <param name="storageUnit">Looks for the file in this</param>
  115. /// <param name="relativePath"></param>
  116. /// <returns>The file's contents as a string</returns>
  117. public string GetFileContents(object storageUnit, string relativePath) {
  118. return GetFileContents(storageUnit, new Uri(NormalizePath(relativePath), UriKind.Relative));
  119. }
  120. /// <summary>
  121. /// Get a file's contents based on a relative Uri, in the given
  122. /// storage-unit.
  123. /// </summary>
  124. /// <param name="storageUnit">Looks for the file in this</param>
  125. /// <param name="relativeUri"></param>
  126. /// <returns>The file's contents as a string</returns>
  127. public string GetFileContents(object storageUnit, Uri relativeUri) {
  128. Stream stream = GetFile(storageUnit, relativeUri);
  129. if (stream == null) {
  130. return null;
  131. }
  132. string result;
  133. using (StreamReader sr = new StreamReader(stream)) {
  134. result = sr.ReadToEnd();
  135. }
  136. return result;
  137. }
  138. /// <summary>
  139. /// Normalizes a path, which in general means making sure the directory
  140. /// separators are forward-slashes.
  141. /// </summary>
  142. /// <param name="path">a string representing a path</param>
  143. /// <returns>a normalized version of "path"</returns>
  144. public static string Normalize(string path) {
  145. return path.Replace('\\', '/');
  146. }
  147. /// <summary>
  148. /// See (static) BrowserVirtualFilesystem.Normalize
  149. /// </summary>
  150. public virtual string NormalizePath(string path) {
  151. return BrowserVirtualFilesystem.Normalize(path);
  152. }
  153. /// <summary>
  154. /// Gets a file's stream
  155. /// </summary>
  156. /// <param name="storageUnit">Looks for the file in this</param>
  157. /// <param name="relativePath">path of the file</param>
  158. /// <returns>a Stream for the file's contents</returns>
  159. protected Stream GetFileInternal(object storageUnit, string relativePath) {
  160. return GetFileInternal(storageUnit, new Uri(NormalizePath(relativePath), UriKind.RelativeOrAbsolute));
  161. }
  162. /// <summary>
  163. /// Defines how the specific virtual file-system gets a file.
  164. /// </summary>
  165. /// <param name="storageUnit"></param>
  166. /// <param name="relativeUri"></param>
  167. /// <returns></returns>
  168. protected abstract Stream GetFileInternal(object storageUnit, Uri relativeUri);
  169. }
  170. /// <summary>
  171. /// Access the XAP file contents
  172. /// </summary>
  173. public class XapVirtualFilesystem : BrowserVirtualFilesystem {
  174. public override string Name() { return "XAP file"; }
  175. /// <summary>
  176. /// Normalizes the path by making sure all directory separators are
  177. /// forward slashes, and makes sure no path starts with "./", as the
  178. /// root of the XAP file is an empty string.
  179. /// </summary>
  180. /// <param name="path"></param>
  181. /// <returns></returns>
  182. public override string NormalizePath(string path) {
  183. var normPath = base.NormalizePath(path);
  184. // Application.GetResource doesn't like paths that start with ./
  185. // BUG: try to get this fixed in SL
  186. if (normPath.StartsWith("./")) {
  187. normPath = normPath.Substring(2);
  188. }
  189. return normPath;
  190. }
  191. /// <summary>
  192. /// Gets a Stream for a file from the given "xap" file.
  193. /// </summary>
  194. /// <param name="xap">a StreamResourceInfo representing a XAP file</param>
  195. /// <param name="relativeUri">a string respresenting a relative URI</param>
  196. /// <returns>a Stream for the file, or null if it did not find the file</returns>
  197. protected override Stream GetFileInternal(object xap, Uri relativeUri) {
  198. relativeUri = new Uri(NormalizePath(relativeUri.ToString()), UriKind.Relative);
  199. StreamResourceInfo sri = null;
  200. // TODO: Application.GetResourceStream always returns null when called from
  201. // a non-UI thread. Should dispatch to the UI thread but block this thread until
  202. // the call completes.
  203. if (xap == null) {
  204. sri = Application.GetResourceStream(relativeUri);
  205. } else {
  206. sri = Application.GetResourceStream((StreamResourceInfo) xap, relativeUri);
  207. }
  208. return sri == null ?
  209. DynamicApplication.GetManifestResourceStream(relativeUri.ToString()) :
  210. sri.Stream;
  211. }
  212. #region Depricated Methods
  213. /// <summary>
  214. /// Get the contents of the entry-point script as a string
  215. /// </summary>
  216. [Obsolete("This method will be unavaliable in the next version")]
  217. public string GetEntryPointContents() {
  218. return BrowserPAL.PAL.VirtualFilesystem.GetFileContents(Settings.EntryPoint);
  219. }
  220. /// <summary>
  221. /// Get a list of the assemblies defined in the AppManifest
  222. /// </summary>
  223. [Obsolete("Use DynamicApplication.Current.AppManifest.AssemblyParts() instead")]
  224. public List<Assembly> GetManifestAssemblies() {
  225. return DynamicApplication.Current.AppManifest.AssemblyParts();
  226. }
  227. #endregion
  228. }
  229. /// <summary>
  230. /// Download and cache files over HTTP
  231. /// </summary>
  232. public class HttpVirtualFilesystem : BrowserVirtualFilesystem {
  233. public override string Name() { return "Web server"; }
  234. /// <summary>
  235. /// The cache of files already downloaded
  236. /// </summary>
  237. private DownloadCache _cache = new DownloadCache();
  238. /// <summary>
  239. /// Gets a file out of the download cache. This does not download the
  240. /// file if it is not in the cache; use "DownloadAndCache" before
  241. /// using this method download the file and cache it.
  242. /// </summary>
  243. /// <param name="baseUri">
  244. /// URI to base relative URI's off of. If null, it defaults to the HTML
  245. /// page's URI.
  246. /// </param>
  247. /// <param name="relativeUri">URI relative to the base URI</param>
  248. /// <returns>A stream for the URI</returns>
  249. protected override Stream GetFileInternal(object baseUri, Uri relativeUri) {
  250. if (!relativeUri.IsAbsoluteUri) {
  251. // TODO: HttpVirtualFilesystem shouldn't manage all these checks,
  252. // needs to be re-organized.
  253. // check in the XAP first
  254. var stream = XapPAL.PAL.VirtualFilesystem.GetFile(relativeUri);
  255. if (stream != null) return stream;
  256. // also check any ZIP files
  257. if (HtmlPage.IsEnabled && (DynamicApplication.Current != null && DynamicApplication.Current.ScriptTags != null)) {
  258. foreach (var zip in DynamicApplication.Current.ScriptTags.ZipPackages) {
  259. var relUriStr = relativeUri.ToString();
  260. var dirname = Path.GetDirectoryName(relUriStr);
  261. if (dirname.Length == 0)
  262. continue;
  263. var dirs = dirname.Split('/');
  264. if (dirs.Length == 0)
  265. continue;
  266. var toplevelDir = dirs[0];
  267. if (toplevelDir != Path.GetFileNameWithoutExtension(zip.ToString()))
  268. continue;
  269. var rest = relUriStr.Split(new string[] { toplevelDir + "/" }, StringSplitOptions.None);
  270. if (rest.Length <= 1)
  271. continue;
  272. var pathToFileInZip = rest[1];
  273. var file = XapPAL.PAL.VirtualFilesystem.GetFile(
  274. new StreamResourceInfo(GetFileInternal(baseUri, zip), null), pathToFileInZip
  275. );
  276. if (file != null)
  277. return file;
  278. }
  279. }
  280. }
  281. var fullUri = DynamicApplication.MakeUri((Uri)baseUri, relativeUri);
  282. if (_cache.Has(fullUri)) {
  283. byte[] bytes = _cache.GetBinary(fullUri);
  284. string strContent = _cache.Get(fullUri);
  285. return new MemoryStream(
  286. strContent != null ?
  287. System.Text.Encoding.UTF8.GetBytes(strContent) :
  288. bytes
  289. );
  290. } else {
  291. return null;
  292. }
  293. }
  294. /// <summary>
  295. /// Download and cache a list of URIs, and execute a delegate when it's
  296. /// complete.
  297. /// </summary>
  298. /// <param name="uris">List of URIs to download and cache</param>
  299. /// <param name="onComplete">
  300. /// Called when the URI's are successfully downloaded and cached
  301. /// </param>
  302. internal void DownloadAndCache(List<Uri> uris, Action onComplete) {
  303. _cache.Download(uris, onComplete);
  304. }
  305. }
  306. /// <summary>
  307. /// A cache of Uris mapping to strings.
  308. /// </summary>
  309. public class DownloadCache {
  310. // TODO: shouldn't differentiate between TextContent and BinaryContent,
  311. // should just have BinaryContent and then convert to UTF8 for TextContent.
  312. internal struct DownloadContent {
  313. internal string TextContent;
  314. internal byte[] BinaryContent;
  315. internal DownloadContent(string tC, byte[] bC) {
  316. TextContent = tC;
  317. BinaryContent = bC;
  318. }
  319. }
  320. private DownloadCacheDownloader _downloader;
  321. public DownloadCache() {
  322. _downloader = new DownloadCacheDownloader(this);
  323. }
  324. private Dictionary<Uri, DownloadContent> _cache = new Dictionary<Uri, DownloadContent>();
  325. /// <summary>
  326. /// Adds a URI/code pair to the cache if the URI doesn't not already
  327. /// exist.
  328. /// </summary>
  329. /// <param name="uri"></param>
  330. /// <param name="code"></param>
  331. public void Add(Uri uri, string code) {
  332. if (!Has(uri)) {
  333. _cache.Add(uri, new DownloadContent(code, null));
  334. }
  335. }
  336. public void Add(Uri uri, byte[] data) {
  337. if (!Has(uri)) {
  338. _cache.Add(uri, new DownloadContent(null, data));
  339. }
  340. }
  341. /// <summary>
  342. /// Gets a string from the cache from a URI. Returns null if the URI is
  343. /// not in the cache.
  344. /// </summary>
  345. /// <param name="uri">A URI to look-up</param>
  346. /// <returns>Binary content associated with that URI.</returns>
  347. public string Get(Uri uri) {
  348. if (!Has(uri)) return null;
  349. return _cache[uri].TextContent;
  350. }
  351. /// <summary>
  352. /// Get binary content from the cache from a URI. Returns null if the
  353. /// URI is not in the cache.
  354. /// </summary>
  355. /// <param name="uri">A URI to look-up</param>
  356. /// <returns>Binary content associated with that URI.</returns>
  357. public byte[] GetBinary(Uri uri) {
  358. if (!Has(uri)) return null;
  359. return _cache[uri].BinaryContent;
  360. }
  361. /// <summary>
  362. /// Indexer to Get and Add string to the cache. Indexes on URIs.
  363. /// </summary>
  364. public string this[Uri uri] {
  365. get { return Get(uri); }
  366. set { Add(uri, value); }
  367. }
  368. /// <summary>
  369. /// Does the cache contain the URI?
  370. /// </summary>
  371. public bool Has(Uri uri) {
  372. return _cache.ContainsKey(uri);
  373. }
  374. /// <summary>
  375. /// Clears the cache completely.
  376. /// </summary>
  377. public void Clear() {
  378. _cache = null;
  379. _cache = new Dictionary<Uri, DownloadContent>();
  380. }
  381. /// <summary>
  382. /// Downloads the list of URIs, caches the result of the download, and
  383. /// calls onComplete when finished.
  384. /// </summary>
  385. public void Download(List<Uri> uris, Action onComplete) {
  386. _downloader.Download(uris, onComplete);
  387. }
  388. internal class DownloadCacheDownloader {
  389. private List<Uri> _downloadQueue;
  390. private DownloadCache _cache;
  391. internal DownloadCacheDownloader(DownloadCache cache) {
  392. _cache = cache;
  393. }
  394. internal void Download(List<Uri> uris, Action onComplete) {
  395. if (uris.Count == 0) {
  396. onComplete.Invoke();
  397. return;
  398. }
  399. _downloadQueue = new List<Uri>(uris);
  400. var iteratingQueue = new List<Uri>(_downloadQueue);
  401. foreach (var uri in iteratingQueue) {
  402. InternalDownload(uri, onComplete);
  403. }
  404. }
  405. private void InternalDownload(Uri uri, Action onComplete) {
  406. if (HasDomainFailed(uri.Host)) {
  407. DownloadWithXmlHttpRequest(uri, onComplete);
  408. } else {
  409. DownloadWithWebClient(uri, onComplete, () => {
  410. DomainFailed(uri.Host);
  411. DownloadWithXmlHttpRequest(uri, onComplete);
  412. });
  413. }
  414. }
  415. private void DownloadComplete(Uri uri, Stream content, Action onComplete) {
  416. DownloadComplete(uri, StreamToByteArray(content), onComplete);
  417. }
  418. private void DownloadComplete(Uri uri, byte[] content, Action onComplete) {
  419. _cache.Add(uri, content);
  420. InternalDownloadComplete(uri, onComplete);
  421. }
  422. private byte[] StreamToByteArray(Stream stream) {
  423. byte[] buffer = new byte[stream.Length];
  424. int read = stream.Read(buffer, 0, buffer.Length);
  425. return buffer;
  426. }
  427. private void DownloadComplete(Uri uri, string content, Action onComplete) {
  428. _cache.Add(uri, content);
  429. InternalDownloadComplete(uri, onComplete);
  430. }
  431. private void InternalDownloadComplete(Uri uri, Action onComplete) {
  432. if (_downloadQueue != null) {
  433. lock (_downloadQueue) {
  434. if (_downloadQueue != null && _downloadQueue.Count > 0 && _downloadQueue.Contains(uri)) {
  435. _downloadQueue.Remove(uri);
  436. if (_downloadQueue.Count == 0) {
  437. _downloadQueue = null;
  438. onComplete.Invoke();
  439. }
  440. }
  441. }
  442. }
  443. }
  444. #region WebRequest / XMLHttpRequest fail-over
  445. private Dictionary<string, bool> _webClientDownloadFailureForDomain = new Dictionary<string, bool>();
  446. private bool HasDomainFailed(string host) {
  447. return _webClientDownloadFailureForDomain.ContainsKey(host) && _webClientDownloadFailureForDomain[host];
  448. }
  449. private void DomainFailed(string host) {
  450. _webClientDownloadFailureForDomain[host] = true;
  451. }
  452. #endregion
  453. #region WebRequest
  454. private void DownloadWithWebClient(Uri uri, Action onComplete, Action onSecurityException) {
  455. var webClient = new WebClient();
  456. webClient.OpenReadCompleted += (s, e) => {
  457. if (e.Error == null && e.Cancelled == false) {
  458. var requestUri = (Uri)((object[])e.UserState)[0];
  459. var completeAction = (Action)((object[])e.UserState)[1];
  460. DownloadComplete(requestUri, e.Result, completeAction);
  461. } else {
  462. if (e.Error.InnerException.GetType() == typeof(System.Security.SecurityException)) {
  463. onSecurityException();
  464. } else {
  465. throw e.Error.InnerException;
  466. }
  467. }
  468. };
  469. webClient.OpenReadAsync(uri, new object[] { uri, onComplete });
  470. }
  471. #endregion
  472. #region XMLHttpRequest
  473. // XMLHttpRequest is used instead of WebClient when this
  474. // scenario happens:
  475. // localhost/index.html --> bar.com/dlr.xap --> localhost/foo.py
  476. // ^^^
  477. // WebClient refuses to do the marked request, because you cannot
  478. // make a request from an internet-zone to a local-zone. XMLHttpRequest
  479. // works though, since it executes in the local domain.
  480. //
  481. // Also, when the XAP is hosted cross-domain, all inbound HTML
  482. // events and interactions are disabled. This can be re-enabled
  483. // by both setting the ExternalCallersFromCrossDomain property in the
  484. // AppManifest.xaml to "ScriptableOnly" and setting the "enableHtmlAccess"
  485. // param on the Silverlight object tag to "true". This not only allows the
  486. // "XMLHttpRequest.onreadstatechange" event to call back into managed
  487. // code, but re-enabled all HTML events, like the REPL. See
  488. // http://msdn.microsoft.com/en-us/library/cc645023(VS.95).aspx for
  489. // more information.
  490. //
  491. // If for some reason you can't change the AppManifest's settings,
  492. // you'll have to use polling to detect when the download is done
  493. // (see "XMLHttpRequest with polling" region below). However this is
  494. // not enabled anywhere, as the scenario is a bit limited.
  495. //
  496. // Also note that OnXmlHttpDownloadComplete catches ALL exceptions
  497. // to make sure they don't leak out into JavaScript.
  498. private bool _emittedXMLHttpRequestHander = false;
  499. private void DownloadWithXmlHttpRequest(Uri uri, Action onComplete) {
  500. if (!_emittedXMLHttpRequestHander) {
  501. if (HtmlPage.BrowserInformation.Name == "Microsoft Internet Explorer") {
  502. // IE's JavaScript API has no way of getting to binary data from XMLHttpRequest,
  503. // so resort to VBScript. However, the performance of this is horrible.
  504. AddScriptTag("vbscript", null, @"
  505. Function BinaryArrayToAscCSV( aBytes )
  506. Dim j, sOutput
  507. sOutput = """"
  508. For j = 1 to LenB(aBytes)
  509. sOutput= sOutput & AscB( MidB(aBytes,j,1) )
  510. sOutput= sOutput & "",""
  511. Next
  512. BinaryArrayToAscCSV = sOutput
  513. End Function
  514. ");
  515. AddScriptTag(null, "text/javascript", @"
  516. function request2csv(request) {
  517. return BinaryArrayToAscCSV(request.responseBody);
  518. }
  519. ");
  520. }
  521. HtmlPage.Window.Eval(@"
  522. function OnXmlHttpRequest_ReadyStateChange(file) {
  523. return function() {
  524. this.currentSLObject.OnXmlHttpDownloadComplete(this, file);
  525. }
  526. }
  527. var isIE = false;
  528. function DLR_DownloadResource(uri, binary) {
  529. request = new XMLHttpRequest();
  530. request.open('GET', uri, false);
  531. if (binary && request.overrideMimeType)
  532. request.overrideMimeType('text/plain; charset=x-user-defined');
  533. request.send();
  534. if (request.status != 200) return '';
  535. var raw;
  536. if (binary && typeof(request.responseBody) !== 'undefined') {
  537. isIE = true;
  538. raw = BinaryArrayToAscCSV(request.responseBody);
  539. return raw.substring(0, raw.length - 2).split(',');
  540. } else {
  541. return request.responseText;
  542. }
  543. }
  544. function DLR_DownloadTextResource(uri) {
  545. return DLR_DownloadResource(uri, false);
  546. }
  547. function DLR_DownloadBinaryResource(uri) {
  548. var raw = DLR_DownloadResource(uri, true);
  549. var data = new Array(raw.length);
  550. if (isIE) {
  551. for(i = 0; i < raw.length; i++) {
  552. data[i] = parseInt(raw[i]);
  553. }
  554. } else {
  555. for(i = 0; i < raw.length; i++) {
  556. data[i] = raw.charCodeAt(i) & 0xff;
  557. }
  558. }
  559. return data;
  560. }
  561. ");
  562. _emittedXMLHttpRequestHander = true;
  563. }
  564. var file = uri.ToString();
  565. // HACK treat zip files as binary content
  566. if (Path.GetExtension(file).Contains("zip")) {
  567. ScriptObject bin = (ScriptObject)HtmlPage.Window.Eval(string.Format(@"DLR_DownloadBinaryResource(""{0}"");", file));
  568. byte[] binaryContent = new byte[(int)(double)bin.GetProperty("length")];
  569. for (int i = 0; i < binaryContent.Length; i++) {
  570. binaryContent[i] = (byte)(double)bin.GetProperty(i.ToString());
  571. }
  572. DownloadComplete(new Uri(file, UriKind.RelativeOrAbsolute), binaryContent, onComplete);
  573. } else {
  574. string content = (string)(HtmlPage.Window.GetProperty("DLR_DownloadTextResource") as ScriptObject).InvokeSelf(file);
  575. DownloadComplete(new Uri(file, UriKind.RelativeOrAbsolute), content, onComplete);
  576. }
  577. }
  578. private void AddScriptTag(string lang, string type, string text) {
  579. var scriptTag = HtmlPage.Document.CreateElement("script");
  580. if (lang != null) scriptTag.SetAttribute("language", lang);
  581. if (type != null) scriptTag.SetAttribute("type", type);
  582. scriptTag.SetProperty("text", text);
  583. (HtmlPage.Document.GetElementsByTagName("head")[0] as HtmlElement).AppendChild(scriptTag);
  584. }
  585. #if false
  586. private Action _onComplete;
  587. // Managed handler of XmlHttpRequest.onreadstatechange; a JavaScript one is currently
  588. // used, but switching back to this might be best for performance.
  589. [ScriptableMember]
  590. public void OnXmlHttpDownloadComplete(ScriptObject handlerThis, string file) {
  591. try {
  592. object objReadyState = handlerThis.GetProperty("readyState");
  593. object objStatus = handlerThis.GetProperty("status");
  594. int readyState = 0;
  595. int status = 0;
  596. if (objStatus != null) status = (int)((double)objStatus / 1);
  597. if (objReadyState != null) readyState = (int)((double)objReadyState / 1);
  598. if (readyState == 4 && status == 200) {
  599. // HACK treat zip files as binary content
  600. if (Path.GetExtension(file).Contains("zip")) {
  601. string content;
  602. byte[] binaryContent;
  603. if (HtmlPage.BrowserInformation.UserAgent.IndexOf("MSIE") != -1) {
  604. content = (string)HtmlPage.Window.Invoke("request2csv", handlerThis);
  605. var strArray = content.Substring("BinaryArrayToAscCSV".Length).Split(',');
  606. binaryContent = new byte[strArray.Length];
  607. for (int i = 0; i < strArray.Length; i++) {
  608. string strByte = strArray[i];
  609. if (strByte.Length == 0) break;
  610. binaryContent[i] = byte.Parse(strByte);
  611. }
  612. } else {
  613. content = (string)handlerThis.GetProperty("responseText");
  614. binaryContent = new byte[content.Length];
  615. for (int i = 0; i < content.Length; i++) {
  616. binaryContent[i] = (byte)(((int)content[i]) & 0xff);
  617. }
  618. }
  619. DownloadComplete(new Uri(file, UriKind.RelativeOrAbsolute), binaryContent, _onComplete);
  620. } else {
  621. string content = (string)handlerThis.GetProperty("responseText");
  622. DownloadComplete(new Uri(file, UriKind.RelativeOrAbsolute), content, _onComplete);
  623. }
  624. } else if (readyState == 4 && status != 200) {
  625. throw new Exception(file + " download failed (status: " + status + ")");
  626. }
  627. } catch (Exception e) {
  628. // This catch-all is necessary since any unhandled exceptions
  629. // will not be processed by Application.UnhandledException, so
  630. // call it directly if reporting errors is enabled.
  631. if (Settings.ReportUnhandledErrors)
  632. DynamicApplication.Current.HandleException(this, e);
  633. }
  634. }
  635. #endif
  636. #endregion
  637. #region XMLHttpRequest with polling
  638. #if false
  639. private void DownloadWithXmlHttpRequestAndPolling(Uri uri, Action onComplete) {
  640. var request = HtmlPage.Window.CreateInstance("XMLHttpRequest");
  641. request.Invoke("open", "GET", uri.ToString());
  642. request.SetProperty("onreadystatechange", HtmlPage.Window.Eval("DLR.__onDownloadCompleteToPoll(\"" + uri.ToString() + "\")"));
  643. request.Invoke("send");
  644. PollForDownloadComplete(uri, onComplete);
  645. }
  646. private void PollForDownloadComplete(Uri uri, Action onComplete) {
  647. var pollCount = 0;
  648. var timer = new DispatcherTimer();
  649. timer.Interval = new TimeSpan(0, 0, 0, 0, 50);
  650. timer.Tick += (sender, args) => {
  651. object objStatus = null;
  652. int status = 0;
  653. var obj = HtmlPage.Document.GetElementById(uri.ToString());
  654. if (obj != null) {
  655. objStatus = obj.GetProperty("status");
  656. if (objStatus != null) status = (int)((double)objStatus / 1);
  657. }
  658. Action<Uri, int> onFailure = (duri, dstatus) => {
  659. timer.Stop();
  660. throw new Exception(duri.ToString() + " download failed (status: " + dstatus + ")");
  661. };
  662. if (status == 200) {
  663. var content = (string)obj.GetProperty("scriptContent");
  664. HtmlPage.Document.Body.RemoveChild(obj);
  665. timer.Stop();
  666. DownloadComplete(uri, content, onComplete);
  667. } else if (status == 400) {
  668. onFailure(uri, status);
  669. } else {
  670. if (pollCount < 50) pollCount++;
  671. else onFailure(uri, status);
  672. }
  673. };
  674. timer.Start();
  675. }
  676. #endif
  677. #endregion
  678. }
  679. }
  680. #if false
  681. /// <summary>
  682. /// Read and write files from Isolated Storage
  683. /// </summary>
  684. public class IsolatedStorageVirtualFilesystem : BrowserVirtualFilesystem {
  685. public override string Name() { return "Isolated Storage"; }
  686. protected override Stream GetFileInternal(object baseUri, Uri relativeUri) {
  687. throw new NotImplementedException("TODO");
  688. }
  689. }
  690. #endif
  691. }