Bug-org 1465702 EditorBase, TextEditor and HTMLEditor should use a stack class to store all information which are necessary to handle each EditAction like TextEditRules and HTMLEditRules
初回投稿日時: 2018年12月07日00時12分26秒
最終更新日時: 2018年12月12日00時03分17秒
カテゴリ: Editor Events Mozilla Core Mozilla65 バグ修正
SNS:
Tweet (list)
InputEvent.inputType
がbeforeinput
イベントの実装時に非常に重要なので、これ(とbeforeinput
)を実装するための準備として、Geckoのエディタを大きく書き換えていました。これをブロックしているバグを見ると、なんと5月ぐらいに取りかかってるようなので、実に半年間もやっていたわけです。
Geckoのエディタのコンクリートクラスは、TextEditor
とHTMLEditor
という名前通りのふたつのクラスがあります。前者は当然、<input>
要素や、<textarea>
要素、後者は、document.designMode
やcontenteditable
属性の指定された要素があるドキュメントでそれぞれインスタンス化されています。これらのエディタは、確実にinput
イベントを何らかの編集後に発火させるために編集が見込まれるメソッドの各所にスタック上にインスタンス化されるクラスを設置し、それの最後のインスタンスが破棄される際にinput
イベントが発火されるようにしています。この設計は普段のメンテナンス性を高める点では優秀ですが、InputEvent.inputType
の実装を行う今回のように、編集内容をイベント発火時に知ることが困難です(複数の編集がネストすることがあり、それぞれに対して別のinput
イベントを発火する必要があるため、なおさらです)。
そこで私が考えたアプローチは、これらの基底クラスも含め、ユーザの入力で編集が始まったとき、もしくは、Webアプリがエディタを通して何らかの編集を開始した時に、スタックに新しいクラスをインスタンス化し、その中にユーザ、もしくはWebアプリが行おうとした編集内容を記録しておき、input
イベントの発火時にはその情報を参照しようというものです。そうすれば、どんなに複雑な編集が行われたとしても、編集内容を示すInputEvent.inputType
は確実に、なおかつ、的確に設定できることが保証されます。しかし、これを行うには、エディタ外部からの呼び出し時にのみそのインスタンスを生成するという必要があります。
ここでまず問題になったのが、外部から呼び出されるメソッドがそのままpublicメソッドでは無いという点です。なぜなら、publicメソッドはクラス内部からも呼び出されるためです。もし、Webアプリによって、ユーザの編集中に他の編集がネストできないならこれは問題になりません。しかし、Web標準仕様はそれを許しています。つまり、純粋に外部から呼び出される場合にのみpublicメソッドを使用し、エディタ内部で編集中にはprotected以下のスコープのメソッドを呼び出すようにエディタ全体を書き変えるしかありません。
ところがここで問題になったのはこれら全てのクラスのpublicメソッドの多さです。具体的には、これらのクラスはnsIEditor
、nsIPlaintextEditor
、nsIHTMLEditor
、nsIEditorMailSupport
、nsIEditorStyleSheets
、nsIHTMLAbsPosEditor
、nsIHTMLInlineTableEditor
、nsIHTMLObjectResizer
、nsITableEditor
といった多くのインターフェースを実装し、さらにQuantum Flowプロジェクトで高速化のために削除した、もしくは複製されたpublicメソッドを多々もつクラスです。
この絶望的な物量を前にしても、内部からも呼び出されるpublicメソッドはprotectedメソッドとして実装し、publicメソッドは外部からの呼び出し時にも用い、対応するprotectedメソッドを呼ぶだけにしていくしかありませんでした。より、C++として安全な実装(ビルド時に内部からpublicメソッドが呼ばれないようにする)があるかもしれませんが、ひとまず、こういった形で半年かけて大きく書き換えたのがこのバグ修正となっています。