C#: Best way to inject CSS into MSHTML instance? - c#

I'm trying to inject some CSS that accompanies some other HTML into a C# managed WebBrowser control. I am trying to do this via the underlying MSHTML (DomDocument property) control, as this code is serving as a prototype of sorts for a full IE8 BHO.
The problem is, while I can inject HTML (via mydomdocument.body.insertAdjacentHTML) and Javascript (via mydomdocument.parentWindow.execScript), it is flat-out rejecting my CSS code.
If I compare the string containing the HTML I want to insert with the destination page source after injection, the MSHTML's source will literally contain everything except for the <style> element and its underlying source.
The CSS passes W3C validation for CSS 2.1. It doesn't do anything too tricky, with the exception that some background-image properties have the image directly embedded into the CSS (e.g. background-image: url("data:image/png;base64 ...), and commenting out those lines doesn't change the result.
More strangely (and I am not sure if this is relevant), was that I was having no problems with this last week. I came back to it this week and, after switching around some of the code that handles the to-be-injected HTML before actual injection, it no longer worked. Naturally I thought that one of my changes might somehow be the problem, but after commenting all that logic out and feeding it a straight string the HTML is still appearing unformatted.
At the moment I'm injecting into the <body> tag, though I've attempted to inject into <head> and that's met with similar results.
Thanks in advance for your help!
tom

Ended up solving this myself:
mshtml.HTMLDocument test = (mshtml.HTMLDocument)webBrowser1.Document.DomDocument;
//inject CSS
if (test.styleSheets.length < 31) { // createStyleSheet throws "Invalid Argument if >31 stylesheets on page
mshtml.IHTMLStyleSheet css = (mshtml.IHTMLStyleSheet)test.createStyleSheet("", 0);
css.cssText = myDataClass.returnInjectionCSS(); // String containing CSS to inject into the page
// CSS should now affect page
} else {
System.Console.WriteLine("Could not inject CSS due to styleSheets.length > 31");
return;
}
What I didn't realize is that createStyleSheet creates a pointer that is still 'live' in the document's DOM... therefore you don't need to append your created stylesheet back to its parent. I ended up figuring this out by studying dynamic CSS code for Javascript as the implementations are pretty much identical.

Related

How do ASP.NET Core's "asp-fallback-*" CDN tag helpers work?

