日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

从零实现的浏览器Web脚本

發(fā)布時間:2023/11/3 C# 82 coder
生活随笔 收集整理的這篇文章主要介紹了 从零实现的浏览器Web脚本 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

從零實現(xiàn)的瀏覽器Web腳本

在之前我們介紹了從零實現(xiàn)Chrome擴展,而實際上瀏覽器級別的擴展整體架構非常復雜,盡管當前有統(tǒng)一規(guī)范但不同瀏覽器的具體實現(xiàn)不盡相同,并且成為開發(fā)者并上架Chrome應用商店需要支付5$的注冊費,如果我們只是希望在Web頁面中進行一些輕量級的腳本編寫,使用瀏覽器擴展級別的能力會顯得成本略高,所以在本文我們主要探討瀏覽器Web級別的輕量級腳本實現(xiàn)。

描述

在前邊的從零實現(xiàn)Chrome擴展中,我們使用了TS完成了整個擴展的實現(xiàn),并且使用Rspack作為打包工具來構建應用,那么雖然我們實現(xiàn)輕量級腳本是完全可以直接使用JS實現(xiàn)的,但是畢竟隨著腳本的能力擴展會變得越來越難以維護,所以同樣的在這里我們依舊使用TS來構建腳本,并且在構建工具上我們可以選擇使用Rollup來打包腳本,本文涉及的相關的實現(xiàn)可以參考個人實現(xiàn)的腳本集合https://github.com/WindrunnerMax/TKScript

當然瀏覽器是不支持我們直接編寫Web級別腳本的,所以我們需要一個運行腳本的基準環(huán)境,當前有很多開源的腳本管理器:

  • GreaseMonkey: 俗稱油猴,最早的用戶腳本管理器,為Firefox提供擴展能力,采用MIT license協(xié)議。
  • TamperMonkey: 俗稱篡改猴,最受歡迎的用戶腳本管理器,能夠為當前主流瀏覽器提供擴展能力,開源版本采用GPL-3.0 license協(xié)議。
  • ViolentMonkey: 俗稱暴力猴,完全開源的用戶腳本管理器,同樣能夠為當前主流瀏覽器提供擴展能力,采用MIT license協(xié)議。
  • ScriptCat: 俗稱腳本貓,完全開源的用戶腳本管理器,同樣能夠為當前主流瀏覽器提供擴展能力,采用 GPL-3.0 license協(xié)議。

此外還有很多腳本集合網(wǎng)站,可以用來分享腳本,例如GreasyFork。在之前我們提到過,在研究瀏覽器擴展能力之后,可以發(fā)現(xiàn)擴展的權限實在是太高了,那么同樣的腳本管理器實際上也是通過瀏覽器擴展來實現(xiàn)的,選擇可信的瀏覽器擴展也是很重要的,例如在上邊提到的TamperMonkey在早期的版本是開源的,但是在18年之后倉庫就不再繼續(xù)更新了,也就是說當前的TamperMonkey實際上是一個閉源的擴展,雖然上架谷歌擴展是會有一定的審核,但是畢竟是閉源的,開源對于類似用戶腳本管理器這類高級用戶工具來說是一個建立信任的信號,所以在選擇管理器時也是需要參考的。

腳本管理器實際上依然是基于瀏覽器擴展來實現(xiàn)的,通過封裝瀏覽器擴展的能力,將部分能力以API的形式暴露出來,并且提供給用戶腳本權限來應用這些API能力,實際上這其中涉及到很多非常有意思的實現(xiàn),例如腳本中可以訪問的windowunsafeWindow,那么如何實現(xiàn)一個完全隔離的window沙箱環(huán)境就值的探索,再比如在Web頁面中是無法跨域訪問資源的,如何實現(xiàn)在Inject Script中跨域訪問資源的CustomEvent通信機制也可以研究一下,以及如何使用createElementNSHTML級別實現(xiàn)Runtime以及Script注入、腳本代碼組裝后//# sourceURL的作用等等,所以如果有興趣的同學可以研究下ScriptCat,這是國內(nèi)的同學開發(fā)的腳本管理器,注釋都是中文會比較容易閱讀。那么本文還是主要關注于應用,我們從最基本的UserScript腳本相關能力,到使用Rollup來構建腳本,再通過實例來探索腳本的實現(xiàn)來展開本文的討論。

UserScript

在最初GreaseMonkey油猴實現(xiàn)腳本管理器時,是以UserScript作為腳本的MetaData也就是元數(shù)據(jù)塊描述,并且還以GM.開頭提供了諸多高級的API使用,例如可跨域的GM.xmlHttpRequest,實際上相當于實現(xiàn)了一整套規(guī)范,而后期開發(fā)的腳本管理器大都會遵循或者兼容這套規(guī)范,以便復用相關的生態(tài)。其實對于開發(fā)者來說這也是個麻煩事,因為我們沒有辦法控制用戶安裝的瀏覽器擴展,而我們的腳本如果用到了某一個擴展單獨實現(xiàn)的API,那么就會導致腳本在其他擴展中無法使用,特別是將腳本放在腳本平臺上之后,沒有辦法構建渠道包去分發(fā),所以平時還是盡量使用各大擴展都支持的MetaAPI來開發(fā),避免不必要的麻煩。

此外在很久之前我一直好奇在GreasyFork上是如何實現(xiàn)用戶腳本的安裝的,因為實際上我并沒有在那個安裝腳本的按鈕之后發(fā)現(xiàn)什么特殊的事件處理,以及如何檢測到當前已經(jīng)安裝腳本管理器并且實現(xiàn)通信的,之后簡單研究了下發(fā)現(xiàn)實際上只要用戶腳本是以.user.js結尾的文件,就會自動觸發(fā)腳本管理器的腳本安裝功能,并且能夠自動記錄腳本安裝來源,以便在打開瀏覽器時檢查腳本更新,同樣的后期這些腳本管理器依然會遵循這套規(guī)范,既然我們了解到了腳本的安裝原理,在后邊實例一節(jié)中我會介紹下我個人進行腳本分發(fā)的最佳實踐。那么在本節(jié),我們主要介紹常見的Meta以及API的使用,一個腳本的整體概覽可以參考https://github.com/WindrunnerMax/TKScript/blob/gh-pages/copy-currency.user.js

Meta

元數(shù)據(jù)是以固定的格式存在的,主要目的是便于腳本管理器能夠解析相關屬性比如名字和匹配的站點等,每一條屬性必須使用雙斜杠//開頭,不得使用塊注釋/* */,與此同時,所有的腳本元數(shù)據(jù)必須放置于// ==UserScript==// ==/UserScript==之間才會被認定為有效的元數(shù)據(jù),即必須按照以下格式填寫:

// ==UserScript==
// @屬性名 屬性值
// ==/UserScript==

常用的屬性如下所示:

  • @name: 腳本的名字,在@namespace級別的腳本的唯一標識符,可以設置語言,例如// @name:zh-CN 文本選中復制(通用)
  • @author: 腳本的作者,例如// @author Czy
  • @license: 腳本的許可證,例如// @license MIT License
  • @description: 腳本功能的描述,在安裝腳本時會在管理對話框中呈現(xiàn)給用戶,同樣可以設置語言,例如// @description:zh-CN 通用版本的網(wǎng)站復制能力支持
  • @namespace: 腳本的命名空間,用于區(qū)分腳本的唯一標識符,例如// @namespace https://github.com/WindrunnerMax/TKScript
  • @version: 腳本的版本號,腳本管理器啟動時通常會對比改字段決定是否下載更新,例如// @version 1.1.2
  • @updateURL: 檢查更新地址,在檢查更新時會首先訪問該地址,來對比@version字段來決定是否更新,該地址應只包含元數(shù)據(jù)而不包含腳本內(nèi)容。
  • @downloadURL: 腳本更新地址(https協(xié)議),在檢查@updateURL后需要更新時,則會請求改地址獲取最新的腳本,若未指定該字段則使用安裝腳本地址。
  • @include: 可以使用*表示任意字符,支持標準正則表達式對象,腳本中可以有任意數(shù)量的@include規(guī)則,例如// @include http://www.example.org/*.bar
  • @exclude: 可以使用*表示任意字符,支持標準正則表達式對象,同樣支持任意數(shù)量的規(guī)則且@exclude的匹配權限比@include要高,例如// @exclude /^https?://www\.example\.com/.*$/
  • @match: 更加嚴格的匹配模式,根據(jù)Chrome的Match Patterns規(guī)則來匹配,例如// @match *://*.google.com/foo*bar
  • @icon: 腳本管理界面顯示的圖標,幾乎任何圖像都可以使用,但32x32像素大小是最合適的資源大小。
  • @resource: 在安裝腳本時,每個@resource都會下載一次,并與腳本一起存儲在用戶的硬盤上,這些資源可以分別通過GM_getResourceTextGM_getResourceURL訪問,例如// @resource name https://xxx/xxx.png
  • @require: 腳本所依賴的其他腳本,通常為可以提供全局對象的庫,例如引用jQuery則使用// @require https://cdn.staticfile.org/jquery/3.7.1/jquery.min.js
  • @run-at: 用于指定腳本執(zhí)行的時機,可用的參數(shù)只能為document-start頁面加載前、document-end頁面加載后資源加載前、document-idle頁面與資源加載后,默認值為document-end
  • @noframes: 當存在時,該命令會限制腳本的執(zhí)行。該腳本將僅在*文檔中運行,而不會在嵌套框架中運行,不需要任何參數(shù),默認情況下此功能處于關閉狀態(tài)即允許腳本在iframe中運行。
  • @grant: 腳本所需要的權限,例如unsafeWindowGM.setValueGM.xmlHttpRequest等,如果沒有指定@grant則默認為none,即不需要任何權限。

