WebSurfer's Home

トップ > Blog 1   |   ログイン
APMLフィルター

Safari は downlevel browser?

by WebSurfer 2010年7月30日 23:14

ブラウザ Safai

Windows 版でしかチェックしていませんが、最新の Safari 5.0.1 は ASP.NET 3.5 でも依然として downlevel(Internet Explorer 4.0 より前のブラウザ)として扱われているようです。

(2014/3/3 追記: 現時点の ASP.NET によるブラウザ判定で Safari が downlevel と判断されることはありません。ただし、ASP.NET 2.0, 3.0, 3.5 での Menu コントロールについては話が別です。詳しくは下の「2014/3/3 追記」を見てください)

2 年ほど前、Menu コントロールの子メニュー/孫メニューをポップアップするスクリプトが生成されないことでこの問題を知りました。

2006 年の初めには Connect にレポートされていますが、そのままになっているようです(ただし、ASP.NET 4.0 でどうなったかは未確認です)。(2011/8/22 追記: ASP.NET 4 では対処済みでした。下の追記を参照)

ニュースグループ Microsoft Discussion Groups に応急処置が書いてあったんですが、すでにリンク切れになっているので、ここにそれを書いておきます。

応急処置は、簡単に言うと、Page クラスの PreInit イベントで "uplevel" であると誤魔化すということです。

具体的には、PreInit イベントで HTTP ヘッダーの中の User-Agent を調べ、Safari という文字列があったら、そこで Page.ClientTarge プロパティを uplevel に設定するというものです。

つまり、以下のようなクラスを作って、"uplevel" に誤魔化す必要のあるページはこれを継承してページを実装するということです。

public class CBaseMasterEvents : System.Web.UI.Page
{
  public CBaseMasterEvents()
  {
    Page.PreInit += new EventHandler(CBaseMaster_PreInit);
  }

  private void CBaseMaster_PreInit(object sender, EventArgs e) 
  { 
    if (Page.Request.Browser.Browser.Contains("Safari")) 
    {
      Page.ClientTarget = "uplevel"; 
    } 
  } 
}

ただし、この応急処置を適用すると、他の部分で互換性の問題が出る恐れがありますので、十分な検証が必要だと思います。


------ 2011/8/22 追記 ------

原因は ASP.NET 2.0 の CONFIG フォルダにあるブラウザ定義ファイル *.browser に Safari 用がないからでした(downlevel と定義されていうわけではなかったです)。それは Chrome も同じで、Menu コントロールは適切に表示されません。

(2014/3/3 追記: 「Menu コントロールは適切に表示されません」という表現は間違ってました。詳しくは下の「2014/3/3 追記」を見てください)

ASP.NET 4 の CONFIG フォルダには safari.browser, chrome.browser が含まれており、Menu コントロールは IE と同様に適切に表示されます。


------ 2014/3/3 追記 ------

ASP.NET 2.0, 3.0, 3.5 用のブラウザ定義ファイルは %windir%\Microsoft.NET\Framework\v2.0.50727 (ASP.NET 4 用は \v4.0.30319)の CONFIG\Browsers サブフォルダにあります。

昔のブラウザ定義ファイルは分かりませんが(Windows Update でアップデートされるので)、2014/3/3 時点では mozilla.browser ファイルの中に id="Safari" という browser 要素があって、Safari および Chrome はその定義の適用を受けます(継承元の Default ← Mozilla ← Gecko の定義も)。

その browser 要素の中に controlAdapters 要素(使用するコントロールアダプタを定義)が含まれており、それが Menu サーバーコントロール用のアダプタに、以下のように MenuAdapter クラス を使うよう指定しています。

<controlAdapters>
 <adapter 
  controlType="System.Web.UI.WebControls.Menu"
  adapterType="System.Web.UI.WebControls.Adapters.MenuAdapter" />
</controlAdapters>

このため、ASP.NET 2.0, 3.0, 3.5 では、Menu コントロールが html コードにレンダリングされた時、Safari, Chrome は span 要素、IE, Firefox は table 要素になり、ブラウザ上での動作が大幅に異なるという結果になります。

一方、ASP.NET 4 の CONFIG\Browsers サブフォルダには safari.browser, chrome.browser という専用のブラウザ定義ファイルがあり、その中にも継承元の Default ← Mozilla ← WebKit にも controlAdapters 要素は含まれていません。

それゆえ、ASP.NET 4 では、Menu コンロトールは IE, Forefox, Safari, Chrome いずれも同じ table 要素としてレンダリングされ、ブラウザ上での動作は同じになります。

(注)ASP.NET が CONFIG\Browsers サブフォルダのブラウザ定義ファイルの通りにブラウザを判定するかと言えば、必ずしもそういうわけではないので注意してください。ASP.NET はブラウザ判定ルーチンを System.Web.dll の中に持っており、既定ではそれを使っているそうです。詳しくは MSDN Blog の記事 ASP.NET の IE10 対応について を見てください。


------ 2016/1/12 追記 ------

MaintainScrollPositionOnPostBack(ポストバック前後で上下左右のスクロール位置を維持する機能)も ASP.NET 2.0, 3.0, 3.5 ではブラウザ定義の問題で Safari, Chrome では動きませんでした。(ASP.NET 4 では問題なし)

スクロール位置を維持する仕組み、Safari, Chrome のブラウザ定義は何故ダメか、その対処方法については別の記事「ポストバック前後でスクロール位置維持」に書きましたので、興味がありましたら見てください。

