ククログ

株式会社クリアコード > ククログ > Webアプリ型業務システムで、手詰まりになった問題をEdge拡張機能とUIオートメーションの合わせ技で解決した事例

Webアプリ型業務システムで、手詰まりになった問題をEdge拡張機能とUIオートメーションの合わせ技で解決した事例

結城です。

当社が受託開発を行う際には、なるべく「筋が良い」設計でソフトウェアを実装したり、仕様に問題がある場合は「筋が良い」仕様になるようご提案したりといった要領で、可能な限り技術的に「筋が良い」解決方法を取るように努めています。 技術的な制約によって理想的な解決の仕方ができない場合でも、当社では様々な可能性を探り、ご相談を頂いた時点では想定されていなかった方法で問題を解決します。

本記事で取り扱うEdge拡張機能の開発事例も、その一つです。 本事例では、「Webブラウザー・Edgeの仕様にない挙動を実現したい」という、通常であれば技術的に不可能と思われるご要望を、その背景にあった事情まで遡ってヒアリングして、無事解決まで導くことができました。 この記事では、当社が本事例でどのように問題を解きほぐして解決したのかをご紹介したいと思います。

頂いたご相談

お客様から頂いたお問い合わせは、要点をまとめると「Edgeのコンテキストメニューの『最新の情報に更新(リロード)』『印刷』や、キーボードショートカットの『F5』『Ctrl-R』『Ctrl-P』を無効化したい」というものでした。

こちらのお客様とはFirefoxのカスタマイズ案件で既にお取引があり、Firefoxでは特定のキーボードショートカットやメニュー項目をピンポイントで無効化するカスタマイズが可能だったことから、同様のカスタマイズをEdgeで行えないか、ということでお問い合わせを頂いたと考えられます。 しかしながら、ChromiumベースのブラウザーであるEdgeは、あらかじめ隠し設定やグループポリシーで制御可能なように実装されている機能以外は、任意で無効化することができません。 当社で技術資料を調査した限りでは、Edgeでは印刷操作は無効化できるものの、リロード操作を無効化する設定は用意されていない様子でした。 キーボードショートカットについては機能ごとに無効化できるようですが、メニュー項目の方が利用可能なままだと、機能の無効化としては不充分です。

このような場合、機能の入口である「メニュー項目」や「キーボードショートカット」を無効化する代わりに、機能の出口である「ページの再読み込み」や「印刷」の部分に介入して、「ページの再読み込みに特有の読み込みパターンを検知してブロックする」「印刷ダイアログが表示されたことを検知して、それを強制的に閉じるようにする」といった方法で、実質的に同等の結果を得られる可能性があります。

ただ、そういった代替案は、技術的な制約から不完全な物になりやすいです。 今回も、技術的に可能な実装方法(詳細は後述します)をとった場合に、本来ブロックされるべきでない操作までブロックされてしまったり、ブロックされて欲しい操作がブロックされない場合があったりする恐れがあり、留保なしに「この方法で完璧に実現できます」とご提案することはできませんでした。

隠れた要件をヒアリングで明らかにする

このような場合、お問い合わせ時点で当社に伝わってきていなかった隠れた要件をヒアリングすることが事態の打開に繋がることがあります。 そこで、お客様に詳しい背景事情を伺ったところ、以下のことが分かりました。

  • 専用の端末上で動作するWebブラウザーからのみアクセスして利用するWebベースの業務システムでの話である。
  • 当該業務システムは、元々IEでの閲覧を前提に製作された物で、業務システムの更新の度に閲覧に使用するブラウザーの要件が変わっており、その都度業務システムの改修で対応してきた。
    • ただし、業務システムの改修で対応できた部分は限定的であった。 (業務システム側での改修ではブラウザーの違いを吸収しきれない部分は、ブラウザーの拡張機能で吸収してきた。)
  • 当該業務システムの端末は、業務システム以外のページをブラウザーで閲覧することを考慮しない。Webブラウザーは業務システムの閲覧専用となる。
  • 当該業務システムにおいて、情報登録用のページなどの特定のページをリロードしてPOSTのフォームデータを再送信すると、情報が二重登録されるなどの問題が起こる場面があり、これを回避したい。
    • 本来であれば「フォームを再送信されても問題ないように業務システム側で対策する」のが最も安全で妥当だが、今回はリリーススケジュールの都合上、それができない業務システムには手を加えずに「フォームの再送信による二重登録などの問題の発生」を防ぐ方法が求められている。
      • つまり、本来やりたかったことは、リロード操作の禁止ではなく、POSTのフォームの再送信の禁止である
    • フォームの再送信を防ぐ必要があるページはURL単位で特定できており、それ以外のページは関知しなくてよい。
  • 印刷は、当該業務システムのWebページ内に用意された「印刷」ボタンをユーザーがクリックしたときにだけ可能にしたい(印刷用のページだけ印刷させたい)。 それ以外のページをユーザーが自由に印刷することを阻止したい

