به اشتراک گذاری صفحه نمایش تب مرورگر در HTML5؟

در چند سال گذشته، من به چند شرکت مختلف کمک کرده‌ام که تنها با استفاده از فناوری‌های مرورگر، به عملکردی شبیه به اشتراک‌گذاری صفحه دست یابند. از تجربه من، پیاده‌سازی VNC تنها در فناوری‌های پلتفرم وب (یعنی بدون افزونه) مشکل سختی است. چیزهای زیادی وجود دارد که باید در نظر بگیرید و چالش های زیادی برای غلبه بر آنها وجود دارد. رله کردن موقعیت اشاره گر ماوس، فوروارد زدن کلیدها، و دستیابی به رنگ آمیزی کامل 24 بیتی رنگ با سرعت 60 فریم در ثانیه، تنها چند مورد از مشکلات است.

گرفتن محتوای برگه

اگر پیچیدگی‌های اشتراک‌گذاری سنتی صفحه را حذف کنیم و بر اشتراک‌گذاری محتویات یک برگه مرورگر تمرکز کنیم، مشکل تا حد زیادی ساده‌تر می‌شود. اساساً، ما به راهی برای گرفتن عکس از DOM و به اشتراک گذاری آن نیاز داریم.

بخش اشتراک گذاری آسان است. سوکت های وب بسیار قادر به ارسال داده ها در فرمت های مختلف (رشته ای، JSON، باینری) هستند. بخش عکس فوری مشکل بسیار سخت تری است. پروژه‌هایی مانند html2canvas با پیاده‌سازی مجدد موتور رندر مرورگر... در جاوا اسکریپت، به تصویربرداری از HTML پرداخته‌اند! مثال دیگر Google Feedback است، اگرچه منبع باز نیست. این نوع پروژه ها بسیار جالب هستند، اما سرعت آنها نیز به شدت کند است. شما خوش شانس خواهید بود که سرعت 1 فریم در ثانیه را دریافت کنید، بسیار کمتر از 60 فریم بر ثانیه.

این مقاله چند مورد از راه حل های اثبات مفهوم مورد علاقه من برای "اشتراک گذاری صفحه" یک برگه را مورد بحث قرار می دهد.

روش 1: مشاهده کنندگان جهش + WebSocket

یکی از روش‌های آینه‌سازی یک برگه توسط + Rafael Weinstein در اوایل سال جاری نشان داده شد. تکنیک او از Mutation Observers و WebSocket استفاده می کند.

اساساً، برگه‌ای که ارائه‌دهنده آن را به اشتراک می‌گذارد، تغییرات صفحه را مشاهده می‌کند و با استفاده از سوکت وب، تفاوت‌ها را برای بیننده ارسال می‌کند. هنگامی که کاربر صفحه را پیمایش می کند یا با آن تعامل می کند، ناظران این تغییرات را برداشته و با استفاده از کتابخانه خلاصه جهش رافائل به بیننده گزارش می دهند. این کارها را به خوبی حفظ می کند. کل صفحه برای هر فریم ارسال نمی شود.

همانطور که رافائل در ویدیو اشاره می کند، این صرفاً یک اثبات مفهوم است. با این حال، من فکر می‌کنم این یک راه منظم برای ترکیب یک ویژگی پلتفرم جدیدتر مانند Mutation Observers با یک ویژگی قدیمی‌تر مانند Websockets است.

روش 2: Blob از یک HTMLDocument + Binary WebSocket

این روش بعدی روشی است که اخیراً برای من مطرح شده است. این شبیه به رویکرد Mutation Observers است، اما به جای ارسال تفاوت‌های خلاصه، یک کلون Blob از کل HTMLDocument ایجاد می‌کند و آن را در یک وب سوکت باینری ارسال می‌کند. در اینجا تنظیمات بر اساس راه اندازی است:

  1. همه URL های صفحه را بازنویسی کنید تا مطلق باشند. این مانع از داشتن لینک های شکسته در تصاویر استاتیک و دارایی های CSS می شود.
  2. کلون عنصر سند صفحه: document.documentElement.cloneNode(true);
  3. کلون را فقط خواندنی، غیرقابل انتخاب کنید و از پیمایش با استفاده از pointer-events: 'none';user-select:'none';overflow:hidden;
  4. موقعیت اسکرول فعلی صفحه را ضبط کنید و آنها را به عنوان ویژگی های data-* در مورد تکراری اضافه کنید.
  5. یک new Blob() از .outerHTML نسخه تکراری ایجاد کنید.