Tags: ,

ASP.NET

ImageButton と W3C 検証

by WebSurfer 2010年7月28日 14:43

ASP.NET の ImageButton コントロールを配置したページを W3C Markup Validation Service で検証すると、「あなたが使用しているドキュメントタイプではサポートされていない属性(border="0")が使われている」というエラーが出ます。

W3C 検証画面

ドキュメントタイプは ASP.NET のデフォルトで、XHTML 1.0 Transitional と宣言しています。border 属性の使用は、Strict ではダメですが、Transitional では許されているはずなんですが・・・

それはともかく、オリジナルの *.aspx コードにはそのような属性を指定していないにも関わらず、何故 ASP.NET が border="0" という古い属性のコードを書き出すのでしょう?

それは、W3C Markup Validation Service から ASP.NET が要求を受けた際、要求元を古いブラウザと解釈するからだそうです。

ちなみに、IE8 や Firefox 3.6.7 などの新しいブラウザから同じページを要求すると、style="border-width:0px;" となって、XHTML1.0 Strict でも有効な HTML/CSS のコードになります。

この件は ASP.NET Forum にもレポートされていて、回避策も書かれています。リンク切れになると困るのでここにもその回避策を書いておきます。(2011/8/20追記: MSDN ライブラリ にも対応方法が書いてありました。下の追記参照)

アプリケーション・ルート直下に App_Browsers フォルダを設け、その中に以下の内容の .browser ファイルを配置します。これによって ASP.NET は W3C Markup Validation Service に対して IE8 や Firefox 3.6.7 と同じコードを送信するようになるはずです。

<browsers>
  <browser id="w3cValidator" parentID="default">
    <identification>
      <userAgent match="^W3C_Validator" />
    </identification>

    <capture>
      <userAgent match="^W3C_Validator/(?'version'(?'major'\d+)(?'minor'\.\d+)\w*).*" />
    </capture>

    <capabilities>
      <capability name="browser" value="w3cValidator" />
      <capability name="majorversion" value="${major}" />
      <capability name="minorversion" value="${minor}" />
      <capability name="version" value="${version}" />
      <capability name="w3cdomversion" value="1.0" />
      <capability name="xml" value="true" />
      <capability name="tagWriter" value="System.Web.UI.HtmlTextWriter" />
    </capabilities>
  </browser>
</browsers>

なお、上記は ASP.NET 3.5 の話で、ASP.NET 4.0 でどうなっているかは未確認です。


------ 2011/8/20 追記 ------

MSDN ライブラリの Visual Studio と ASP.NET の XHTML 標準 の「マークアップ検証に対するブラウザー機能の構成」というセクションに、本件に関する記述がありました。

.NET Framework 4 の記事なので、サーバを ASP.NET 4 にアップグレードしても、ブラウザー定義を追加しての対応は必要なようです。

なお、そこで紹介されている W3C 検証用のブラウザ定義は以下の通りです。

<browsers>
  <browser id="W3C_Validator" parentID="default">
    <identification>
      <userAgent match="^W3C_Validator" />
    </identification>
    <capabilities>
      <capability name="browser" value="W3C Validator" />
      <capability name="ecmaScriptVersion" value="1.2" />
      <capability name="javascript" value="true" />
      <capability name="supportsCss" value="true" />
      <capability name="tables" value="true" />
      <capability name="tagWriter" 
         value="System.Web.UI.HtmlTextWriter" />
      <capability name="w3cdomversion" value="1.0" />
    </capabilities>
  </browser>
</browsers>

W3C のホームページの User's guide for the W3C Markup Validator によると、通常の User-Agent は W3C_Validator/xx.xxxx とのことなので、MSDN ライブラリのブラウザ定義の方が適切だと思います。

Tags: ,

ASP.NET

再帰を使って FindControl

by WebSurfer 2010年7月27日 22:01

先のポストで、特定のコントロールのオブジェクトを取得するには FindControl メソッドを使うと書きましたが、見つけられないケースがあります。

例えば、Wizard コントロールの FinishButton は、Wizard.FindControl メソッドでは見つかりません。

階層構造は以下のようになっていて、FinishButton は FinishNavigationTemplateContainer に含まれています。そこでなら FindControl メソッドで取得できますが、それより上位のコントロールからでは取得できません。

Wizard

WizardChildTable

TableRow

TableCell

FinishNavigationTemplateContainer

この例に限らず、FindControl メソッドでコントロールを見つけられないというケースは結構あると思います。そのあたりの詳しい理由は MSDN ライブラリの 方法: ID を使用してサーバー コントロールにアクセスする を見てください。

そのような場合で、かつ、名前つきコンテナーへの参照が取得できない場合は、上記の MSDN ライブラリの「名前付けコンテナーの内部にあるコントロールの検索」のセクションに書いてあるように、再帰によってコントロールを見つけるメソッドを自力で書いて使うのが確実だと思います。

以下のような感じです。

protected Control FindControlRecursive(Control root, string id)
{
  if (root.ID == id)
  {
    return root;
  }

  foreach (Control ctrl in root.Controls)
  {
    Control foundCtrl = FindControlRecursive(ctrl, id);
    if (foundCtrl != null)
    {
        return foundCtrl;
    }
  }
  return null;
}

Tags:

ASP.NET

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。

Calendar

<<  2024年5月  >>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

View posts in large calendar