こうして明らかになった前提条件を踏まえて、先に検討した代替案の有効性を改めて評価したところ、この前提であれば代替案が有効に機能する(要件を満たせる)と分かりました。

代替案の実装

要件を満たすために実際にどのような代替案の実装を行ったのか、技術的な解説を交えつつご紹介します。

特定のページでのフォーム再送信抑止

前述したとおり、Edgeではあらかじめオプションが提供されていない部分の機能は無効化できません。 フォームデータを再送しようとしたときに、Edgeは「フォームを再送信しますか?」というメッセージを伴った確認ダイアログを表示します。 Edgeは実は「この確認ダイアログを表示せずにフォームを再送信できるようにする」オプションを持っていますが、「この確認を表示せずにフォームの再送信を却下する」オプションは持っていません。

オプションが提供されていない部分でブラウザーの挙動を変える手段としては、拡張機能があります。 しかし、EdgeなどのChromiumベースのブラウザーでは、拡張機能はブラウザーが提供するサンドボックス内で実行され、ブラウザーがあらかじめ用意しておいたAPIを介してのみブラウザーに影響を与えることができます。 Edgeはフォームの再送時の確認ダイアログを制御するAPIを拡張機能向けには提供していないため、拡張機能でもこの要件は実現できません。

このように、Edgeに備わったカスタマイズ手段の範囲では手詰まりとなってしまいますが、視点をブラウザーの外に移すと解決の道が見えてきます。 鍵になるのは、Windowsアプリケーションを他のアプリケーションから自動操作するためのUIオートメーションという仕組みです。

UIオートメーションのAPIを使うと、あるWindowsアプリケーションから、別のアプリケーションのウィンドウを様々なヒントを元に特定して、その中にあるUIコントロールを操作することができます。 「フォームを再送信しますか?」というメッセージを伴う確認ダイアログを検出して「キャンセル」ボタンを即座に押すような常駐アプリケーションを用意すれば、「Edgeでのフォームの再送信を禁止する」という目的も容易に達成可能です。

ただ、ここで問題になるのが「誤爆」の問題です。 UIオートメーションはWindowsアプリケーションのGUIを外から見て操作する物なので、操作対象のアプリケーションの内部状態を詳しく見て判断を行うといったことはできません。 今回の事例の場合、再送してはまずいフォーム以外のフォーム再送信までブロックしてしまうことになり、却ってトラブルが発生してしまう恐れがあります。

また、UIオートメーションを使用したEdgeのダイアログの検出処理は高負荷のため、UIオートメーションクライアントが常駐して短い間隔で検出を行うと、システムのパフォーマンスに悪影響を及ぼして実用に支障をきたす恐れもあります。

  • 確認を無効化してフォーム再送を禁止するオプションはない。
  • 拡張機能でもフォーム再送は禁止できない。
  • UIオートメーションではフォーム再送を禁止できるが、禁止したくない場面の操作まで禁止されてしまうし、常駐するとシステム負荷が高い。

この八方塞がりの状況で、本当に操作をキャンセルしたい場面でだけピンポイントでUIオートメーションを使ってフォームの再送確認を自動キャンセルさせて、不要な「誤爆」やパフォーマンス低下を最小化する方法として、今回当社ではEdge拡張機能とUIオートメーションクライアントの併用という解決策を採ることにしました。

Edge拡張機能は、Native Messagingという仕組みを用いて、事前に登録しておいたWindowsアプリケーション(Native Messaging Host)を起動することができます。 これを用いて、

  1. Edge拡張機能が、フォームの再送を禁止したいページが開かれたことを検知する。
  2. Edge拡張機能がNative Messagingを使用し、Native Messaging Hostを起動する。
  3. Native Messaging HostがUIオートメーションクライアントを起動する。
  4. UIオートメーションクライアントがEdgeのフォームの再送確認のダイアログを検出し、キャンセルする。(フォームの再送を禁止したいページを開いている間、常駐する。)
  5. フォームの再送を禁止したいページを閉じたら、UIオートメーションクライアントが自動的に終了する。

とすることにより、意図しない副作用・誤爆を最小限に抑えてフォーム再送を禁止することができました。

この一連のことを行うEdge拡張機能とNative Messaging Host、UIオートメーションクライアントは、RepostConfirmationCancelerプロジェクトとしてソースコードを公開しています。

特定のページ以外での印刷の抑止

印刷についても、フォーム再送時の確認と同様のことが言えます。

