App ShareSheetのURL貼り付けをリンクとして処理する
クリップボードに text/uri-list のみが含まれるApp ShareSheet由来のURLを、リンクとして正しく処理できるようになりました。これまで Safari の挙動(text/plain + text/uri-list の組み合わせ)のみを想定していた判定ロジックが、単独の text/uri-list にも対応します。
背景
iOSなどのApp ShareSheetからURLを共有する場合、クリップボードには text/uri-list のみが格納されます。一方、SafariでURLをコピーした場合は text/plain と text/uri-list の2つのMIMEタイプが組み合わされます。
Lexxyの #isOnlyURLPasted メソッドは「URLのみが貼り付けられた」ことを検出し、リンク要素を生成する判定に使われます。しかしこのメソッドは types.length === 2 かつ両タイプが揃っていることを必須条件としていたため、App ShareSheetから貼り付けた場合は条件を満たさず、URLがプレーンテキストとして扱われるか、リンクへの変換がスキップされていました。
技術的な変更
#isOnlyURLPasted の判定条件を緩和し、text/uri-list 単独のケースも受け入れるよう変更されました。
変更前:
#isOnlyURLPasted(clipboardData) {
// Safari URLs are copied as a text/plain + text/uri-list object
const types = Array.from(clipboardData.types)
return types.length === 2 && types.includes("text/uri-list") && types.includes("text/plain")
}
変更後:
#isOnlyURLPasted(clipboardData) {
// Safari URLs are copied as a text/plain + text/uri-list object
// App ShareSheet URLs are copied as solo text/uri-list object
const types = Array.from(clipboardData.types)
return types.length
&& types.length <= 2
&& types.includes("text/uri-list")
&& (types.length < 2 || types.includes("text/plain"))
}
新しい条件は次の3つの要件を表現しています:
-
types.length: タイプが1つ以上存在する(空のクリップボードを除外) -
types.length <= 2: タイプが最大2つ(URL以外のリッチデータを含む貼り付けを除外) -
types.includes("text/uri-list")かつtypes.length < 2 || types.includes("text/plain"):text/uri-listが必須、2つ目のタイプが存在する場合はtext/plainでなければならない
テストヘルパー EditorHandle.paste も同様に拡張されました。uriList オプションが追加され、text パラメータが null の場合でも text/plain のセットをスキップしながら text/uri-list のみをクリップボードに設定できるようになっています。
async paste(text, { html, files = [], uriList } = {}) {
// ...
if (typeof text === "string") event.clipboardData.setData("text/plain", text)
if (html) event.clipboardData.setData("text/html", html)
if (uriList) event.clipboardData.setData("text/uri-list", uriList)
}
設計判断
=== 2 の厳格な等値比較を <= 2 の上限チェックへ変換するアプローチが採用されました。これにより、タイプ数を「ちょうど2つ」から「1つ以上2つ以下」へ緩和しつつ、3つ以上のMIMEタイプを持つリッチなクリップボードデータ(例:text/html を伴うコピー)をリンク変換の対象外に保つ安全ネットを維持しています。
2つ目のタイプに対する条件 (types.length < 2 || types.includes("text/plain")) は短絡評価を利用した記述で、単独 text/uri-list(length < 2)はそのまま通し、2タイプの場合のみ text/plain の存在を要求します。条件の追加を最小限に抑えながら既存のSafari対応を壊さない実装といえます。
まとめ
#isOnlyURLPasted の判定条件を数行変更するだけで、App ShareSheetというモバイル起点のURL共有シナリオへの対応が実現されました。types.length === 2 という暗黙の前提をより明示的な上限・組み合わせ条件に置き換えることで、既存のSafari挙動との互換性を保ちつつ、クリップボードの多様なMIMEタイプ組み合わせに対して堅牢なコードになっています。