API

API是腳本管理器提供用來增強腳本功能的對象,通過這些腳本我們可以實現(xiàn)針對于Web頁面更加高級的能力,例如跨域請求、修改頁面布局、數(shù)據(jù)存儲、通知能力、剪貼板等等,甚至于在Beta版的TamperMonkey中,還有著允許用戶腳本讀寫HTTP OnlyCookie的能力。同樣的,使用API也有著固定的格式,在使用之前必須要在Meta中聲明相關的權限,以便腳本將相關函數(shù)動態(tài)注入,否則會導致腳本無法正常運行,此外還需要注意的是相關函數(shù)的命名可能不同,在使用時還需要參考相關文檔。

// ==UserScript==
// @grant unsafeWindow
// ==/UserScript==
  • GM.info: 獲取當前腳本的元數(shù)據(jù)以及腳本管理器的相關信息。
  • GM.setValue(name: string, value: string | number | boolean): Promise<void>: 用于寫入數(shù)據(jù)并儲存,數(shù)據(jù)通常會存儲在腳本管理器本體維護的IndexDB中。
  • GM.getValue(name: string, default?: T): : Promise<string | number | boolean | T | undefined>: 用于獲取腳本之前使用GM.setValue賦值儲存的數(shù)據(jù)。
  • GM.deleteValue(name: string): Promise<void>: 用于刪除之前使用GM.setValue賦值儲存的數(shù)據(jù)。
  • GM.getResourceUrl(name: string): Promise<string>: 用于獲取之前使用@resource聲明的資源地址。
  • GM.notification(text: string, title?: string, image?: string, onclick?: () => void): Promise<void>: 用于調(diào)用系統(tǒng)級能力的窗口通知。
  • GM.openInTab(url: string, open_in_background?: boolean ): 用于在新選項卡中打開指定的URL
  • GM.registerMenuCommand(name: string, onclick: () => void, accessKey?: string): void: 用于在腳本管理器的菜單中添加一個菜單項。
  • GM.setClipboard(text: string): void: 用于將指定的文本數(shù)據(jù)寫入剪貼板。
  • GM.xmlHttpRequest(options: { method?: string, url: string, headers?: Record<string, string>, onload?: (response: { status: number; responseText: string , ... }) => void , ... }): 用于與標準XMLHttpRequest對象類似的發(fā)起請求的功能,但允許這些請求跨越同源策略。
  • unsafeWindow: 用于訪問頁面原始的window對象,在腳本中直接訪問的window對象是經(jīng)過腳本管理器封裝過的沙箱環(huán)境。

單看這些常用的API其實并不好玩,特別是其中很多能力我們也可以直接換種思路借助腳本來實現(xiàn),當然有一些例如unsafeWindowGM.xmlHttpRequest我們必須要借助腳本管理器的API來完成。那么在這里我們還可以聊一下腳本管理器中非常有意思的實現(xiàn)方案,首先是unsafeWindow這個非常特殊的API,試想一下如果我們完全信任用戶當前頁面的window,那么我們可能會直接將API掛載到window對象上,聽起來似乎沒有什么問題,但是設想這么一個場景,假如用戶訪問了一個惡意頁面,然后這個網(wǎng)頁又恰好被類似https://*/*規(guī)則匹配到了,那么這個頁面就可以獲得訪問我們的腳本管理器的相關API,這相當于是瀏覽器擴展級別的權限,例如直接獲取用戶磁盤中的文件內(nèi)容,并且可以直接將內(nèi)容跨域發(fā)送到惡意服務器,這樣的話我們的腳本管理器就會成為一個安全隱患,再比如當前頁面已經(jīng)被XSS攻擊了,攻擊者便可以借助腳本管理器GM.cookie.get來獲取HTTP OnlyCookie,并且即使不開啟CORS也可以輕松將請求發(fā)送到服務端。那么顯然我們本身是準備使用腳本管理器來Hook瀏覽器的Web頁面,此時反而卻被越權訪問了更高級的函數(shù),這顯然是不合理的,所以GreaseMonkey實現(xiàn)了XPCNativeWrappers機制,也可以理解為針對于window對象的沙箱環(huán)境。

那么我們在隔離的環(huán)境中,可以得到window對象是一個隔離的安全window環(huán)境,而unsafeWindow就是用戶頁面中的window對象。曾經(jīng)我很長一段時間都認為這些插件中可以訪問的window對象實際上是瀏覽器拓展的Content Scripts提供的window對象,而unsafeWindow是用戶頁面中的window,以至于我用了比較長的時間在探尋如何直接在瀏覽器拓展中的Content Scripts直接獲取用戶頁面的window對象,當然最終還是以失敗告終,這其中比較有意思的是一個逃逸瀏覽器拓展的實現(xiàn),因為在Content ScriptsInject Scripts是共用DOM的,所以可以通過DOM來實現(xiàn)逃逸,當然這個方案早已失效。

var unsafeWindow;
(function() {
    var div = document.createElement("div");
    div.setAttribute("onclick", "return window");
    unsafeWindow = div.onclick();
})();

此外在FireFox中還提供了一個wrappedJSObject來幫助我們從Content Scripts中訪問頁面的的window對象,但是這個特性也有可能因為不安全在未來的版本中被移除。那么為什么現(xiàn)在我們可以知道其實際上是同一個瀏覽器環(huán)境呢,除了看源碼之外我們也可以通過以下的代碼來驗證腳本在瀏覽器的效果,可以看出我們對于window的修改實際上是會同步到unsafeWindow上,證明實際上是同一個引用。

unsafeWindow.name = "111111";
console.log(window === unsafeWindow); // false
console.log(window); // Proxy?{Symbol(Symbol.toStringTag): 'Window'}
console.log(window.onblur); // null
unsafeWindow.onblur = () => 111;
console.log(unsafeWindow); // Window?{ ... }
console.log(unsafeWindow.name, window.name); // 111111 111111
console.log(window.onblur === unsafeWindow.onblur); // true
const win = new Function("return this")();
console.log(win === unsafeWindow); // true

實際上在@grant none的情況下,腳本管理器會認為當前的環(huán)境是安全的,同樣也不存在越權訪問的問題了,所以此時訪問的window就是頁面原本的window對象。此外,如果觀察仔細的話,我們可以看到上邊的驗證代碼最后兩行我們突破了這些擴展的沙盒限制,從而可以在未@grant unsafeWindow情況下能夠直接訪問unsafeWindow,當然這并不是什么大問題,因為腳本管理器本身也是提供unsafeWindow訪問的,而且如果在頁面未啟用unsafe-evalCSP情況下這個例子就失效了。只不過我們也可以想一下其他的方案,是不是直接禁用Function函數(shù)以及eval的執(zhí)行就可以了,但是很明顯即使我們直接禁用了Function對象的訪問,也同樣可以通過構造函數(shù)的方式即(function(){}).constructor來訪問Function對象,所以針對于window沙箱環(huán)境也是需要不斷進行攻防的,例如小程序不允許使用FunctionevalsetTimeoutsetInterval來動態(tài)執(zhí)行代碼,那么社區(qū)就開始有了手寫解釋器的實現(xiàn),對于我們這個場景來說,我們甚至可以直接使用iframe創(chuàng)建一個about:blankwindow對象作為隔離環(huán)境。

那么我們緊接著可以簡單地討論下如何實現(xiàn)沙箱環(huán)境隔離,其實在上邊的例子中也可以看到直接打印window輸出的是一個Proxy對象,那么在這里我們同樣使用Proxy來實現(xiàn)簡單的沙箱環(huán)境,我們需要實現(xiàn)的是對于window對象的代理,在這里我們簡單一些,我們希望的是所有的操作都在新的對象上,不會操作原本的對象,在取值的時候可以做到首先從我們新的對象取,取不到再去window對象上取,寫值的時候只會在我們新的對象上操作,在這里我們還用到了with操作符,主要是為了將代碼的作用域設置到一個特定的對象中,在這里就是我們創(chuàng)建的的context,在最終結果中我們可以看到我們對于window對象的讀操作是正確的,并且寫操作都只作用在沙箱環(huán)境中。

const context = Object.create(null);
const global = window;
const proxy = new Proxy(context, {
    // `Proxy`使用`in`操作符號判斷是否存在屬性
    has: () => true,
    // 寫入屬性作用到`context`上
    set: (target, prop, value) => {
        target[prop] = value;
        return true;
    },
    // 特判特殊屬性與方法 讀取屬性依次讀`context`、`window`
    get: (target, prop) => {
        switch (prop) {
            // 重寫特殊屬性指向
            case "globalThis":
            case "window":
            case "parent":
            case "self":
                return proxy;
            default:
                if (prop in target) {
                    return target[prop];
                }
                const value = global[prop];
                // `alert`、`setTimeout`等方法作用域必須在`window`下
                if (typeof value === "function" && !value.prototype) {
                    return value.bind(global);
                }
                return value;
        }
    },
});

window.name = "111";
with (proxy) {
    console.log(window.name); // 111
    window.name = "222";
    console.log(name); // 222
    console.log(window.name); // 222
}
console.log(window.name); // 111
console.log(context); // { name: '222' }