Edgeは印刷を全面的に禁止するオプション印刷プレビューを表示しないで印刷ダイアログを直接開くようにするオプションは持っていますが、それ以上の細かい制御は行うことができません。

他方、拡張機能による印刷の無効化は、部分的には可能です。 Webページの名前空間内で任意のJavaScriptコードを実行するコンテンツスクリプトの機能を用いて、JavaScriptのwindow.print()メソッドを置き換えれば、Webページ内のスクリプトからの印刷操作を任意に許可したり禁止したりといった制御ができます。

しかし、コンテンツスクリプトで印刷を無効化できるケースは、それだけです。 Edgeではページのコンテキストメニューなどからいつでもユーザーの判断で印刷操作を行うことができ、その際はbeforeprintイベントが通知されますが、このイベントはキャンセル不可能な仕様となっており、イベントを監視したとしても印刷は禁止できません。 また、セキュリティの関係からか、このコンテンツスクリプトの仕組みはPDFファイルを表示したタブでは機能しないため、PDFをタブ内で表示したときに表示される「印刷」ボタンによる印刷操作も素通しとなってしまいます。

UIオートメーションを使えば、Edgeの印刷プレビューダイアログが開かれたことを検出して自動的にキャンセルできます。 しかし先述した通り、アプリケーション内部の細かい状態までは分からないため、それがユーザーによるメニュー操作で行われた印刷なのか、Webページ内の「印刷」ボタンで行われた物なのかで印刷の可否を変えるということができません。 また、先述した通り、UIオートメーションクライアントを常駐させるとシステム負荷が問題となり得ます。

  • 印刷を特定の場面でだけ禁止するオプションはない。
  • 拡張機能でも印刷を禁止できる場面は限定的である。
  • UIオートメーションでは印刷を禁止できるが、禁止したくない場面の操作まで禁止されてしまうし、常駐するとシステム負荷が高い。

ということで、ここでもやはり、本当にキャンセルしたい場面でだけピンポイントでUIオートメーションを使って印刷ダイアログを自動キャンセルさせて、不要な「誤爆」やパフォーマンス低下を最小化する方法として、Edge拡張機能とUIオートメーションクライアントの併用が解決策となります。 Edge拡張機能のNative Messagingの仕組みを用いて、

  1. Edge拡張機能が、Webページにコンテンツスクリプトを読み込ませてwindow.print()を置き換えて、このメソッドが呼ばれた際に「許可されている印刷操作が実行されたこと」を自分自身に通知するようにする。 それと同時に、beforeprintイベントの監視を開始する。
  2. Edge拡張機能が、何らかのきっかけで印刷が実行されたことをbeforeprintイベントで検知する。 許可されている印刷操作が実行された(window.print()が呼ばれた)直後だった場合は、「UIオートメーションクライアントを終了する」指示を渡す形でNative Messaging Hostを起動する。 それ以外の場合は、「UIオートメーションクライアントを起動する」指示を渡す形でNative Messaging Hostを起動する。
  3. Native Messaging Hostが、渡された指示に従ってUIオートメーションクライアントを起動または終了する。
  4. 起動したUIオートメーションクライアントがEdgeの印刷ダイアログを検出し、キャンセルする。(すぐに検出できなければ、検出できるまで一定時間待つ。)
  5. ダイアログをキャンセルした(か、もしくは一定時間が経過してタイムアウトした)ら、UIオートメーションクライアントが自動的に終了する。

とすることにより、意図しない副作用・誤爆を最小限に抑えて、必要な場面以外での印刷操作を禁止することができました。

なお、この方法ではPDFを開いたタブからの印刷を禁止することができません。 しかし、お客様へのヒアリングの結果、「当該業務システムでPDFをタブで開く場面は印刷を行ってもよい場面だけであるため、この点は問題にならない」旨のご確認をいただくことができました。 技術的な制約によって実現できない部分はあるものの、要件は満たせている、という幸運な状態です。

この一連のことを行うEdge拡張機能とNative Messaging Host、UIオートメーションクライアントは、PrintCancelerプロジェクトとしてソースコードを公開しています。

まとめ

以上、Edgeの仕様上は実現が困難なご要望について、ヒアリングに基づいて要件を絞り込み、有効な代替案を実装・提供した事例をご紹介しました。

株式会社クリアコードは、FirefoxやThunderbird以外にも、ChromeやEdgeといったChromiumベースのブラウザーの法人運用において生じた様々な問題について、ソースコードレベルの調査や拡張機能などの開発も含めたサポートを行うサービスを有償でご提供しています。 これらのソフトウェアの運用で何かお困りの企業の運用担当者さまは、お問い合わせフォームよりお問い合わせ下さい。