1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

スマホブラウザの「PC版サイトを表示」が裏で何をやっているのか気になったので、調査をしてみました。

長いので、結論だけ知りたい場合は こちら

仮説

まずは仮説を立てて、それを検証して、裏付けるといった感じで進めます。

仮説として、思いつく候補は2つです。

1. User Agent(UA)の偽装

ブラウザはリクエストヘッダーに UA を含めて送信します。
サーバー側で UA を見てコンテンツを出し分けているサイトでは、UA をデスクトップ用に変えれば PC 向けのコンテンツが返ってきます。

これは、モバイル用のサイトは m.example.com にして、PC 版サイトは example.com にしているような、昔ながらのサイトでの動作から予想できます。

2. Viewport 幅の変更

レスポンシブデザインのサイトは CSS メディアクエリで画面幅を見てレイアウトを切り替えています。
width=device-width の viewport 指定を無視して幅を広くすれば、PC 向けのレイアウトが表示されます。

実際、「PC版サイト表示」を ON にするとレスポンシブなサイトが PC 向けレイアウトに変わります。
これは、viewport 幅が変わっているからと推測できます。

検証

1. UA の変更を確認する

Chrome DevTools の Network conditions パネルを使うと、viewport はそのままで UA だけを変更することができます。

  1. DevTools → ⋮ → More tools → Network conditions
  2. User agent の "Use browser default" のチェックを外す
  3. プルダウンから "Chrome — Android Mobile" などを選択

device emulation モード(ツールバーのモバイルアイコン)は UA と viewport を両方変えてしまい、切り分けにならないため、ここでは利用しません。

この状態で、https://www.yahoo.co.jp/ にアクセスすると、m.yahoo.co.jp にリダイレクトされました。UA を元に戻すと yahoo.co.jp に戻ります。
UA を変えるだけでリダイレクトが起きたので、Yahoo はサーバーサイドで UA を見て URL を振り分けていることがわかります。

2. viewport の変更を確認する

UA は PC のままで、viewport だけを狭めて https://www.yahoo.co.jp/ にアクセスしてみます。
これでは、リダイレクトは起きませんでした。
viewport 幅はサーバーには届かないので、これは当たり前かもしれません。

一方、Qiita 等のレスポンシブ設計のサイトは、viewport 幅を狭めるだけでモバイル向けレイアウトに切り替わります。
CSS メディアクエリが viewport 幅に反応するからです。

検証結果

検証内容をまとめると、2つの変更はそれぞれ別の役割を持っています。

変更 サーバーサイドへの影響 レンダリングへの影響
UA 変更 UA 検出しているサイトが別コンテンツを返す なし
viewport 変更 なし レスポンシブ CSS が反応してレイアウトが変わる

旧来型のサイト(m.yahoo.co.jp への振り分けなど)には UA 変更が、レスポンシブデザインのサイトには viewport 変更が効きます。

したがって、この両方のサイトの状態に対応できる「PC版サイト表示」は、UA 変更と viewport 変更の両方を行っていると言えます。

更なる検証

Chromium のコードを実際に見ることで、裏付けを行います。

UA 変更の実装

Chromium の UA 生成コードを見ると、Mobile トークンはデフォルトではなく、条件付きで追加される仕組みになっています。

components/embedder_support/user_agent_utils.cc
std::string GetUserAgentInternal() {
  ...
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
  if (base::CommandLine::ForCurrentProcess()->HasSwitch(kUseMobileUserAgent)) {
    product += " Mobile";
  }
#endif
  ...
}

Android の Chrome はプロセス起動時に kUseMobileUserAgent スイッチを設定するため、通常は Mobile 付きの UA になります。
「PC版サイト表示」はこのスイッチが立っていない状態の UA を代わりに使う操作です。

「PC版サイト表示」をオンにすると、NavigationController.setUseDesktopUserAgent() が呼ばれ、C++ 側の SetUseDesktopUserAgentInternal() に処理が渡ります。

content/browser/renderer_host/navigation_controller_android.cc
void NavigationControllerAndroid::SetUseDesktopUserAgentInternal(
    bool enabled,
    bool reload_on_state_change,
    bool skip_on_initial_navigation) {
  ...
  // Set the flag in the NavigationEntry.
  entry->SetIsOverridingUserAgent(enabled);
  navigation_controller_->delegate()->UpdateOverridingUserAgent();
  ...
}

IsOverridingUserAgent() フラグが現在の NavigationEntry に保存され、ページがリロードされます。
リロード時には kUseMobileUserAgent スイッチなしで生成した UA が使われ、Mobile トークンが消えます。

Android の Chrome での UA の変化は以下のようになります。

// 通常時(kUseMobileUserAgent あり)
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/X Mobile Safari/537.36

