/// <summary> /// Calls the ContentPlaceHolderHeadRepair.Repair method /// </summary> /// <param name="e"></param> protected override void OnLoad(EventArgs e) { //Parses link, meta, and script tags that got missed by the Head>CPH bug. ContentPlaceHolderFixes.RepairPageHeader(this); //Fire the events later base.OnLoad(e); }
/// <summary> /// Reparses meta and link tags that weren't parsed correctly the first time. /// Only works inside ContentPlaceHolder controls located inside the head. /// 1) Parses literal controls: looks for self-closing and empty meta, link, and script controls. /// Supports Visible and EnableViewState attributes correctly. EnableViewState=false if not specified. /// The TemplateControl is re-calculated. /// 2) Parses HtmlGenericControls (link, meta, script tags with runat="server"). HtmlGenericControl instances for 'script' can /// only exist if added through code, since <script runat="server"> is for server side code. Supports it anyway :) /// Supports Visible and EnableViewState attributes correctly. EnableViewState=false if not specified. /// The TemplateControl is re-calculated. /// Note: script references don't have a built-in control, so we use a custom ScriptReference instance to provide the rebasing support. /// </summary> /// <param name="p"></param> public static void RepairPageHeader(Page p) { //Aquire a collection of references to each contentplaceholder in the Head section. //ContentPlaceHolders do not account for being located in an HtmlHead, and simply //perform the normal parsing logic. We will patch this by iterating through incorrectly identified tags such as //<link> and <meta>, and replacing them with the proper control type. //As a bonus, we also make script references work, but that is an extra - Head doesn't usually do anything special //with those anyway. //Note that each contentplaceholder usually contains a child ContentPlaceHolder if (p.Header != null) { ////////////////////// Literal parsing ////////////////////////////// //Get a collection of all of the LiteralControls in the head //This will handle link, meta, and script includes List <LiteralControl> toParse = ControlUtils.GetControlsOfType <LiteralControl>(p.Header); //handle literal links if (toParse != null) { foreach (LiteralControl lc in toParse) { //if the literal is directly inside a content tag, on a content page, the TemplateControl property will //incorrectly point to the MasterPage. //So if we use lc.AppRelativeTemplateSourceDirectory, it doesn't work //However, if we use this.AppRelativeTemplateSourceDirectory and //we have a MasterPage>MasterPage(with relative literal stylesheet reference)>Page //Then the relative stylesheet reference will be broke, relative to the Page. //The solution is to find the child TemplateControl of lc's nearest ContentPlaceHolder parent. //We do this before ParseLiteral control, because this value will be propogated to the //component controls. lc.TemplateControl = ContentPlaceHolderFixes.GetAdjustedParentTemplateControl(lc); //Parse literal control Control c = ParseLiteralControl(lc, lc.Text); //notused: //Article.SetPathRecursive(c, getAdjustedParent(lc).AppRelativeTemplateSourceDirectory);//used to be this. //Replace lc.Parent.Controls.AddAt(lc.Parent.Controls.IndexOf(lc), c); lc.Parent.Controls.Remove(lc); } } //handle links with runat="server" //We just want the outermost cphs List <ContentPlaceHolder> cphList = ControlUtils.GetControlsOfType <ContentPlaceHolder>(p.Header, false, true); if (cphList != null) { //There may be multiple CPHs in the head section foreach (ContentPlaceHolder cph in cphList) { //Get a collection of all of the HtmlGenericControls in the current ContentPlaceHolder. List <HtmlGenericControl> toFix = ControlUtils.GetControlsOfType <HtmlGenericControl>(cph); if (toFix == null) { continue; } //This will handle all link tags, meta tags, or script tags with runat="server" //Also affects script tags parsed in above section (URL resolution) //Iterate through the collection, replacing or modifying the neccesary objects. foreach (HtmlGenericControl hgc in toFix) { HtmlControl replacement = null; switch (hgc.TagName.ToLower()) { case "link": //Create a replacement HtmlLink object with identical attributes. //HtmlLink will resolve virtual URLs on the href attribute at render-time, unlike HtmlGenericControl. replacement = new HtmlLink(); break; case "meta": //Create a replacement HtmlMeta object with identical attributes. replacement = new HtmlMeta(); break; case "script": //Create a new script reference, which resolves the src attribute at render-time replacement = new ScriptReference(); break; } if (replacement != null) { //Adjust the TemplateControl for the *other* ContentPlaceHolder bug. replacement.TemplateControl = GetAdjustedParentTemplateControl(hgc.TemplateControl); //Turn off ViewState replacement.EnableViewState = false; //Copy attributes foreach (string s in hgc.Attributes.Keys) { string val = hgc.Attributes[s]; replacement.Attributes.Add(s, val); } //Assign known properties that aren.t collection-backed replacement.EnableViewState = hgc.EnableViewState; replacement.Visible = hgc.Visible; //Insert the new object next to the old, then remove the old. hgc.Parent.Controls.AddAt(hgc.Parent.Controls.IndexOf(hgc), replacement); hgc.Parent.Controls.Remove(hgc); } } } } } /* Analyize TemplateControls. Prints TemplateControl/TemplateSourceDirectory tree for diagnostics. * List<Control> allcontrols = GetControlsOfType<Control>(this); * List<TemplateControl> uniqueTemplateControls =new List<TemplateControl>(); * Debug.WriteLine(Page.AppRelativeTemplateSourceDirectory); * foreach (Control c in allcontrols) * { * //Debug.WriteLine(c.ID + " (" + c.TemplateControl.ID + ") - " + c.AppRelativeTemplateSourceDirectory); * if (!uniqueTemplateControls.Contains(c.TemplateControl)){ * uniqueTemplateControls.Add(c.TemplateControl); * string s = c.TemplateSourceDirectory; * } * * } * StringWriter sw = new StringWriter(); * PrintTree(this.Header, 0, sw); * this.Header.Controls.AddAt(0, new LiteralControl(Server.HtmlEncode((sw.ToString())))); */ }