那么現(xiàn)在到目前為止我們使用Proxy實現(xiàn)了window對象隔離的沙箱環(huán)境,總結起來我們的目標是實現(xiàn)一個干凈的window沙箱環(huán)境,也就是說我們希望網(wǎng)站本身執(zhí)行的任何不會影響到我們的window對象,比如網(wǎng)站本體在window上掛載了$$對象,我們本身不希望其能直接在開發(fā)者的腳本中訪問到這個對象,我們的沙箱環(huán)境是完全隔離的,而用戶腳本管理器的目標則是不同的,比如用戶需要在window上掛載事件,那么我們就應該將這個事件處理函數(shù)掛載到原本的window對象上,那么我們就需要區(qū)分讀或者寫的屬性是原本window上的還是Web頁面新寫入的屬性,顯然如果想解決這個問題就要在用戶腳本執(zhí)行之前將原本window對象上的key記錄副本,相當于以白名單的形式操作沙箱。由此引出了我們要討論的下一個問題,如何在document-start即頁面加載之前執(zhí)行腳本。

實際上document-start是用戶腳本管理器中非常重要的實現(xiàn),如果能夠保證腳本是最先執(zhí)行的,那么我們幾乎可以做到在語言層面上的任何事情,例如修改window對象、Hook函數(shù)定義、修改原型鏈、阻止事件等等等等。當然其本身的能力也是源自于瀏覽器拓展,而如何將瀏覽器擴展的這個能力暴露給Web頁面就是需要考量的問題了。首先我們大概率會寫過動態(tài)/異步加載JS腳本的實現(xiàn),類似于下面這種方式:

const loadScriptAsync = (url: string) => {
    return new Promise<Event>((resolve, reject) => {
        const script = document.createElement("script");
        script.src = url;
        script.async = true;
        script.onload = e => {
            script.remove();
            resolve(e);
        };
        script.onerror = e => {
            script.remove();
            reject(e);
        };
        document.body.appendChild(script);
    });
};

那么現(xiàn)在就有一個明顯的問題,我們?nèi)绻?code>body標簽構建完成也就是大概在DOMContentLoaded時機再加載腳本肯定是達不到document-start的目標的,甚至于在head標簽完成之后處理也不行,很多網(wǎng)站都會在head內(nèi)編寫部分JS資源,在這里加載同樣時機已經(jīng)不合適了。那么對于整個頁面來說,最先加載的必定是html這個標簽,那么很明顯我們只要將腳本在html標簽級別插入就好了,配合瀏覽器擴展中backgroundchrome.tabs.executeScript動態(tài)執(zhí)行代碼以及content.js"run_at": "document_start"建立消息通信確認注入的tab,這個方法是不是看起來很簡單,但就是這么簡單的問題讓我思索了很久是如何做到的。此外這個方案目前在擴展V2中是可以行的,在V3中移除了chrome.tabs.executeScript,替換為了chrome.scripting.executeScript,當前的話使用這個API可以完成框架的注入,但是做不到用戶腳本的注入,因為無法動態(tài)執(zhí)行代碼。

(function () {
    const script = document.createElementNS("http://www.w3.org/1999/xhtml", "script");
    script.setAttribute("type", "text/javascript");
    script.innerText = "console.log(111);";
    script.className = "injected-js";
    document.documentElement.appendChild(script);
    script.remove();
})();

此外我們可能納悶,為什么腳本管理器框架和用戶腳本都是采用這種方式注入的,而在瀏覽器控制臺的Sources控制面板下只能看到一個userscript.html?name=xxxxxx.user.js卻看不到腳本管理器的代碼注入,實際上這是因為腳本管理器會在用戶腳本的最后部分注入一個類似于//# sourceURL=chrome.runtime.getURL(xxx.user.js)的注釋,其中這個sourceURL會將注釋中指定的URL作為腳本的源URL,并在Sources面板中以該URL標識和顯示該腳本,這對于在調(diào)試和追蹤代碼時非常有用,特別是在加載動態(tài)生成的或內(nèi)聯(lián)腳本時。

window["xxxxxxxxxxxxx"] = function (context, GM_info) {
  with (context)
    return (() => {
      // ==UserScript==
      // @name       TEST
      // @description       TEST
      // @version    1.0.0
      // @match      http://*/*
      // @match      https://*/*
      // ==/UserScript==

      console.log(window);

      //# sourceURL=chrome-extension://xxxxxx/DEBUG.user.js
    })();
};

還記得我們最初的問題嗎,即使我們完成了沙箱環(huán)境的構建,但是如何將這個對象傳遞給用戶腳本,我們不能將這些變量暴露給網(wǎng)站本身,但是又需要將相關的變量傳遞給腳本,而腳本本身就是運行在用戶頁面上的,否則我們沒有辦法訪問用戶頁面的window對象,所以接下來我們就來討論如何保證我們的高級方法安全地傳遞到用戶腳本的問題。實際上在上邊的source-map我們也可以明顯地看出來,我們可以直接借助閉包以及with訪問變量即可,并且在這里還需要注意this的問題,所以在調(diào)用該函數(shù)的時候通過如下方式調(diào)用即可將當前作用域的變量作為傳遞給腳本執(zhí)行。

script.apply(proxyContent, [ proxyContent, GM_info ]);

我們都知道瀏覽器會有跨域的限制,但是為什么我們的腳本可以通過GM.xmlHttpRequest來實現(xiàn)跨域接口的訪問,而且我們之前也提到了腳本是運行在用戶頁面也就是作為Inject Script執(zhí)行的,所以是會受到跨域訪問的限制的。那么解決這個問題的方式也比較簡單,很明顯在這里發(fā)起的通信并不是直接從頁面的window發(fā)起的,而是從瀏覽器擴展發(fā)出去的,所以在這里我們就需要討論如何做到在用戶頁面與瀏覽器擴展之間進行通信的問題。在Content Script中的DOM和事件流是與Inject Script共享的,那么實際上我們就可以有兩種方式實現(xiàn)通信,首先我們常用的方法是window.addEventListener + window.postMessage,只不過這種方式很明顯的一個問題是在Web頁面中也可以收到我們的消息,即使我們可以生成一些隨機的token來驗證消息的來源,但是這個方式畢竟能夠非常簡單地被頁面本身截獲不夠安全,所以在這里通常是用的另一種方式,即document.addEventListener + document.dispatchEvent + CustomEvent自定義事件的方式,在這里我們需要注意的是事件名要隨機,通過在注入框架時于background生成唯一的隨機事件名,之后在Content ScriptInject Script都使用該事件名通信,就可以防止用戶截獲方法調(diào)用時產(chǎn)生的消息了。

// Content Script
document.addEventListener("xxxxxxxxxxxxx" + "content", e => {
    console.log("From Inject Script", e.detail);
});

// Inject Script
document.addEventListener("xxxxxxxxxxxxx" + "inject", e => {
    console.log("From Content Script", e.detail);
});

// Inject Script
document.dispatchEvent(
    new CustomEvent("xxxxxxxxxxxxx" + "content", {
        detail: { message: "call api" },
    }),
);

// Content Script
document.dispatchEvent(
    new CustomEvent("xxxxxxxxxxxxx" + "inject", {
        detail: { message: "return value" },
    }),
);

腳本構建

在構建Chrome擴展的時候我們是使用Rspack來完成的,這次我們換個構建工具使用Rollup來打包,主要還是Rspack更適合打包整體的Web應用,而Rollup更適合打包工具類庫,我們的Web腳本是單文件的腳本,相對來說更適合使用Rollup來打包,當然如果想使用Rspack來體驗Rust構建工具的打包速度也是沒問題的,甚至也可以直接使用SWC來完成打包,實際上在這里我并沒有使用Babel而是使用ESBuild來構建的腳本,速度也是非常不錯的。

此外,之前我們也提到過腳本管理器的API雖然都對GreaseMonkey兼容,但實際上各個腳本管理器會出現(xiàn)特有的API,這也是比較正常的現(xiàn)象畢竟是不同的腳本管理器,完全實現(xiàn)相同的功能是意義不大的,至于不同瀏覽器的差異還不太一樣,瀏覽器之間的API差異是需要運行時判斷的。那么如果我們需要全平臺支持的話就需要實現(xiàn)渠道包,這個概念在Android開發(fā)中是非常常見的,那么每個包都由開發(fā)者手寫顯然是不現(xiàn)實的,使用現(xiàn)代化的構建工具除了方便維護之外,對于渠道包的支持也更加方便,利用環(huán)境變量與TreeShaking可以輕松地實現(xiàn)渠道包的構建,再配合腳本管理器以及腳本網(wǎng)站的同步功能,就可以實現(xiàn)分發(fā)不同渠道的能力。

Rollup

這一部分比較類似于各種SDK的打包,假設在這里我們有多個腳本需要打包,而我們的目標是將每個工程目錄打包成單獨的包,Rollup提供了這種同時打包多個輸入輸出能力,我們可以直接通過rollup.config.js配置一個數(shù)組,通過input來指定入口文件,通過output來指定輸出文件,通過plugins來指定插件即可,我們輸出的包一般需要使用iife立執(zhí)行函數(shù)也就是能夠自動執(zhí)行的腳本,適合作為script標簽這樣的輸出格式。

[
  {
    input: "./packages/copy/src/index.ts",
    output: {
      file: "./dist/copy.user.js",
      format: "iife",
      name: "CopyModule",
    },
    plugins: [ /* ... */ ],
  },
  // ...
];

如果需要使用@updateURL來檢查更新的話,我們還需要單獨打包一個meta文件,打包meta文件與上邊同理,只需要提供一個空白的blank.js作為input,之后將meta數(shù)據(jù)注入就可以了,這里需要注意的一點是這里的format要設置成es,因為我們要輸出的腳本不能帶有自執(zhí)行函數(shù)的(function () {})();包裹。