کد چیزی شبیه به این است (من از منبع کامل موارد را ساده کرده ام):

function screenshotPage() {
    // 1. Rewrite current doc's imgs, css, and script URLs to be absolute before
    // we duplicate. This ensures no broken links when viewing the duplicate.
    urlsToAbsolute(document.images);
    urlsToAbsolute(document.querySelectorAll("link[rel='stylesheet']"));
    urlsToAbsolute(document.scripts);

    // 2. Duplicate entire document tree.
    var screenshot = document.documentElement.cloneNode(true);

    // 3. Screenshot should be readyonly, no scrolling, and no selections.
    screenshot.style.pointerEvents = 'none';
    screenshot.style.overflow = 'hidden';
    screenshot.style.userSelect = 'none'; // Note: need vendor prefixes

    // 4. … read on …

    // 5. Create a new .html file from the cloned content.
    var blob = new Blob([screenshot.outerHTML], {type: 'text/html'});

    // Open a popup to new file by creating a blob URL.
    window.open(window.URL.createObjectURL(blob));
}

urlsToAbsolute() حاوی regex های ساده برای بازنویسی URL های نسبی/بی طرح به URL های مطلق است. این ضروری است تا تصاویر، css، فونت‌ها و اسکریپت‌ها هنگام مشاهده در زمینه URL لکه‌ای (مثلاً از منبعی متفاوت) شکسته نشوند.

آخرین ترفندی که انجام دادم اضافه کردن پشتیبانی از اسکرول بود. وقتی ارائه دهنده صفحه را پیمایش می کند، بیننده باید آن را دنبال کند. برای انجام این کار، موقعیت‌های scrollX و scrollY فعلی را به عنوان ویژگی‌های data-* در HTMLDocument تکراری ذخیره می‌کنم. قبل از ایجاد Blob نهایی، یک بیت JS تزریق می شود که در بارگذاری صفحه فعال می شود:

// 4. Preserve current x,y scroll position of this page. See addOnPageLoad().
screenshot.dataset.scrollX = window.scrollX;
screenshot.dataset.scrollY = window.scrollY;

// 4.5. When screenshot loads (e.g. in blob URL), scroll it to the same location
// of this page. Do this by appending a window.onDOMContentLoaded listener
// which pulls out the screenshot (dupe's) saved scrollX/Y state on the DOM.
var script = document.createElement('script');
script.textContent = '(' + addOnPageLoad_.toString() + ')();'; // self calling.
screenshot.querySelector('body').appendChild(script);

