
cr-documentor - issue #1
Security warning appears in preview window and preview doesn't render right.
The current mechanism that CR_Documentor uses to render the preview is by dynamically poking information into the IE DOM. This, combined with navigating to temporary pages on disk, causes the security warning to be issued when the preview window displays because the preview also uses JavaScript.
Unfortunately, this isn't something that can be reproduced on demand, but it is common enough that a reasonable amount of usage should display the behavior.
This should be fixable by implementing an embedded web server and serving the preview from there. That will make the IE control think it's really navigating to a web site and will put the content in a different security zone than "My Computer."
The first attempt at the embedded web server should be simple, like with HttpListener: http://www.paraesthesia.com/archive/2008/07/16/simplest- embedded-web-server-ever-with-httplistener.aspx
Comment #1
Posted on Jul 16, 2008 by Helpful Wombat(No comment was entered for this change.)
Comment #2
Posted on Jul 19, 2008 by Happy BearTwo things actually. 1) Why not use built in components such as System.Windows.Forms.WebBrowser instead of AxWebBrowser, saves you some dependencies.
2) One of the comments in the code mentions that 'about:blank' doesn't work. But when I do that, it works fine. Snippet:
public virtual void ResetToBaseDocument(string html) { this.Navigate("about:"); PreviewDocument = html; }
It works perfectly here, but maybe that is caused by some library version. (I got Vista with VS2005)
I also tested this on the Windows.Forms.WebBrowser where you can navigate to 'about:blank' and subsequently call something in the direction of "WebBrowser.Document.OpenNew(true).Write(html);" which, if I read it correctly, creates an entirely new doc. Even Navigate to "about:Loading..." seems to work (which displays "Loading..." as webpage)
Either way, hopefully one of those will work without having to resort to things like a webserver. Those will always have security restrictions.
DFyNt2U
Comment #3
Posted on Jul 19, 2008 by Helpful WombatGreat feedback. Actually, I'm going to look into the WebBrowser control. The AxWebBrowser implementation is something I inherited from the original Documentor from Lutz and I was sort of like, "If it ain't broke, don't fix it." Looks like it might be broke now, but we'll get back to that in a second.
The "about:blank" thing in the code - the way the code currently works, it fires up the web browser, points it to a temporary page, then uses the DOM to update the body of the page (and deletes the temporary page). What the comment meant was that I tried navigating to "about:blank" and then using the DOM to update THAT instead of navigating to a temporary page, but I wasn't able to get past the security problem - I still was getting the warning. I didn't mean that navigating to "about:blank" didn't work, just that it didn't solve the problem.
That said, it looks like manipulating the DOM is actually what was breaking things security-wise. Even checking "Allow active content to run in files on My Computer" didn't really fix the issue. I have the internal web server version of things working now (in a task branch) and it does fix the security warning, but it introduces a new problem.
Every time I tell the browser to reload, it steals focus, but - and here's the weird part - only if you've given it focus at some point.
Scenario 1: You fire up VS, fire up CR_Documentor, open up some code, write some XML documents, and the preview window updates fine. You work on your code, then close down VS. No problems.
Scenario 2: You fire up VS, fire up CR_Documentor, open up some code, write some XML documents, and the preview window updates fine. You decide to look at a long preview in the window, so you go to the CR_Documentor window and scroll down in the browser. You've now given the browser focus once. You go back to work on your code, but now every time the browser refreshes it steals focus.
I'm not sure why Scenario 2 is happening, but it brings me back to the original point, and one of your points of feedback: Try the WebBrowser control instead of AxWebBrowser. That's actually the next task on the list to fix this - if it fixes the focus problem, I'll publish it. If it doesn't, I'll keep looking for a solution because, as it stands, it sort of makes working difficult while you have CR_Documentor open.
But I'm making headway. If you want to try out the version in my task branch for this issue, feel free. I can't promise it's totally stable at any given point, but that's where I'm working things out.
Comment #4
Posted on Jul 19, 2008 by Happy BearYesterday I already peeked in the branch. And I'll be testing various security configurations to see if I can reproduce the problem with about:blank.
I booted up the branch and got some compile errors but nothing unexpected. First thing though I got was a IE bar on top saying that my intranet security settings were disabled. Nothing weird again, but it proves the point that by going outside the browser (file/intranet) will always have a chance of violating some of IEs security policies and cause one a message popup.
Scenario 2 is strange, but there is a workaround, put this.Visible = false & true around the this.Refresh2 call. It simply forces the axwebbrowser to the invisible realm, and ghosts can't touch you.
Will let ya know as soon as I know more about the about:blank.
Comment #5
Posted on Jul 19, 2008 by Helpful WombatOn about:blank - I remember actually trying two different things. One was trying to poke things via DOM into about:blank, the other was setting the Mark of the Web on the base/temporary document to look like it was saved from "about:blank" (instead of, say, "about:internet" or "http://localhost/"). Neither of those tactics worked - at some point, I always got the security error.
You're right, though, no matter what I do I'm going to violate someone's security policy. I mean, you have to manually unblock CHM files and such now, too. Not sure I can win that battle, but the internal server opens up some doors to fairly easily start pushing icons and such into the previews, making them richer, and doesn't require me to maintain a temporary filesystem. It also means I can do less work when taking "updates" from Sandcastle - right now I have to manually massage things into a single document, but if I have a mini-server, I can pretty much take the style items unchanged.
Will have to try the this.Visible thing. I wonder if that would cause an odd experience for the user - like a weird little flash (slightly more flash than the refresh already causes) or something. Maybe not. Might be so fast you don't notice. Worth a shot anyway. Maybe try that before I try converting to the WebBrowser control.
Thanks for checking this out. It's great to have another set of eyes on it.
Comment #6
Posted on Jul 19, 2008 by Happy BearYou're welcome travis. When I found the plugin (Actually a few weeks ago, the v1 and v2 just a few days ago). I thought it would be useful but was frustrated by the focus issue. Fortunately, opensource opens doors to solutions.
I actually ran into a problem with the Visible, somehow it only works once. after that the refresh doesn't work at all. I was quite positive I tested that, but apparently I didn't. I checked out WebBrowser combined with your WebServer, it exhibits the exact same behaviour on Refresh. Long live Reflector, internally WebBrowser uses same thing as AxWebBrowser, its just wrapped more neatly than simple AxHost.
Actually now you mention it, there are advantages to the WebServer as you can simple serve a virtual file system. Didn't really look at it from that direction.
Wouldn't have been a happy ending for my weekend, so I continued to scour the internet and found someone suggesting that one should Disable the control containing the webbrowser. The AxWebBrowser is not a Control, so I tested it on the WebBrowser only; I used a UserControl containing the WebBrowser earlier, so it was less work to test. I set this.Enabled = false prior to calling the Refresh command on the webBrowser, then in a ProgressChanged event I restore it with this.Enabled = true IF progress has reached maximum. (DocumentCompleted was also available, but it doesn't get invoked for a Refresh) I jumped around several comments, and scrolled/clicked in the browser several times. It still didn't steal focus. Like Visible this inhibits the focus event but doesn't appear to have the ill side effect I described earlier. But there is one effect I can predict and thats when the browser fails to load the document, a timeout could deal with that.
Comment #7
Posted on Jul 20, 2008 by Helpful WombatThat works like FREAKING MAGIC. You rock!
I've converted the AxWebBrowser to WebBrowser and do the disable/enable thing when I refresh, ProgressChanged event handler and all, just as you describe, and it seems to me to totally fix it.
Give it a run, let me know what you think. I'm going to pass the info along to a couple of other interested folks and try it out for a day or so, then release it if it looks good to people.
WOOHOO!
Comment #8
Posted on Jul 20, 2008 by Happy BearGood work, its working here fine. I tested printing a bit, but the width of the window determines the way it gets printed. Maybe using ShowPrintDialog offers more options, or an extra button for that purpose. But thats another issue :P
I just had one instance where it went wrong, the browser remained white. I added a few ToolWindow.Log messages and saw that in that situation the whole Refresh mechanism fails: - Server listening for requests. - CR_Documentor: Starting to refresh browser. - Updating transform options from storage. - The following recognized tags will be passed through or are handled implicitly: ... - CR_Documentor: Starting to refresh browser. - SEID_WindowFrame value changed from 'Messages' to 'Documentor'. - SEID_UserContext value changed from 'NULL' to 'NULL'.
In a normal initialization its followed by: - CR_Documentor: Browser progress changed (100 of 10000). - CR_Documentor: Browser progress changed (100 of 10000). - CR_Documentor: Browser progress changed (10000 of 10000). - CR_Documentor: Finished refreshing browser. - CR_Documentor: Browser progress changed (-1 of 10000). - CR_Documentor: Browser progress changed (10000 of 10000). - CR_Documentor: Browser progress changed (0 of 0).
And in a normal refresh (after initialization): - CR_Documentor: Starting to refresh browser. - CR_Documentor: Browser progress changed (-1 of 0). - CR_Documentor: Browser progress changed (0 of 0). - CR_Documentor: Finished refreshing browser.
Definitely has to do with timing. It could be a WebServer vs. WebBrowser timing issue, but i'm not sure. Going to have a busy day today so I won't be able to investigate further, just put the release on hold :)
Comment #9
Posted on Jul 20, 2008 by Happy BearLOL, just before closing I restarted it once again... white screen: suddenly I noticed progress changed messages with slowly crawling up numbers, first 50 bytes a second, then at 2000 of 10000 it slowing becomes 1 byte a second! And it halted completely at 3883 of 10000. And spawns that message twice a second in the log window. Kind weird.
Comment #10
Posted on Jul 20, 2008 by Helpful WombatRe: printing - I'll have to check that out. I have it on a list of things to test; looks like you beat me to it. (I'm also going to disable the right-click "Back" and "Forward" options if I can so people don't accidentally try to navigate away somehow.)
Re: server - uh... hmmm. That really is weird. Can you make it happen consistently? It sounds like something weird going on with HttpListener, which may not be something I have control over. I just tell it to listen for requests and pass the content out.
A couple of ideas (since I haven't been able to reproduce this myself yet): * When the screen comes up white, what's the Document in the browser? If it's null, I wonder if the RefreshBrowser call should instead do a Navigate to the base URL? * On the slow server... Haven't checked yet, but maybe there's a way to see how fast the content is getting served, or at least put a timeout (like one second?) on any given request and if the request isn't finished by then, kill and restart the server. * Is it a matter of the browser trying to navigate to the server before it's started? Maybe some sort of WaitHandle or something to hold things up until the server's had a chance to start?
This is a sticky one. Particularly difficult since I've not been able to see it myself. That said, I should probably add a few more log messages anyway so this can be traced if it happens in the wild, so I'll do that.
In the meantime, I really appreciate your help. Please keep me posted on this and I'll put in whatever fix is needed.
Comment #11
Posted on Jul 21, 2008 by Helpful WombatThe printing issue has been fixed. The problem was with the CSS styling in the Sandcastle preview; adding a print media override fixed it. (I also show the print dialog now instead of just silently sending to the printer.)
The forward/back should be effectively disabled, too, since I've set the browser to not allow navigation after the first page is hit. Since all we're doing is refreshing that first page, it should be okay.
I've added several logging statements to help trace down the issue you're seeing with the server hang, but I haven't been able to reproduce it myself yet. (Windows XP, VS 2008)
Comment #12
Posted on Jul 21, 2008 by Happy BearI've tried to reproduce it 10 times now, no success yet. But using tricks I can reproduce the symptoms. If I sleep the Async thread. The counting 'bytes' are actually not bytes but that neat progressbar you see in IE. 10000 bytes is 100% in this case. (Is only 'bytes' if IE knows how much it's gonna be.)
When I delay the thread, it starts counting up until the delay is complete and the async thread handles the request. This makes me believe the async thread somehow doesn't run in certain situations.
few minutes later
But alas, that wasn't the case. In fact, it was a timing problem: Reproduce the problem by putting a 3000 msec sleep in the DocumentationControl: this.NavigateToInitialPage(); System.Threading.Thread.Sleep(3000); this.Transformer = new CR_Documentor.Transformation.MSDN.Engine();
That reproduces the problem :) Simply said, the webbrowser tries to retrieve a page before there is any available (since the Transformer creates it).
The WebServer runs flat on: byte[] buffer = Encoding.UTF8.GetBytes(this.Content); this.Content is still null at that point, which crashes the entire thread, hangs your server forever.
Just change the order: this.Transformer = new CR_Documentor.Transformation.MSDN.Engine(); this.NavigateToInitialPage();
Have fun fixing it :)
I would also recommend safeguarding against Content == null, to prevent thread termination.
Greets
DFyNt2U
Comment #13
Posted on Jul 21, 2008 by Helpful WombatOK, I've committed a two-part fix that should take care of this:
1) The web server now checks to see if it is trying to serve up empty/null content. If so, it serves up " " just so it's not empty.
2) I moved the navigate-to-initial-page call to the RefreshBrowser method. That way the first time something happens (e.g., when the transformation engine gets set), THAT'S when it'll do its navigation. That should bypass the issue you're seeing.
Give it a run, let me know what you think. I'm going to use it myself for a day, and if it works, I'll release it tomorrow.
Comment #14
Posted on Jul 21, 2008 by Happy BearI'll take it with me to work tomorrow (Tuesday), I'll probably be working on documentation and other stuff of a C++/CLI library (yeah, the issue 7 one) so i'll have ample time to test it. If the webbrowser acts up again, you'll be the first to know. But I think it's gonna be just fine.
DFy
Comment #15
Posted on Jul 22, 2008 by Happy BearIt went just fine, got a long way with my documentation, 356 entries and counting, yikes. No weird WebBrowser things, no focus lost (not once). So lets just say this issue is fixed!
Comment #16
Posted on Jul 22, 2008 by Helpful WombatSweet. I'm buttoning up a few last things (better logging, etc.) and then I'll get this out the door.
Found an odd thing as I was writing unit tests. Running them in VS went fine, but on the command line, the tests where I'd actually serve content would fail. Turns out adding the /noisolation parameter to the command line fixed that, but it was still a little odd.
Status: Verified
Labels:
Type-Defect
Priority-Critical