[
  {
    input: "./meta/blank.js",
    output: {
      file: "./dist/meta/copy.meta.js",
      format: "es",
      name: "CopyMeta",
    },
    plugins: [{ /* ... */}],
  },
  // ...
];

前邊我們也提到了渠道包的問題,那么如果想打包渠道包的話主要有以下幾個需要注意的地方:首先是在命令執(zhí)行的時候,我們要設置好環(huán)境變量,例如在這里我設置的環(huán)境變量是process.env.CHANNEL;其次在打包工具中,我們需要在打包的時候將定義的整個環(huán)境變量替換掉,實際上這里也是個非常有意思的事情,雖然我們認為process是個變量,但是在打包的時候我們是當字符串處理的,利用@rollup/plugin-replaceprocess.env.CHANNEL字符串替換成執(zhí)行命令的時候設置的環(huán)境變量;之后在代碼中我們需要定義環(huán)境變量的使用,在這里特別要注意的是要寫成直接表達式而不是函數(shù)的形式,因為如果寫成了函數(shù)我們就無法觸發(fā)TreeShakingTreeShaking是靜態(tài)檢測的方式,我們需要在代碼中明確指明這個表達式的Boolean值;最后再通過環(huán)境變量來設置文件的輸出,最終將所有的文件打包出來即可。

// package.json scripts
// "build:special": "cross-env CHANNEL=SPECIAL rollup -c"

// index.ts
const isSpecialEnv = process.env.CHANNEL === "SPECIAL";
if (isSpecialEnv) {
    console.log("IS IN SPECIAL ENV");
}

// @rollup/plugin-replace
replace({
    "process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV),
    "process.env.CHANNEL": JSON.stringify(process.env.CHANNEL),
    "preventAssignment": true,
})

// rollup.config.js
if(process.env.CHANNEL === "SPECIAL"){
    config.output.file = "./dist/copy.special.user.js";
}

此外,我們不能使用rollup-plugin-terser等模塊去壓縮打包的產(chǎn)物,特別是要分發(fā)到GreasyFork等平臺中,因為本身腳本的權限也可以說是非常高的,所以配合代碼審查是非常有必要的。同樣的也因為類似的原因,類似于jQuery這種包我們是不能夠直接打包到項目中的,一般是需要作為external配合@require外部引入的,類似于GreasyFork也會采取白名單機制審查外部引入的包。大部分情況下我們需要使用document-start去前置執(zhí)行代碼,但是在此時head標簽是沒有完成的,所以在這里還需要特別關注下CSS注入的時機,如果腳本是在document-start執(zhí)行的話通常就需要自行注入CSS而不能直接使用rollup-plugin-postcss的默認注入能力。那么到這里實際上Rollup打包這部分并沒有特別多需要注意的能力,基本就是我們普通的前端工程化項目,完整的配置可以參考https://github.com/WindrunnerMax/TKScript/blob/master/rollup.config.js

// `Plugins Config` 
const buildConfig = {
    postcss: {
        minimize: true,
        extensions: [".css"],
    },
    esbuild: {
        exclude: [/node_modules/],
        sourceMap: false,
        target: "es2015",
        minify: false,
        charset: "utf8",
        tsconfig: path.resolve(__dirname, "tsconfig.json"),
    },
};

// `Script Config` 
const scriptConfig = [
    {
        name: "Copy",
        meta: {
            input: "./meta/blank.js",
            output: "./dist/meta/copy.meta.js",
            metaFile: "./packages/copy/meta.json",
        },
        script: {
            input: "./packages/copy/src/index.ts",
            output: "./dist/copy.user.js",
            injectCss: false,
        },
    },
    // ...
];


export default [
    // `Meta`
    ...scriptConfig.map(item => ({
        input: item.meta.input,
        output: {
            file: item.meta.output,
            format: "es",
            name: item.name + "Meta",
        },
        plugins: [metablock({ file: item.meta.metaFile })],
    })),
    // `Script`
    ...scriptConfig.map(item => ({
        input: item.script.input,
        output: {
            file: item.script.output,
            format: "iife",
            name: item.name + "Module",
        },
        plugins: [
            postcss({ ...buildConfig.postcss, inject: item.script.injectCss }),
            esbuild(buildConfig.esbuild),
            // terser({ format: { comments: true } }),
            metablock({ file: item.meta.metaFile }),
        ],
    })),
];

Meta

在上邊雖然我們完成了主體包的構建,但是似乎我們遺漏了一個大問題,也就是腳本管理器腳本描述Meta的生成,幸運的是在這里有Rollup的插件可以讓我們直接調(diào)用,當然實現(xiàn)類似于這種插件的能力本身并不復雜,首先是需要準備一個meta.json的文件,在其中使用json的形式將各種配置描述出來,之后便可以通過遍歷的方式生成字符串,在Rollup的鉤子函數(shù)中講字符串注入到輸出的文件中即可。當然這個包還做了很多事情,例如對于字段格式的檢查、輸出內(nèi)容的美化等等。

{
    "name": {
        "default": "??????文本選中復制(通用)??????",
        "en": "Text Copy Universal",
        "zh-CN": "??????文本選中復制(通用)??????"
      },
    "namespace": "https://github.com/WindrunnerMax/TKScript",
    "version": "1.1.2",
    "description": {
        "default": "文本選中復制通用版本,適用于大多數(shù)網(wǎng)站",
        "en": "Text copy general version, suitable for most websites.",
        "zh-CN": "文本選中復制通用版本,適用于大多數(shù)網(wǎng)站"
      },
    "author": "Czy",
    "match": [
        "http://*/*",
        "https://*/*"
    ],
    "supportURL": "https://github.com/WindrunnerMax/TKScript/issues",
    "license": "GPL License",
    "installURL": "https://github.com/WindrunnerMax/TKScript",
    "run-at": "document-end",
    "grant": [
        "GM_registerMenuCommand",
        "GM_unregisterMenuCommand",
        "GM_notification"
    ]
}

實例

那么在這部分我們實現(xiàn)用戶腳本的實例,雖然我們平時可能Ctrl C+V代碼比較多,但是Ctrl C+V也不是僅僅用來搞代碼的,平時抄作業(yè)抄報告也是很需要用到的,尤其是當時我還是學生黨的時候,要是不能復制粘貼純自己寫報告那簡直要了命。那么問題來了,總有一些網(wǎng)站不想讓我們愉快地進行復制粘貼,所以在這里我們來實現(xiàn)解除瀏覽器復制限制的通用方案,具體代碼可以參考https://github.com/WindrunnerMax/TKScript文本選中復制-通用這部分。

CSS

某些網(wǎng)站會會通過CSS來禁用復制粘貼,具體表現(xiàn)為文字無法直接選中,特別是很多文庫類的網(wǎng)站,例如隨便在百度上搜索一下實習報告,那么很多搜出來的網(wǎng)站都是無法復制的,當然我們可以直接使用F12看到這部分文本,但是當他是這種嵌套層次很深并且分開展示的數(shù)據(jù)使用F12復制起來還是比較麻煩的,當然可以直接使用$0.innerText來獲取文本,但是畢竟過于麻煩,不如讓我們來看看CSS是如何禁用的文本選中能力。

那么平時如果我們寫過一些文本類操作的能力,比如富文本Void塊元素的時候,很容易就可以了解到use-select這個CSS屬性,user-select屬性用于控制用戶是否可以選擇文本,這不會對作為瀏覽器用戶界面的一部分的內(nèi)容加載產(chǎn)生任何影響,除非是在文本框中。

user-select: none; /* 元素及其子元素的文本不可選中 */
user-select: auto; /* 具體取值取決于一系列條件 */
user-select: text; /* 元素及其子元素的文本內(nèi)容可選中 */
user-select: contain; /* 元素的子元素的文本可選中 但元素本身的文本不可選中 */
user-select: all; /* 元素及其子元素的文本內(nèi)容可選中 */

那么我們在這些網(wǎng)站中檢索一下,就可以很明顯的看到user-select: none;,那么如果想解除這個限制,我們可以很輕松地想到CSS的優(yōu)先級,利用優(yōu)先級來強行覆蓋所有屬性的值即可,這也是比較通用的實現(xiàn)方案,可以輕松適配絕大多數(shù)利用這種方式禁止復制的頁面。

const style = document.createElement("style"); 
style.type = "text/css";
style.innerText = "*{user-select: auto !important; -webkit-user-select: auto !important;}"; 
document.head.appendChild(style);

Event

在大部分時候網(wǎng)站都不僅僅是使用CSS來禁止用戶復制行為的,特別是使用Canvas繪制的內(nèi)容,當然這種方式不在本文討論的范圍,在這里我們要討論利用事件來限制用戶復制的方式,那么能夠影響到用戶復制行為的事件主要有onCopyonSelectStart事件。onCopy事件很明顯,我們在觸發(fā)復制例如使用Ctrl + C或者右鍵復制的時候就會觸發(fā),在這里我們只要將其截獲就可以做到阻止復制了,同樣的onSelectStart事件也是,只要阻止其默認行為就可以阻止用戶的文本選中,自然也就無法復制了。在這里為了簡單直接使用DOM0事件,如果在控制輸入這段代碼就可以發(fā)現(xiàn)無法正常復制了。

document.oncopy = event => event.preventDefault();
document.onselectstart = event => event.preventDefault();

在研究如何處理這些事件的行為之前,我們先來看一下getEventListeners方法,Chrome瀏覽器提供的getEventListeners方法來獲取所有的事件監(jiān)聽,但是這畢竟是在控制臺中才能使用的函數(shù),不具有通用性,只是方便我們調(diào)試用。

