iOS Safari閱讀模式研究

Horky發表於2016-03-23

這是一篇在2013年準備的資料,現在分享出來,供有需要的同學參考。

要點

(1) 閱讀模式的檢測
在frame載入完成後,觸發一個timer來檢測是否可以使用閱讀模式。檢測的方式是使用JavaScriptCore framework的介面執行一段JS指令碼,然後取JS中屬性值來判斷是否可以進入閱讀模式。如果當前頁面可以進入閱讀模式,將在位址列顯示閱讀模式切換按鈕。

(2) 閱讀模式的執行
  當使用者點選閱讀模式切換按鈕時,會依次執行:

i. 執行閱讀模式檢查指令碼,判斷目前是否可以進入閱讀模式。
ii. 建立WebView並載入閱讀模式頁面的HTML頁面,iPad下為Reader~iPad.html .
iii. 在頁面允許修改Window物件的位置,執行閱讀模式處理指令碼。
v. 顯示頁面

(3) 閱讀模式頁面的控制
 Safari實現了幾個類來處理閱讀模式的顯示和操作。

主要涉及的類

classes
TabDocument代表了一個頁簽下的頁面文件,這裡有頁面的主要控制操作和閱讀模式的控制操作(ReaderControllerDelegate)。
BrowserReaderView是負責閱讀模式頁面顯示的類。

閱讀模式檢測的序列圖

下面是一個在正常頁面載入後觸發TabDocument_readerAvailabilityDetectionTimer的序列圖:
sequence #1

除此之外,另外兩個函式-[BrowserController stopFromAddressView:]-[TabDocument _progressDidStall] 也會觸發閱讀模式檢測的執行。

當Timer觸發後會開始真正執行指令碼:
sequence #2

JSEvaluateScript,JSObject等都是JavaScriptCore framework提供的介面。

執行完成指令碼,會執行回撥函式-[TabDocument _didDetectReaderAvailability:]將把指令碼中的ReaderArticleFinder.isReaderModeAvailable的值傳入,再根據這個值判斷是否要顯示閱讀模式按鈕。

閱讀模式的顯示

當點選閱讀模式按鈕時,下圖的2.1是重新發起檢測指令碼的執行,步驟2.2則開始載入顯示閱讀模式。
sequence #3

WebView放出一個介面webView:didClearWindowObject:forFrame,允許使用者修改全域性物件, Safari就是在這個位置提前於頁面真正載入就去執行閱讀模式處理指令碼,然後在頁面載入完時會依據下面的寫法,執行ReaderJS.loaded(),顯示頁面內容。

<body class="preloading"onload="ReaderJS.loaded();" onscroll="articleHasScrolled();">

ReaderJS就是閱讀模式內容抽取指令碼中的物件。

下面是執行閱讀模式內容抽取的指令碼執行過程:
sequence #4

*這是Apple關於webView:didClearWindowObject:forFrame的說明:

Use this method to set custom properties on the window object before the page is actually loaded. Every time a frame loads or is reloaded all DOM properties are cleared from the window object so the new page has a fresh window object to use. If the page you are loading depends on specific window object properties to exist, they should be added at this point before any scripts are executed.

附上三個使用到檔案,分別是:
Reader~ipad.html iPad下使用的閱讀模式頁面html檔案
safari_reader_check.js 閱讀模式檢測JS指令碼
safari_reader_clicked.js 閱讀模式內容抽取JS指令碼
到GitHub上檢視,連結:iOSSafariReaderMode

參考

. 分析的過程記錄

相關文章