// PC版サイト表示時(kUseMobileUserAgent なし相当)
Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/X Safari/537.36

Chrome 107 以降の UA Reduction により platform 文字列は Linux; Android 10; K に固定されているため、PC版表示に切り替えても platform 部分は変わりません。
変わるのは Mobile トークンの有無だけです。

また、Sec-CH-UA-Mobile ヘッダーも同じ kUseMobileUserAgent スイッチで制御されています。

components/embedder_support/user_agent_utils.cc
bool GetMobileBitForUAMetadata() {
  // The mobile bit for UA-CH is true if the platform is iOS, or if it's
  // Android and not a desktop form factor, AND the kUseMobileUserAgent switch
  // is present.
#if BUILDFLAG(IS_ANDROID)
  if (base::android::device_info::is_desktop() ||
      base::android::device_info::is_xr()) {
    return false;
  }
#endif

#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
  return base::CommandLine::ForCurrentProcess()->HasSwitch(kUseMobileUserAgent);
#else
  return false;
#endif
}

UA 文字列に Mobile を付加するかどうかと、Sec-CH-UA-Mobile?1 にするかどうかが、同じスイッチで一元管理されています。
PC 版表示に切り替えると、UA 文字列と Sec-CH-UA-Mobile の両方がまとめて変わります。

viewport 変更の実装

viewport_meta_enabled フラグも UA と同じ構造で、Android/iOS ではモバイル向けにデフォルトで viewport_meta_enabled = true が設定されています。

third_party/blink/public/common/web_preferences/web_preferences.h
  bool viewport_meta_enabled = BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS);

UA override がアクティブになると、web_contents_impl.cc でこのフラグが false にリセットされます。

content/browser/web_contents/web_contents_impl.cc
  if (GetController().GetVisibleEntry() &&
      GetController().GetVisibleEntry()->GetIsOverridingUserAgent()) {
#if BUILDFLAG(IS_ANDROID)
    // Only ignore viewport meta tag when Request Desktop Site is used, but not
    // in other situations where embedder changes to arbitrary mobile UA string.
    is_request_android_desktop_site =
        renderer_preferences_.user_agent_override.ua_metadata_override &&
        !renderer_preferences_.user_agent_override.ua_metadata_override->mobile;
    prefs.viewport_meta_enabled = !is_request_android_desktop_site;
#else
    prefs.viewport_meta_enabled = false;
#endif
  }

また、レンダリングエンジン側では GetViewportDescription() でこのフラグを参照し、<meta name="viewport"> のパース結果を適用するかどうかを制御しています。

third_party/blink/renderer/core/frame/viewport_data.cc
ViewportDescription ViewportData::GetViewportDescription() const {
  ViewportDescription applied_viewport_description = viewport_description_;
  bool viewport_meta_enabled =
      document_->GetSettings() &&
      document_->GetSettings()->GetViewportMetaEnabled();
  if (legacy_viewport_description_.type !=
          ViewportDescription::kUserAgentStyleSheet &&
      viewport_meta_enabled)
    applied_viewport_description = legacy_viewport_description_;
  if (ShouldOverrideLegacyDescription(viewport_description_.type))
    applied_viewport_description = viewport_description_;
  ...
}

legacy_viewport_description_ は、<meta name="viewport"> をパースした結果です。viewport_meta_enabledfalse のとき、この値は applied_viewport_description に代入されず、viewport_description_(UA のデフォルト)がそのまま返ります。

この UA のデフォルト viewport 幅は、viewport_style_resolver.ccViewportStyle::kMobile ケースで定義されています。

third_party/blink/renderer/core/css/resolver/viewport_style_resolver.cc
    // We only want to use the device scale portion of the zoom factor, because
    // the page layout size should remain fixed relative to page zoom in order
    // to reflow into it.
    case mojom::blink::ViewportStyle::kMobile: {
      description.min_width = ViewportLength::Fixed(980 * DeviceScaleZoom());
      return description;
    }

したがって、<meta name="viewport"> のパース結果が適用されない場合は、この 980px がデフォルト viewport 幅になります。

まとめ

「PC版サイト表示」は、Chrome がモバイル向けに施した2つの設定をそれぞれ解除する操作です。

  1. kUseMobileUserAgent スイッチで追加されていた、Mobile トークンを UA から除く
    • 同じスイッチで制御される Sec-CH-UA-Mobile?1 から ?0 に変わる
  2. viewport_meta_enabledfalse にすることで、<meta name="viewport"> の代わりにデフォルトの 980px viewport を使わせる

Chromium の内部では NavigationController.setUseDesktopUserAgent() がトリガーとなり、この2つが同時に処理されます。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?