console.log(getEventListeners(document));
// {
//     click: Array(4), 
//     DOMContentLoaded: Array(3),
//     // ...
// }

那么既然不具有通用性,我們?yōu)槭裁匆倪@個方法呢,這其中涉及一個問題,對于這些事件監(jiān)聽,如果我們想解除這些事件處理函數(shù),對于DOM0級的事件而言,我們只需要將屬性設置為null即可,但是對于DOM2級的事件而言,我們需要使用removeEventListener來移除事件處理函數(shù),那么問題來了,使用removeEventListener函數(shù)我們必須要獲取當時addEventListener時的函數(shù)引用,但是我們并沒有保存這個引用,那么我們?nèi)绾潍@取這個引用呢,這就是我們討論的getEventListeners方法的作用了,我們可以通過這個方法獲取到所有的事件監(jiān)聽,之后再通過removeEventListener來移除事件處理函數(shù)即可,當然在這里我們只能進行事件判定的調(diào)試用,并不能實現(xiàn)一個通用的方案。

const listeners = getEventListeners(document);
Object.keys(listeners).forEach(key => {
    console.log(key);
    listeners[key].forEach(item => {
        document.removeEventListener(item.type, item.listener);
    });
});

那么我們是不是可以換個思路,非得移除事件監(jiān)聽是比較鉆牛角尖了,俗話說得好,最高端的食物往往只需要最簡單的烹飪方式,既然移除不了,我們就不讓他執(zhí)行就完事了,既然不想讓他執(zhí)行,那就很自然的聯(lián)想到了JS的事件流模型,那就給他阻止冒泡唄。

document.body.addEventListener("copy", e => e.stopPropagation()); 
document.body.addEventListener("selectstart", e => e.stopPropagation());

看似這個方式是沒有問題的,那么假如此時Web頁面本身監(jiān)聽的事件是在body上的話,那么很明顯在document上去阻止冒泡就已經(jīng)太晚了,并不能達到效果,所以這就很尷尬,那說明這個方案不夠通用。那既然冒泡不行,我們直接在捕獲階段給他干掉就ok了,并且配合上腳本管理器的document-start來保證我們的事件捕獲是最先執(zhí)行的,這樣不光能夠解決這類DOM0事件的問題,對于DOM2級的事件也同樣有效果。

document.body.addEventListener("copy", e => e.stopPropagation(), true); 
document.body.addEventListener("selectstart", e => e.stopPropagation(), true);

這個方案已經(jīng)是一個比較通用的復制方案了,我們可以解決大多數(shù)網(wǎng)站的限制,但通過直接在捕獲階段攔截事件也是可能有一定的副作用的,例如我們在捕獲階段就阻止了鍵盤的事件,然后在編輯語雀的文檔的時候就會出現(xiàn)問題,因為語雀的文檔也跟飛書類似,都是按行處理文本,然后猜測他是阻止了contenteditable的默認行為,然后編輯器完全接管了鍵盤的事件,所以會導致其無法換行和處理快捷啟動菜單。同理,如果直接阻止了onCopy的冒泡,就可能導致編輯器復制采用了默認行為,而通常編輯器會對于復制文本的格式進行一些處理,所以在有編輯功能的時候還是要慎重,完全作為展覽端倒是就問題不大了,整體來說是收益更大。

前一段時間我發(fā)現(xiàn)了另一種非常有意思的事情,onFocusonBlur事件也可以做到限制用戶文本選中,隨便找個頁面然后將下邊的代碼在控制臺執(zhí)行,我們可以驚奇地發(fā)現(xiàn),我們無法正常選中文本了。

const button = document.createElement("button");
button.onblur = () => button.focus();
button.textContent = "BUTTON";
document.body.appendChild(button);
button.focus();

那么實際上這里的原理也很簡單,通常在HTMLInputElementHTMLSelectElementHTMLTextAreaElementHTMLAnchorElementHTMLButtonElement等元素會有焦點這個概念,而文本的選中也有焦點這個行為,那么既然焦點不能夠同時聚焦在一起,我們就直接強行將焦點聚焦在其他的地方,比如上邊的例子就是將焦點強行聚焦在了按鈕上,這樣因為文本內(nèi)容無法獲取焦點,就無法正常選中了。

那么我們同樣可以使用捕獲階段阻止事件執(zhí)行的方式解決這個問題,分別將onFocusonBlur事件處理掉即可,只不過這種方式可能會導致頁面的焦點控制出現(xiàn)一些問題,所以在這里我們還有另一種方式,通過在document-start執(zhí)行MutationObserver,在發(fā)現(xiàn)類似的DOM節(jié)點的時候直接將其移出,讓其無法插入到DOM樹中自然也就不會有相關問題了,只不過這就不是一個通用的解決方案,通常需要case by case地處理才可以。

const handler = mutationsList => {
    for (const mutation of mutationsList) {
        const addedNodes = mutation.addedNodes;
        for (let i = 0; i < addedNodes.length; i++) {
            const target = addedNodes[i];
            if (target.nodeType != 1) return void 0;
            if (
                target instanceof HTMLButtonElement &&
                target.textContent === "BUTTON"
            ) {
                target.remove();
            }
        }
    }
};
const observer = new MutationObserver(handler);
observer.observe(document, { childList: true, subtree: true });

腳本分發(fā)

那么基于上述方式我們完成了腳本的編寫與打包,在這里也分享一個腳本分發(fā)的最佳實踐,GreasyFork等腳本網(wǎng)站通常會有源代碼同步的能力,我們可以直接填入一個腳本鏈接就可以自動同步腳本更新,就不需要我們到處填寫了,那么這里還有一個問題,這個腳本鏈接應該從哪里來呢,那么同樣我們可以借助GitHubGitPages來生成腳本鏈接,并且GitHub還有GitAction可以幫助我們自動構建腳本。

那么整個流程就是這樣的,我們首先在GitHub配置好GitAction,當我們推送代碼的時候就可以觸發(fā)自動構建流程,在構建完成后我們可以將代碼自動地推送到GitPages,之后我們就可以手動獲取GitPages的腳本鏈接并且填入到各個腳本網(wǎng)站了,并且如果打了渠道包也可以分別分發(fā)不同的腳本鏈接,這樣就完成了整個流程的自動化,并且借助GitHub還可以將jsDelivr作為CDN使用,下面就是完整的GitAction的配置。

name: publish gh-pages

on:
  push:
    branches:
      - master

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: checkout
        uses: actions/checkout@v2
        with:
          persist-credentials: false

      - name: install and build
        run: |
          npm install -g pnpm@6.24.3
          pnpm install
          pnpm run build
      - name: deploy
        uses: JamesIves/github-pages-deploy-action@releases/v3
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          BRANCH: gh-pages
          FOLDER: dist

每日一題

https://github.com/WindrunnerMax/EveryDay

參考

https://wiki.greasespot.net/Security
https://docs.scriptcat.org/docs/dev/api/
https://en.wikipedia.org/wiki/Greasemonkey
https://wiki.greasespot.net/Metadata_Block
https://juejin.cn/post/6844903977759293448  
https://www.tampermonkey.net/documentation.php
https://wiki.greasespot.net/Greasemonkey_Manual:API
https://learn.scriptcat.org/docs/%E7%AE%80%E4%BB%8B/
http://jixunmoe.github.io/gmDevBook/#/doc/intro/gmScript

總結

以上是生活随笔為你收集整理的从零实现的浏览器Web脚本的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