I understand what the asp-fallback-* tag helpers do. What I don't understand is how.
For example:
<link rel="stylesheet"
href="//ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/css/bootstrap.min.css"
asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css"
asp-fallback-test-class="sr-only"
asp-fallback-test-property="position"
asp-fallback-test-value="absolute" />
This loads bootstrap from the CDN, and loads the local copy if the CDN is down.
But how does it decide to do that? I assume it checks asp-fallback-test-class, asp-fallback-test-property, and asp-fallback-test-value. But what do those attributes mean?
If I want to hook up some other library off a CDN, I'll need to supply something for those, but I'm not sure what to put there.
There are lots of examples of this in action, but I can't find explanations about how this works.
UPDATE
I'm not really trying to understand how the tag helpers work - how they render, and so on. I'm trying to understand how to choose values for those attributes. For example, the jQuery fallback script usually has asp-fallback-test="window.jQuery" which makes sense - it's a test to see if jQuery has loaded. But the ones I've shown above are quite different. How does one choose them? If I want to use some other CDN delivered library, I'll need to specify values for those attributes... what would I use? Why were those ones chosen for bootstrap?
UPDATE 2
To understand how the fallback process itself works, and how those tags are written, see #KirkLarkin's answer. To understand why those test values were used, see my answer.
UPDATE 3
In bootstrap 5 the sr-only class was renamed to visually-hidden.
TL;DR:
A <meta> tag is added to the DOM that has a CSS class of sr-only.
Additional JavaScript is written to the DOM, which:
Locates said <meta> element.
Checks whether said element has a CSS property position that is set to absolute.
If no such property value is set, an additional <link> element is written to the DOM with a href of ~/lib/bootstrap/dist/css/bootstrap.min.css.
The LinkTagHelper class that runs against your <link> elements inserts a <meta> element in the output HTML that is given a CSS class of sr-only. The element ends up looking like this:
<meta name="x-stylesheet-fallback-test" content="" class="sr-only" />
The code that generates the element looks like this (source):
builder
.AppendHtml("<meta name=\"x-stylesheet-fallback-test\" content=\"\" class=\"")
.Append(FallbackTestClass)
.AppendHtml("\" />");
Unsurprisingly, the value for FallbackTestClass is obtained from the <link>'s asp-fallback-test-class attribute.
Right after this element is inserted, a corresponding <script> block is also inserted (source). The code for that starts off like this:
// Build the <script /> tag that checks the effective style of <meta /> tag above and renders the extra
// <link /> tag to load the fallback stylesheet if the test CSS property value is found to be false,
// indicating that the primary stylesheet failed to load.
// GetEmbeddedJavaScript returns JavaScript to which we add '"{0}","{1}",{2});'
builder
.AppendHtml("<script>")
.AppendHtml(JavaScriptResources.GetEmbeddedJavaScript(FallbackJavaScriptResourceName))
.AppendHtml("\"")
.AppendHtml(JavaScriptEncoder.Encode(FallbackTestProperty))
.AppendHtml("\",\"")
.AppendHtml(JavaScriptEncoder.Encode(FallbackTestValue))
.AppendHtml("\",");
There are a few things of interest here:
The last line of the comment, which refers to placeholders {0}, {1} and {2}.
FallbackJavaScriptResourceName, which represents a JavaScript resource that is output into the HTML.
FallbackTestProperty and FallbackTestValue, which are obtained from the attributes asp-fallback-test-property and asp-fallback-test-value respectively.
So, let's have a look at that JavaScript resource (source), which boils down to a function with the following signature:
function loadFallbackStylesheet(cssTestPropertyName, cssTestPropertyValue, fallbackHrefs, extraAttributes)
Combining this with the last line of the comment I called out earlier and the values of asp-fallback-test-property and asp-fallback-test-value, we can reason that this is invoked like so:
loadFallbackStylesheet('position', 'absolute', ...)
I won't dig into the fallbackHrefs and extraAttributes parameters as that should be somewhat obvious and easy to explore on your own.
The implementation of loadFallbackStylesheet does not do a great deal - I encourage you to explore the full implementation on your own. Here's the actual check from the source:
if (metaStyle && metaStyle[cssTestPropertyName] !== cssTestPropertyValue) {
for (i = 0; i < fallbackHrefs.length; i++) {
doc.write('<link href="' + fallbackHrefs[i] + '" ' + extraAttributes + '/>');
}
}
The script obtains the relevant <meta> element (it's assumed to be directly above the <script> itself) and simply checks that it has a property of position that is set to absolute. If it does not, additional <link> elements are written to the output for each fallback URL.
Ok I think I get it now, by combining #KirkLarkin's answer and common sense.
The sr-only is applied to a hidden meta element. If bootstrap is loaded then that element would get a css value of position:absolute. So that is tested, and if it's so, then it means Bootstrap has been loaded.
So for any library, you need to choose a good example of something only that library can do, and style a hidden <meta> tag accordingly, then specify which css style to test, and what value you are expecting.
For javscript it's even easier, because you can just test for the library itself, which usually has some well known variable added to the window or something to the DOM. So for jQuery it's window.jQuery, and for Bootstrap it can be tested as window.jQuery && window.jQuery.fn && window.jQuery.fn.modal and so on.

inheritance/overwriting existing CSS rules from different external files

I got an existing project, which used to be without Bootstrap(3), but now I have to change that and keep the old css stuff.
My current CSS order:
var bundleCSS = new StyleBundle("~/Views/bundle/newBundle").Include(
"~/Views/Shared/normalize.css",
"~/Scripts/jquery.ui/jquery-ui-latest.custom.css",
"~/Scripts/jquery.jqGrid/ui.jqgrid.css",
"~/Scripts/sweetalert/sweet-alert.css",
"~/Content/bootstrap.css",
"~/Views/Shared/OldButNeededLayout.css",
"~/Content/custom.css"); //will be rendered in that order
The custom.css is used to fix all errors that come from adding bootstrap.css to the project.
The problem here is, that OldButNeededLayout.css has roundabout 3000 lines of css-code and by the way the current project is quite big.
So when overwriting bootstrap with that old css and than fixing the errors, which came by adding bootstrap, than I get an unpredictable results.(because I dont't know where it will change something)
What to do or how to solve integrating bootstrap css/js into (my) existing project(s) ?
This sounds like a messy way to integrate Bootstrap whilst trying to avoid making any changes to the OldButNeededLayout file...
In my experience, integrating Bootstrap into an existing project is quite an undertaking and requires extensive modification of your HTML (adding classes and modifying markup structures) as well as extensive modification of your existing CSS, in order to make it play nice with Bootstrap.
Your approach will make maintenance difficult and will also deliver a lot of unnecessary CSS code to the client.
I would suggest:
Remove the OldButNeededLayout css from your project and clear the
contents of custom so that the vast majority of css is from
Bootstrap.
Obviously, this will look nothing like your site should, so
Methodically fix each bit of layout or styling that needs to change,
in your custom css until you have a site which resembles your
pre-Bootstrap site. Use bits from OldButNeededLayout that are
necessary, but make sure each and every style property is necessary.
This is a lot of work, of course, but it will result in a project which is more maintainable, predictable and concise for the client.
I don't see it as a reasonable expectation that you can just throw Bootstrap onto a large project and not expect to make major changes to your existing HTML and CSS.
The following suggestion isn't ideal and there's certainly better ways of implementing what you need, but I've kept my suggestion in line with your requirement...
In your new custom.css file, you could try being more specific when declaring an element. So if in OldButNeededLayout.css you overrode Bootstrap's style such as:
.divclass .jumbotron h1 {
color: red;
}
You could be declare a higher level of specificity in your custom.css file to ensure it will override what you in OldButNeededLayout.css:
.parent .divclass .jumbotron h1 {
color: blue;
}

Having trouble using razor C# with JavaScript

I am trying to get Razor (C#) and javascript to play nicely together but I can't seem to do it. I have searched the other articles on StackOverflow, but none of them seem
to work for me.
Some noticeable differences from other posts and mine include the following:
I am using an external JavaScript file (not mandatory, but it is there).
I am using a cshtml file for the header layout for all pages (which puts the head tag in a different file than the one actually attempting to call the function.
I also use jQuery, if it would be easier that way.
What I am trying to accomplish:
All I need to do is get the contents of a tag (innerHTML, or .html in jQuery) (by id, class, whatever) and assign that value to "AppState["gEntryID"] for use with the next page.
Some things I have tried:
function entryClickHandler()
{
#AppState["gEntryID"] = document.getElementById("tester").innerHTML;
}
AND
function entryClickHandler()
{
<text>
#AppState["gEntryID"] = document.getElementById("tester").innerHTML;
</text>
}
I have tried these (and a few other variations on these) in both the external file and the head section within the HeaderLayout File.
I understand that C# runs before the page and the JavaScript mostly runs after the page (at least with events such as this).
Any help would be greatly appreciated.
It doesn't work that way. You cannot set variables in the C#/Razor side with JavaScript without using a form post or ajax submit.
Javascript doesn't get access to the page until after Razor has done it's part and rendered and sent the page.

asp.net dynamically add usercontrols and position them

My issue is that I have a designer that will create a custom aspx page bu without any .net controls. I need a way of adding the controls dynamically. So far the only types of controls will be textboxes and a button, but there are 30 variations of what the textboxes can be (name, phone #, email, etc). Also the textboxes may or may not need to be required. Once the textboxes are added the form will be submitted to a db.
My first thought was to have the designer place something like [name] and then replace that with a user control that has a name textbox and a required field validator. In order to determine if the validator should be enabled I was thinking that the place holder could look like this, [name;val] or [name;noval]. I could either do replace the place holders in code dynamically or set up a tool that the user pastes their html into a textbox and clicks a button which then spits out the necessary code to create the aspx page.
I'm sure there must be a better way to do this but its a fairly unique problem so I haven't been able to find any alternatives. Does anyone have any ideas?
Thanks,
Kirk
IF your designer gives you html pages, just create a new website. copy and pages all the HTML pages with the Image folders and everything to your project. then for every HTML page create an aspx page, (with the same name) copy and pages the html's tags which are between to the aspx page's and for the body copy and paste HTML page's tags which are between into the of the aspx page.
Now you have your aspx page, exactly the same as html page.
Sounds like an attempt to over-engineer a solution to what should be a non-issue.
As #Alessandro mentioned in a comment above, why can't the designer provide you with pages that have the control markup? As it stands right now, the designer isn't providing you with "a custom aspx" so much as "a custom html page." If the designer is promising ASPX but delivering only HTML, that's a misinterpretation somewhere in the business requirements.
However, even if the designer is rightfully providing only HTML, there shouldn't be a problem with that. At worst, you can set each element you need on the server to runat="server" to access them on the server-side. Or, probably better, would be to simply replace them with the ASPX control markup for the relevant controls.
Write a simple parser that will recognize the [...] tags and replace them with corresponding controls. Its pretty easy to do and i've often done this... the tag i use is usually $$(..); though, but that doesn't matter as long as your parser knows your tags.
Such a parser will consist of a simple state-machine that can be in two states; text-mode or tag-mode. Loop through the whole page-text, char for char. As long as you're in text-mode you keep appending each char into a temporary buffer. As soon as you get into tag-mode you create a LiteralControl with the content of the temporary buffer and add it to the bottom of your Control-tree, and emtpy the buffer.
Now, you still keep adding each char into the buffer, but when you hit text-mode again, you analyze the content of the buffer and create the correct control - could be a simple switch case statement. Add the control to the bottom of your control tree and keep looping through the rest of the chars unto you read the end and keep switching back and forth between text-mode and tag-mode adding LiteralControls and concrete controls.
Simple example of such a parser... written in notepad in 4 minutes, but you should get the idea.
foreach (var c in text)
{
buffer.Append(c);
if (c== '[' && mode == Text)
{
mode = Tag;
Controls.Add(new LiteralControl(buffer));
buffer.Clear();
}
if (c == ']' && mode == Tag)
{
mode = Text;
switch (buffer)
{
case "[name]": Controls.Add(new NameControl());
... the rest of possible tags
}
buffer.Clear();
}

parsing html, source code, javascript problem

http://booking.travel24.com/index.php?KID=610000&&id=lmpergebnis&showresult=1&detail=zielgebiet&region=-1&ziel=-1&termin=20.02.2011&ruecktermin=17.03.2011&dauer=-1&abflughafen=46&personen=25;25&kategorie=-1&verpflegung=-1&zimmer=-1
I am trying to parse some HTML parts of this page, but when I check the source code I can not find this: "Tunesien, Marokko".
If I check with xdeveloper I can see this as html:
<a class="reglreg" href="javascript:s_hliste(20009);">Tunesien, Marokko</a>
but if i check source code of the page I can't find this. Why?
If you view the source and search for "Marokko" you will see there are several places where it occurs (loaded as data in several JavaScript arrays).
It appears as if some of the content is produced dynamically through the JavaScript loaded onto the page. That JavaScript builds HTML and changes the page to include the content you are looking for.
To answer your first real question
Why?
Because when you check the source code inside a browser, you'll get the original html code. Then javascript comes along and modify the DOM which you can follow in any modern browser's console.
can i get somehow whole source code
then? if i can not see it in browser
how can i see it?
To make it simple, it depends how you're trying to parse it. With what language?
maybe the data is coming via AJAX call, so it's not there on the html at the start, but dynamically added to it.
if you need to parse this, you can try to "emulate" the ajax call yourself.

Categories

Resources