// NOTE: Not to be invoked directly. When the screenshot loads, scroll it
// to the same x,y location of original page.
function addOnPageLoad() {
    window.addEventListener('DOMContentLoaded', function(e) {
    var scrollX = document.documentElement.dataset.scrollX || 0;
    var scrollY = document.documentElement.dataset.scrollY || 0;
    window.scrollTo(scrollX, scrollY);
    });

جعل کردن اسکرول این تصور را ایجاد می کند که ما بخشی از صفحه اصلی را اسکرین شات گرفته ایم، در حالی که در واقع، کل صفحه را کپی کرده ایم و صرفاً آن را تغییر مکان داده ایم. #باهوش

نسخه ی نمایشی

اما برای به اشتراک گذاری تب، باید به طور مداوم برگه را گرفته و برای بینندگان ارسال کنیم. برای این کار، یک سرور کوچک Node websocket، برنامه و بوکمارکلت نوشته ام که جریان را نشان می دهد. اگر به کد علاقه مند نیستید، در اینجا یک ویدیوی کوتاه از کارها وجود دارد:

بهبودهای آینده

یک بهینه سازی این است که کل سند را روی هر فریم کپی نکنید. این اتلاف است و مثال Mutation Observer در آن به خوبی عمل می کند. یکی دیگر از بهبودها مدیریت تصاویر پس زمینه CSS نسبی در urlsToAbsolute() است. این چیزی است که فیلمنامه فعلی در نظر نمی گیرد.

روش 3: Chrome Extension API + Binary WebSocket

در Google I/O 2012 ، من روش دیگری را برای اشتراک‌گذاری صفحه نمایش محتویات برگه مرورگر نشان دادم. با این حال، این یکی یک تقلب است. به یک API برنامه افزودنی Chrome نیاز دارد: جادوی خالص HTML5 نیست.

منبع این مورد نیز در Github موجود است، اما اصل مطلب این است:

  1. برگه فعلی را به عنوان یک dataURL .png بگیرید. برنامه‌های افزودنی Chrome یک API برای آن chrome.tabs.captureVisibleTab() دارند.
  2. dataURL را به Blob تبدیل کنید. به کمک کننده convertDataURIToBlob() مراجعه کنید.
  3. با تنظیم socket.responseType='blob' ، هر Blob (فریم) را با استفاده از یک وب سوکت باینری برای بیننده ارسال کنید.

مثال

در اینجا کدی برای عکس گرفتن از برگه فعلی به صورت png و ارسال فریم از طریق وب سوکت وجود دارد:

var IMG_MIMETYPE = 'images/jpeg'; // Update to image/webp when crbug.com/112957 is fixed.
var IMG_QUALITY = 80; // [0-100]
var SEND_INTERVAL = 250; // ms

var ws = new WebSocket('ws://…', 'dumby-protocol');
ws.binaryType = 'blob';

function captureAndSendTab() {
    var opts = {format: IMG_MIMETYPE, quality: IMG_QUALITY};
    chrome.tabs.captureVisibleTab(null, opts, function(dataUrl) {
    // captureVisibleTab returns a dataURL. Decode it -> convert to blob -> send.
    ws.send(convertDataURIToBlob(dataUrl, IMG_MIMETYPE));
    });
}

var intervalId = setInterval(function() {
    if (ws.bufferedAmount == 0) {
    captureAndSendTab();
    }
}, SEND_INTERVAL);

بهبودهای آینده

نرخ فریم به طور شگفت انگیزی برای این یکی خوب است، اما می تواند حتی بهتر باشد. یکی از پیشرفت ها حذف سربار تبدیل dataURL به Blob است. متأسفانه، chrome.tabs.captureVisibleTab() فقط یک dataURL به ما می دهد. اگر یک Blob یا Typed Array را برگرداند، می‌توانیم آن را مستقیماً از طریق وب سوکت ارسال کنیم، نه اینکه خودمان تبدیل به Blob را انجام دهیم. لطفاً crbug.com/32498 را ستاره دار کنید تا این اتفاق بیفتد!

روش 4: WebRTC - آینده واقعی

آخرین موضوع ولی به همان اهمیت!

آینده اشتراک‌گذاری صفحه نمایش در مرورگر توسط WebRTC محقق خواهد شد. در 14 آگوست 2012، تیم یک WebRTC Tab Content Capture API برای اشتراک‌گذاری محتویات برگه پیشنهاد کرد:

تا زمانی که این مرد آماده شود، روش های 1-3 باقی مانده است.

نتیجه

بنابراین اشتراک گذاری تب مرورگر با فناوری وب امروزی امکان پذیر است!

اما ... این اظهارات را باید با کمی نمک در نظر گرفت. در حالی که تکنیک‌های این مقاله کاملاً مرتب هستند، به هر طریقی از یک UX اشتراک‌گذاری عالی برخوردار نیستند. همه اینها با تلاش WebRTC Tab Content Capture تغییر خواهد کرد، اما تا زمانی که واقعیت پیدا نکند، ما با پلاگین های مرورگر یا راه حل های محدودی مانند مواردی که در اینجا توضیح داده شده است، باقی خواهیم ماند.

تکنیک های بیشتری دارید؟ ارسال نظر!