久久久久国产一区二区三区四区 | 国产精品久久久久aaaa九色 | 亚洲综合在线一区二区三区 | 在线激情小视频 | 亚洲综合导航 | 国产97色在线 | 国产精品av电影 | 亚洲欧美观看 | 亚洲欧美经典 | 蜜臀av性久久久久av蜜臀妖精 | 亚洲精品国产品国语在线 | 欧美极品少妇xxxxⅹ欧美极品少妇xxxx亚洲精品 | 91桃色免费观看 | 5月丁香婷婷综合 | 黄色大片网 | 国产成人精品国内自产拍免费看 | av网址最新 | 婷婷四房综合激情五月 | 亚洲免费小视频 | 亚洲理论在线 | 欧美巨大 | 日韩成人欧美 | 国产一区在线视频播放 | 欧美一级小视频 | 在线精品视频免费播放 | 天天干天天操天天干 | 人人模人人爽 | 精品国产片| 国内久久视频 | 337p日本欧洲亚洲大胆裸体艺术 | japanesexxxhd奶水| 国产精品原创视频 | 91在线播放综合 | 久久69精品久久久久久久电影好 | 成人一级片免费看 | av高清免费在线 | 国产精品一区二区av影院萌芽 | 色综合天天狠天天透天天伊人 | 国产精品区二区三区日本 | 精品在线二区 | 亚洲视频在线看 | 97视频在线 | 亚洲日本va午夜在线电影 | 麻豆 free xxxx movies hd| 四季av综合网站 | 最新极品jizzhd欧美 | 视频在线精品 | 欧美日韩精品区 | 色a综合 | 99精品在这里 | 久草视频在线看 | 99精品国自产在线 | 久久精品国产精品亚洲 | 99久高清在线观看视频99精品热在线观看视频 | 福利视频导航网址 | 国产欧美日韩精品一区二区免费 | 在线午夜av| 视频在线观看91 | 国产 字幕 制服 中文 在线 | 欧美性大战 | 天天av资源| 伊人午夜视频 | 91人人澡人人爽 | 十八岁以下禁止观看的1000个网站 | 国产成人av电影在线观看 | 美女网站视频免费都是黄 | 国产精品 国产精品 | 精品一区二区三区香蕉蜜桃 | 免费观看日韩av | 日批视频在线观看免费 | 在线观看欧美成人 | 亚洲伊人网在线观看 | 超碰在线1| 在线观看涩涩 | 国内精品久久久久影院男同志 | 精品a视频| 婷婷激情五月综合 | 免费在线观看中文字幕 | 园产精品久久久久久久7电影 | 六月丁香激情综合色啪小说 | 日韩视频一区二区在线 | 亚洲精品免费在线播放 | 日日干美女 | 国产精品欧美日韩 | 亚洲一级黄色片 | 免费在线观看亚洲视频 | 免费黄在线观看 | 97网| 综合网久久 | 亚洲精选久久 | 毛片网站观看 | 久久字幕精品一区 | 国产精品视频专区 | 91精品一区在线观看 | 成人一级片免费看 | 亚洲精品久久久久久国 | 欧美午夜精品久久久久久孕妇 | 国产91精品一区二区麻豆亚洲 | 六月色婷婷 | 美女av免费 | 日韩av电影一区 | 久久只有精品 | 亚洲毛片久久 | 中文字幕在线视频一区二区三区 | 天天插狠狠干 | 国产成人精品一区二区在线 | 人人草人人草 | 久久综合久久综合九色 | 中文字幕av在线电影 | 国产精品一区二区久久 | 欧美不卡视频在线 | 精品国产色 | 日韩av偷拍 | 中文字幕视频 | 国产精品日韩久久久久 | 国产亚洲精品成人av久久ww | 久久国产精品99久久久久久老狼 | 精品国产电影 | 国产精品视频最多的网站 | 99久久99热这里只有精品 | 亚洲成年片 | 久操操 | 一区三区视频 | 婷婷综合久久 | 久久久久免费精品 | 免费在线激情视频 | 日韩在线观看精品 | 欧美亚洲精品在线观看 | 美女黄视频免费 | 国产精品久久久久久久久久不蜜月 | 国产一级电影免费观看 | 在线播放 一区 | 亚洲精品视频在线观看免费视频 | 一区二区三区精品久久久 | 免费日韩一区二区三区 | 九九九九九精品 | 久久国产精品精品国产色婷婷 | 欧美日韩裸体免费视频 | 91热精品 | 国产精品第 | 婷婷免费视频 | 日韩不卡高清 | 久久老司机精品视频 | 国产免费又粗又猛又爽 | 中文字幕一二三区 | 81精品国产乱码久久久久久 | www日韩视频 | 最近中文字幕大全中文字幕免费 | 成年人看片网站 | 国产亚洲精品电影 | 最新超碰在线 | 久久综合视频网 | 久久手机精品视频 | 一区中文字幕电影 | 国内久久久久 | 国产视频一区二区三区在线 | 欧美日韩高清国产 | 久久久午夜剧场 | 91久久精| 午夜婷婷在线观看 | 伊人婷婷综合 | www.777奇米 | 九九99| 久热国产视频 | 香蕉视频在线播放 | 久久久电影网站 | 一区 二区 精品 | 久久精品99北条麻妃 | 欧美精品免费一区二区 | 涩涩网站在线观看 | 91精品国产99久久久久久红楼 | 久久久精品欧美 | 久久99深爱久久99精品 | 久久9精品| 日日日操 | 青草视频免费观看 | 91视频久久久 | 欧美视频在线二区 | 亚洲激情中文 | 中文字幕丰满人伦在线 | 日韩在线视频播放 | 国产精品不卡在线 | 92国产精品久久久久首页 | 1024久久 | 探花在线观看 | 亚洲国产资源 | 蜜桃视频在线观看一区 | 黄色免费网站大全 | 国产一级电影免费观看 | 久久精品久久99精品久久 | 日本丰满少妇免费一区 | 天堂av在线7 | 在线有码中文 | 亚洲精品国产精品国自产 | 超碰在线97免费 | 婷婷五综合 | 日日干天天 | 国产精品成人在线观看 | 欧美激情综合色综合啪啪五月 | 91久久久久久久一区二区 | 97超碰资源总站 | 国产亚洲一级高清 | 久久视频二区 | 日韩精品不卡 | 国产在线观看高清视频 | h视频在线看 | 久久久国产精品人人片99精片欧美一 | 久久资源在线 | 日本精品久久久久中文字幕5 | 国产亚洲精品久久久久久无几年桃 | 免费观看国产精品 | 亚洲精品伦理在线 | 亚洲欧美国产日韩在线观看 | 怡红院成人在线 | 国产99久久 | 亚洲精品国产自产拍在线观看 | 国产麻豆视频网站 | 狠狠干婷婷 | 成人黄色小说视频 | 久久高清毛片 | 国产97在线观看 | 综合激情网...| 国产看片 色 | 国内精品久久久精品电影院 | 欧洲一区二区在线观看 | 日韩中文字幕在线观看 | 91精品国产九九九久久久亚洲 | 中文字幕第一页av | 日韩午夜剧场 | 日韩大片免费在线观看 | 久久久久99精品国产片 | 日韩精品久久久久久久电影竹菊 | 最近中文字幕在线 | 亚洲国产精品第一区二区 | 精品国产一区二区三区久久久蜜臀 | 欧美激情在线网站 | 国产手机在线视频 | 美女国产 | 色国产精品一区在线观看 | 成人h视频在线 | 久久久91精品国产 | 在线观看www视频 | 在线视频日韩一区 | 欧美精品一区二区三区一线天视频 | 日韩精品不卡在线 | www.少妇| 国产精品 中文在线 | 99视频在线免费观看 | 欧美精品久久久久久久久久丰满 | 狠狠色综合欧美激情 | 日韩综合第一页 | www国产一区 | 国产综合在线观看视频 | 久久国产精品免费 | 伊人影院99 | 国产在线p | 日本精品视频免费观看 | 中文字幕成人在线 | 婷婷激情5月天 | 日韩三级.com | 国产亚洲精品美女 | 国产伦精品一区二区三区在线 | 天天综合网~永久入口 | 国产1区2区3区在线 亚洲自拍偷拍色图 | 日韩欧美中文 | 91麻豆精品91久久久久同性 | 日本久久综合网 | 国产日韩精品在线观看 | 国产精品日韩在线播放 | 日本中文字幕在线电影 | 免费一级片视频 | 在线观看视频色 | 国产最新精品视频 | 不卡视频国产 | 欧美乱码精品一区 | 激情网婷婷 | 精品国产一区二区三区噜噜噜 | 看污网站 | 久久影院中文字幕 | 91麻豆看国产在线紧急地址 | 一级片黄色片网站 | 五月婷婷丁香在线观看 | 国产精品久久久久影院 | 美女一级毛片视频 | 欧洲精品在线视频 | 久久久久久久久艹 | 国产尤物视频在线 | 欧美成人一二区 | 夜夜躁日日躁狠狠躁 | 中文字幕一区二区三区在线观看 | 99久久精品久久亚洲精品 | 99爱视频 | 欧美午夜精品久久久久 | 欧美黄污视频 | 在线观看完整版 | 黄色软件视频大全免费下载 | 在线观看视频h | 99热99热| 91精品久久久久久粉嫩 | www.狠狠 | 亚洲午夜精品久久久 | 91在线国内视频 | 伊人亚洲精品 | 亚洲精品影视在线观看 | 亚洲另类在线视频 | 国产在线精品国自产拍影院 | 日韩欧美精品在线观看 | 伊人五月综合 | 日韩系列 | 成人av午夜 | 国产精品99久久久久的智能播放 | 激情伊人五月天 | 少妇性aaaaaaaaa视频 | 九九久久电影 | 午夜精品久久久久久99热明星 | 久久高清片| 亚洲精品在线电影 | 国产精品久久久久久久久久久久 | 三级av免费观看 | 天堂视频中文在线 | 一区二区三区电影大全 | 黄色高清视频在线观看 | 午夜资源站 | 亚洲综合激情小说 | 五月婷网站 | 五月婷婷六月丁香在线观看 | 视频一区二区精品 | 欧美国产日韩在线观看 | 国产一区精品在线观看 | 亚洲视频2| 99视频在线观看免费 | 久久99亚洲精品久久久久 | 中文字幕在线观看你懂的 | 国产精品久久久免费看 | 日韩精品专区在线影院重磅 | 激情综合色播五月 | 一区二区 精品 | 亚洲 欧洲 国产 日本 综合 | 最新av网址在线观看 | 亚洲人成人在线 | 免费看毛片在线 | 91成人看片 | 日韩精品一区电影 | 国内精品久久久久影院男同志 | 国产精品免费视频观看 | av在线一级 | 欧美激情视频一区二区三区免费 | 97精品国产97久久久久久春色 | 激情欧美xxxx | 五月婷婷色丁香 | 久久综合色影院 | 亚洲成人免费观看 | 日本黄色免费在线 | 欧美日在线观看 | 四虎永久免费在线观看 | 国产激情免费 | 久久黄色免费 | 五月婷婷视频在线 | 免费黄在线观看 | 国产精品久久久久av福利动漫 | 欧美日韩国产xxx | 在线国产福利 | 久久99精品久久久久久秒播蜜臀 | 日韩欧美在线视频一区二区三区 | 国产97视频 | 欧美激情综合五月色丁香小说 | 国产在线无| 在线高清 | 精品毛片久久久久久 | 中文理论片| 四虎国产精品成人免费影视 | 麻豆国产网站 | 91麻豆精品国产91久久久久久 | 亚洲另类交 | 亚洲狠狠操 | 国产青草视频在线观看 | 天天色视频 | 一区三区视频 | 久久视频这里有久久精品视频11 | 波多野结衣理论片 | 人人操日日干 | 操久在线 | 欧美日韩视频精品 | 久久久鲁 | 日韩精品一区二区三区不卡 | www.亚洲黄色 | 亚洲精品女人 | 久久99亚洲精品久久 | 欧美日韩3p | 婷婷成人在线 | 黄色高清视频在线观看 | 免费久久久久久久 | 亚洲男男gaygay无套 | 国产精品久免费的黄网站 | 天天综合在线观看 | 五月天激情视频 | 免费观看黄色av | 日韩久久片 | 国产精品扒开做爽爽的视频 | 亚洲精品网站在线 | 久久久免费少妇 | 色com| 91精品爽啪蜜夜国产在线播放 | 国产精品九色 | 午夜精品福利一区二区三区蜜桃 | 国产精品久久久久三级 | 涩涩爱夜夜爱 | 欧美激情视频久久 | 久久国产精品久久久久 | 亚洲第一成网站 | 九九导航| 日本性动态图 | 婷婷五情天综123 | 五月婷婷黄色网 | 国产专区视频 | 国产成人精品福利 | 免费在线视频一区二区 | 亚洲精品五月天 | 我爱av激情网 | 9992tv成人免费看片 | 欧美国产日韩在线观看 | www免费看| 五月婷婷在线观看 | 18国产精品福利片久久婷 | 国产一区二区三区高清播放 | 久久精品视频中文字幕 | 国产经典av | 久久在现 | 在线中文字幕电影 | 在线观看视频你懂得 | 午夜婷婷网 | av成人动漫在线观看 | 亚洲免费在线观看视频 | 国产在线中文 | 亚洲成人999 | 欧美黑人性猛交 | 亚洲国产中文字幕在线 | 日韩在线视频网 | 麻豆一区二区三区视频 | 久久成人亚洲欧美电影 | 久久久高清 | 成人免费一区二区三区在线观看 | 一区在线观看 | 国产成人av综合色 | 天天干,天天操,天天射 | 欧美性脚交 | 日日操天天操狠狠操 | 亚洲成人欧美 | 97碰碰精品嫩模在线播放 | 国产成人精品一区二区 | 色综合久久中文字幕综合网 | 美女免费网视频 | 中文av字幕在线观看 | 国产一区精品在线 | 国产69精品久久99不卡的观看体验 | 日韩一区二区三区视频在线 | 在线日韩视频 | 成人丁香花 | 成人影片在线播放 | 国产精品久久久久一区二区三区共 | 日韩黄色软件 | 亚州精品在线视频 | 国产午夜精品在线 | 国产精品 中文在线 | 五月婷婷在线观看视频 | 久久成人午夜 | 日韩精品久久久久久久电影竹菊 | www成人精品| 成x99人av在线www | 在线免费观看黄色av | 成人一级视频在线观看 | 夜夜夜夜夜夜操 | 欧美成人在线免费 | 国产理论在线 | 午夜久久精品 | 久久久精品高清 | 亚洲国产中文字幕 | 狠狠干五月天 | 三级视频国产 | 久久久久久国产精品久久 | 色狠狠婷婷 | 国产精品18久久久久vr手机版特色 | 久久免费黄色网址 | 超碰在线公开免费 | 亚洲国产高清在线 | 99精品视频播放 | 亚洲欧美日韩精品久久奇米一区 | av在线精品 | 久久久国产视频 | 91av在线视频播放 | 亚洲爱av | 亚洲一二三久久 | 日韩在线三区 | 午夜精品久久久久久久久久 | 免费黄色一区 | 日本性xxx| 久久污视频 | 日韩精品一区电影 | 99久免费精品视频在线观看 | 香蕉视频4aa | 国产另类av | 欧美成人中文字幕 | 成人h在线| 国产精品入口久久 | 亚洲黄在线观看 | 天天色棕合合合合合合 | www.福利| 欧美精品一二 | a亚洲视频 | 国产精品毛片一区视频播 | 久久综合色影院 | www.色com| 中文字幕视频三区 | 国产精品久久一卡二卡 | 91九色视频国产 | 国产精品手机在线观看 | 又黄又爽又刺激视频 | 在线免费观看国产视频 | 精品亚洲免a | 久草网在线 | 91精品国自产在线观看 | 手机在线永久免费观看av片 | 国产黄在线看 | 91精品久久香蕉国产线看观看 | 国产精品久久一卡二卡 | 国产精品区二区三区日本 | 亚洲精品久久久久久久蜜桃 | 色香蕉在线 | 看av免费| 97色狠狠 | 国产私拍在线 | 精品国产一区二区三区久久久久久 | 韩国av免费观看 | 人人舔人人舔 | 久久精品99国产国产精 | 亚洲 综合 专区 | 狠狠久久 | 欧美日韩在线视频一区 | 亚洲视频分类 | 激情五月婷婷激情 | 国产精品欧美日韩 | 天天插天天干天天操 | 99国产精品久久久久久久久久 | 国产999精品 | 日日干干 | 人人爽人人看 | 国产亚洲精品久久久久动 | 天天干天天拍 | 久久婷亚洲五月一区天天躁 | 欧美一级日韩三级 | 美女一二三区 | 麻豆 videos| 伊人色播 | 成人中文字幕在线 | 免费成人av网站 | 97在线观看免费高清 | 午夜视频免费 | 国产精品久久久久久久久久直播 | 欧美一级片免费播放 | 在线观看自拍 | www.777奇米| 亚洲精品成人 | 日韩在线免费小视频 | 97视频免费播放 | 亚洲精品mv在线观看 | 亚洲一级电影视频 | 深爱激情综合网 | 99爱精品视频 | 日韩在线激情 | 中文字幕在线日亚洲9 | 婷婷六月综合网 | 国产v在线| 久草在线视频网站 | 久久超碰99 | 成人三级黄色 | 色夜视频 | 91porny九色在线播放 | 欧美激情片在线观看 | 婷婷夜夜 | 在线精品视频在线观看高清 | 亚洲精品国偷拍自产在线观看蜜桃 | 久久婷婷开心 | 一区二区视频在线观看免费 | 久久免费精品一区二区三区 | 亚洲区精品 | 国产视频资源 | 天天草天天色 | 2024av| 香蕉影视 | 91在线视频免费 | 天天干夜夜爽 | 97小视频| 欧美亚洲国产精品久久高清浪潮 | 四虎成人精品 | 97超视频免费观看 | 色七七亚洲影院 | 国产 字幕 制服 中文 在线 | 色婷婷狠狠五月综合天色拍 | 亚洲精品乱码白浆高清久久久久久 | 在线观看av麻豆 | 亚洲撸撸 | 亚洲国产精品久久久久久 | 国产成人高清 | 久久久久久久综合色一本 | 精品久久一级片 | 97综合在线| 免费日韩高清 | 久久综合中文字幕 | 免费日韩 精品中文字幕视频在线 | 一级全黄毛片 | 亚洲视频一级 | 久久在线免费 | 婷五月激情 | 久久综合九色综合97婷婷女人 | 日韩精品无码一区二区三区 | av福利在线导航 | 草久在线观看视频 | 美女在线免费视频 | 91精品免费 | 天天综合网天天 | 午夜精品视频福利 | 成年人在线免费看片 | 久久久精品在线观看 | 在线国产福利 | 一区二区三区四区五区在线视频 | 五月天激情在线 | 在线免费观看不卡av | 人人澡人人草 | av在线免费播放网站 | 久久兔费看a级 | 日韩视频一区二区在线观看 | 久久精品欧美一 | 日韩aⅴ视频 | 日韩啪啪小视频 | 日日麻批40分钟视频免费观看 | 日本精品久久久久影院 | 天天做天天爱天天综合网 | 久久99久久99精品 | www.天天射 | 999视频在线观看 | 日韩精品一区二区三区视频播放 | 国产精品色| av在线免费在线 | 成人在线网站观看 | 日本aaa在线观看 | 三级黄色大片在线观看 | 91成人精品一区在线播放69 | 免费视频黄色 | 国产黄色片久久久 | 91亚洲网 | 97av视频 | 成人av在线直播 | 亚洲精品一区二区三区在线观看 | 五月天亚洲婷婷 | 亚洲视频久久久久 | av免费电影在线观看 | 天天操天天射天天操 | 欧美专区亚洲专区 | 97综合在线 | 中文字幕a在线 | 人人插人人搞 | 黄色最新网址 | 精品久久一二三区 | 免费av网站在线看 | 夜夜操天天干 | 开心激情综合网 | 夜色资源站国产www在线视频 | 毛片在线网 | 国产亚洲视频在线免费观看 | 国产精品久久久久久久久久久久久久 | 欧美日韩视频一区二区三区 | 日韩经典一区二区三区 | 久久久久成人精品 | 伊人春色电影网 | 久久婷亚洲五月一区天天躁 | 日本黄色免费在线 | 国产精品岛国久久久久久久久红粉 | 夜夜躁狠狠躁日日躁 | 欧美一区二区日韩一区二区 | 日韩字幕在线观看 | 黄色av一区二区 | 麻豆精品传媒视频 | 日本护士撒尿xxxx18 | 天天操天天操一操 | 91免费网 | 欧美色图视频一区 | 欧美亚洲另类在线视频 | 一区二区三区四区五区在线 | 日韩黄色网络 | 久久免费国产精品 | 高清av中文在线字幕观看1 | 国产精品中文久久久久久久 | 少妇高潮流白浆在线观看 | 国产精品久久久久久久久久久久久 | 国内揄拍国内精品 | 色狠狠综合 | 久久国产系列 | 玖玖视频免费在线 | 黄免费在线观看 | 中文字幕在线观看第一页 | 国产高清在线免费观看 | 天天干天天做 | 黄色成人av网址 | 91精品小视频 | 成人免费看片98欧美 | 国产美女免费视频 | 一级性视频 | av在线精品| 丁香九月激情综合 | 午夜久久久久久久久久影院 | 99久久精品免费看国产四区 | 国产视频一二区 | 99在线热播精品免费99热 | 伊人色播 | 99久久er热在这里只有精品15 | 国产精品免费不 | 国产精品毛片一区二区 | 国产精品免费久久久久久久久久中文 | 操久在线 | 国产精品久久久区三区天天噜 | 欧美日韩一区二区久久 | 91亚洲精品乱码久久久久久蜜桃 | 久久黄色影院 | 亚洲美女免费精品视频在线观看 | 久久与婷婷 | 亚洲人天堂 | 超碰午夜 | 人人干人人艹 | 成人免费视频网站在线观看 | 99久久久久久国产精品 | 夜夜骑天天操 | 青青网视频| 色黄www小说 | 国产精品久久久久久久免费大片 | 99re热精品视频 | 九九九视频精品 | 亚洲成人免费 | av在线免费播放网站 | 亚洲一级理论片 | 中文字幕在线免费观看视频 | 日日麻批40分钟视频免费观看 | 99热精品免费观看 | 91网页版在线观看 | 91字幕| 欧美日韩免费在线观看视频 | 国产日韩精品在线观看 | 成年人毛片在线观看 | 九九色视频 | 国产精品18久久久久白浆 | 99久高清在线观看视频99精品热在线观看视频 | 久久久国产精品成人免费 | 久久不射电影网 | 国产特级毛片aaaaaa | 亚洲综合在线一区二区三区 | 午夜黄色一级片 | 99视频在线精品国自产拍免费观看 | 日韩久久一区 | 国产精品嫩草影院9 | 久久这里有 | 黄色小说在线观看视频 | 亚洲国产三级在线 | 在线观看免费黄视频 | 五月开心婷婷 | 夜添久久精品亚洲国产精品 | 久久久久久久久久久久国产精品 | 日韩a级免费视频 | 99视频这里只有 | 欧美日韩精品在线观看 | av千婊在线免费观看 | 在线观看视频国产 | 日韩欧美精品免费 | 丝袜av网站 | 综合黄色网 | 久久午夜影院 | 免费av看片 | 韩国一区二区三区在线观看 | 亚洲精品小视频在线观看 | 国产日韩精品在线 | 人人射人人爱 | 久久久久女人精品毛片 | 精品在线观看免费 | 奇米四色影狠狠爱7777 | 91在线国产观看 | 黄色av网站在线观看 | 日韩精品视频免费在线观看 | 中文字幕高清视频 | 天天操天天射天天爱 | 丁香激情婷婷 | 丁香九月激情综合 | 911免费视频 | 天天摸天天舔 | 国产精品女主播一区二区三区 | av免费电影网站 | 欧美在线视频第一页 | 成年人在线免费看视频 | 精品麻豆入口免费 | 成人小视频免费在线观看 | 91视频 - 88av | 日韩精品一区二 | 草久久av| 99热这里只有精品免费 | 国产精品久久久久久久久久久久午夜 | 国产打女人屁股调教97 | 香蕉视频色 | 波多野结衣在线播放视频 | 国产色视频 | 操操操干干干 | 免费黄色a网站 | 久久视频在线观看免费 | 成人欧美一区二区三区黑人麻豆 | 中文字幕在线不卡国产视频 | 国产成人精品一区二区三区免费 | 在线看片中文字幕 | 黄色在线观看网站 | 国产在线观看中文字幕 | 国产高清视频在线 | 国产一区二区视频在线 | 99国产在线视频 | 激情影院在线观看 | 久久激五月天综合精品 | 久久成人人人人精品欧 | 在线中文字幕观看 | 香蕉久久久久久av成人 | 国产精品一区二区三区四 | 亚洲国产一区av | 91热| 一区二区视频免费在线观看 | 日韩精品最新在线观看 | 91亚洲精品视频 | 国产人免费人成免费视频 | 久久99精品国产99久久 | 久久99精品热在线观看 | 精品国产成人在线 | av电影一区二区 | 中国精品少妇 | 国产手机在线 | 欧美性生活一级片 | 亚洲精品成人网 | 日本中文一区二区 | 国产一区二区高清视频 | 最近中文国产在线视频 | 处女av在线 | 免费在线观看av的网站 | 久久国产视频网站 | 欧美日本一二三 | 精品国产乱码久久久久 | 不卡的av电影 | 97精品国产97久久久久久粉红 | 丁香5月婷婷久久 | 丰满少妇在线观看 | 91av在线视频免费观看 | 69精品久久 | 国产精品久久在线观看 | 天天舔天天射天天操 | 在线观看你懂的网址 | 日韩欧美一区二区不卡 | 成人免费观看a | 91九色在线播放 | 中文乱幕日产无线码1区 | 在线高清av | 中文字幕有码在线播放 | 中文字幕高清在线 | 国产精品第| 久久国产成人午夜av影院宅 | 国产成人精品一区二区在线 | 2024国产精品视频 | 91免费视频黄 | 人人玩人人添人人澡超碰 | 久久久五月婷婷 | 中文字幕精品一区二区三区电影 | 国产精品普通话 | 中文字幕高清免费日韩视频在线 | 国产日韩欧美视频在线观看 | 欧美日韩在线播放一区 | 精品国产诱惑 | 亚洲最新av在线网址 | 亚洲综合欧美精品电影 | 免费看黄在线观看 | 日韩xxxbbb | 99精品欧美一区二区蜜桃免费 | 天天操天天综合网 | 91色蜜桃| 91chinese在线| 日韩欧美一二三 | 免费在线日韩 | 日韩精品免费在线播放 | 久久精品电影院 | 一区二区三区 中文字幕 | 成人在线黄色电影 | 一级淫片在线观看 | 久久99久久99精品中文字幕 | 欧美日韩一级久久久久久免费看 | 免费看日韩 | 国产精品视频在线观看 | 97人人模人人爽人人喊网 | 香蕉视频91 | 亚洲 欧美 国产 va在线影院 | 九九九九热精品免费视频点播观看 | 色视频成人在线观看免 | 黄色一级在线视频 | 91成人精品在线 | 国产精品毛片久久蜜 | 香蕉日日 | 97人人超 | 中文字幕一二三区 | 国产精品一区二区av影院萌芽 | av视屏在线播放 | 夜色资源站国产www在线视频 | 日韩中文在线视频 | 亚洲资源在线网 | 午夜av在线 | 丁香六月中文字幕 | 日本巨乳在线 | 国内视频一区二区 | 国内精品久久久久影院一蜜桃 | 久久伊人精品一区二区三区 | 91亚洲永久精品 | 女人魂免费观看 | 国产精品免费高清 | 日本精品一区二区 | 五月激情久久久 | 欧美在线1 | 国产久草在线观看 | 91视频久久久 | 99久久99久久免费精品蜜臀 | 手机在线小视频 | 久久精品欧美一区二区三区麻豆 | 伊人婷婷色 | 黄色大全免费观看 | 夜夜视频欧洲 | 91视频传媒 | av超碰在线观看 | 999ZYZ玖玖资源站永久 | 亚洲蜜桃av| 国产精品视频在线看 | 成人免费观看大片 | 日韩天堂在线观看 | 日韩毛片在线播放 | 草久久精品 | 国产一级片播放 | 亚洲日本一区二区在线 | 日韩av在线影视 | 婷婷五月情 | 狠狠干2018 | 亚洲精品免费观看视频 | 成年人在线免费看视频 | 国产高清视频在线观看 | 综合铜03 | 日韩欧美在线一区 | 五月天久久精品 | 国产精品久久久久久久久久直播 | 国产精品久久久久久久午夜 | 国产玖玖在线 | 日韩电影中文字幕在线 | 国产香蕉视频在线播放 | 亚洲黄色一级电影 | 日韩免费电影一区二区三区 | 日本最新高清不卡中文字幕 | 精品播放 | 亚洲成人国产 | 国产亚洲精品bv在线观看 | 日韩av电影一区 | 国产精品国产三级国产专区53 | 日韩视频专区 | 精品久久久久久亚洲综合网站 | 日韩在线视频网站 | 国产精品va最新国产精品视频 | 色资源网在线观看 | 激情欧美一区二区三区 | 美女一区网站 | 日韩av电影中文字幕在线观看 | 久久视频这里有久久精品视频11 | 国产高清视频免费观看 | 最近中文字幕mv免费高清在线 | 黄色99视频 | www.天天色 | 天天草天天干天天射 | 午夜色大片在线观看 | 国产精品一区二区av日韩在线 | 免费国产在线观看 | 亚洲精品2区 | 久草av在线播放 | 91视频 - v11av | 天天干 夜夜操 | 91精品婷婷国产综合久久蝌蚪 | 精品久久久久久国产偷窥 | 国产正在播放 | 中文字幕在线观看一区二区三区 | 国产99自拍 | 97视频人人澡人人爽 | 午夜精品视频福利 | 亚洲精品动漫久久久久 | 国产精品久久99 | 日本黄区免费视频观看 | 亚洲精品综合在线 | 九九影视理伦片 | 久久超碰在线 | 97视频成人|