Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Sat, 30 Jul 2016 16:23:21 +0200
changeset 349592 1b848daf2102cb0636c9475ae424836abef5ca7f
parent 349591 ce119faadbc5999895c73ecbdab003d761ce296e (current diff)
parent 349490 e5859dfe0bcbd40f4e33f4a633f73ea3473a7849 (diff)
child 349593 77950beb90dd5636e0d3929638bdd11c899c9bd8
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
browser/base/content/test/webrtc/browser_devices_get_user_media_about_urls.js
browser/components/uitour/test/browser_UITour_loop.js
browser/components/uitour/test/browser_UITour_loop_panel.js
browser/extensions/loop/.gitignore
browser/extensions/loop/README.txt
browser/extensions/loop/bootstrap.js
browser/extensions/loop/chrome/content/modules/DomainWhitelist.jsm
browser/extensions/loop/chrome/content/modules/LoopRooms.jsm
browser/extensions/loop/chrome/content/modules/LoopRoomsCache.jsm
browser/extensions/loop/chrome/content/modules/MozLoopAPI.jsm
browser/extensions/loop/chrome/content/modules/MozLoopPushHandler.jsm
browser/extensions/loop/chrome/content/modules/MozLoopService.jsm
browser/extensions/loop/chrome/content/modules/MozLoopWorker.js
browser/extensions/loop/chrome/content/modules/tabFrame.js
browser/extensions/loop/chrome/content/panels/conversation.html
browser/extensions/loop/chrome/content/panels/copy.html
browser/extensions/loop/chrome/content/panels/css/copy.css
browser/extensions/loop/chrome/content/panels/css/desktop.css
browser/extensions/loop/chrome/content/panels/css/panel.css
browser/extensions/loop/chrome/content/panels/css/slideshow.css
browser/extensions/loop/chrome/content/panels/js/conversation.js
browser/extensions/loop/chrome/content/panels/js/conversationAppStore.js
browser/extensions/loop/chrome/content/panels/js/copy.js
browser/extensions/loop/chrome/content/panels/js/desktopViews.js
browser/extensions/loop/chrome/content/panels/js/feedbackViews.js
browser/extensions/loop/chrome/content/panels/js/models.js
browser/extensions/loop/chrome/content/panels/js/otconfig.js
browser/extensions/loop/chrome/content/panels/js/panel.js
browser/extensions/loop/chrome/content/panels/js/roomStore.js
browser/extensions/loop/chrome/content/panels/js/roomViews.js
browser/extensions/loop/chrome/content/panels/js/slideshow.js
browser/extensions/loop/chrome/content/panels/panel.html
browser/extensions/loop/chrome/content/panels/slideshow.html
browser/extensions/loop/chrome/content/panels/test/.eslintrc
browser/extensions/loop/chrome/content/panels/test/conversationAppStore_test.js
browser/extensions/loop/chrome/content/panels/test/conversation_test.js
browser/extensions/loop/chrome/content/panels/test/copy_test.js
browser/extensions/loop/chrome/content/panels/test/desktopViews_test.js
browser/extensions/loop/chrome/content/panels/test/fake-components.js
browser/extensions/loop/chrome/content/panels/test/feedbackViews_test.js
browser/extensions/loop/chrome/content/panels/test/index.html
browser/extensions/loop/chrome/content/panels/test/l10n_test.js
browser/extensions/loop/chrome/content/panels/test/panel_test.js
browser/extensions/loop/chrome/content/panels/test/roomStore_test.js
browser/extensions/loop/chrome/content/panels/test/roomViews_test.js
browser/extensions/loop/chrome/content/panels/test/slideshow_test.js
browser/extensions/loop/chrome/content/panels/test/test_desktop_all.py
browser/extensions/loop/chrome/content/panels/vendor/l10n.js
browser/extensions/loop/chrome/content/panels/vendor/simpleSlideshow.css
browser/extensions/loop/chrome/content/panels/vendor/simpleSlideshow.js
browser/extensions/loop/chrome/content/preferences/prefs.js
browser/extensions/loop/chrome/content/shared/css/common.css
browser/extensions/loop/chrome/content/shared/css/conversation.css
browser/extensions/loop/chrome/content/shared/css/reset.css
browser/extensions/loop/chrome/content/shared/img/02.png
browser/extensions/loop/chrome/content/shared/img/02@2x.png
browser/extensions/loop/chrome/content/shared/img/animated-spinner.svg
browser/extensions/loop/chrome/content/shared/img/arrow-01.svg
browser/extensions/loop/chrome/content/shared/img/audio-call-avatar.svg
browser/extensions/loop/chrome/content/shared/img/audio-default-16x16@1.5x.png
browser/extensions/loop/chrome/content/shared/img/audio-default-16x16@2x.png
browser/extensions/loop/chrome/content/shared/img/audio-hover.svg
browser/extensions/loop/chrome/content/shared/img/audio-mute-hover.svg
browser/extensions/loop/chrome/content/shared/img/audio-mute.svg
browser/extensions/loop/chrome/content/shared/img/audio-muted-darkgrey.svg
browser/extensions/loop/chrome/content/shared/img/audio.svg
browser/extensions/loop/chrome/content/shared/img/avatars.svg
browser/extensions/loop/chrome/content/shared/img/beta-ribbon.svg
browser/extensions/loop/chrome/content/shared/img/cam_audio-no.svg
browser/extensions/loop/chrome/content/shared/img/cam_audio.svg
browser/extensions/loop/chrome/content/shared/img/cam_audio_h.svg
browser/extensions/loop/chrome/content/shared/img/chatbubble-arrow-left.svg
browser/extensions/loop/chrome/content/shared/img/chatbubble-arrow-right.svg
browser/extensions/loop/chrome/content/shared/img/check.svg
browser/extensions/loop/chrome/content/shared/img/close-02.svg
browser/extensions/loop/chrome/content/shared/img/cursor.svg
browser/extensions/loop/chrome/content/shared/img/ellipsis-v.svg
browser/extensions/loop/chrome/content/shared/img/empty_search.svg
browser/extensions/loop/chrome/content/shared/img/exit.svg
browser/extensions/loop/chrome/content/shared/img/facemute-14x14.png
browser/extensions/loop/chrome/content/shared/img/facemute-14x14@2x.png
browser/extensions/loop/chrome/content/shared/img/firefox-avatar.svg
browser/extensions/loop/chrome/content/shared/img/firefox-hello_logo.svg
browser/extensions/loop/chrome/content/shared/img/firefox-hello_tour-slide-01.svg
browser/extensions/loop/chrome/content/shared/img/firefox-hello_tour-slide-02.svg
browser/extensions/loop/chrome/content/shared/img/firefox-hello_tour-slide-03.svg
browser/extensions/loop/chrome/content/shared/img/firefox-hello_tour-slide-04.svg
browser/extensions/loop/chrome/content/shared/img/firefox-logo.png
browser/extensions/loop/chrome/content/shared/img/glyph-email-16x16.svg
browser/extensions/loop/chrome/content/shared/img/glyph-facebook-16x16.svg
browser/extensions/loop/chrome/content/shared/img/glyph-help-16x16.svg
browser/extensions/loop/chrome/content/shared/img/glyph-help-no-circle-16x16.svg
browser/extensions/loop/chrome/content/shared/img/glyph-help-no-circle-blue-16x16.svg
browser/extensions/loop/chrome/content/shared/img/glyph-link-16x16.svg
browser/extensions/loop/chrome/content/shared/img/glyph-user-16x16.svg
browser/extensions/loop/chrome/content/shared/img/hangup-inverse-14x14.png
browser/extensions/loop/chrome/content/shared/img/hangup-inverse-14x14@2x.png
browser/extensions/loop/chrome/content/shared/img/happy.png
browser/extensions/loop/chrome/content/shared/img/hello-web-share.svg
browser/extensions/loop/chrome/content/shared/img/hello_logo.svg
browser/extensions/loop/chrome/content/shared/img/helloicon.svg
browser/extensions/loop/chrome/content/shared/img/icon_32.png
browser/extensions/loop/chrome/content/shared/img/icon_64.png
browser/extensions/loop/chrome/content/shared/img/icons-10x10.svg
browser/extensions/loop/chrome/content/shared/img/icons-14x14.svg
browser/extensions/loop/chrome/content/shared/img/icons-16x16.svg
browser/extensions/loop/chrome/content/shared/img/join_notification.svg
browser/extensions/loop/chrome/content/shared/img/leave.svg
browser/extensions/loop/chrome/content/shared/img/leave_notification.svg
browser/extensions/loop/chrome/content/shared/img/media-group-left-hover.svg
browser/extensions/loop/chrome/content/shared/img/media-group-right-hover.svg
browser/extensions/loop/chrome/content/shared/img/media-group.svg
browser/extensions/loop/chrome/content/shared/img/movistar.png
browser/extensions/loop/chrome/content/shared/img/movistar@2x.png
browser/extensions/loop/chrome/content/shared/img/mute-inverse-14x14.png
browser/extensions/loop/chrome/content/shared/img/mute-inverse-14x14@2x.png
browser/extensions/loop/chrome/content/shared/img/pause-12x12.svg
browser/extensions/loop/chrome/content/shared/img/paused-hello.svg
browser/extensions/loop/chrome/content/shared/img/play-12x12.svg
browser/extensions/loop/chrome/content/shared/img/sad.png
browser/extensions/loop/chrome/content/shared/img/sad_hello_icon_64x64.svg
browser/extensions/loop/chrome/content/shared/img/settings-hover.svg
browser/extensions/loop/chrome/content/shared/img/settings.svg
browser/extensions/loop/chrome/content/shared/img/sharing-active.svg
browser/extensions/loop/chrome/content/shared/img/sharing-hover.svg
browser/extensions/loop/chrome/content/shared/img/sharing-pending.svg
browser/extensions/loop/chrome/content/shared/img/sharing.svg
browser/extensions/loop/chrome/content/shared/img/spinner.png
browser/extensions/loop/chrome/content/shared/img/spinner.svg
browser/extensions/loop/chrome/content/shared/img/spinner@2x.png
browser/extensions/loop/chrome/content/shared/img/stop-12x12.svg
browser/extensions/loop/chrome/content/shared/img/telefonica-logo.svg
browser/extensions/loop/chrome/content/shared/img/video-hover.svg
browser/extensions/loop/chrome/content/shared/img/video-mute-hover.svg
browser/extensions/loop/chrome/content/shared/img/video-mute.svg
browser/extensions/loop/chrome/content/shared/img/video-muted-darkgrey.svg
browser/extensions/loop/chrome/content/shared/img/video.svg
browser/extensions/loop/chrome/content/shared/img/vivo.png
browser/extensions/loop/chrome/content/shared/img/vivo@2x.png
browser/extensions/loop/chrome/content/shared/js/actions.js
browser/extensions/loop/chrome/content/shared/js/activeRoomStore.js
browser/extensions/loop/chrome/content/shared/js/crypto.js
browser/extensions/loop/chrome/content/shared/js/dispatcher.js
browser/extensions/loop/chrome/content/shared/js/linkifiedTextView.js
browser/extensions/loop/chrome/content/shared/js/loopapi-client.js
browser/extensions/loop/chrome/content/shared/js/mixins.js
browser/extensions/loop/chrome/content/shared/js/otSdkDriver.js
browser/extensions/loop/chrome/content/shared/js/remoteCursorStore.js
browser/extensions/loop/chrome/content/shared/js/store.js
browser/extensions/loop/chrome/content/shared/js/textChatStore.js
browser/extensions/loop/chrome/content/shared/js/textChatView.js
browser/extensions/loop/chrome/content/shared/js/urlRegExps.js
browser/extensions/loop/chrome/content/shared/js/utils.js
browser/extensions/loop/chrome/content/shared/js/validate.js
browser/extensions/loop/chrome/content/shared/js/views.js
browser/extensions/loop/chrome/content/shared/sounds/connected.ogg
browser/extensions/loop/chrome/content/shared/sounds/connecting.ogg
browser/extensions/loop/chrome/content/shared/sounds/failure.ogg
browser/extensions/loop/chrome/content/shared/sounds/message.ogg
browser/extensions/loop/chrome/content/shared/sounds/room-joined-in.ogg
browser/extensions/loop/chrome/content/shared/sounds/room-joined.ogg
browser/extensions/loop/chrome/content/shared/sounds/room-left.ogg
browser/extensions/loop/chrome/content/shared/sounds/terminated.ogg
browser/extensions/loop/chrome/content/shared/test/.eslintrc
browser/extensions/loop/chrome/content/shared/test/activeRoomStore_test.js
browser/extensions/loop/chrome/content/shared/test/crypto_test.js
browser/extensions/loop/chrome/content/shared/test/dispatcher_test.js
browser/extensions/loop/chrome/content/shared/test/frontend_tester.py
browser/extensions/loop/chrome/content/shared/test/index.html
browser/extensions/loop/chrome/content/shared/test/linkifiedTextView_test.js
browser/extensions/loop/chrome/content/shared/test/loop_mocha_utils.js
browser/extensions/loop/chrome/content/shared/test/loopapi-client_test.js
browser/extensions/loop/chrome/content/shared/test/mixins_test.js
browser/extensions/loop/chrome/content/shared/test/otSdkDriver_test.js
browser/extensions/loop/chrome/content/shared/test/remoteCursorStore_test.js
browser/extensions/loop/chrome/content/shared/test/sdk_mock.js
browser/extensions/loop/chrome/content/shared/test/store_test.js
browser/extensions/loop/chrome/content/shared/test/test_shared_all.py
browser/extensions/loop/chrome/content/shared/test/textChatStore_test.js
browser/extensions/loop/chrome/content/shared/test/textChatView_test.js
browser/extensions/loop/chrome/content/shared/test/utils_test.js
browser/extensions/loop/chrome/content/shared/test/validate_test.js
browser/extensions/loop/chrome/content/shared/test/vendor/chai-as-promised.js
browser/extensions/loop/chrome/content/shared/test/vendor/chai.js
browser/extensions/loop/chrome/content/shared/test/vendor/mocha.css
browser/extensions/loop/chrome/content/shared/test/vendor/mocha.js
browser/extensions/loop/chrome/content/shared/test/vendor/react-dom-server.js
browser/extensions/loop/chrome/content/shared/test/vendor/sinon.js
browser/extensions/loop/chrome/content/shared/test/views_test.js
browser/extensions/loop/chrome/content/shared/vendor/backbone.js
browser/extensions/loop/chrome/content/shared/vendor/classnames.js
browser/extensions/loop/chrome/content/shared/vendor/lodash.js
browser/extensions/loop/chrome/content/shared/vendor/react-dom-prod.js
browser/extensions/loop/chrome/content/shared/vendor/react-dom.js
browser/extensions/loop/chrome/content/shared/vendor/react-prod.js
browser/extensions/loop/chrome/content/shared/vendor/react.js
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/css/ot.css
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/access-denied-chrome.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/access-denied-copy-firefox.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/access-denied-firefox.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/access-predenied-chrome.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/access-prompt-chrome.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/audioonly-publisher.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/audioonly-subscriber.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/buttons.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/loader.gif
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/mic-off.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/mic-on.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/speaker-off.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/images/rtc/speaker-on.png
browser/extensions/loop/chrome/content/shared/vendor/sdk-content/js/dynamic_config.min.js
browser/extensions/loop/chrome/content/shared/vendor/sdk.js
browser/extensions/loop/chrome/locale/af/loop.properties
browser/extensions/loop/chrome/locale/ar/loop.properties
browser/extensions/loop/chrome/locale/as/loop.properties
browser/extensions/loop/chrome/locale/ast/loop.properties
browser/extensions/loop/chrome/locale/az/loop.properties
browser/extensions/loop/chrome/locale/be/loop.properties
browser/extensions/loop/chrome/locale/bg/loop.properties
browser/extensions/loop/chrome/locale/bn-BD/loop.properties
browser/extensions/loop/chrome/locale/bn-IN/loop.properties
browser/extensions/loop/chrome/locale/bs/loop.properties
browser/extensions/loop/chrome/locale/ca/loop.properties
browser/extensions/loop/chrome/locale/cs/loop.properties
browser/extensions/loop/chrome/locale/cy/loop.properties
browser/extensions/loop/chrome/locale/da/loop.properties
browser/extensions/loop/chrome/locale/de/loop.properties
browser/extensions/loop/chrome/locale/dsb/loop.properties
browser/extensions/loop/chrome/locale/el/loop.properties
browser/extensions/loop/chrome/locale/en-GB/loop.properties
browser/extensions/loop/chrome/locale/en-US/loop.properties
browser/extensions/loop/chrome/locale/eo/loop.properties
browser/extensions/loop/chrome/locale/es-AR/loop.properties
browser/extensions/loop/chrome/locale/es-CL/loop.properties
browser/extensions/loop/chrome/locale/es-ES/loop.properties
browser/extensions/loop/chrome/locale/es-MX/loop.properties
browser/extensions/loop/chrome/locale/et/loop.properties
browser/extensions/loop/chrome/locale/eu/loop.properties
browser/extensions/loop/chrome/locale/fa/loop.properties
browser/extensions/loop/chrome/locale/ff/loop.properties
browser/extensions/loop/chrome/locale/fi/loop.properties
browser/extensions/loop/chrome/locale/fr/loop.properties
browser/extensions/loop/chrome/locale/fy-NL/loop.properties
browser/extensions/loop/chrome/locale/fy/loop.properties
browser/extensions/loop/chrome/locale/ga/loop.properties
browser/extensions/loop/chrome/locale/gd/loop.properties
browser/extensions/loop/chrome/locale/gl/loop.properties
browser/extensions/loop/chrome/locale/gu-IN/loop.properties
browser/extensions/loop/chrome/locale/he/loop.properties
browser/extensions/loop/chrome/locale/hi-IN/loop.properties
browser/extensions/loop/chrome/locale/hr/loop.properties
browser/extensions/loop/chrome/locale/hsb/loop.properties
browser/extensions/loop/chrome/locale/ht/loop.properties
browser/extensions/loop/chrome/locale/hu/loop.properties
browser/extensions/loop/chrome/locale/hy-AM/loop.properties
browser/extensions/loop/chrome/locale/id/loop.properties
browser/extensions/loop/chrome/locale/it/loop.properties
browser/extensions/loop/chrome/locale/ja/loop.properties
browser/extensions/loop/chrome/locale/jar.mn
browser/extensions/loop/chrome/locale/kk/loop.properties
browser/extensions/loop/chrome/locale/km/loop.properties
browser/extensions/loop/chrome/locale/kn/loop.properties
browser/extensions/loop/chrome/locale/ko/loop.properties
browser/extensions/loop/chrome/locale/ku/loop.properties
browser/extensions/loop/chrome/locale/lij/loop.properties
browser/extensions/loop/chrome/locale/lt/loop.properties
browser/extensions/loop/chrome/locale/lv/loop.properties
browser/extensions/loop/chrome/locale/mk/loop.properties
browser/extensions/loop/chrome/locale/ml/loop.properties
browser/extensions/loop/chrome/locale/mn/loop.properties
browser/extensions/loop/chrome/locale/moz.build
browser/extensions/loop/chrome/locale/ms/loop.properties
browser/extensions/loop/chrome/locale/my/loop.properties
browser/extensions/loop/chrome/locale/nb-NO/loop.properties
browser/extensions/loop/chrome/locale/ne-NP/loop.properties
browser/extensions/loop/chrome/locale/nl/loop.properties
browser/extensions/loop/chrome/locale/nn-NO/loop.properties
browser/extensions/loop/chrome/locale/or/loop.properties
browser/extensions/loop/chrome/locale/pa-IN/loop.properties
browser/extensions/loop/chrome/locale/pa/loop.properties
browser/extensions/loop/chrome/locale/pl/loop.properties
browser/extensions/loop/chrome/locale/pt-BR/loop.properties
browser/extensions/loop/chrome/locale/pt-PT/loop.properties
browser/extensions/loop/chrome/locale/pt/loop.properties
browser/extensions/loop/chrome/locale/rm/loop.properties
browser/extensions/loop/chrome/locale/ro/loop.properties
browser/extensions/loop/chrome/locale/ru/loop.properties
browser/extensions/loop/chrome/locale/si/loop.properties
browser/extensions/loop/chrome/locale/sk/loop.properties
browser/extensions/loop/chrome/locale/sl/loop.properties
browser/extensions/loop/chrome/locale/son/loop.properties
browser/extensions/loop/chrome/locale/sq/loop.properties
browser/extensions/loop/chrome/locale/sr/loop.properties
browser/extensions/loop/chrome/locale/sv-SE/loop.properties
browser/extensions/loop/chrome/locale/ta/loop.properties
browser/extensions/loop/chrome/locale/te/loop.properties
browser/extensions/loop/chrome/locale/th/loop.properties
browser/extensions/loop/chrome/locale/tr/loop.properties
browser/extensions/loop/chrome/locale/uk/loop.properties
browser/extensions/loop/chrome/locale/ur/loop.properties
browser/extensions/loop/chrome/locale/vi/loop.properties
browser/extensions/loop/chrome/locale/xh/loop.properties
browser/extensions/loop/chrome/locale/zh-CN/loop.properties
browser/extensions/loop/chrome/locale/zh-TW/loop.properties
browser/extensions/loop/chrome/locale/zu/loop.properties
browser/extensions/loop/chrome/skin/linux/menuPanel.png
browser/extensions/loop/chrome/skin/linux/menuPanel@2x.png
browser/extensions/loop/chrome/skin/linux/platform.css
browser/extensions/loop/chrome/skin/linux/toolbar-inverted.png
browser/extensions/loop/chrome/skin/linux/toolbar-inverted@2x.png
browser/extensions/loop/chrome/skin/linux/toolbar.png
browser/extensions/loop/chrome/skin/linux/toolbar@2x.png
browser/extensions/loop/chrome/skin/osx/menuPanel-yosemite.png
browser/extensions/loop/chrome/skin/osx/menuPanel-yosemite@2x.png
browser/extensions/loop/chrome/skin/osx/menuPanel.png
browser/extensions/loop/chrome/skin/osx/menuPanel@2x.png
browser/extensions/loop/chrome/skin/osx/platform.css
browser/extensions/loop/chrome/skin/osx/toolbar-inverted.png
browser/extensions/loop/chrome/skin/osx/toolbar-inverted@2x.png
browser/extensions/loop/chrome/skin/osx/toolbar-yosemite.png
browser/extensions/loop/chrome/skin/osx/toolbar-yosemite@2x.png
browser/extensions/loop/chrome/skin/osx/toolbar.png
browser/extensions/loop/chrome/skin/osx/toolbar@2x.png
browser/extensions/loop/chrome/skin/shared/loop.css
browser/extensions/loop/chrome/skin/windows/menuPanel-aero.png
browser/extensions/loop/chrome/skin/windows/menuPanel-aero@2x.png
browser/extensions/loop/chrome/skin/windows/menuPanel.png
browser/extensions/loop/chrome/skin/windows/menuPanel@2x.png
browser/extensions/loop/chrome/skin/windows/platform.css
browser/extensions/loop/chrome/skin/windows/toolbar-XP.png
browser/extensions/loop/chrome/skin/windows/toolbar-XP@2x.png
browser/extensions/loop/chrome/skin/windows/toolbar-aero.png
browser/extensions/loop/chrome/skin/windows/toolbar-aero@2x.png
browser/extensions/loop/chrome/skin/windows/toolbar-inverted.png
browser/extensions/loop/chrome/skin/windows/toolbar-inverted@2x.png
browser/extensions/loop/chrome/skin/windows/toolbar-lunaSilver.png
browser/extensions/loop/chrome/skin/windows/toolbar-lunaSilver@2x.png
browser/extensions/loop/chrome/skin/windows/toolbar-win8.png
browser/extensions/loop/chrome/skin/windows/toolbar-win8@2x.png
browser/extensions/loop/chrome/skin/windows/toolbar.png
browser/extensions/loop/chrome/skin/windows/toolbar@2x.png
browser/extensions/loop/chrome/test/mochitest/.eslintrc
browser/extensions/loop/chrome/test/mochitest/browser.ini
browser/extensions/loop/chrome/test/mochitest/browser_LoopRooms_channel.js
browser/extensions/loop/chrome/test/mochitest/browser_copypanel.js
browser/extensions/loop/chrome/test/mochitest/browser_fxa_login.js
browser/extensions/loop/chrome/test/mochitest/browser_loop_fxa_server.js
browser/extensions/loop/chrome/test/mochitest/browser_menuitem.js
browser/extensions/loop/chrome/test/mochitest/browser_mozLoop_appVersionInfo.js
browser/extensions/loop/chrome/test/mochitest/browser_mozLoop_chat.js
browser/extensions/loop/chrome/test/mochitest/browser_mozLoop_context.js
browser/extensions/loop/chrome/test/mochitest/browser_mozLoop_infobar.js
browser/extensions/loop/chrome/test/mochitest/browser_mozLoop_sharingListeners.js
browser/extensions/loop/chrome/test/mochitest/browser_mozLoop_telemetry.js
browser/extensions/loop/chrome/test/mochitest/browser_panel_privateBrowsing.js
browser/extensions/loop/chrome/test/mochitest/browser_sharingTitleListeners.js
browser/extensions/loop/chrome/test/mochitest/browser_throttler.js
browser/extensions/loop/chrome/test/mochitest/browser_toolbarbutton.js
browser/extensions/loop/chrome/test/mochitest/head.js
browser/extensions/loop/chrome/test/mochitest/loop_fxa.sjs
browser/extensions/loop/chrome/test/mochitest/test_loopLinkClicker_channel.html
browser/extensions/loop/chrome/test/xpcshell/.eslintrc
browser/extensions/loop/chrome/test/xpcshell/head.js
browser/extensions/loop/chrome/test/xpcshell/test_loopapi_app_version.js
browser/extensions/loop/chrome/test/xpcshell/test_loopapi_doNotDisturb.js
browser/extensions/loop/chrome/test/xpcshell/test_loopapi_ftu_url.js
browser/extensions/loop/chrome/test/xpcshell/test_loopapi_internal.js
browser/extensions/loop/chrome/test/xpcshell/test_loopapi_prefs.js
browser/extensions/loop/chrome/test/xpcshell/test_looppush_initialize.js
browser/extensions/loop/chrome/test/xpcshell/test_looprooms.js
browser/extensions/loop/chrome/test/xpcshell/test_looprooms_encryption_in_fxa.js
browser/extensions/loop/chrome/test/xpcshell/test_looprooms_first_notification.js
browser/extensions/loop/chrome/test/xpcshell/test_looprooms_getall.js
browser/extensions/loop/chrome/test/xpcshell/test_looprooms_upgrade_to_encryption.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_dnd.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_encryptionkey.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_hawk_errors.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_hawk_request.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_initialize.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_locales.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_loop_prefs.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_registration.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_registration_retry.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_restart.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_token_invalid.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_token_save.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_token_send.js
browser/extensions/loop/chrome/test/xpcshell/test_loopservice_token_validation.js
browser/extensions/loop/chrome/test/xpcshell/xpcshell.ini
browser/extensions/loop/install.rdf.in
browser/extensions/loop/jar.mn
browser/extensions/loop/manifest.ini
browser/extensions/loop/moz.build
browser/extensions/loop/run-all-loop-tests.sh
browser/extensions/loop/test/functional/config.py
browser/extensions/loop/test/functional/hanging_threads.py
browser/extensions/loop/test/functional/loopTestDriver.py
browser/extensions/loop/test/functional/manifest.ini
browser/extensions/loop/test/functional/serversetup.py
browser/extensions/loop/test/functional/test_1_browser_call.py
browser/extensions/loop/test/functional/test_2_linkclicker.py
browser/locales/en-US/chrome/browser/loop/loop.properties
browser/themes/osx/KUI-background.png
browser/themes/windows/KUI-background.png
--- a/.eslintignore
+++ b/.eslintignore
@@ -69,19 +69,16 @@ browser/components/privatebrowsing/**
 browser/components/sessionstore/**
 browser/components/shell/**
 browser/components/tabview/**
 browser/components/translation/**
 browser/extensions/pdfjs/**
 browser/extensions/pocket/content/panels/js/vendor/**
 browser/locales/**
 
-# Ignore all of loop since it is imported from github and checked at source.
-browser/extensions/loop/**
-
 # devtools/ exclusions
 devtools/client/canvasdebugger/**
 devtools/client/commandline/**
 devtools/client/debugger/**
 devtools/client/eyedropper/**
 devtools/client/framework/**
 devtools/client/jsonview/lib/**
 devtools/client/memory/**
--- a/.gitignore
+++ b/.gitignore
@@ -6,19 +6,16 @@
 *.pyo
 TAGS
 tags
 ID
 .DS_Store*
 *.pdb
 *.egg-info
 
-# Allow the id locale directory for loop ('ID' matches this normally)
-!browser/extensions/loop/chrome/locale/id
-
 # Vim swap files.
 .*.sw[a-z]
 
 # Emacs directory variable files.
 **/.dir-locals.el
 
 # User files that may appear at the root
 /.mozconfig*
--- a/.hgignore
+++ b/.hgignore
@@ -82,26 +82,16 @@
 ^media/libstagefright/android$
 
 # Tag files generated by GNU Global
 GTAGS
 GRTAGS
 GSYMS
 GPATH
 
-# Various items for Loop
-^browser/components/loop/standalone/content/config\.js$
-^browser/extensions/loop/.*/node_modules/
-^browser/extensions/loop/.*\.module-cache
-^browser/extensions/loop/test/coverage/desktop
-^browser/extensions/loop/test/coverage/shared_standalone
-^browser/extensions/loop/test/visual-regression/diff
-^browser/extensions/loop/test/visual-regression/new
-^browser/extensions/loop/test/visual-regression/refs
-
 # Git clone directory for updating web-platform-tests
 ^testing/web-platform/sync/
 
 # Android Gradle artifacts.
 ^mobile/android/gradle/.gradle
 
 # XCode project cruft
 ^embedding/ios/GeckoEmbed/GeckoEmbed.xcodeproj/project.xcworkspace/xcuserdata
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1272693 - Clobber required to rebuild NSS.
+Bug 1287827 - Clobber needed because this patch removes files, second landing AND Bug 1272693 - Clobber required to rebuild NSS.
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -579,17 +579,19 @@ pref("mousewheel.with_meta.action", 1); 
 #endif
 pref("mousewheel.with_control.action",3);
 pref("mousewheel.with_win.action", 1);
 
 pref("browser.xul.error_pages.enabled", true);
 pref("browser.xul.error_pages.expert_bad_cert", false);
 
 // Enable captive portal detection.
+#ifdef NIGHTLY_BUILD
 pref("network.captive-portal-service.enabled", true);
+#endif
 
 // If true, network link events will change the value of navigator.onLine
 pref("network.manage-offline-status", true);
 
 // We want to make sure mail URLs are handled externally...
 pref("network.protocol-handler.external.mailto", true); // for mail
 pref("network.protocol-handler.external.news", true);   // for news
 pref("network.protocol-handler.external.snews", true);  // for secure news
@@ -1470,9 +1472,9 @@ pref("extensions.pocket.enabled", true);
 
 pref("signon.schemeUpgrades", true);
 
 // Enable the "Simplify Page" feature in Print Preview
 pref("print.use_simplify_page", true);
 
 // Space separated list of URLS that are allowed to send objects (instead of
 // only strings) through webchannels. This list is duplicated in mobile/android/app/mobile.js
-pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https://content.cdn.mozilla.net https://hello.firefox.com https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");
+pref("webchannel.allowObject.urlWhitelist", "https://accounts.firefox.com https://content.cdn.mozilla.net https://input.mozilla.org https://support.mozilla.org https://install.mozilla.org");
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -290,28 +290,16 @@
            side="right"
            type="arrow"
            hidden="true"
            flip="slide"
            rolluponmousewheel="true"
            noautofocus="true"
            position="topcenter topright"/>
 
-    <panel id="loop-notification-panel"
-           class="loop-panel social-panel"
-           type="arrow"
-           hidden="true"
-           noautofocus="true"/>
-
-    <panel id="loop-panel"
-           class="loop-panel social-panel"
-           type="arrow"
-           orient="horizontal"
-           hidden="true"/>
-
     <menupopup id="toolbar-context-menu"
                onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('viewToolbarsMenuSeparator'));">
       <menuitem oncommand="gCustomizeMode.addToPanel(document.popupNode)"
                 accesskey="&customizeMenu.moveToPanel.accesskey;"
                 label="&customizeMenu.moveToPanel.label;"
                 contexttype="toolbaritem"
                 class="customize-context-moveToPanel"/>
       <menuitem oncommand="gCustomizeMode.removeFromArea(document.popupNode)"
@@ -405,17 +393,17 @@
                 accesskey="&showRecentlyBookmarked.accesskey;"
                 oncommand="BookmarkingUI.showRecentlyBookmarked();"
                 closemenu="single"
                 ignoreitem="true"
                 ordinal="2"
                 hidden="true"/>
     </menupopup>
 
-    <panel id="ctrlTab-panel" class="KUI-panel" hidden="true" norestorefocus="true" level="top">
+    <panel id="ctrlTab-panel" hidden="true" norestorefocus="true" level="top">
       <hbox>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
         <button class="ctrlTab-preview" flex="1"/>
       </hbox>
--- a/browser/base/content/test/general/browser_parsable_css.js
+++ b/browser/base/content/test/general/browser_parsable_css.js
@@ -15,26 +15,16 @@ let whitelist = [
    isFromDevTools: true},
   // PDFjs is futureproofing its pseudoselectors, and those rules are dropped.
   {sourceName: /web\/viewer\.css$/i,
    errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i,
    isFromDevTools: false},
   // Tracked in bug 1004428.
   {sourceName: /aboutaccounts\/(main|normalize)\.css$/i,
     isFromDevTools: false},
-  // TokBox SDK assets, see bug 1032469.
-  {sourceName: /loop\/.*sdk-content\/.*\.css$/i,
-    isFromDevTools: false},
-  // Loop standalone client CSS uses placeholder cross browser pseudo-element
-  {sourceName: /loop\/.*\.css$/i,
-   errorMessage: /Unknown pseudo-class.*placeholder/i,
-   isFromDevTools: false},
-  {sourceName: /loop\/.*shared\/css\/common.css$/i,
-   errorMessage: /Unknown property .user-select./i,
-   isFromDevTools: false},
   // Highlighter CSS uses a UA-only pseudo-class, see bug 985597.
   {sourceName: /highlighters\.css$/i,
    errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i,
    isFromDevTools: true},
   // Responsive Design Mode CSS uses a UA-only pseudo-class, see Bug 1241714.
   {sourceName: /responsive-ua\.css$/i,
    errorMessage: /Unknown pseudo-class.*moz-dropdown-list/i,
    isFromDevTools: true},
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -1,12 +1,10 @@
 [DEFAULT]
 support-files =
   get_user_media.html
   get_user_media_content_script.js
   head.js
 
 [browser_devices_get_user_media.js]
 skip-if = buildapp == 'mulet' || (os == "linux" && debug) # linux: bug 976544
-[browser_devices_get_user_media_about_urls.js]
-skip-if = e10s && debug
 [browser_devices_get_user_media_anim.js]
 [browser_devices_get_user_media_in_frame.js]
deleted file mode 100644
--- a/browser/base/content/test/webrtc/browser_devices_get_user_media_about_urls.js
+++ /dev/null
@@ -1,219 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const PREF_LOOP_CSP = "loop.CSP";
-
-var gTab;
-
-// Taken from dom/media/tests/mochitest/head.js
-function isMacOSX10_6orOlder() {
-  var is106orOlder = false;
-
-  if (navigator.platform.indexOf("Mac") == 0) {
-    var version = Cc["@mozilla.org/system-info;1"]
-                    .getService(Ci.nsIPropertyBag2)
-                    .getProperty("version");
-    // the next line is correct: Mac OS 10.6 corresponds to Darwin version 10.x !
-    // Mac OS 10.7 is Darwin version 11.x. the |version| string we've got here
-    // is the Darwin version.
-    is106orOlder = (parseFloat(version) < 11.0);
-  }
-  return is106orOlder;
-}
-
-// Screensharing is disabled on older platforms (WinXP and Mac 10.6).
-function isOldPlatform() {
-  const isWinXP = navigator.userAgent.indexOf("Windows NT 5.1") != -1;
-  if (isMacOSX10_6orOlder() || isWinXP) {
-    info(true, "Screensharing disabled for OSX10.6 and WinXP");
-    return true;
-  }
-  return false;
-}
-
-// Linux prompts aren't working for screensharing.
-function isLinux() {
-  return navigator.platform.indexOf("Linux") != -1;
-}
-
-function loadPage(aUrl) {
-  let deferred = Promise.defer();
-
-  gTab.linkedBrowser.addEventListener("load", function onload() {
-    gTab.linkedBrowser.removeEventListener("load", onload, true);
-
-    is(PopupNotifications._currentNotifications.length, 0,
-       "should start the test without any prior popup notification");
-
-    deferred.resolve();
-  }, true);
-  content.location = aUrl;
-  return deferred.promise;
-}
-
-registerCleanupFunction(function() {
-  gBrowser.removeCurrentTab();
-});
-
-const permissionError = "error: NotAllowedError: The request is not allowed " +
-    "by the user agent or the platform in the current context.";
-
-var gTests = [
-
-{
-  desc: "getUserMedia about:loopconversation shouldn't prompt",
-  run: function checkAudioVideoLoop() {
-    yield SpecialPowers.pushPrefEnv({
-      "set": [[PREF_LOOP_CSP, "default-src 'unsafe-inline'"]],
-    });
-
-    yield loadPage("about:loopconversation");
-
-    info("requesting devices");
-    let promise = promiseObserverCalled("recording-device-events");
-    yield promiseRequestDevice(true, true);
-    yield promise;
-
-    // Wait for the devices to actually be captured and running before
-    // proceeding.
-    yield promisePopupNotification("webRTC-sharingDevices");
-
-    is((yield getMediaCaptureState()), "CameraAndMicrophone",
-       "expected camera and microphone to be shared");
-
-    yield closeStream();
-
-    yield SpecialPowers.popPrefEnv();
-  }
-},
-
-{
-  desc: "getUserMedia about:loopconversation should prompt for window sharing",
-  run: function checkShareScreenLoop() {
-    if (isOldPlatform() || isLinux()) {
-      return;
-    }
-
-    yield SpecialPowers.pushPrefEnv({
-      "set": [[PREF_LOOP_CSP, "default-src 'unsafe-inline'"]],
-    });
-
-    yield loadPage("about:loopconversation");
-
-    info("requesting screen");
-    let promise = promiseObserverCalled("getUserMedia:request");
-    yield promiseRequestDevice(false, true, null, "window");
-
-    // Wait for the devices to actually be captured and running before
-    // proceeding.
-    yield promisePopupNotification("webRTC-shareDevices");
-
-    is((yield getMediaCaptureState()), "none",
-       "expected camera and microphone not to be shared");
-
-    yield promiseMessage(permissionError, () => {
-      PopupNotifications.panel.firstChild.button.click();
-    });
-
-    yield expectObserverCalled("getUserMedia:response:deny");
-    yield expectObserverCalled("recording-window-ended");
-
-    yield SpecialPowers.popPrefEnv();
-  }
-},
-
-{
-  desc: "getUserMedia about:evil should prompt",
-  run: function checkAudioVideoNonLoop() {
-    yield loadPage("about:evil");
-
-    let promise = promiseObserverCalled("getUserMedia:request");
-    yield promiseRequestDevice(true, true);
-    yield promise;
-
-    is((yield getMediaCaptureState()), "none",
-       "expected camera and microphone not to be shared");
-  }
-},
-
-];
-
-function test() {
-  waitForExplicitFinish();
-
-  gTab = gBrowser.addTab();
-  gBrowser.selectedTab = gTab;
-
-  gTab.linkedBrowser.messageManager.loadFrameScript(CONTENT_SCRIPT_HELPER, true);
-
-  Task.spawn(function () {
-    yield ContentTask.spawn(gBrowser.selectedBrowser,
-                            getRootDirectory(gTestPath) + "get_user_media.html",
-                            function* (url) {
-      const Ci = Components.interfaces;
-      Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-      Components.utils.import("resource://gre/modules/Services.jsm");
-
-      /* A fake about module to map get_user_media.html to different about urls. */
-      function fakeLoopAboutModule() {
-      }
-
-      fakeLoopAboutModule.prototype = {
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsIAboutModule]),
-        newChannel: function (aURI, aLoadInfo) {
-          let uri = Services.io.newURI(url, null, null);
-          let chan = Services.io.newChannelFromURIWithLoadInfo(uri, aLoadInfo);
-          chan.owner = Services.scriptSecurityManager.getSystemPrincipal();
-          return chan;
-        },
-        getURIFlags: function (aURI) {
-          return Ci.nsIAboutModule.URI_SAFE_FOR_UNTRUSTED_CONTENT |
-                 Ci.nsIAboutModule.ALLOW_SCRIPT |
-                 Ci.nsIAboutModule.URI_CAN_LOAD_IN_CHILD |
-                 Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT;
-        }
-      };
-
-      var factory = XPCOMUtils._getFactory(fakeLoopAboutModule);
-      var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-      let UUIDGenerator = Components.classes["@mozilla.org/uuid-generator;1"]
-                                    .getService(Ci.nsIUUIDGenerator);
-      registrar.registerFactory(UUIDGenerator.generateUUID(), "",
-                                "@mozilla.org/network/protocol/about;1?what=loopconversation",
-                                factory);
-      registrar.registerFactory(UUIDGenerator.generateUUID(), "",
-                                "@mozilla.org/network/protocol/about;1?what=evil",
-                                factory);
-    });
-
-    yield SpecialPowers.pushPrefEnv({
-      "set": [[PREF_PERMISSION_FAKE, true],
-              ["media.getusermedia.screensharing.enabled", true]],
-    });
-
-    for (let test of gTests) {
-      info(test.desc);
-      yield test.run();
-
-      // Cleanup before the next test
-      expectNoObserverCalled();
-    }
-
-    yield ContentTask.spawn(gBrowser.selectedBrowser, null,
-                            function* () {
-      const Ci = Components.interfaces;
-      const Cc = Components.classes;
-      var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
-      let cid = Cc["@mozilla.org/network/protocol/about;1?what=loopconversation"];
-      registrar.unregisterFactory(cid,
-                                  registrar.getClassObject(cid, Ci.nsIFactory));
-      cid = Cc["@mozilla.org/network/protocol/about;1?what=evil"];
-      registrar.unregisterFactory(cid,
-                                  registrar.getClassObject(cid, Ci.nsIFactory));
-    });
-  }).then(finish, ex => {
-    ok(false, "Unexpected Exception: " + ex);
-    finish();
-  });
-}
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -97,32 +97,16 @@ static RedirEntry kRedirMap[] = {
   { "downloads", "chrome://browser/content/downloads/contentAreaDownloadsView.xul",
     nsIAboutModule::ALLOW_SCRIPT },
 #ifdef MOZ_SERVICES_HEALTHREPORT
   { "healthreport", "chrome://browser/content/abouthealthreport/abouthealth.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
 #endif
   { "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
-  // Linkable because of indexeddb use (bug 1228118)
-  { "loopconversation", "chrome://loop/content/panels/conversation.html",
-    nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
-    nsIAboutModule::ALLOW_SCRIPT |
-    nsIAboutModule::HIDE_FROM_ABOUTABOUT |
-    nsIAboutModule::MAKE_LINKABLE |
-    nsIAboutModule::ENABLE_INDEXED_DB },
-  // Linkable because of indexeddb use (bug 1228118)
-  { "looppanel", "chrome://loop/content/panels/panel.html",
-    nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
-    nsIAboutModule::ALLOW_SCRIPT |
-    nsIAboutModule::HIDE_FROM_ABOUTABOUT |
-    nsIAboutModule::MAKE_LINKABLE |
-    nsIAboutModule::ENABLE_INDEXED_DB,
-    // Shares an IndexedDB origin with about:loopconversation.
-    "loopconversation" },
   { "reader", "chrome://global/content/reader/aboutReader.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT },
 };
 static const int kRedirTotal = ArrayLength(kRedirMap);
 
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -103,18 +103,16 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_HEALTHREPORT
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "healthreport", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
-    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "looppanel", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
-    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "loopconversation", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "reader", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #if defined(XP_WIN)
     { NS_IEHISTORYENUMERATOR_CONTRACTID, &kNS_WINIEHISTORYENUMERATOR_CID },
 #elif defined(XP_MACOSX)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
 #endif
     { nullptr }
 };
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -59,17 +59,16 @@ const kSubviewEvents = [
 var kVersion = 6;
 
 /**
  * Buttons removed from built-ins by version they were removed. kVersion must be
  * bumped any time a new id is added to this. Use the button id as key, and
  * version the button is removed in as the value.  e.g. "pocket-button": 5
  */
 var ObsoleteBuiltinButtons = {
-  "loop-button": 5,
   "pocket-button": 6
 };
 
 /**
  * gPalette is a map of every widget that CustomizableUI.jsm knows about, keyed
  * on their IDs.
  */
 var gPalette = new Map();
@@ -233,17 +232,16 @@ var CustomizableUIInternal = {
     PanelWideWidgetTracker.init();
 
     let navbarPlacements = [
       "urlbar-container",
       "search-container",
       "bookmarks-menu-button",
       "downloads-button",
       "home-button",
-      "loop-button",
     ];
 
     if (AppConstants.MOZ_DEV_EDITION) {
       navbarPlacements.splice(2, 0, "developer-button");
     }
 
     if (Services.prefs.getBoolPref(kPrefWebIDEInNavbar)) {
       navbarPlacements.push("webide-button");
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -237,18 +237,16 @@
                      label="&showAllBookmarks2.label;"
                      class="subviewbutton panel-subview-footer"
                      command="Browser:ShowAllBookmarks"
                      onclick="PanelUI.hide();"/>
     </panelview>
 
     <panelview id="PanelUI-socialapi" flex="1"/>
 
-    <panelview id="PanelUI-loopapi" flex="1"/>
-
     <panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);">
       <label value="&feedsMenu2.label;" class="panel-subview-header"/>
     </panelview>
 
     <panelview id="PanelUI-containers" flex="1">
       <label value="&containersMenu.label;" class="panel-subview-header"/>
       <vbox id="PanelUI-containersItems"/>
     </panelview>
--- a/browser/components/extensions/ext-commands.js
+++ b/browser/components/extensions/ext-commands.js
@@ -121,17 +121,17 @@ CommandList.prototype = {
     // and it is currently ignored when set to the empty string.
     keyElement.setAttribute("oncommand", "//");
 
     /* eslint-disable mozilla/balanced-listeners */
     // We remove all references to the key elements when the extension is shutdown,
     // therefore the listeners for these elements will be garbage collected.
     keyElement.addEventListener("command", (event) => {
       if (name == "_execute_page_action") {
-        let win = event.target.ownerGlobal;
+        let win = event.target.ownerDocument.defaultView;
         pageActionFor(this.extension).triggerAction(win);
       } else {
         this.emit("command", name);
       }
     });
     /* eslint-enable mozilla/balanced-listeners */
 
     return keyElement;
--- a/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
+++ b/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
@@ -1,98 +1,229 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
+Cu.import("resource://gre/modules/AppConstants.jsm");
+
 add_task(function* test_user_defined_commands() {
+  const testCommands = [
+    // Ctrl Shortcuts
+    {
+      name: "toggle-ctrl-a",
+      shortcut: "Ctrl+A",
+      key: "A",
+      modifiers: {
+        accelKey: true,
+      },
+    },
+    {
+      name: "toggle-ctrl-up",
+      shortcut: "Ctrl+Up",
+      key: "VK_UP",
+      modifiers: {
+        accelKey: true,
+      },
+    },
+    // Alt Shortcuts
+    {
+      name: "toggle-alt-a",
+      shortcut: "Alt+A",
+      key: "A",
+      modifiers: {
+        altKey: true,
+      },
+    },
+    {
+      name: "toggle-alt-down",
+      shortcut: "Alt+Down",
+      key: "VK_DOWN",
+      modifiers: {
+        altKey: true,
+      },
+    },
+    // Mac Shortcuts
+    {
+      name: "toggle-command-shift-page-up",
+      shortcutMac: "Command+Shift+PageUp",
+      key: "VK_PAGE_UP",
+      modifiers: {
+        accelKey: true,
+        shiftKey: true,
+      },
+    },
+    {
+      name: "toggle-mac-control-shift+period",
+      shortcut: "Ctrl+Shift+Period",
+      shortcutMac: "MacCtrl+Shift+Period",
+      key: "VK_PERIOD",
+      modifiers: {
+        ctrlKey: true,
+        shiftKey: true,
+      },
+    },
+    // Ctrl+Shift Shortcuts
+    {
+      name: "toggle-ctrl-shift-left",
+      shortcut: "Ctrl+Shift+Left",
+      key: "VK_LEFT",
+      modifiers: {
+        accelKey: true,
+        shiftKey: true,
+      },
+    },
+    // Alt+Shift Shortcuts
+    {
+      name: "toggle-alt-shift-1",
+      shortcut: "Alt+Shift+1",
+      key: "1",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+    },
+    {
+      name: "toggle-alt-shift-a",
+      shortcut: "Alt+Shift+A",
+      key: "A",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+    },
+    {
+      name: "toggle-alt-shift-right",
+      shortcut: "Alt+Shift+Right",
+      key: "VK_RIGHT",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+    },
+    // Misc Shortcuts
+    {
+      name: "valid-command-with-unrecognized-property-name",
+      shortcut: "Alt+Shift+3",
+      key: "3",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+      unrecognized_property: "with-a-random-value",
+    },
+    {
+      name: "spaces-in-shortcut-name",
+      shortcut: "  Alt + Shift + 2  ",
+      key: "2",
+      modifiers: {
+        altKey: true,
+        shiftKey: true,
+      },
+    },
+  ];
+
   // Create a window before the extension is loaded.
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
   yield BrowserTestUtils.loadURI(win1.gBrowser.selectedBrowser, "about:robots");
   yield BrowserTestUtils.browserLoaded(win1.gBrowser.selectedBrowser);
 
+  let commands = {};
+  let isMac = AppConstants.platform == "macosx";
+  let totalMacOnlyCommands = 0;
+
+  for (let testCommand of testCommands) {
+    let command = {
+      suggested_key: {},
+    };
+
+    if (testCommand.shortcut) {
+      command.suggested_key.default = testCommand.shortcut;
+    }
+
+    if (testCommand.shortcutMac) {
+      command.suggested_key.mac = testCommand.shortcutMac;
+    }
+
+    if (testCommand.shortcutMac && !testCommand.shortcut) {
+      totalMacOnlyCommands++;
+    }
+
+    if (testCommand.unrecognized_property) {
+      command.unrecognized_property = testCommand.unrecognized_property;
+    }
+
+    commands[testCommand.name] = command;
+  }
+
   let extension = ExtensionTestUtils.loadExtension({
     manifest: {
-      "commands": {
-        "toggle-feature-using-alt-shift-3": {
-          "suggested_key": {
-            "default": "Alt+Shift+3",
-          },
-        },
-        "toggle-feature-using-alt-shift-comma": {
-          "suggested_key": {
-            "default": "Alt+Shift+Comma",
-          },
-          "unrecognized_property": "with-a-random-value",
-        },
-        "toggle-feature-with-whitespace-in-suggested-key": {
-          "suggested_key": {
-            "default": "  Alt + Shift + 2  ",
-          },
-        },
-      },
+      "commands": commands,
     },
 
     background: function() {
       browser.commands.onCommand.addListener(commandName => {
         browser.test.sendMessage("oncommand", commandName);
       });
       browser.test.sendMessage("ready");
     },
   });
 
-
   SimpleTest.waitForExplicitFinish();
   let waitForConsole = new Promise(resolve => {
     SimpleTest.monitorConsole(resolve, [{
       message: /Reading manifest: Error processing commands.*.unrecognized_property: An unexpected property was found/,
     }]);
   });
 
   yield extension.startup();
   yield extension.awaitMessage("ready");
 
+  function* runTest() {
+    for (let testCommand of testCommands) {
+      if (testCommand.shortcutMac && !testCommand.shortcut && !isMac) {
+        continue;
+      }
+      EventUtils.synthesizeKey(testCommand.key, testCommand.modifiers);
+      let message = yield extension.awaitMessage("oncommand");
+      is(message, testCommand.name, `Expected onCommand listener to fire with the correct name: ${testCommand.name}`);
+    }
+  }
+
   // Create another window after the extension is loaded.
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
   yield BrowserTestUtils.loadURI(win2.gBrowser.selectedBrowser, "about:config");
   yield BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser);
 
+  let totalTestCommands = Object.keys(testCommands).length;
+  let expectedCommandsRegistered = isMac ? totalTestCommands : totalTestCommands - totalMacOnlyCommands;
+
   // Confirm the keysets have been added to both windows.
   let keysetID = `ext-keyset-id-${makeWidgetId(extension.id)}`;
   let keyset = win1.document.getElementById(keysetID);
   ok(keyset != null, "Expected keyset to exist");
-  is(keyset.childNodes.length, 3, "Expected keyset to have 3 children");
+  is(keyset.childNodes.length, expectedCommandsRegistered, "Expected keyset to have the correct number of children");
 
   keyset = win2.document.getElementById(keysetID);
   ok(keyset != null, "Expected keyset to exist");
-  is(keyset.childNodes.length, 3, "Expected keyset to have 3 children");
+  is(keyset.childNodes.length, expectedCommandsRegistered, "Expected keyset to have the correct number of children");
 
   // Confirm that the commands are registered to both windows.
   yield focusWindow(win1);
-  EventUtils.synthesizeKey("3", {altKey: true, shiftKey: true});
-  let message = yield extension.awaitMessage("oncommand");
-  is(message, "toggle-feature-using-alt-shift-3", "Expected onCommand listener to fire with correct message");
+  yield runTest();
 
   yield focusWindow(win2);
-  EventUtils.synthesizeKey("VK_COMMA", {altKey: true, shiftKey: true});
-  message = yield extension.awaitMessage("oncommand");
-  is(message, "toggle-feature-using-alt-shift-comma", "Expected onCommand listener to fire with correct message");
-
-  EventUtils.synthesizeKey("2", {altKey: true, shiftKey: true});
-  message = yield extension.awaitMessage("oncommand");
-  is(message, "toggle-feature-with-whitespace-in-suggested-key", "Expected onCommand listener to fire with correct message");
+  yield runTest();
 
   yield extension.unload();
 
   // Confirm that the keysets have been removed from both windows after the extension is unloaded.
   keyset = win1.document.getElementById(keysetID);
   is(keyset, null, "Expected keyset to be removed from the window");
 
   keyset = win2.document.getElementById(keysetID);
   is(keyset, null, "Expected keyset to be removed from the window");
 
   yield BrowserTestUtils.closeWindow(win1);
   yield BrowserTestUtils.closeWindow(win2);
 
   SimpleTest.endMonitorConsole();
   yield waitForConsole;
 });
-
-
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -148,64 +148,16 @@ this.UITour = {
     ["devtools",    {query: "#developer-button"}],
     ["help",        {query: "#PanelUI-help"}],
     ["home",        {query: "#home-button"}],
     ["forget", {
       allowAdd: true,
       query: "#panic-button",
       widgetName: "panic-button",
     }],
-    ["loop",        {
-      allowAdd: true,
-      query: "#loop-button",
-      widgetName: "loop-button",
-    }],
-    ["loop-newRoom", {
-      infoPanelPosition: "leftcenter topright",
-      query: (aDocument) => {
-        let loopUI = aDocument.defaultView.LoopUI;
-        // Use the parentElement full-width container of the button so our arrow
-        // doesn't overlap the panel contents much.
-        return loopUI.browser.contentDocument.querySelector(".new-room-button").parentElement;
-      },
-    }],
-    ["loop-roomList", {
-      infoPanelPosition: "leftcenter topright",
-      query: (aDocument) => {
-        let loopUI = aDocument.defaultView.LoopUI;
-        return loopUI.browser.contentDocument.querySelector(".room-list");
-      },
-    }],
-    ["loop-selectedRoomButtons", {
-      infoPanelOffsetY: -20,
-      infoPanelPosition: "start_after",
-      query: (aDocument) => {
-        let chatbox = aDocument.querySelector("chatbox[src^='about\:loopconversation'][selected]");
-
-        // Check that the real target actually exists
-        if (!chatbox || !chatbox.contentDocument ||
-            !chatbox.contentDocument.querySelector(".call-action-group")) {
-          return null;
-        }
-
-        // But anchor on the <browser> in the chatbox so the panel doesn't jump to undefined
-        // positions when the copy/email buttons disappear e.g. when the feedback form opens or
-        // somebody else joins the room.
-        return chatbox.content;
-      },
-    }],
-    ["loop-signInUpLink", {
-      query: (aDocument) => {
-        let loopBrowser = aDocument.defaultView.LoopUI.browser;
-        if (!loopBrowser) {
-          return null;
-        }
-        return loopBrowser.contentDocument.querySelector(".signin-link");
-      },
-    }],
     ["pocket", {
       allowAdd: true,
       query: "#pocket-button",
       widgetName: "pocket-button",
     }],
     ["privateWindow",  {query: "#privatebrowsing-button"}],
     ["quit",        {query: "#PanelUI-quit"}],
     ["readerMode-urlBar", {query: "#reader-mode-button"}],
@@ -890,26 +842,22 @@ this.UITour = {
     if (aTourPageClosing && openTourBrowsers) {
       openTourBrowsers.delete(aBrowser);
     }
 
     this.hideHighlight(aWindow);
     this.hideInfo(aWindow);
     // Ensure the menu panel is hidden before calling recreatePopup so popup events occur.
     this.hideMenu(aWindow, "appMenu");
-    this.hideMenu(aWindow, "loop");
     this.hideMenu(aWindow, "controlCenter");
 
     // Clean up panel listeners after calling hideMenu above.
     aWindow.PanelUI.panel.removeEventListener("popuphiding", this.hideAppMenuAnnotations);
     aWindow.PanelUI.panel.removeEventListener("ViewShowing", this.hideAppMenuAnnotations);
     aWindow.PanelUI.panel.removeEventListener("popuphidden", this.onPanelHidden);
-    let loopPanel = aWindow.document.getElementById("loop-notification-panel");
-    loopPanel.removeEventListener("popuphidden", this.onPanelHidden);
-    loopPanel.removeEventListener("popuphiding", this.hideLoopPanelAnnotations);
     let controlCenterPanel = aWindow.gIdentityHandler._identityPopup;
     controlCenterPanel.removeEventListener("popuphidden", this.onPanelHidden);
     controlCenterPanel.removeEventListener("popuphiding", this.hideControlCenterAnnotations);
 
     this.resetTheme();
 
     // If there are no more tour tabs left in the window, teardown the tour for the whole window.
     if (!openTourBrowsers || openTourBrowsers.size == 0) {
@@ -1730,41 +1678,16 @@ this.UITour = {
 
       this.recreatePopup(popup);
 
       // Open the control center
       if (aOpenCallback) {
         popup.addEventListener("popupshown", onPopupShown);
       }
       aWindow.document.getElementById("identity-box").click();
-    } else if (aMenuName == "loop") {
-      let toolbarButton = aWindow.LoopUI.toolbarButton;
-      // It's possible to have a node that isn't placed anywhere
-      if (!toolbarButton || !toolbarButton.node ||
-          !CustomizableUI.getPlacementOfWidget(toolbarButton.node.id)) {
-        log.debug("Can't show the Loop menu since the toolbarButton isn't placed");
-        return;
-      }
-
-      let panel = aWindow.document.getElementById("loop-notification-panel");
-      panel.setAttribute("noautohide", true);
-      if (panel.state != "open") {
-        this.recreatePopup(panel);
-        this.clearAvailableTargetsCache();
-      }
-
-      // An event object is expected but we don't want to toggle the panel with a click if the panel
-      // is already open.
-      aWindow.LoopUI.openPanel({ target: toolbarButton.node, }, "rooms").then(() => {
-        if (aOpenCallback) {
-          aOpenCallback();
-        }
-      });
-      panel.addEventListener("popuphidden", this.onPanelHidden);
-      panel.addEventListener("popuphiding", this.hideLoopPanelAnnotations);
     } else if (aMenuName == "pocket") {
       this.getTarget(aWindow, "pocket").then(Task.async(function* onPocketTarget(target) {
         let widgetGroupWrapper = CustomizableUI.getWidget(target.widgetName);
         if (widgetGroupWrapper.type != "view" || !widgetGroupWrapper.viewId) {
           log.error("Can't open the pocket menu without a view");
           return;
         }
         let placement = CustomizableUI.getPlacementOfWidget(target.widgetName);
@@ -1813,19 +1736,16 @@ this.UITour = {
     if (aMenuName == "appMenu") {
       aWindow.PanelUI.hide();
     } else if (aMenuName == "bookmarks") {
       let menuBtn = aWindow.document.getElementById("bookmarks-menu-button");
       closeMenuButton(menuBtn);
     } else if (aMenuName == "controlCenter") {
       let panel = aWindow.gIdentityHandler._identityPopup;
       panel.hidePopup();
-    } else if (aMenuName == "loop") {
-      let panel = aWindow.document.getElementById("loop-notification-panel");
-      panel.hidePopup();
     }
   },
 
   hideAnnotationsForPanel: function(aEvent, aTargetPositionCallback) {
     let win = aEvent.target.ownerGlobal;
     let annotationElements = new Map([
       // [annotationElement (panel), method to hide the annotation]
       [win.document.getElementById("UITourHighlightContainer"), UITour.hideHighlight.bind(UITour)],
@@ -1848,22 +1768,16 @@ this.UITour = {
     });
     UITour.appMenuOpenForAnnotation.clear();
   },
 
   hideAppMenuAnnotations: function(aEvent) {
     UITour.hideAnnotationsForPanel(aEvent, UITour.targetIsInAppMenu);
   },
 
-  hideLoopPanelAnnotations: function(aEvent) {
-    UITour.hideAnnotationsForPanel(aEvent, (aTarget) => {
-      return aTarget.targetName.startsWith("loop-") && aTarget.targetName != "loop-selectedRoomButtons";
-    });
-  },
-
   hideControlCenterAnnotations(aEvent) {
     UITour.hideAnnotationsForPanel(aEvent, (aTarget) => {
       return aTarget.targetName.startsWith("controlCenter-");
     });
   },
 
   onPanelHidden: function(aEvent) {
     aEvent.target.removeAttribute("noautohide");
@@ -1926,22 +1840,16 @@ this.UITour = {
         appinfo["canSetDefaultBrowserInBackground"] =
           canSetDefaultBrowserInBackground;
 
         this.sendPageCallback(aMessageManager, aCallbackID, appinfo);
         break;
       case "availableTargets":
         this.getAvailableTargets(aMessageManager, aWindow, aCallbackID);
         break;
-      case "loop":
-        const FTU_VERSION = 1;
-        this.sendPageCallback(aMessageManager, aCallbackID, {
-          gettingStartedSeen: (Services.prefs.getIntPref("loop.gettingStarted.latestFTUVersion") >= FTU_VERSION),
-        });
-        break;
       case "search":
       case "selectedSearchEngine":
         Services.search.init(rv => {
           let data;
           if (Components.isSuccessCode(rv)) {
             let engines = Services.search.getVisibleEngines();
             data = {
               searchEngineIdentifier: Services.search.defaultEngine.identifier,
@@ -1975,20 +1883,16 @@ this.UITour = {
         // be set, not unset.
         try {
           let shell = aWindow.getShellService();
           if (shell) {
             shell.setDefaultBrowser(true, false);
           }
         } catch (e) {}
         break;
-      case "Loop:ResumeTourOnFirstJoin":
-        // Ignore aValue in this case to avoid accidentally setting it to false.
-        Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
-        break;
       default:
         log.error("setConfiguration: Unknown configuration requested: " + aConfiguration);
         break;
     }
   },
 
   getAvailableTargets: function(aMessageManager, aChromeWindow, aCallbackID) {
     Task.spawn(function*() {
--- a/browser/components/uitour/test/browser.ini
+++ b/browser/components/uitour/test/browser.ini
@@ -26,19 +26,16 @@ skip-if = os == "linux" # Intermittent f
 [browser_UITour3.js]
 skip-if = os == "linux" # Linux: Bug 986760, Bug 989101.
 [browser_UITour_availableTargets.js]
 [browser_UITour_annotation_size_attributes.js]
 [browser_UITour_defaultBrowser.js]
 [browser_UITour_detach_tab.js]
 [browser_UITour_forceReaderMode.js]
 [browser_UITour_heartbeat.js]
-[browser_UITour_loop.js]
-skip-if = true # Bug 1225832 - New Loop architecture is not compatible with test.
-[browser_UITour_loop_panel.js]
 [browser_UITour_modalDialog.js]
 skip-if = os != "mac" # modal dialog disabling only working on OS X.
 [browser_UITour_observe.js]
 [browser_UITour_panel_close_annotation.js]
 skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
 [browser_UITour_pocket.js]
 skip-if = true # Disabled pending removal of pocket UI Tour
 [browser_UITour_registerPageID.js]
--- a/browser/components/uitour/test/browser_UITour_availableTargets.js
+++ b/browser/components/uitour/test/browser_UITour_availableTargets.js
@@ -16,17 +16,16 @@ add_UITour_task(function* test_available
     "accountStatus",
     "addons",
     "appMenu",
     "backForward",
     "bookmarks",
     "customize",
     "help",
     "home",
-    "loop",
     "devtools",
       ...(hasPocket ? ["pocket"] : []),
     "privateWindow",
     "quit",
     "readerMode-urlBar",
     "search",
     "searchIcon",
     "trackingProtection",
@@ -45,17 +44,16 @@ add_UITour_task(function* test_available
   let data = yield getConfigurationPromise("availableTargets");
   ok_targets(data, [
     "accountStatus",
     "addons",
     "appMenu",
     "backForward",
     "customize",
     "help",
-    "loop",
     "devtools",
     "home",
       ...(hasPocket ? ["pocket"] : []),
     "privateWindow",
     "quit",
     "readerMode-urlBar",
     "search",
     "searchIcon",
@@ -81,17 +79,16 @@ add_UITour_task(function* test_available
     "accountStatus",
     "addons",
     "appMenu",
     "backForward",
     "bookmarks",
     "customize",
     "help",
     "home",
-    "loop",
     "devtools",
       ...(hasPocket ? ["pocket"] : []),
     "privateWindow",
     "quit",
     "readerMode-urlBar",
     "trackingProtection",
     "urlbar",
       ...(hasWebIDE ? ["webide"] : [])
deleted file mode 100644
--- a/browser/components/uitour/test/browser_UITour_loop.js
+++ /dev/null
@@ -1,414 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/ */
-
-"use strict";
-
-var gTestTab;
-var gContentAPI;
-var gContentWindow;
-var gMessageHandlers;
-var loopButton;
-var fakeRoom;
-var loopPanel = document.getElementById("loop-notification-panel");
-
-const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
-const { LoopRooms } = Cu.import("chrome://loop/content/modules/LoopRooms.jsm", {});
-const { MozLoopServiceInternal } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
-
-const FTU_VERSION = 1;
-
-function test() {
-  UITourTest();
-}
-
-function runOffline(fun) {
-  return (done) => {
-    Services.io.offline = true;
-    fun(function onComplete() {
-      Services.io.offline = false;
-      done();
-    });
-  }
-}
-
-var tests = [
-  taskify(function* test_gettingStartedClicked_linkOpenedWithExpectedParams() {
-    // Set latestFTUVersion to lower number to show FTU panel.
-    Services.prefs.setIntPref("loop.gettingStarted.latestFTUVersion", 0);
-    Services.prefs.setCharPref("loop.gettingStarted.url", "http://example.com");
-    is(loopButton.open, false, "Menu should initially be closed");
-    loopButton.click();
-
-    yield waitForConditionPromise(() => {
-      return loopButton.open;
-    }, "Menu should be visible after showMenu()");
-
-    gContentAPI.registerPageID("hello-tour_OpenPanel_testPage");
-    yield new Promise(resolve => {
-      gContentAPI.ping(() => resolve());
-    });
-
-    let loopDoc = document.getElementById("loop-notification-panel").children[0].contentDocument;
-    yield waitForConditionPromise(() => {
-      return loopDoc.readyState == 'complete';
-    }, "Loop notification panel document should be fully loaded.", 50);
-    let gettingStartedButton = loopDoc.getElementById("fte-button");
-    ok(gettingStartedButton, "Getting Started button should be found");
-
-    let newTabPromise = waitForConditionPromise(() => {
-      return gBrowser.currentURI.path.includes("utm_source=firefox-browser");
-    }, "New tab with utm_content=testPageNewID should have opened");
-
-    gettingStartedButton.click();
-    yield newTabPromise;
-    ok(gBrowser.currentURI.path.includes("utm_content=hello-tour_OpenPanel_testPage"),
-        "Expected URL opened (" + gBrowser.currentURI.path + ")");
-    yield gBrowser.removeCurrentTab();
-
-    checkLoopPanelIsHidden();
-  }),
-  taskify(function* test_gettingStartedClicked_linkOpenedWithExpectedParams2() {
-    // Set latestFTUVersion to lower number to show FTU panel.
-    Services.prefs.setIntPref("loop.gettingStarted.latestFTUVersion", 0);
-    // Force a refresh of the loop panel since going from seen -> unseen doesn't trigger
-    // automatic re-rendering.
-    let loopWin = document.getElementById("loop-notification-panel").children[0].contentWindow;
-    var event = new loopWin.CustomEvent("GettingStartedSeen", { detail: false });
-    loopWin.dispatchEvent(event);
-
-    UITour.pageIDsForSession.clear();
-    Services.prefs.setCharPref("loop.gettingStarted.url", "http://example.com");
-    is(loopButton.open, false, "Menu should initially be closed");
-    loopButton.click();
-
-    yield waitForConditionPromise(() => {
-      return loopButton.open;
-    }, "Menu should be visible after showMenu()");
-
-
-    gContentAPI.registerPageID("hello-tour_OpenPanel_testPageOldId");
-    yield new Promise(resolve => {
-      gContentAPI.ping(() => resolve());
-    });
-    // Set the time of the page ID to 10 hours earlier, so that it is considered "expired".
-    UITour.pageIDsForSession.set("hello-tour_OpenPanel_testPageOldId",
-                                   {lastSeen: Date.now() - (10 * 60 * 60 * 1000)});
-
-    let loopDoc = loopWin.document;
-    let gettingStartedButton = loopDoc.getElementById("fte-button");
-    ok(gettingStartedButton, "Getting Started button should be found");
-
-    let newTabPromise = waitForConditionPromise(() => {
-      Services.console.logStringMessage(gBrowser.currentURI.path);
-      return gBrowser.currentURI.path.includes("utm_source=firefox-browser");
-    }, "New tab with utm_content=testPageNewID should have opened");
-
-    gettingStartedButton.click();
-    yield newTabPromise;
-    ok(!gBrowser.currentURI.path.includes("utm_content=hello-tour_OpenPanel_testPageOldId"),
-       "Expected URL opened without the utm_content parameter (" +
-        gBrowser.currentURI.path + ")");
-    yield gBrowser.removeCurrentTab();
-
-    checkLoopPanelIsHidden();
-  }),
-  // Test the menu was cleaned up in teardown.
-  taskify(function* setup_menu_cleanup() {
-    gContentAPI.showMenu("loop");
-
-    yield waitForConditionPromise(() => {
-      return loopButton.open;
-    }, "Menu should be visible after showMenu()");
-
-    // Leave it open so it gets torn down and we can test below that teardown was succesful.
-  }),
-  taskify(function* test_menu_cleanup() {
-    // Test that the open menu from above was torn down fully.
-    checkLoopPanelIsHidden();
-  }),
-  function test_availableTargets(done) {
-    gContentAPI.showMenu("loop");
-    gContentAPI.getConfiguration("availableTargets", (data) => {
-      for (let targetName of ["loop-newRoom", "loop-roomList", "loop-signInUpLink"]) {
-        isnot(data.targets.indexOf(targetName), -1, targetName + " should exist");
-      }
-      done();
-    });
-  },
-  function test_getConfigurationLoop(done) {
-    let gettingStartedSeen = Services.prefs.getIntPref("loop.gettingStarted.latestFTUVersion") >= FTU_VERSION;
-    gContentAPI.getConfiguration("loop", (data) => {
-      is(data.gettingStartedSeen, gettingStartedSeen,
-         "The configuration property should equal that of the pref");
-      done();
-    });
-  },
-  function test_hideMenuHidesAnnotations(done) {
-    let infoPanel = document.getElementById("UITourTooltip");
-    let highlightPanel = document.getElementById("UITourHighlightContainer");
-
-    gContentAPI.showMenu("loop", function menuCallback() {
-      gContentAPI.showHighlight("loop-roomList");
-      gContentAPI.showInfo("loop-newRoom", "Make a new room", "AKA. conversation");
-      UITour.getTarget(window, "loop-newRoom").then((target) => {
-        waitForPopupAtAnchor(infoPanel, target.node, Task.async(function* checkPanelIsOpen() {
-          isnot(loopPanel.state, "closed", "Loop panel should still be open");
-          ok(loopPanel.hasAttribute("noautohide"), "@noautohide should still be on the loop panel");
-          is(highlightPanel.getAttribute("targetName"), "loop-roomList", "Check highlight @targetname");
-          is(infoPanel.getAttribute("targetName"), "loop-newRoom", "Check info panel @targetname");
-
-          info("Close the loop menu and make sure the annotations inside disappear");
-          let hiddenPromises = [promisePanelElementHidden(window, infoPanel),
-                                promisePanelElementHidden(window, highlightPanel)];
-          gContentAPI.hideMenu("loop");
-          yield Promise.all(hiddenPromises);
-          isnot(infoPanel.state, "open", "Info panel should have automatically hid");
-          isnot(highlightPanel.state, "open", "Highlight panel should have automatically hid");
-          done();
-        }), "Info panel should be anchored to the new room button");
-      });
-    });
-  },
-  runOffline(function test_notifyLoopChatWindowOpenedClosed(done) {
-    gContentAPI.observe((event, params) => {
-      is(event, "Loop:ChatWindowOpened", "Check Loop:ChatWindowOpened notification");
-      gContentAPI.observe((event, params) => {
-        is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
-        gContentAPI.observe((event, params) => {
-          is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
-          gContentAPI.observe((event, params) => {
-            ok(false, "No more notifications should have arrived");
-          });
-        });
-        done();
-      });
-      document.querySelector("#pinnedchats > chatbox").close();
-    });
-    LoopRooms.open("fakeTourRoom");
-  }),
-  runOffline(function test_notifyLoopRoomURLCopied(done) {
-    gContentAPI.observe((event, params) => {
-      is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
-      gContentAPI.observe((event, params) => {
-        is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
-
-        let chat = document.querySelector("#pinnedchats > chatbox");
-        gContentAPI.observe((event, params) => {
-          is(event, "Loop:RoomURLCopied", "Check Loop:RoomURLCopied notification");
-          gContentAPI.observe((event, params) => {
-            is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
-          });
-          chat.close();
-          done();
-        });
-
-        let window = chat.content.contentWindow;
-        waitForConditionPromise(
-          () => chat.content.contentDocument.querySelector(".btn-copy"),
-          "Copy button should be there"
-        ).then(() => chat.content.contentDocument.querySelector(".btn-copy").click());
-      });
-    });
-    LoopRooms.open("fakeTourRoom");
-  }),
-  runOffline(function test_notifyLoopRoomURLEmailed(done) {
-    gContentAPI.observe((event, params) => {
-      is(event, "Loop:ChatWindowOpened", "Loop chat window should've opened");
-      gContentAPI.observe((event, params) => {
-        is(event, "Loop:ChatWindowShown", "Check Loop:ChatWindowShown notification");
-
-        let chat = document.querySelector("#pinnedchats > chatbox");
-        let composeEmailCalled = false;
-
-        gContentAPI.observe((event, params) => {
-          is(event, "Loop:RoomURLEmailed", "Check Loop:RoomURLEmailed notification");
-          ok(composeEmailCalled, "mozLoop.composeEmail should be called");
-          gContentAPI.observe((event, params) => {
-            is(event, "Loop:ChatWindowClosed", "Check Loop:ChatWindowClosed notification");
-          });
-          chat.close();
-          done();
-        });
-
-        gMessageHandlers.ComposeEmail = function(message, reply) {
-          let [subject, body, recipient] = message.data;
-          ok(subject, "composeEmail should be invoked with at least a subject value");
-          composeEmailCalled = true;
-          reply();
-        };
-
-        waitForConditionPromise(
-          () => chat.content.contentDocument.querySelector(".btn-email"),
-          "Email button should be there"
-        ).then(() => chat.content.contentDocument.querySelector(".btn-email").click());
-      });
-    });
-    LoopRooms.open("fakeTourRoom");
-  }),
-  taskify(function* test_arrow_panel_position() {
-    is(loopButton.open, false, "Menu should initially be closed");
-    let popup = document.getElementById("UITourTooltip");
-
-    yield showMenuPromise("loop");
-
-    let currentTarget = "loop-newRoom";
-    yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
-    is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
-
-    currentTarget = "loop-roomList";
-    yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be on the side");
-    is(popup.popupBoxObject.alignmentPosition, "start_before", "Check " + currentTarget + " position");
-
-    currentTarget = "loop-signInUpLink";
-    yield showInfoPromise(currentTarget, "This is " + currentTarget, "My arrow should be underneath");
-    is(popup.popupBoxObject.alignmentPosition, "after_end", "Check " + currentTarget + " position");
-  }),
-  taskify(function* test_setConfiguration() {
-    is(Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin"), false, "pref should be false but exist");
-    gContentAPI.setConfiguration("Loop:ResumeTourOnFirstJoin", true);
-
-    yield waitForConditionPromise(() => {
-      return Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin");
-    }, "Pref should change to true via setConfiguration");
-
-    Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
-  }),
-  taskify(function* test_resumeViaMenuPanel_roomClosedTabOpen() {
-    Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
-
-    // Create a fake room and then add a fake non-owner participant
-    let roomsMap = setupFakeRoom();
-    roomsMap.get("fakeTourRoom").participants = [{
-      owner: false,
-    }];
-
-    // Set the tour URL to be the current page with a different query param
-    let gettingStartedURL = gTestTab.linkedBrowser.currentURI.resolve("?gettingstarted=1");
-    Services.prefs.setCharPref("loop.gettingStarted.url", gettingStartedURL);
-
-    let observationPromise = new Promise((resolve) => {
-      gContentAPI.observe((event, params) => {
-        is(event, "Loop:IncomingConversation", "Page should have been notified about incoming conversation");
-        is(params.conversationOpen, false, "conversationOpen should be false");
-        is(gBrowser.selectedTab, gTestTab, "The same tab should be selected");
-        resolve();
-      });
-    });
-
-    // Now open the menu while that non-owner is in the fake room to trigger resuming the tour
-    yield showMenuPromise("loop");
-
-    yield observationPromise;
-    Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
-  }),
-  taskify(function* test_resumeViaMenuPanel_roomClosedTabClosed() {
-    Services.prefs.setBoolPref("loop.gettingStarted.resumeOnFirstJoin", true);
-
-    // Create a fake room and then add a fake non-owner participant
-    let roomsMap = setupFakeRoom();
-    roomsMap.get("fakeTourRoom").participants = [{
-      owner: false,
-    }];
-
-    // Set the tour URL to a page that's not open yet
-    Services.prefs.setCharPref("loop.gettingStarted.url", gBrowser.currentURI.prePath);
-
-    let newTabPromise = waitForConditionPromise(() => {
-      return gBrowser.currentURI.path.includes("incomingConversation=waiting");
-    }, "New tab with incomingConversation=waiting should have opened");
-
-    // Now open the menu while that non-owner is in the fake room to trigger resuming the tour
-    yield showMenuPromise("loop");
-
-    yield newTabPromise;
-
-    yield gBrowser.removeCurrentTab();
-    Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
-  }),
-];
-
-// End tests
-
-function checkLoopPanelIsHidden() {
-  ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
-  ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
-  isnot(loopPanel.state, "open", "The panel shouldn't be open");
-  is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
-}
-
-function setupFakeRoom() {
-  let room = Object.create(fakeRoom);
-  let roomsMap = new Map([
-    [room.roomToken, room]
-  ]);
-  LoopRooms.stubCache(roomsMap);
-  return roomsMap;
-}
-
-if (Services.prefs.getBoolPref("loop.enabled")) {
-  loopButton = window.LoopUI.toolbarButton.node;
-
-  fakeRoom = {
-    decryptedContext: { roomName: "fakeTourRoom" },
-    participants: [],
-    maxSize: 2,
-    ctime: Date.now()
-  };
-  for (let prop of ["roomToken", "roomOwner", "roomUrl"])
-    fakeRoom[prop] = "fakeTourRoom";
-
-  LoopAPI.stubMessageHandlers(gMessageHandlers = {
-    // Stub the rooms object API to fully control the test behavior.
-    "Rooms:*": function(action, message, reply) {
-      switch (action.split(":").pop()) {
-        case "GetAll":
-          reply([fakeRoom]);
-          break;
-        case "Get":
-          reply(fakeRoom);
-          break;
-        case "Join":
-          reply({
-            apiKey: "fakeTourRoom",
-            sessionToken: "fakeTourRoom",
-            sessionId: "fakeTourRoom",
-            expires: Date.now() + 240000
-          });
-          break;
-        case "RefreshMembership":
-          reply({ expires: Date.now() + 240000 });
-        default:
-          reply();
-      }
-    },
-    // Stub the metadata retrieval to suppress console warnings and return faster.
-    GetSelectedTabMetadata: function(message, reply) {
-      reply({ favicon: null });
-    }
-  });
-
-  registerCleanupFunction(() => {
-    Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
-    Services.prefs.clearUserPref("loop.gettingStarted.latestFTUVersion");
-    Services.prefs.clearUserPref("loop.gettingStarted.url");
-    Services.io.offline = false;
-
-    // Copied from browser/components/loop/test/mochitest/head.js
-    // Remove the iframe after each test. This also avoids mochitest complaining
-    // about leaks on shutdown as we intentionally hold the iframe open for the
-    // life of the application.
-    let frameId = loopButton.getAttribute("notificationFrameId");
-    let frame = document.getElementById(frameId);
-    if (frame) {
-      frame.remove();
-    }
-
-    // Remove the stubbed rooms.
-    LoopRooms.stubCache(null);
-    // Restore the stubbed handlers.
-    LoopAPI.restore();
-  });
-} else {
-  ok(true, "Loop is disabled so skip the UITour Loop tests");
-  tests = [];
-}
deleted file mode 100644
--- a/browser/components/uitour/test/browser_UITour_loop_panel.js
+++ /dev/null
@@ -1,67 +0,0 @@
-"use strict";
-
-var gTestTab;
-var gContentAPI;
-var gContentWindow;
-var gMessageHandlers;
-var loopButton;
-var fakeRoom;
-var loopPanel = document.getElementById("loop-notification-panel");
-
-const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
-const { LoopRooms } = Cu.import("chrome://loop/content/modules/LoopRooms.jsm", {});
-
-if (!Services.prefs.getBoolPref("loop.enabled")) {
-  ok(true, "Loop is disabled so skip the UITour Loop tests");
-} else {
-  function checkLoopPanelIsHidden() {
-    ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
-    ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
-    isnot(loopPanel.state, "open", "The panel shouldn't be open");
-    is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
-  }
-
-  add_task(setup_UITourTest);
-
-  add_task(function() {
-    loopButton = window.LoopUI.toolbarButton.node;
-
-    registerCleanupFunction(() => {
-      Services.prefs.clearUserPref("loop.gettingStarted.latestFTUVersion");
-      Services.io.offline = false;
-
-      // Copied from browser/components/loop/test/mochitest/head.js
-      // Remove the iframe after each test. This also avoids mochitest complaining
-      // about leaks on shutdown as we intentionally hold the iframe open for the
-      // life of the application.
-      let frameId = loopButton.getAttribute("notificationFrameId");
-      let frame = document.getElementById(frameId);
-      if (frame) {
-        frame.remove();
-      }
-    });
-  });
-
-  add_UITour_task(function* test_menu_show_hide() {
-    // The targets to highlight only appear after getting started is launched.
-    // Set latestFTUVersion to lower number to show FTU panel.
-    Services.prefs.setIntPref("loop.gettingStarted.latestFTUVersion", 0);
-    is(loopButton.open, false, "Menu should initially be closed");
-    gContentAPI.showMenu("loop");
-
-    yield waitForConditionPromise(() => {
-      return loopPanel.state == "open";
-    }, "Menu should be visible after showMenu()");
-
-    ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
-    ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
-    ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
-
-    gContentAPI.hideMenu("loop");
-    yield waitForConditionPromise(() => {
-        return !loopButton.open;
-    }, "Menu should be hidden after hideMenu()");
-
-    checkLoopPanelIsHidden();
-  });
-}
deleted file mode 100644
--- a/browser/extensions/loop/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-.module-cache
-test/coverage/desktop
-test/coverage/shared_standalone
-test/node_modules
-test/visual-regression/diff
-test/visual-regression/new
-test/visual-regression/refs
deleted file mode 100644
--- a/browser/extensions/loop/README.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-This is the loop project output, https://github.com/mozilla/loop
-
-Current extension version is: 0.1
deleted file mode 100644
--- a/browser/extensions/loop/bootstrap.js
+++ /dev/null
@@ -1,1495 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";
-
-/* exported startup, shutdown, install, uninstall */var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {return typeof obj;} : function (obj) {return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;};function _toConsumableArray(arr) {if (Array.isArray(arr)) {for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {arr2[i] = arr[i];}return arr2;} else {return Array.from(arr);}}var _Components = 
-
-Components;var Ci = _Components.interfaces;var Cu = _Components.utils;var Cc = _Components.classes;
-
-var kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-var kBrowserSharingNotificationId = "loop-sharing-notification";
-
-var CURSOR_MIN_DELTA = 3;
-var CURSOR_MIN_INTERVAL = 100;
-var CURSOR_CLICK_DELAY = 1000;
-// Due to bug 1051238 frame scripts are cached forever, so we can't update them
-// as a restartless add-on. The Math.random() is the work around for this.
-var FRAME_SCRIPT = "chrome://loop/content/modules/tabFrame.js?" + Math.random();
-
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/AppConstants.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", 
-"resource://gre/modules/PrivateBrowsingUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", 
-"resource:///modules/CustomizableUI.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Task", 
-"resource://gre/modules/Task.jsm");
-
-// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
-var PREF_LOG_LEVEL = "loop.debug.loglevel";
-
-XPCOMUtils.defineLazyGetter(this, "log", function () {
-  var ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
-  var consoleOptions = { 
-    maxLogLevelPref: PREF_LOG_LEVEL, 
-    prefix: "Loop" };
-
-  return new ConsoleAPI(consoleOptions);});
-
-
-/**
- * This window listener gets loaded into each browser.xul window and is used
- * to provide the required loop functions for the window.
- */
-var WindowListener = { 
-  // Records the add-on version once we know it.
-  addonVersion: "unknown", 
-
-  /**
-   * Sets up the chrome integration within browser windows for Loop.
-   *
-   * @param {Object} window The window to inject the integration into.
-   */
-  setupBrowserUI: function setupBrowserUI(window) {
-    var document = window.document;var 
-    gBrowser = window.gBrowser;var gURLBar = window.gURLBar;
-    var xhrClass = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"];
-    var FileReader = window.FileReader;
-    var menuItem = null;
-    var isSlideshowOpen = false;
-    var titleChangedListener = null;
-
-    // the "exported" symbols
-    var LoopUI = { 
-      /**
-       * @var {XULWidgetSingleWrapper} toolbarButton Getter for the Loop toolbarbutton
-       *                                             instance for this window. This should
-       *                                             not be used in the hidden window.
-       */
-      get toolbarButton() {
-        delete this.toolbarButton;
-        return this.toolbarButton = CustomizableUI.getWidget("loop-button").forWindow(window);}, 
-
-
-      /**
-       * @var {XULElement} panel Getter for the Loop panel element.
-       */
-      get panel() {
-        delete this.panel;
-        return this.panel = document.getElementById("loop-notification-panel");}, 
-
-
-      /**
-       * @var {XULElement|null} browser Getter for the Loop panel browser element.
-       *                                Will be NULL if the panel hasn't loaded yet.
-       */
-      get browser() {
-        var browser = document.querySelector("#loop-notification-panel > #loop-panel-iframe");
-        if (browser) {
-          delete this.browser;
-          this.browser = browser;}
-
-        return browser;}, 
-
-
-      get isSlideshowOpen() {
-        return isSlideshowOpen;}, 
-
-
-      set isSlideshowOpen(aOpen) {
-        isSlideshowOpen = aOpen;
-        this.updateToolbarState();}, 
-
-      /**
-       * @return {Object} Getter for the Loop constants
-       */
-      get constants() {var _this = this;
-        if (!this._constants) {
-          // GetAllConstants is synchronous even though it's using a callback.
-          this.LoopAPI.sendMessageToHandler({ 
-            name: "GetAllConstants" }, 
-          function (result) {
-            _this._constants = result;});}
-
-
-
-        return this._constants;}, 
-
-
-      get mm() {
-        return window.getGroupMessageManager("browsers");}, 
-
-
-      /**
-       * @return {Promise}
-       */
-      promiseDocumentVisible: function promiseDocumentVisible(aDocument) {
-        if (!aDocument.hidden) {
-          return Promise.resolve(aDocument);}
-
-
-        return new Promise(function (resolve) {
-          aDocument.addEventListener("visibilitychange", function onVisibilityChanged() {
-            aDocument.removeEventListener("visibilitychange", onVisibilityChanged);
-            resolve(aDocument);});});}, 
-
-
-
-
-      /**
-       * Toggle between opening or hiding the Loop panel.
-       *
-       * @param {DOMEvent} [event] Optional event that triggered the call to this
-       *                           function.
-       * @return {Promise}
-       */
-      togglePanel: function togglePanel(event) {var _this2 = this;
-        if (!this.panel) {var _ret = function () {
-            // We're on the hidden window! What fun!
-            var obs = function obs(win) {
-              Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
-              win.LoopUI.togglePanel(event);};
-
-            Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
-            return { v: window.OpenBrowserWindow() };}();if ((typeof _ret === "undefined" ? "undefined" : _typeof(_ret)) === "object") return _ret.v;}
-
-        if (this.panel.state == "open") {
-          return new Promise(function (resolve) {
-            _this2.panel.hidePopup();
-            resolve();});}
-
-
-
-        if (this.isSlideshowOpen) {
-          return Promise.resolve();}
-
-
-        return this.openPanel(event).then(function (mm) {
-          if (mm) {
-            mm.sendAsyncMessage("Social:EnsureFocusElement");}}).
-
-        catch(function (err) {
-          Cu.reportError(err);});}, 
-
-
-
-      /**
-       * Called when a closing room has just been created, so we offer the
-       * user the chance to modify the name. For that we need to open the panel.
-       * Showing the proper layout is done on panel.jsx
-       */
-      renameRoom: function renameRoom() {
-        this.openPanel();}, 
-
-
-      /**
-       * Opens the panel for Loop and sizes it appropriately.
-       *
-       * @param {event}  event   The event opening the panel, used to anchor
-       *                         the panel to the button which triggers it.
-       * @return {Promise}
-       */
-      openPanel: function openPanel(event) {var _this3 = this;
-        if (PrivateBrowsingUtils.isWindowPrivate(window)) {
-          return Promise.reject();}
-
-
-        return new Promise(function (resolve) {
-          var callback = function callback(iframe) {
-            var mm = iframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
-            if (!("messageManager" in iframe)) {
-              iframe.messageManager = mm;}
-
-
-            if (!_this3._panelInitialized) {
-              _this3.hookWindowCloseForPanelClose(iframe);
-              _this3._panelInitialized = true;}
-
-
-            mm.sendAsyncMessage("Social:WaitForDocumentVisible");
-            mm.addMessageListener("Social:DocumentVisible", function onDocumentVisible() {
-              mm.removeMessageListener("Social:DocumentVisible", onDocumentVisible);
-              resolve(mm);});
-
-
-            var buckets = _this3.constants.LOOP_MAU_TYPE;
-            _this3.LoopAPI.sendMessageToHandler({ 
-              name: "TelemetryAddValue", 
-              data: ["LOOP_ACTIVITY_COUNTER", buckets.OPEN_PANEL] });};
-
-
-
-          // Used to clear the temporary "login" state from the button.
-          Services.obs.notifyObservers(null, "loop-status-changed", null);
-
-          _this3.shouldResumeTour().then(function (resume) {
-            if (resume) {
-              // Assume the conversation with the visitor wasn't open since we would
-              // have resumed the tour as soon as the visitor joined if it was (and
-              // the pref would have been set to false already.
-              _this3.MozLoopService.resumeTour("waiting");
-              resolve(null);
-              return;}
-
-
-            _this3.LoopAPI.initialize();
-
-            var anchor = event ? event.target : _this3.toolbarButton.anchor;
-            _this3.PanelFrame.showPopup(
-            window, 
-            anchor, 
-            "loop", // Notification Panel Type
-            null, // Origin
-            "about:looppanel", // Source
-            null, // Size
-            callback);});});}, 
-
-
-
-
-      /**
-       * Wrapper for openPanel - to support Firefox 46 and 45.
-       *
-       * @param {event}  event   The event opening the panel, used to anchor
-       *                         the panel to the button which triggers it.
-       * @return {Promise}
-       */
-      openCallPanel: function openCallPanel(event) {
-        return this.openPanel(event);}, 
-
-
-      /**
-       * Method to know whether actions to open the panel should instead resume the tour.
-       *
-       * We need the panel to be opened via UITour so that it gets @noautohide.
-       *
-       * @return {Promise} resolving with a {Boolean} of whether the tour should be resumed instead of
-       *                   opening the panel.
-       */
-      shouldResumeTour: Task.async(function* () {
-        // Resume the FTU tour if this is the first time a room was joined by
-        // someone else since the tour.
-        if (!Services.prefs.getBoolPref("loop.gettingStarted.resumeOnFirstJoin")) {
-          return false;}
-
-
-        if (!this.LoopRooms.participantsCount) {
-          // Nobody is in the rooms
-          return false;}
-
-
-        var roomsWithNonOwners = yield this.roomsWithNonOwners();
-        if (!roomsWithNonOwners.length) {
-          // We were the only one in a room but we want to know about someone else joining.
-          return false;}
-
-
-        return true;}), 
-
-
-      /**
-       * @return {Promise} resolved with an array of Rooms with participants (excluding owners)
-       */
-      roomsWithNonOwners: function roomsWithNonOwners() {var _this4 = this;
-        return new Promise(function (resolve) {
-          _this4.LoopRooms.getAll(function (error, rooms) {
-            var roomsWithNonOwners = [];var _iteratorNormalCompletion = true;var _didIteratorError = false;var _iteratorError = undefined;try {
-              for (var _iterator = rooms[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {var room = _step.value;
-                if (!("participants" in room)) {
-                  continue;}
-
-                var numNonOwners = room.participants.filter(function (participant) {return !participant.owner;}).length;
-                if (!numNonOwners) {
-                  continue;}
-
-                roomsWithNonOwners.push(room);}} catch (err) {_didIteratorError = true;_iteratorError = err;} finally {try {if (!_iteratorNormalCompletion && _iterator.return) {_iterator.return();}} finally {if (_didIteratorError) {throw _iteratorError;}}}
-
-            resolve(roomsWithNonOwners);});});}, 
-
-
-
-
-      /**
-       * Triggers the initialization of the loop service if necessary.
-       * Also adds appropraite observers for the UI.
-       */
-      init: function init() {var _this5 = this;
-        // This is a promise for test purposes, but we don't want to be logging
-        // expected errors to the console, so we catch them here.
-        this.MozLoopService.initialize(WindowListener.addonVersion).catch(function (ex) {
-          if (!ex.message || 
-          !ex.message.contains("not enabled") && 
-          !ex.message.contains("not needed")) {
-            console.error(ex);}});
-
-
-
-        // If we're in private browsing mode, then don't add the menu item,
-        // also don't add the listeners as we don't want to update the button.
-        if (PrivateBrowsingUtils.isWindowPrivate(window)) {
-          return;}
-
-
-        this.addMenuItem();
-
-        // Don't do the rest if this is for the hidden window - we don't
-        // have a toolbar there.
-        if (window == Services.appShell.hiddenDOMWindow) {
-          return;}
-
-
-        // Load the frame script into any tab, plus any that get created in the
-        // future.
-        this.mm.loadFrameScript(FRAME_SCRIPT, true);
-
-        // Cleanup when the window unloads.
-        window.addEventListener("unload", function () {
-          Services.obs.removeObserver(_this5, "loop-status-changed");});
-
-
-        Services.obs.addObserver(this, "loop-status-changed", false);
-
-        this.maybeAddCopyPanel();
-        this.updateToolbarState();}, 
-
-
-      /**
-       * Adds a menu item to the browsers' Tools menu that open the Loop panel
-       * when selected.
-       */
-      addMenuItem: function addMenuItem() {var _this6 = this;
-        var menu = document.getElementById("menu_ToolsPopup");
-        if (!menu || menuItem) {
-          return;}
-
-
-        menuItem = document.createElementNS(kNSXUL, "menuitem");
-        menuItem.setAttribute("id", "menu_openLoop");
-        menuItem.setAttribute("label", this._getString("loopMenuItem_label"));
-        menuItem.setAttribute("accesskey", this._getString("loopMenuItem_accesskey"));
-
-        menuItem.addEventListener("command", function () {return _this6.togglePanel();});
-
-        menu.insertBefore(menuItem, document.getElementById("sync-setup"));}, 
-
-
-      /**
-       * Removes the menu item from the browsers' Tools menu.
-       */
-      removeMenuItem: function removeMenuItem() {
-        if (menuItem) {
-          menuItem.parentNode.removeChild(menuItem);}}, 
-
-
-
-      /**
-       * Maybe add the copy panel if it's not throttled and passes other checks.
-       * @return {Promise} Resolved when decided and maybe panel-added.
-       */
-      maybeAddCopyPanel: function maybeAddCopyPanel() {var _this7 = this;
-        // Don't bother adding the copy panel if we're in private browsing or
-        // the user wants to never see it again or we've shown it enough times.
-        if (PrivateBrowsingUtils.isWindowPrivate(window) || 
-        Services.prefs.getBoolPref("loop.copy.shown") || 
-        Services.prefs.getIntPref("loop.copy.showLimit") <= 0) {
-          return Promise.resolve();}
-
-
-        return Throttler.check("loop.copy").then(function () {return _this7.addCopyPanel();});}, 
-
-
-      /**
-       * Hook into the location bar copy command to open up the copy panel.
-       * @param {Function} onClickHandled Optional callback for finished clicks.
-       */
-      addCopyPanel: function addCopyPanel(onClickHandled) {var _this8 = this, _arguments = arguments;
-        // Make a copy of the loop panel as a starting point for the copy panel.
-        var copy = this.panel.cloneNode(false);
-        copy.id = "loop-copy-notification-panel";
-        this.panel.parentNode.appendChild(copy);
-
-        // Record a telemetry copy panel action.
-        var addTelemetry = function addTelemetry(bucket) {
-          _this8.LoopAPI.sendMessageToHandler({ 
-            data: ["LOOP_COPY_PANEL_ACTIONS", _this8.constants.COPY_PANEL[bucket]], 
-            name: "TelemetryAddValue" });};
-
-
-
-        // Handle events from the copy panel iframe content.
-        var onIframe = function onIframe(iframe) {
-          // Watch for events from the copy panel when loaded.
-          iframe.addEventListener("DOMContentLoaded", function onLoad() {
-            iframe.removeEventListener("DOMContentLoaded", onLoad);
-
-            // Size the panel to fit the rendered content adjusting for borders.
-            iframe.contentWindow.requestAnimationFrame(function () {
-              var height = iframe.contentDocument.documentElement.offsetHeight;
-              height += copy.boxObject.height - iframe.boxObject.height;
-              copy.style.height = height + "px";});
-
-
-            // Hide the copy panel then show the loop panel.
-            iframe.contentWindow.addEventListener("CopyPanelClick", function (event) {
-              iframe.parentNode.hidePopup();
-
-              // Show the Loop panel if the user wants it.
-              var _event$detail = event.detail;var accept = _event$detail.accept;var stop = _event$detail.stop;
-              if (accept) {
-                LoopUI.openPanel();}
-
-
-              // Stop showing the panel if the user says so.
-              if (stop) {
-                LoopUI.removeCopyPanel();
-                Services.prefs.setBoolPref("loop.copy.shown", true);}
-
-
-              // Generate the appropriate NO_AGAIN, NO_NEVER, YES_AGAIN,
-              // YES_NEVER probe based on the user's action.
-              var probe = (accept ? "YES" : "NO") + "_" + (stop ? "NEVER" : "AGAIN");
-              addTelemetry(probe);
-
-              // For testing, indicate that handling the click has finished.
-              try {
-                onClickHandled(event.detail);} 
-              catch (ex) {
-                // Do nothing.
-              }});});};
-
-
-
-
-        // Override the default behavior of the copy command.
-        var controller = gURLBar._copyCutController;
-        controller._doCommand = controller.doCommand;
-        controller.doCommand = function () {
-          // Do the normal behavior first.
-          controller._doCommand.apply(controller, _arguments);
-
-          // Remove the panel if the user has seen it enough times.
-          var showLimit = Services.prefs.getIntPref("loop.copy.showLimit");
-          if (showLimit <= 0) {
-            LoopUI.removeCopyPanel();
-            return;}
-
-
-          // Don't bother prompting the user if already sharing.
-          if (_this8.MozLoopService.screenShareActive) {
-            return;}
-
-
-          // Update various counters.
-          Services.prefs.setIntPref("loop.copy.showLimit", showLimit - 1);
-          addTelemetry("SHOWN");
-
-          // Open up the copy panel at the loop button.
-          LoopUI.PanelFrame.showPopup(window, LoopUI.toolbarButton.anchor, "loop-copy", 
-          null, "chrome://loop/content/panels/copy.html", null, onIframe);};}, 
-
-
-
-      /**
-       * Removes the copy panel copy hook and the panel.
-       */
-      removeCopyPanel: function removeCopyPanel() {
-        var controller = gURLBar && gURLBar._copyCutController;
-        if (controller && controller._doCommand) {
-          controller.doCommand = controller._doCommand;
-          delete controller._doCommand;}
-
-
-        var copy = document.getElementById("loop-copy-notification-panel");
-        if (copy) {
-          copy.parentNode.removeChild(copy);}}, 
-
-
-
-      // Implements nsIObserver
-      observe: function observe(subject, topic, data) {
-        if (topic != "loop-status-changed") {
-          return;}
-
-        this.updateToolbarState(data);}, 
-
-
-      /**
-       * Updates the toolbar/menu-button state to reflect Loop status. This should
-       * not be called from the hidden window.
-       *
-       * @param {string} [aReason] Some states are only shown if
-       *                           a related reason is provided.
-       *
-       *                 aReason="login": Used after a login is completed
-       *                   successfully. This is used so the state can be
-       *                   temporarily shown until the next state change.
-       */
-      updateToolbarState: function updateToolbarState() {var _this9 = this;var aReason = arguments.length <= 0 || arguments[0] === undefined ? null : arguments[0];
-        if (!this.toolbarButton.node) {
-          return;}
-
-        var state = "";
-        var mozL10nId = "loop-call-button3";
-        var suffix = ".tooltiptext";
-        if (this.MozLoopService.errors.size) {
-          state = "error";
-          mozL10nId += "-error";} else 
-        if (this.isSlideshowOpen) {
-          state = "slideshow";
-          suffix = ".label";} else 
-        if (this.MozLoopService.screenShareActive) {
-          state = "action";
-          mozL10nId += "-screensharing";} else 
-        if (aReason == "login" && this.MozLoopService.userProfile) {
-          state = "active";
-          mozL10nId += "-active";
-          suffix += "2";} else 
-        if (this.MozLoopService.doNotDisturb) {
-          state = "disabled";
-          mozL10nId += "-donotdisturb";} else 
-        if (this.MozLoopService.roomsParticipantsCount > 0) {
-          state = "active";
-          this.roomsWithNonOwners().then(function (roomsWithNonOwners) {
-            if (roomsWithNonOwners.length > 0) {
-              mozL10nId += "-participantswaiting";} else 
-            {
-              mozL10nId += "-active";}
-
-
-            suffix += "2";
-            _this9.updateTooltiptext(mozL10nId + suffix);
-            _this9.toolbarButton.node.setAttribute("state", state);});
-
-          return;} else 
-        {
-          suffix += "2";}
-
-
-        this.toolbarButton.node.setAttribute("state", state);
-        this.updateTooltiptext(mozL10nId + suffix);}, 
-
-
-      /**
-       * Updates the tootltiptext to reflect Loop status. This should not be called
-       * from the hidden window.
-       *
-       * @param {string} [mozL10nId] l10n ID that refelct the current
-       *                           Loop status.
-       */
-      updateTooltiptext: function updateTooltiptext(mozL10nId) {
-        this.toolbarButton.node.setAttribute("tooltiptext", mozL10nId);
-        var tooltiptext = CustomizableUI.getLocalizedProperty(this.toolbarButton, "tooltiptext");
-        this.toolbarButton.node.setAttribute("tooltiptext", tooltiptext);}, 
-
-
-      /**
-       * Show a desktop notification when 'do not disturb' isn't enabled.
-       *
-       * @param {Object} options Set of options that may tweak the appearance and
-       *                         behavior of the notification.
-       *                         Option params:
-       *                         - {String}   title       Notification title message
-       *                         - {String}   [message]   Notification body text
-       *                         - {String}   [icon]      Notification icon
-       *                         - {String}   [sound]     Sound to play
-       *                         - {String}   [selectTab] Tab to select when the panel
-       *                                                  opens
-       *                         - {Function} [onclick]   Callback to invoke when
-       *                                                  the notification is clicked.
-       *                                                  Opens the panel by default.
-       */
-      showNotification: function showNotification(options) {var _this10 = this;
-        if (this.MozLoopService.doNotDisturb) {
-          return;}
-
-
-        if (!options.title) {
-          throw new Error("Missing title, can not display notification");}
-
-
-        var notificationOptions = { 
-          body: options.message || "" };
-
-        if (options.icon) {
-          notificationOptions.icon = options.icon;}
-
-        if (options.sound) {
-          // This will not do anything, until bug bug 1105222 is resolved.
-          notificationOptions.mozbehavior = { 
-            soundFile: "" };
-
-          this.playSound(options.sound);}
-
-
-        var notification = new window.Notification(options.title, notificationOptions);
-        notification.addEventListener("click", function () {
-          if (window.closed) {
-            return;}
-
-
-          try {
-            window.focus();} 
-          catch (ex) {}
-          // Do nothing.
-
-
-          // We need a setTimeout here, otherwise the panel won't show after the
-          // window received focus.
-          window.setTimeout(function () {
-            if (typeof options.onclick == "function") {
-              options.onclick();} else 
-            {
-              // Open the Loop panel as a default action.
-              _this10.openPanel(null, options.selectTab || null);}}, 
-
-          0);});}, 
-
-
-
-      /**
-       * Play a sound in this window IF there's no sound playing yet.
-       *
-       * @param {String} name Name of the sound, like 'ringtone' or 'room-joined'
-       */
-      playSound: function playSound(name) {var _this11 = this;
-        if (this.ActiveSound || this.MozLoopService.doNotDisturb) {
-          return;}
-
-
-        this.activeSound = new window.Audio();
-        this.activeSound.src = "chrome://loop/content/shared/sounds/" + name + ".ogg";
-        this.activeSound.load();
-        this.activeSound.play();
-
-        this.activeSound.addEventListener("ended", function () {
-          _this11.activeSound = undefined;}, 
-        false);}, 
-
-
-      /**
-       * Start listening to selected tab changes and notify any content page that's
-       * listening to 'BrowserSwitch' push messages.  Also sets up a "joined"
-       * and "left" listener for LoopRooms so that we can toggle the infobar
-       * sharing messages when people come and go.
-       *
-       * @param {(String)} roomToken  The current room that the link generator is connecting to.
-       */
-      startBrowserSharing: function startBrowserSharing(roomToken) {var _this12 = this;
-        if (!this._listeningToTabSelect) {
-          gBrowser.tabContainer.addEventListener("TabSelect", this);
-          this._listeningToTabSelect = true;
-
-          titleChangedListener = this.handleDOMTitleChanged.bind(this);
-
-          this._roomsListener = this.handleRoomJoinedOrLeft.bind(this);
-
-          this.LoopRooms.on("joined", this._roomsListener);
-          this.LoopRooms.on("left", this._roomsListener);
-
-          // Watch for title changes as opposed to location changes as more
-          // metadata about the page is available when this event fires.
-          this.mm.addMessageListener("loop@mozilla.org:DOMTitleChanged", 
-          titleChangedListener);
-
-          this._browserSharePaused = false;
-
-          // Add this event to the parent gBrowser to avoid adding and removing
-          // it for each individual tab's browsers.
-          gBrowser.addEventListener("mousemove", this);
-          gBrowser.addEventListener("click", this);}
-
-
-        this._currentRoomToken = roomToken;
-        this._maybeShowBrowserSharingInfoBar(roomToken);
-
-        // Get the first window Id for the listener.
-        var browser = gBrowser.selectedBrowser;
-        return new Promise(function (resolve) {
-          if (browser.outerWindowID) {
-            resolve(browser.outerWindowID);
-            return;}
-
-
-          browser.messageManager.addMessageListener("Browser:Init", function initListener() {
-            browser.messageManager.removeMessageListener("Browser:Init", initListener);
-            resolve(browser.outerWindowID);});}).
-
-        then(function (outerWindowID) {return (
-            _this12.LoopAPI.broadcastPushMessage("BrowserSwitch", outerWindowID));});}, 
-
-
-      /**
-       * Stop listening to selected tab changes.
-       */
-      stopBrowserSharing: function stopBrowserSharing() {
-        if (!this._listeningToTabSelect) {
-          return;}
-
-
-        this._hideBrowserSharingInfoBar();
-        gBrowser.tabContainer.removeEventListener("TabSelect", this);
-        this.LoopRooms.off("joined", this._roomsListener);
-        this.LoopRooms.off("left", this._roomsListener);
-
-        if (titleChangedListener) {
-          this.mm.removeMessageListener("loop@mozilla.org:DOMTitleChanged", 
-          titleChangedListener);
-          titleChangedListener = null;}
-
-
-        // Remove shared pointers related events
-        gBrowser.removeEventListener("mousemove", this);
-        gBrowser.removeEventListener("click", this);
-        this.removeRemoteCursor();
-
-        this._listeningToTabSelect = false;
-        this._browserSharePaused = false;
-        this._currentRoomToken = null;}, 
-
-
-      /**
-       *  If sharing is active, paints and positions the remote cursor
-       *  over the screen
-       *
-       *  @param cursorData Object with the correct position for the cursor
-       *                    {
-       *                      ratioX: position on the X axis (percentage value)
-       *                      ratioY: position on the Y axis (percentage value)
-       *                    }
-       */
-      addRemoteCursor: function addRemoteCursor(cursorData) {
-        if (this._browserSharePaused || !this._listeningToTabSelect) {
-          return;}
-
-
-        var browser = gBrowser.selectedBrowser;
-        var cursor = document.getElementById("loop-remote-cursor");
-        if (!cursor) {
-          // Create a container to keep the pointer inside.
-          // This allows us to hide the overflow when out of bounds.
-          var cursorContainer = document.createElement("div");
-          cursorContainer.setAttribute("id", "loop-remote-cursor-container");
-
-          cursor = document.createElement("img");
-          cursor.setAttribute("id", "loop-remote-cursor");
-          cursorContainer.appendChild(cursor);
-          // Note that browser.parent is a xul:stack so container will use
-          // 100% of space if no other constrains added.
-          browser.parentNode.appendChild(cursorContainer);}
-
-
-        // Update the cursor's position with CSS.
-        cursor.style.left = 
-        Math.abs(cursorData.ratioX * browser.boxObject.width) + "px";
-        cursor.style.top = 
-        Math.abs(cursorData.ratioY * browser.boxObject.height) + "px";}, 
-
-
-      /**
-       *  Adds the ripple effect animation to the cursor to show a click on the
-       *  remote end of the conversation.
-       *  Will only add it when:
-       *  - A click is received (cursorData = true)
-       *  - Sharing is active (this._listeningToTabSelect = true)
-       *  - Remote cursor is being painted (cursor != undefined)
-       *
-       *  @param clickData bool click event
-       */
-      clickRemoteCursor: function clickRemoteCursor(clickData) {
-        if (!clickData || !this._listeningToTabSelect) {
-          return;}
-
-
-        var class_name = "clicked";
-        var cursor = document.getElementById("loop-remote-cursor");
-        if (!cursor) {
-          return;}
-
-
-        cursor.classList.add(class_name);
-
-        // after the proper time, we get rid of the animation
-        window.setTimeout(function () {
-          cursor.classList.remove(class_name);}, 
-        CURSOR_CLICK_DELAY);}, 
-
-
-      /**
-       *  Removes the remote cursor from the screen
-       */
-      removeRemoteCursor: function removeRemoteCursor() {
-        var cursor = document.getElementById("loop-remote-cursor");
-
-        if (cursor) {
-          cursor.parentNode.removeChild(cursor);}}, 
-
-
-
-      /**
-       * Helper function to fetch a localized string via the MozLoopService API.
-       * It's currently inconveniently wrapped inside a string of stringified JSON.
-       *
-       * @param  {String} key The element id to get strings for.
-       * @return {String}
-       */
-      _getString: function _getString(key) {
-        var str = this.MozLoopService.getStrings(key);
-        if (str) {
-          str = JSON.parse(str).textContent;}
-
-        return str;}, 
-
-
-      /**
-       * Set correct strings for infobar notification based on if paused or empty.
-       */
-
-      _setInfoBarStrings: function _setInfoBarStrings(nonOwnerParticipants, sharePaused) {
-        var message = void 0;
-        if (nonOwnerParticipants) {
-          // More than just the owner in the room.
-          message = this._getString(
-          sharePaused ? "infobar_screenshare_stop_sharing_message2" : 
-          "infobar_screenshare_browser_message3");} else 
-
-        {
-          // Just the owner in the room.
-          message = this._getString(
-          sharePaused ? "infobar_screenshare_stop_no_guest_message" : 
-          "infobar_screenshare_no_guest_message");}
-
-        var label = this._getString(
-        sharePaused ? "infobar_button_restart_label2" : "infobar_button_stop_label2");
-        var accessKey = this._getString(
-        sharePaused ? "infobar_button_restart_accesskey" : "infobar_button_stop_accesskey");
-
-        return { message: message, label: label, accesskey: accessKey };}, 
-
-
-      /**
-       * Indicates if tab sharing is paused.
-       * Set by tab pause button, startBrowserSharing and stopBrowserSharing.
-       * Defaults to false as link generator(owner) enters room we are sharing tabs.
-       */
-      _browserSharePaused: false, 
-
-      /**
-       * Stores details about the last notification.
-       *
-       * @type {Object}
-       */
-      _lastNotification: {}, 
-
-      /**
-       * Used to determine if the browser sharing info bar is currently being
-       * shown or not.
-       */
-      _showingBrowserSharingInfoBar: function _showingBrowserSharingInfoBar() {
-        var browser = gBrowser.selectedBrowser;
-        var box = gBrowser.getNotificationBox(browser);
-        var notification = box.getNotificationWithValue(kBrowserSharingNotificationId);
-
-        return !!notification;}, 
-
-
-      /**
-       * Shows an infobar notification at the top of the browser window that warns
-       * the user that their browser tabs are being broadcasted through the current
-       * conversation.
-       * @param  {String} currentRoomToken Room we are currently joined.
-       * @return {void}
-       */
-      _maybeShowBrowserSharingInfoBar: function _maybeShowBrowserSharingInfoBar(currentRoomToken) {var _this13 = this;
-        var participantsCount = this.LoopRooms.getNumParticipants(currentRoomToken);
-
-        if (this._showingBrowserSharingInfoBar()) {
-          // When we first open the room, there will be one or zero partipicants
-          // in the room. The notification box changes when there's more than one,
-          // so work that out here.
-          var notAlone = participantsCount > 1;
-          var previousNotAlone = this._lastNotification.participantsCount <= 1;
-
-          // If we're not actually changing the notification bar, then don't
-          // re-display it. This avoids the bar sliding in twice.
-          if (notAlone !== previousNotAlone && 
-          this._browserSharePaused === this._lastNotification.paused) {
-            return;}
-
-
-          this._hideBrowserSharingInfoBar();}
-
-
-        var initStrings = this._setInfoBarStrings(participantsCount > 1, this._browserSharePaused);
-
-        var box = gBrowser.getNotificationBox();
-        var bar = box.appendNotification(
-        initStrings.message, // label
-        kBrowserSharingNotificationId, // value
-        // Icon defined in browser theme CSS.
-        null, // image
-        box.PRIORITY_WARNING_LOW, // priority
-        [{ // buttons (Pause, Stop)
-          label: initStrings.label, 
-          accessKey: initStrings.accesskey, 
-          isDefault: false, 
-          callback: function callback(event, buttonInfo, buttonNode) {
-            _this13._browserSharePaused = !_this13._browserSharePaused;
-            var guestPresent = _this13.LoopRooms.getNumParticipants(_this13._currentRoomToken) > 1;
-            var stringObj = _this13._setInfoBarStrings(guestPresent, _this13._browserSharePaused);
-            bar.label = stringObj.message;
-            bar.classList.toggle("paused", _this13._browserSharePaused);
-            buttonNode.label = stringObj.label;
-            buttonNode.accessKey = stringObj.accesskey;
-            LoopUI.MozLoopService.toggleBrowserSharing(_this13._browserSharePaused);
-            if (_this13._browserSharePaused) {
-              // if paused we stop sharing remote cursors
-              _this13.removeRemoteCursor();}
-
-            return true;}, 
-
-          type: "pause" }, 
-
-        { 
-          label: this._getString("infobar_button_disconnect_label"), 
-          accessKey: this._getString("infobar_button_disconnect_accesskey"), 
-          isDefault: true, 
-          callback: function callback() {
-            _this13.removeRemoteCursor();
-            _this13._hideBrowserSharingInfoBar();
-            LoopUI.MozLoopService.hangupAllChatWindows();}, 
-
-          type: "stop" }]);
-
-
-
-        // Sets 'paused' class if needed.
-        bar.classList.toggle("paused", !!this._browserSharePaused);
-
-        // Keep showing the notification bar until the user explicitly closes it.
-        bar.persistence = -1;
-
-        this._lastNotification.participantsCount = participantsCount;
-        this._lastNotification.paused = this._browserSharePaused;}, 
-
-
-      /**
-       * Hides the infobar, permanantly if requested.
-       *
-       * @param   {Object}  browser Optional link to the browser we want to
-       *                    remove the infobar from. If not present, defaults
-       *                    to current browser instance.
-       * @return  {Boolean} |true| if the infobar was hidden here.
-       */
-      _hideBrowserSharingInfoBar: function _hideBrowserSharingInfoBar(browser) {
-        browser = browser || gBrowser.selectedBrowser;
-        var box = gBrowser.getNotificationBox(browser);
-        var notification = box.getNotificationWithValue(kBrowserSharingNotificationId);
-        var removed = false;
-        if (notification) {
-          box.removeNotification(notification);
-          removed = true;}
-
-
-        return removed;}, 
-
-
-      /**
-       * Broadcast 'BrowserSwitch' event.
-       */
-      _notifyBrowserSwitch: function _notifyBrowserSwitch() {
-        // Get the first window Id for the listener.
-        this.LoopAPI.broadcastPushMessage("BrowserSwitch", 
-        gBrowser.selectedBrowser.outerWindowID);}, 
-
-
-      /**
-       * Handles updating of the sharing infobar when the room participants
-       * change.
-       */
-      handleRoomJoinedOrLeft: function handleRoomJoinedOrLeft() {
-        // Don't attempt to show it if we're not actively sharing.
-        if (!this._listeningToTabSelect) {
-          return;}
-
-        this._maybeShowBrowserSharingInfoBar(this._currentRoomToken);}, 
-
-
-      /**
-       * Handles events from the frame script.
-       *
-       * @param {Object} message The message received from the frame script.
-       */
-      handleDOMTitleChanged: function handleDOMTitleChanged(message) {
-        if (!this._listeningToTabSelect || this._browserSharePaused) {
-          return;}
-
-
-        if (gBrowser.selectedBrowser == message.target) {
-          // Get the new title of the shared tab
-          this._notifyBrowserSwitch();}}, 
-
-
-
-      /**
-       * Handles events from gBrowser.
-       */
-      handleEvent: function handleEvent(event) {
-
-        switch (event.type) {
-          case "TabSelect":{
-              var wasVisible = false;
-              // Hide the infobar from the previous tab.
-              if (event.detail.previousTab) {
-                wasVisible = this._hideBrowserSharingInfoBar(
-                event.detail.previousTab.linkedBrowser);
-                // And remove the cursor.
-                this.removeRemoteCursor();}
-
-
-              // We've changed the tab, so get the new window id.
-              this._notifyBrowserSwitch();
-
-              if (wasVisible) {
-                // If the infobar was visible before, we should show it again after the
-                // switch.
-                this._maybeShowBrowserSharingInfoBar(this._currentRoomToken);}
-
-              break;}
-
-          case "mousemove":
-            this.handleMousemove(event);
-            break;
-          case "click":
-            this.handleMouseClick(event);
-            break;}}, 
-
-
-
-      /**
-       * Handles mousemove events from gBrowser and send a broadcast message
-       * with all the data needed for sending link generator cursor position
-       * through the sdk.
-       */
-      handleMousemove: function handleMousemove(event) {
-        // Won't send events if not sharing (paused or not started).
-        if (this._browserSharePaused || !this._listeningToTabSelect) {
-          return;}
-
-
-        // Only update every so often.
-        var now = Date.now();
-        if (now - this.lastCursorTime < CURSOR_MIN_INTERVAL) {
-          return;}
-
-        this.lastCursorTime = now;
-
-        // Skip the update if cursor is out of bounds or didn't move much.
-        var browserBox = gBrowser.selectedBrowser.boxObject;
-        var deltaX = event.screenX - browserBox.screenX;
-        var deltaY = event.screenY - browserBox.screenY;
-        if (deltaX < 0 || deltaX > browserBox.width || 
-        deltaY < 0 || deltaY > browserBox.height || 
-        Math.abs(deltaX - this.lastCursorX) < CURSOR_MIN_DELTA && 
-        Math.abs(deltaY - this.lastCursorY) < CURSOR_MIN_DELTA) {
-          return;}
-
-        this.lastCursorX = deltaX;
-        this.lastCursorY = deltaY;
-
-        this.LoopAPI.broadcastPushMessage("CursorPositionChange", { 
-          ratioX: deltaX / browserBox.width, 
-          ratioY: deltaY / browserBox.height });}, 
-
-
-
-      /**
-       * Handles mouse click events from gBrowser and send a broadcast message
-       * with all the data needed for sending link generator cursor click position
-       * through the sdk.
-       */
-      handleMouseClick: function handleMouseClick() {
-        // We want to stop sending events if sharing is paused.
-        if (this._browserSharePaused) {
-          return;}
-
-
-        this.LoopAPI.broadcastPushMessage("CursorClick");}, 
-
-
-      /**
-       * Fetch the favicon of the currently selected tab in the format of a data-uri.
-       *
-       * @param  {Function} callback Function to be invoked with an error object as
-       *                             its first argument when an error occurred or
-       *                             a string as second argument when the favicon
-       *                             has been fetched.
-       */
-      getFavicon: function getFavicon(callback) {
-        var pageURI = gBrowser.selectedTab.linkedBrowser.currentURI.spec;
-        // If the tab page’s url starts with http(s), fetch icon.
-        if (!/^https?:/.test(pageURI)) {
-          callback();
-          return;}
-
-
-        this.PlacesUtils.promiseFaviconLinkUrl(pageURI).then(function (uri) {
-          // We XHR the favicon to get a File object, which we can pass to the FileReader
-          // object. The FileReader turns the File object into a data-uri.
-          var xhr = xhrClass.createInstance(Ci.nsIXMLHttpRequest);
-          xhr.open("get", uri.spec, true);
-          xhr.responseType = "blob";
-          xhr.overrideMimeType("image/x-icon");
-          xhr.onload = function () {
-            if (xhr.status != 200) {
-              callback(new Error("Invalid status code received for favicon XHR: " + xhr.status));
-              return;}
-
-
-            var reader = new FileReader();
-            reader.onload = reader.onload = function () {return callback(null, reader.result);};
-            reader.onerror = callback;
-            reader.readAsDataURL(xhr.response);};
-
-          xhr.onerror = callback;
-          xhr.send();}).
-        catch(function (err) {
-          callback(err || new Error("No favicon found"));});} };
-
-
-
-
-    XPCOMUtils.defineLazyModuleGetter(LoopUI, "hookWindowCloseForPanelClose", "resource://gre/modules/MozSocialAPI.jsm");
-    XPCOMUtils.defineLazyModuleGetter(LoopUI, "LoopAPI", "chrome://loop/content/modules/MozLoopAPI.jsm");
-    XPCOMUtils.defineLazyModuleGetter(LoopUI, "LoopRooms", "chrome://loop/content/modules/LoopRooms.jsm");
-    XPCOMUtils.defineLazyModuleGetter(LoopUI, "MozLoopService", "chrome://loop/content/modules/MozLoopService.jsm");
-    XPCOMUtils.defineLazyModuleGetter(LoopUI, "PanelFrame", "resource:///modules/PanelFrame.jsm");
-    XPCOMUtils.defineLazyModuleGetter(LoopUI, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
-
-    LoopUI.init();
-    window.LoopUI = LoopUI;
-
-    // Export the Throttler to allow tests to overwrite parts of it.
-    window.LoopThrottler = Throttler;}, 
-
-
-  /**
-   * Take any steps to remove UI or anything from the browser window
-   * document.getElementById() etc. will work here.
-   *
-   * @param {Object} window The window to remove the integration from.
-   */
-  tearDownBrowserUI: function tearDownBrowserUI(window) {
-    if (window.LoopUI) {
-      window.LoopUI.removeCopyPanel();
-      window.LoopUI.removeMenuItem();
-
-      // This stops the frame script being loaded to new tabs, but doesn't
-      // remove it from existing tabs (there's no way to do that).
-      window.LoopUI.mm.removeDelayedFrameScript(FRAME_SCRIPT);
-
-      // XXX Bug 1229352 - Add in tear-down of the panel.
-    }}, 
-
-
-  // nsIWindowMediatorListener functions.
-  onOpenWindow: function onOpenWindow(xulWindow) {
-    // A new window has opened.
-    var domWindow = xulWindow.QueryInterface(Ci.nsIInterfaceRequestor).
-    getInterface(Ci.nsIDOMWindow);
-
-    // Wait for it to finish loading.
-    domWindow.addEventListener("load", function listener() {
-      domWindow.removeEventListener("load", listener, false);
-
-      // If this is a browser window then setup its UI.
-      if (domWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser") {
-        WindowListener.setupBrowserUI(domWindow);}}, 
-
-    false);}, 
-
-
-  onCloseWindow: function onCloseWindow() {}, 
-
-
-  onWindowTitleChange: function onWindowTitleChange() {} };
-
-
-
-/**
- * Provide a way to throttle functionality using DNS to distribute 3 numbers for
- * various distributions channels. DNS is used to scale distribution of the
- * numbers as an A record pointing to a loopback address (127.*.*.*). Prefs are
- * used to control behavior (what domain to check) and keep state (a ticket
- * number to track if it needs to initialize, to wait for its turn, or is
- * completed).
- */
-var Throttler = { 
-  // Each 8-bit block of the IP address allows for 0% rollout (value 0) to 100%
-  // rollout (value 255).
-  TICKET_LIMIT: 255, 
-
-  // Allow the DNS service to be overwritten for testing.
-  _dns: Cc["@mozilla.org/network/dns-service;1"].getService(Ci.nsIDNSService), 
-
-  /**
-   * Check if a given feature should be throttled or not.
-   * @param {string} [prefPrefix] Start of the preference name for the feature.
-   * @return {Promise} Resolved on success, and rejected on throttled.
-   */
-  check: function check(prefPrefix) {var _this14 = this;
-    return new Promise(function (resolve, reject) {
-      // Initialize the ticket (0-254) if it doesn't have a valid value yet.
-      var prefTicket = prefPrefix + ".ticket";
-      var ticket = Services.prefs.getIntPref(prefTicket);
-      if (ticket < 0) {
-        ticket = Math.floor(Math.random() * _this14.TICKET_LIMIT);
-        Services.prefs.setIntPref(prefTicket, ticket);}
-
-      // Short circuit if the special ticket value indicates we're good to go.
-      else if (ticket >= _this14.TICKET_LIMIT) {
-          resolve();
-          return;}
-
-
-      // Handle responses from the DNS resolution service request.
-      var onDNS = function onDNS(request, record) {
-        // Failed to get A-record, so skip for now.
-        if (record === null) {
-          reject();
-          return;}
-
-
-        // Ensure we have a special loopback value before checking other blocks.
-        var ipBlocks = record.getNextAddrAsString().split(".");
-        if (ipBlocks[0] !== "127") {
-          reject();
-          return;}
-
-
-        // Use a specific part of the A-record IP address depending on the
-        // channel. I.e., 127.[release/other].[beta].[aurora/nightly].
-        var index = 1;
-        switch (Services.prefs.getCharPref("app.update.channel")) {
-          case "beta":
-            index = 2;
-            break;
-          case "aurora":
-          case "nightly":
-            index = 3;
-            break;}
-
-
-        // Select the 1 out of 4 parts of the "."-separated IP address to check
-        // if the 8-bit threshold (0-255) exceeds the ticket (0-254).
-        if (ticket < ipBlocks[index]) {
-          // Remember that we're good to go to avoid future DNS checks.
-          Services.prefs.setIntPref(prefTicket, _this14.TICKET_LIMIT);
-          resolve();} else 
-
-        {
-          reject();}};
-
-
-
-      // Look up the DNS A-record of a throttler hostname to decide to show.
-      _this14._dns.asyncResolve(Services.prefs.getCharPref(prefPrefix + ".throttler"), 
-      _this14._dns.RESOLVE_DISABLE_IPV6, onDNS, Services.tm.mainThread);});} };
-
-
-
-
-/**
- * Creates the loop button on the toolbar. Due to loop being a system-addon
- * CustomizableUI already has a placement location for the button, so that
- * we can be on the toolbar.
- */
-function createLoopButton() {
-  CustomizableUI.createWidget({ 
-    id: "loop-button", 
-    type: "custom", 
-    label: "loop-call-button3.label", 
-    tooltiptext: "loop-call-button3.tooltiptext2", 
-    privateBrowsingTooltiptext: "loop-call-button3-pb.tooltiptext", 
-    defaultArea: CustomizableUI.AREA_NAVBAR, 
-    removable: true, 
-    onBuild: function onBuild(aDocument) {
-      // If we're not supposed to see the button, return zip.
-      if (!Services.prefs.getBoolPref("loop.enabled")) {
-        return null;}
-
-
-      var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView);
-
-      var node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
-      node.setAttribute("id", this.id);
-      node.classList.add("toolbarbutton-1");
-      node.classList.add("chromeclass-toolbar-additional");
-      node.classList.add("badged-button");
-      node.setAttribute("label", CustomizableUI.getLocalizedProperty(this, "label"));
-      if (isWindowPrivate) {
-        node.setAttribute("disabled", "true");}
-
-      var tooltiptext = isWindowPrivate ? 
-      CustomizableUI.getLocalizedProperty(this, "privateBrowsingTooltiptext", 
-      [CustomizableUI.getLocalizedProperty(this, "label")]) : 
-      CustomizableUI.getLocalizedProperty(this, "tooltiptext");
-      node.setAttribute("tooltiptext", tooltiptext);
-      node.setAttribute("removable", "true");
-      node.addEventListener("command", function (event) {
-        aDocument.defaultView.LoopUI.togglePanel(event);});
-
-
-      return node;} });}
-
-
-
-
-/**
- * Loads the default preferences from the prefs file. This loads the preferences
- * into the default branch, so they don't appear as user preferences.
- */
-function loadDefaultPrefs() {
-  var branch = Services.prefs.getDefaultBranch("");
-  Services.scriptloader.loadSubScript("chrome://loop/content/preferences/prefs.js", { 
-    pref: function pref(key, val) {
-      // If a previously set default pref exists don't overwrite it.  This can
-      // happen for ESR or distribution.ini.
-      if (branch.getPrefType(key) != branch.PREF_INVALID) {
-        return;}
-
-      switch (typeof val === "undefined" ? "undefined" : _typeof(val)) {
-        case "boolean":
-          branch.setBoolPref(key, val);
-          break;
-        case "number":
-          branch.setIntPref(key, val);
-          break;
-        case "string":
-          branch.setCharPref(key, val);
-          break;}} });
-
-
-
-
-  if (Services.vc.compare(Services.appinfo.version, "47.0a1") < 0) {
-    branch.setBoolPref("loop.remote.autostart", false);}}
-
-
-
-/**
- * Called when the add-on is started, e.g. when installed or when Firefox starts.
- */
-function startup(data) {
-  // Record the add-on version for when the UI is initialised.
-  WindowListener.addonVersion = data.version;
-
-  loadDefaultPrefs();
-  if (!Services.prefs.getBoolPref("loop.enabled")) {
-    return;}
-
-
-  createLoopButton();
-
-  // Attach to hidden window (for OS X).
-  if (AppConstants.platform == "macosx") {
-    try {
-      WindowListener.setupBrowserUI(Services.appShell.hiddenDOMWindow);} 
-    catch (ex) {(function () {
-        // Hidden window didn't exist, so wait until startup is done.
-        var topic = "browser-delayed-startup-finished";
-        Services.obs.addObserver(function observer() {
-          Services.obs.removeObserver(observer, topic);
-          WindowListener.setupBrowserUI(Services.appShell.hiddenDOMWindow);}, 
-        topic, false);})();}}
-
-
-
-  // Attach to existing browser windows, for modifying UI.
-  var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
-  var windows = wm.getEnumerator("navigator:browser");
-  while (windows.hasMoreElements()) {
-    var domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
-    WindowListener.setupBrowserUI(domWindow);}
-
-
-  // Wait for any new browser windows to open.
-  wm.addListener(WindowListener);
-
-  // Load our stylesheets.
-  var styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].
-  getService(Components.interfaces.nsIStyleSheetService);
-  var sheets = [
-  "chrome://loop-shared/skin/loop.css", 
-  "chrome://loop/skin/platform.css"];var _iteratorNormalCompletion2 = true;var _didIteratorError2 = false;var _iteratorError2 = undefined;try {
-
-
-
-    for (var _iterator2 = sheets[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {var sheet = _step2.value;
-      var styleSheetURI = Services.io.newURI(sheet, null, null);
-      styleSheetService.loadAndRegisterSheet(styleSheetURI, 
-      styleSheetService.AUTHOR_SHEET);}} catch (err) {_didIteratorError2 = true;_iteratorError2 = err;} finally {try {if (!_iteratorNormalCompletion2 && _iterator2.return) {_iterator2.return();}} finally {if (_didIteratorError2) {throw _iteratorError2;}}}}
-
-
-
-/**
- * Called when the add-on is shutting down, could be for re-installation
- * or just uninstall.
- */
-function shutdown(data, reason) {
-  // Close any open chat windows
-  Cu.import("resource:///modules/Chat.jsm");
-  var isLoopURL = function isLoopURL(_ref) {var src = _ref.src;return (/^about:loopconversation#/.test(src));};
-  [].concat(_toConsumableArray(Chat.chatboxes)).filter(isLoopURL).forEach(function (chatbox) {
-    chatbox.content.contentWindow.close();});
-
-
-  // Detach from hidden window (for OS X).
-  if (AppConstants.platform == "macosx") {
-    WindowListener.tearDownBrowserUI(Services.appShell.hiddenDOMWindow);}
-
-
-  // Detach from browser windows.
-  var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
-  var windows = wm.getEnumerator("navigator:browser");
-  while (windows.hasMoreElements()) {
-    var domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
-    WindowListener.tearDownBrowserUI(domWindow);}
-
-
-  // Stop waiting for browser windows to open.
-  wm.removeListener(WindowListener);
-
-  // If the app is shutting down, don't worry about cleaning up, just let
-  // it fade away...
-  if (reason == APP_SHUTDOWN) {
-    return;}
-
-
-  CustomizableUI.destroyWidget("loop-button");
-
-  // Unload stylesheets.
-  var styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"].
-  getService(Components.interfaces.nsIStyleSheetService);
-  var sheets = ["chrome://loop/content/addon/css/loop.css", 
-  "chrome://loop/skin/platform.css"];var _iteratorNormalCompletion3 = true;var _didIteratorError3 = false;var _iteratorError3 = undefined;try {
-    for (var _iterator3 = sheets[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {var sheet = _step3.value;
-      var styleSheetURI = Services.io.newURI(sheet, null, null);
-      if (styleSheetService.sheetRegistered(styleSheetURI, 
-      styleSheetService.AUTHOR_SHEET)) {
-        styleSheetService.unregisterSheet(styleSheetURI, 
-        styleSheetService.AUTHOR_SHEET);}}
-
-
-
-    // Unload modules.
-  } catch (err) {_didIteratorError3 = true;_iteratorError3 = err;} finally {try {if (!_iteratorNormalCompletion3 && _iterator3.return) {_iterator3.return();}} finally {if (_didIteratorError3) {throw _iteratorError3;}}}Cu.unload("chrome://loop/content/modules/MozLoopAPI.jsm");
-  Cu.unload("chrome://loop/content/modules/LoopRooms.jsm");
-  Cu.unload("chrome://loop/content/modules/MozLoopService.jsm");}
-
-
-function install() {}
-
-function uninstall() {}
deleted file mode 100644
--- a/browser/extensions/loop/chrome/content/modules/DomainWhitelist.jsm
+++ /dev/null
@@ -1,3018 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-/* exported DomainWhitelist */
-this.EXPORTED_SYMBOLS = ["DomainWhitelist"];
-
-this.DomainWhitelist = Object.freeze({ 
-  check: function check(domain) {
-    return gWhitelist.has(domain);} });
-
-
-
-// Convert the multiline list of domains into a Set for lookup.
-var gWhitelist = new Set("\n01net.com\n07073.com\n10086.cn\n104.com.tw\n1111.com.tw\n114la.com\n11st.co.kr\n120ask.com\n12306.cn\n123cha.com\n123rf.com\n126.com\n1337x.to\n163.com\n1688.com\n16lao.com\n17173.com\n178.com\n17k.com\n17ok.com\n17track.net\n189.cn\n1905.com\n1and1.com\n1edisource.com\n1tv.ru\n1und1.de\n20minutes.fr\n20minutos.es\n2345.com\n247sports.com\n24h.com.vn\n24hourfitness.com\n28.com\n2ch-c.net\n2ch.net\n2chan.net\n2chblog.jp\n2gis.ru\n315che.com\n360.cn\n360.com\n360doc.com\n39.net\n3dmgame.com\n3m.com\n3tailer.com\n42mr.com\n4399.com\n4chan.org\n4dsply.com\n4gamer.net\n4pda.ru\n4shared.com\n500px.com\n51.la\n51auto.com\n51cto.com\n51sole.com\n51yes.com\n525j.com.cn\n58.com\n58pic.com\n5dcar.com\n6park.com\n6pm.com\n7769domain.com\n99acres.com\n9gag.com\na10.com\naa.com\naafp.org\naamc.org\naao.org\naaos.org\naarp.org\naastocks.com\nabc.es\nabc.net.au\nabchina.com\nabebooks.com\nabercrombie.com\nabout.com\nabplive.in\nabril.com.br\nabs-cbn.com\nabsoluteclickscom.com\nacademia.edu\nacademy.com\naccommodationforstudents.com\naccorhotels.com\naccuweather.com\nacer.com\nacfun.tv\nactiverain.com\nad-tech.com\nad4game.com\nadcash.com\nadditudemag.com\naddthis.com\nadf.ly\nadidas.com\nadk2x.com\nadmaimai.com\nadme.ru\nadnetworkperformance.com\nadobe.com\nadorama.com\nadp.com\nadplxmd.com\nadservingsolutionsinc.com\nadservone.com\nadslgate.com\nae.com\naegeanair.com\naerlingus.com\naeroflot.ru\naeromexico.com\naeroplan.com\naerosoles.com\naftonbladet.se\nagar.io\nagoda.com\nahg.com\nahit.com\nahrq.gov\nairarabia.com\nairasia.com\nairastana.com\nairberlin.com\nairbnb.com\naircanada.com\naireuropa.com\nairfrance.com\nairlinequality.com\nairmiles.ca\nairnewzealand.co.nz\nairtel.in\najanshaber.com\nakamaihd.net\nakb48matomemory.com\nalarabiya.net\nalaskaair.com\nalbawabhnews.com\nalexa.cn\nalexa.com\nalfabank.ru\nalfalfalfa.com\nalfredangelo.com\nali213.net\nalibaba.com\nalicdn.com\nalice.it\naliexpress.com\nalimama.com\nalipay.com\nalitalia.com\naliyun.com\naljaras.com\naljazeera.net\nall-free-download.com\nall2lnk.com\nallabout.co.jp\nallegiantair.com\nallegro.pl\nallmyvideos.net\nallocine.fr\nallrecipes.com\nalmasryalyoum.com\naltervista.org\nalwafd.org\nalz.org\nam15.net\nama-assn.org\namadeus.net\namarujala.com\namazon.ca\namazon.cn\namazon.co.jp\namazon.co.uk\namazon.com\namazon.de\namazon.es\namazon.fr\namazon.in\namazon.it\namazonaws.com\nameba.jp\nameblo.jp\namericanas.com.br\namericanexpress.com\namericanschoolnj.com\namorepacific.com\nampclicks.com\nampxchange.com\nana.co.jp\nancestry.com\nandroid.com\nandroidauthority.com\nandroidcentral.com\nangieslist.com\nanitube.se\nanjuke.com\nannualcreditreport.com\nanswers.com\nanthropologie.com\naol.com\napa.org\napache.org\naparat.com\napartmenttherapy.com\naplaceformom.com\napple.com\nappledaily.com.tw\nappraisalbuzz.com\nappraisalinstitute.org\nappraisers.org\nappraisersforum.com\nappraiserusa.com\narbeitsagentur.de\narcelik.com.tr\narchive.org\narchiveofourown.org\narcot.com\narcteryx.com\narduino.cc\nareaa.org\nargaam.com\nargos.co.uk\nariba.com\narinet.com\narstechnica.com\nas.com\nasahi.com\nasana.com\nascii.jp\nasda.com\nasha.org\nashleyfurniture.com\nasia-lists.com\nasiatravel.com\nasicsamerica.com\nask.com\nask.fm\naskmebazaar.com\naskubuntu.com\nasos.com\nasriran.com\nassetline.com\nasus.com\natkins.com\natlassian.net\natt.com\natt.net\natwiki.jp\naucfan.com\nauction.co.kr\naudible.com\naudio-technica.com\nauntyacid.com\nausopen.com\naustrian.com\nauthoritynutrition.com\nauto.ru\nautoblog.com\nautobytel.com\nautodesk.com\nautohome.com.cn\nautoscout24.de\nautotrader.co.uk\nautotrader.com\navast.com\navclub.com\naveda.com\navery.com\navforums.com\navg.com\navira.com\navito.ma\navito.ru\naweber.com\naxia.com\naxisbank.co.in\naxisbank.com\nazet.sk\nazlyrics.com\nb2btech.com\nb3net.com\nb9dm.com\nbab.la\nbabycenter.com\nbabylon.com\nbabytree.com\nbackcountry.com\nbackpage.com\nbackyardchickens.com\nbadoo.com\nbahn.de\nbaidu.com\nbaike.com\nbaimao.com\nbaixaki.com.br\nbandcamp.com\nbanggood.com\nbanki.ru\nbankmellat.ir\nbankofamerica.com\nbankrate.com\nbarbie.com\nbarbour.com\nbarcelo.com\nbarclaycardus.com\nbarclays.co.uk\nbarnesandnoble.com\nbartarinha.ir\nbasecamp.com\nbassettfurniture.com\nbasspro.com\nbastillepost.com\nbathandbodyworks.com\nbattle.net\nbattlefield.com\nbayt.com\nbb.com.br\nbbb.org\nbbc.co.uk\nbbc.com\nbbt.com\nbbteam.com\nbc.vc\nbedandbreakfast.com\nbedbathandbeyond.com\nbeeline.ru\nbehance.net\nbelkin.com\nbenetton.com\nberetta.com\nberkeley.edu\nbestadbid.com\nbestbuy.com\nbestwestern.com\nbet365.com\nbettycrocker.com\nbeytoote.com\nbgr.com\nbhaskar.com\nbhg.com\nbhphotovideo.com\nbiblegateway.com\nbidvertiser.com\nbig5sportinggoods.com\nbiglike.com\nbiglobe.ne.jp\nbild.de\nbilibili.com\nbillboard.com\nbilldesk.com\nbing.com\nbintang.com\nbiobiochile.cl\nbiomedcentral.com\nbioyun.com\nbirdsallinteractive.com\nbissell.com\nbitauto.com\nbitbucket.org\nbitly.com\nbizjournals.com\nbkstr.com\nblablacar.es\nblackboard.com\nblackhatworld.com\nbleacherreport.com\nblkget.com\nblobla.com\nblocket.se\nblog.com\nblog.ir\nblog.jp\nblog.me\nblogfa.com\nblogger.com\nblogimg.jp\nblogphongthuy.com\nblogsky.com\nblomaga.jp\nbloomberg.com\nblu-ray.com\nbluehost.com\nbmi.ir\nbmj.com\nbobvila.com\nboc.cn\nbodybuilding.com\nbol.com\nbollywoodbubble.com\nbom.gov.au\nboma.org\nbomb01.com\nbonanza.com\nbonappetit.com\nbongda60.net\nbooking.com\nbookmyshow.com\nboredpanda.com\nbose.com\nbottlenose-wine.com\nbox.com\nboxofficemojo.com\nboylesoftware.com\nbp.blogspot.com\nbradesco.com.br\nbrainyquote.com\nbrassring.com\nbraun.com\nbreastcancer.org\nbreitbart.com\nbrides.com.cn\nbridgestreet.com\nbritishairways.com\nbrusselsairlines.com\nbs.to\nbsnl.in\nbt.com\nbtolat.com\nbttiantang.com\nbuffiniandcompany.com\nbukalapak.com\nbullhorn.com\nburton.com\nbuscape.com.br\nbusinessinsider.com\nbusinessol.com\nbusinessweekly.com.tw\nbustle.com\nbuyerzone.com\nbuyreman.com\nbuzzfeed.com\nbuzzfil.net\nbuzzlie.com\nc-loans.com\nc3i-inc.com\nca.gov\ncabelas.com\ncaf.fr\ncafemom.com\ncafepress.com\ncaijing.com.cn\ncaisse-epargne.fr\ncaixa.gov.br\ncaixin.com\ncambridge.org\ncamdenliving.com\ncamelbak.com\ncameloteurope.com\ncancer.gov\ncancer.org\ncankaoxiaoxi.com\ncannondale.com\ncanva.com\ncapitalone.com\ncapitalone360.com\ncarcomplaints.com\ncare.com\ncareerbuilder.com\ncareerwebschool.com\ncarfax.com\ncargurus.com\ncarhartt.com\ncarmax.com\ncarnival.com\ncars.com\ncarsensor.net\ncarters.com\ncartier.com\ncarview.co.jp\ncarwale.com\ncasasbahia.com.br\ncateye.com\ncathaypacific.com\ncb01.co\ncbc.ca\ncbre.com\ncbrehotels.com\ncbs.com\ncbslocal.com\ncbsnews.com\ncbssports.com\ncc.com\nccb.com\nccb.com.cn\nccim.com\nccm.net\ncda.pl\ncdc.gov\ncdiscount.com\ncdstm.cn\nce.cn\ncebupacificair.com\ncecil.de\nceconline.com\ncelebritycruises.com\nceneo.pl\ncentury21.com\ncerner.com\ncesweb.org\nchampionat.com\nchampionsschool.com\nchange.org\ncharitynavigator.org\ncharter.net\nchase.com\nchatwork.com\ncheapflights.co.uk\ncheaptickets.com\ncheezburger.com\nchefkoch.de\nchegg.com\nchekb.com\nchess.com\nchicagotribune.com\nchildrensplace.com\nchina-airlines.com\nchina.com\nchina.com.cn\nchinadaily.com.cn\nchinaso.com\nchinatimes.com\nchinaz.com\nchip.de\nchiphell.com\nchoicehotels.com\nchosun.com\nchouftv.ma\nchristian-dogma.com\nchristianlouboutin.com\nchron.com\nci123.com\ncimls.com\ncinemablend.com\ncisco.com\nciti.com\ncitibank.co.in\ncitibankonline.com\ncitilink.ru\ncitizensadvice.org.uk\ncitrixonline.com\ncity-data.com\ncityadspix.com\ncityheaven.net\nck101.com\nclarin.com\nclarkhoward.com\ncleartrip.com\nclevelandclinic.org\nclickadu.com\nclickbank.com\nclicksvenue.com\nclien.net\ncliftonlabs.com\nclinicaltrials.gov\nclinique.com\nclipconverter.cc\nclixsense.com\ncloudflare.com\ncloudsrvtrk.com\ncms.gov\ncna.com.tw\ncnbc.com\ncnbeta.com\ncnblogs.com\ncnet.com\ncnmo.com\ncnn.com\ncnnic.cn\ncntraveler.com\ncntv.cn\ncnzz.com\ncoccoc.com\ncocolog-nifty.com\ncodecademy.com\ncodecanyon.net\ncodepen.io\ncodeproject.com\ncoldwellbanker.com\ncolgate.com\ncollegeboard.org\ncollegerentals.com\ncolliers.com\ncolumbia.com\ncolumbia.edu\ncomcast.net\ncomenity.net\ncommbank.com.au\ncommentcamarche.net\ncommerx.com\ncomplaintsboard.com\ncomplex.com\ncomputerbild.de\nconcursolutions.com\ncondor.com\nconservativetribune.com\nconstantcontact.com\nconsumeraffairs.com\nconsumercomplaints.in\nconsumerist.com\nconsumerlab.com\nconsumerreports.org\nconsumersearch.com\ncontainerstore.com\ncontentabc.com\ncontinuingedexpress.com\nconvert2mp3.net\ncookinglight.com\ncookpad.com\ncooks.com\ncooksillustrated.com\ncoolmath-games.com\ncopaair.com\ncorelogic.com\ncornell.edu\ncorporatehousingbyowner.com\ncorreios.com.br\ncorriere.it\ncosmopolitan.com\ncostar.com\ncostco.com\ncouchsurfing.com\ncountryliving.com\ncoupons.com\ncoursera.org\ncovalentworks.com\ncox.net\ncpasbien.io\ncpsc.gov\ncqnews.net\ncracked.com\ncraigslist.ca\ncraigslist.org\ncrateandbarrel.com\ncreativemarket.com\ncredai.org\ncredit-agricole.fr\ncreditkarma.com\ncreditmutuel.fr\ncrefcoa.com\ncrewnetwork.org\ncrhoy.com\ncricbuzz.com\ncriteo.com\ncrm-daily.com\ncrmbuyer.com\ncrs.com\ncrunchbase.com\ncrunchyroll.com\ncrutchfield.com\ncsc.com\ncsdn.net\nctitv.com.tw\nctrip.com\ncuisinart.com\ncushwake.com\ncusthelp.com\ncvs.com\ncxml.org\ncygnet-infotech.com\ndafont.com\ndaikynguyenvn.com\ndaily.co.jp\ndailykos.com\ndailymail.co.uk\ndailymotion.com\ndailypakistan.com.pk\ndailysnark.com\ndanawa.com\ndangdang.com\ndantri.com.vn\ndartappraisal.com\ndataart.com\ndaum.net\ndaveramsey.com\ndavidsbridal.com\ndavita.com\ndawn.com\ndeadspin.com\ndealnews.com\ndebate.com.mx\ndecathlon.co.uk\ndeere.com\ndeesign.com\ndeezer.com\ndell.com\ndelta.com\ndemandstar.com\ndemc.com\ndepositphotos.com\ndescartes.com\ndessy.com\ndetik.com\ndeviantart.com\ndeviantart.net\ndhgate.com\ndhl.com\ndhl.de\ndiabetes.co.uk\ndiabetes.org\ndiamondresorts.com\ndianping.com\ndickssportinggoods.com\ndict.cc\ndiesel.com\ndigg.com\ndigikala.com\ndigitalenterprise.org\ndigitalmesh.com\ndigitalocean.com\ndigitalriver.com\ndigitaltrends.com\ndigitas.com\ndillards.com\ndingit.tv\ndiply.com\ndirectrev.com\ndirectv.com\ndiscogs.com\ndiscover.com\ndiscovercard.com\ndiscuss.com.hk\ndish.com\ndisney.com\ndisneystore.com\ndisq.us\ndisqus.com\ndivar.ir\ndiynetwork.com\ndjpunjab.info\ndl-protect.com\ndmm.com\ndmv.org\ndnaindia.com\ndns-shop.ru\ndocin.com\ndoculabs.com\ndocusign.net\ndogfoodadvisor.com\ndoisongphapluat.com\ndomaintools.com\ndominos.com\ndonanimhaber.com\ndonga.com\ndoodle.com\ndoorblog.jp\ndostor.org\ndouban.com\ndoubleclick.net\ndouguo.com\ndouyutv.com\ndpreview.com\ndreamstime.com\ndribbble.com\ndrive2.ru\ndrom.ru\ndropbooks.tv\ndropbox.com\ndropboxusercontent.com\ndrudgereport.com\ndrugabuse.gov\ndrugs.com\ndrugstore.com\ndrweil.com\ndslreports.com\ndsw.com\ndtz.com\nduba.com\nduckduckgo.com\ndummies.com\nduolingo.com\nduowan.com\ndw.com\ndx.com\ndynamicnet.net\ndytt8.net\ne-bizsoft.com\ne-global.es\nea.com\nearthclinic.com\nearthlink.net\nearthwaterfire.com\neastday.com\neastmoney.com\neasyjet.com\neasypano.com\neatingwell.com\nebates.com\nebay-kleinanzeigen.de\nebay.ca\nebay.co.uk\nebay.com\nebay.com.au\nebay.de\nebay.es\nebay.fr\nebay.in\nebay.it\nebookers.com\nebrun.com\necco.com\necho.msk.ru\necnavi.jp\necollege.com\necommerce-digest.com\necommercebytes.com\necommercepartners.net\necommercetimes.com\neconomist.com\ned.gov\neddiebauer.com\nedeal.com\nedmodo.com\nedmunds.com\nedx.org\neenadu.net\negain.com\negou.com\nehow.com\neia.gov\nekantipur.com\neksisozluk.com\nel-nacional.com\nelcomercio.com\nelcomercio.pe\nelconfidencial.com\nelcorteingles.es\neldorado.ru\nelectronicmarkets.org\nelevenia.co.id\nelfagr.org\nelintransigente.com\nelmogaz.com\nelmundo.es\nelpais.com\nelsevier.com\neluniverso.com\nelwatannews.com\nemao.com\nemarketingassociation.com\nemgn.com\nemirates.com\nemol.com\nenet.com.cn\nenews.com.tw\nengadget.com\nengageya.com\nensonhaber.com\nentrepreneur.com\nenvato.com\neonline.com\nepic.com\nepicurious.com\nepochtimes.com\nepweike.com\nera.com\neram.fr\nergotron.com\nernestjones.co.uk\nero-advertising.com\nero-video.net\neroterest.net\nespncricinfo.com\nesquire.com\nesuteru.com\netao.com\nethanallen.com\netrade.com\netsy.com\nettoday.net\neuropa.eu\neventbrite.com\nevernote.com\nevite.com\new.com\nex.ua\nexamine.com\nexblog.jp\nexcite.co.jp\nexecustay.com\nexhentai.org\nexhibitions.co.uk\nexoclick.com\nexpedia.ca\nexpedia.com\nexpress-scripts.com\nexpress.co.uk\nexpress.pk\nextra.com.br\nextratorrent.cc\neyny.com\neztv.ag\nface-masr.com\nfacebook.com\nfacenama.com\nfaithtap.com\nfamilydoctor.com.cn\nfamilydoctor.org\nfamitsu.com\nfanatik.com.tr\nfandango.com\nfanfiction.net\nfang.com\nfanhuan.com\nfanli.com\nfanniemae.com\nfanpage.gr\nfarfesh.com\nfarsnews.com\nfashiongo.net\nfastclass.com\nfastcompany.com\nfatosdesconhecidos.com.br\nfatwallet.com\nfaz.net\nfbcdn.net\nfc2.com\nfda.gov\nfederaltitle.com\nfedex.com\nfeedly.com\nfema.gov\nfeng.com\nfibre2fashion.com\nfidelity.com\nfiducia.de\nfilehippo.com\nfilmesonlinegratis.net\nfilmibeat.com\nfilmon.com\nfilmweb.pl\nfinecooking.com\nfinishclueobscure.info\nfinishline.com\nfinn.no\nfinnair.com\nfirmseek.com\nfirstpost.com\nfisher-price.com\nfitbit.com\nfitnessmagazine.com\nfitpregnancy.com\nfiverr.com\nfivethirtyeight.com\nfixya.com\nflashscore.com\nflashx.tv\nflatmates.com.au\nflickr.com\nflightnetwork.com\nflightradar24.com\nflipboard.com\nflipkart.com\nflirchi.com\nflybe.com\nflyfrontier.com\nflysaa.com\nflyuia.com\nfnac.com\nfnb.co.za\nfntic.com\nfocus.cn\nfocus.de\nfodors.com\nfood.com\nfood52.com\nfoodandwine.com\nfoodnetwork.com\nfool.com\nforbes.com\nforce.com\nford.com\nforestcity.net\nforever21.com\nforgeofempires.com\nforocoches.com\nfortune.com\nfotostrana.ru\nfoursquare.com\nfoxnews.com\nfoxsports.com\nfree.fr\nfreecharge.in\nfreejobalert.com\nfreelancer.com\nfreepeople.com\nfreepik.com\nfrigidaire.com\nfriv.com\nfromdoctopdf.com\nfrommers.com\nfrontpoint.it\nfrys.com\nft.com\nfueleconomy.gov\nfurnishedhousing.com\ng2a.com\ngaana.com\ngalleryofguns.com\ngamefactory.jp\ngamefaqs.com\ngameforge.com\ngamepedia.com\ngamer.com.tw\ngamersky.com\ngamespot.com\ngamestop.com\ngamewith.jp\ngamme.com.tw\ngap.com\ngaranti.com.tr\ngardenweb.com\ngarmin.com\ngasbuddy.com\ngawker.com\ngazeta.pl\ngazeta.ru\ngazetaexpress.com\ngazetevatan.com\ngazzetta.it\nge.com\ngeappliances.com\ngearbest.com\ngeekologie.com\ngeico.com\ngemius.pl\ngenius.com\ngeocities.jp\ngetbootstrap.com\ngethuman.com\ngetpocket.com\ngfycat.com\nghanaweb.com\ngia.edu\ngiant-bicycles.com\ngidonline.club\ngigazine.net\ngiphy.com\ngismeteo.ru\ngithub.com\ngittigidiyor.com\ngivemesport.com\ngizmodo.com\ngizmodo.jp\nglassdoor.com\nglobalpropertyguide.com\nglobalsources.com\nglobaltestmarket.com\nglobaltimes.cn\nglobest.com\nglobo.com\nglock.com\ngmanetwork.com\ngmarket.co.kr\ngmw.cn\ngmx.net\ngnavi.co.jp\ngnc.com\ngo.com\ngo2000.com\ngo2cloud.org\ngoal.com\ngocomics.com\ngodaddy.com\ngofundme.com\ngoibibo.com\ngoldcoastschools.com\ngome.com.cn\ngongchang.com\ngoo-net.com\ngoo.gl\ngoo.ne.jp\ngoodgamestudios.com\ngoodhousekeeping.com\ngoodreads.com\ngoodrx.com\ngoogle-analytics.com\ngoogle.ae\ngoogle.at\ngoogle.az\ngoogle.ba\ngoogle.be\ngoogle.bg\ngoogle.by\ngoogle.ca\ngoogle.ch\ngoogle.cl\ngoogle.cn\ngoogle.co.id\ngoogle.co.il\ngoogle.co.in\ngoogle.co.jp\ngoogle.co.kr\ngoogle.co.ma\ngoogle.co.nz\ngoogle.co.th\ngoogle.co.tz\ngoogle.co.uk\ngoogle.co.ve\ngoogle.co.za\ngoogle.com\ngoogle.com.af\ngoogle.com.ar\ngoogle.com.au\ngoogle.com.bd\ngoogle.com.bo\ngoogle.com.br\ngoogle.com.co\ngoogle.com.do\ngoogle.com.ec\ngoogle.com.eg\ngoogle.com.gh\ngoogle.com.gt\ngoogle.com.hk\ngoogle.com.kw\ngoogle.com.ly\ngoogle.com.mx\ngoogle.com.my\ngoogle.com.ng\ngoogle.com.pe\ngoogle.com.ph\ngoogle.com.pk\ngoogle.com.pr\ngoogle.com.sa\ngoogle.com.sg\ngoogle.com.tr\ngoogle.com.tw\ngoogle.com.ua\ngoogle.com.vn\ngoogle.cz\ngoogle.de\ngoogle.dk\ngoogle.dz\ngoogle.es\ngoogle.fi\ngoogle.fr\ngoogle.gr\ngoogle.hr\ngoogle.hu\ngoogle.ie\ngoogle.iq\ngoogle.it\ngoogle.kz\ngoogle.lk\ngoogle.lt\ngoogle.lv\ngoogle.nl\ngoogle.no\ngoogle.pl\ngoogle.pt\ngoogle.ro\ngoogle.rs\ngoogle.ru\ngoogle.se\ngoogle.si\ngoogle.sk\ngoogle.tn\ngoogleadservices.com\ngoogleusercontent.com\ngorillavid.in\ngotomeeting.com\ngotowebinar.com\ngotporn.com\ngougou.com\ngovome.com\ngracobaby.com\ngrammarly.com\ngraphicriver.net\ngreatandhra.com\ngreatclips.com\ngreatergood.com\ngroupon.com\ngs1.org\ngsmarena.com\ngstatic.com\nguess.com\nguidestar.org\ngulfair.com\ngumtree.co.za\ngumtree.com\ngumtree.com.au\ngumtree.pl\ngunbroker.com\ngutefrage.net\ngyazo.com\ngymboree.com\nhaber7.com\nhaberler.com\nhaberturk.com\nhabrahabr.ru\nhaier.com\nhalifax-online.co.uk\nhamariweb.com\nhamusoku.com\nhanleywood.com\nhao123.com\nhaosou.com\nharborfreight.com\nharmankardon.com\nharrods.com\nharvard.edu\nhasbro.com\nhatena.ne.jp\nhatenablog.com\nhawaiianairlines.com\nhclips.com\nhdfcbank.com\nhdfilmifullizle.com.tr\nhealth.com\nhealthboards.com\nhealthcare.gov\nhealthgrades.com\nhealthline.com\nhealthstream.com\nheart.org\nheise.de\nhellou.co.uk\nhellyhansen.com\nhepsiburada.com\nhere.com\nhermanmiller.com\nhespress.com\nhexun.com\nhfflp.com\nhgtv.com\nhh.ru\nhibapress.com\nhighrisehq.com\nhihi2.com\nhillspet.com\nhilton.com\nhimado.in\nhimasoku.com\nhindustantimes.com\nhines.com\nhistats.com\nhitc.com\nhizliresim.com\nhm.com\nholiday-weather.com\nhollandamerica.com\nhollywoodlife.com\nhollywoodreporter.com\nhomeaway.com\nhomedepot.com\nhomepage-web.com\nhomes.co.jp\nhomesalez.com\nhongkiat.com\nhootsuite.com\nhopkinsmedicine.org\nhorizonhobby.com\nhostelbookers.com\nhostelworld.com\nhostgator.com\nhotel.de\nhotels.com\nhoteltravel.com\nhotnewhiphop.com\nhotpepper.jp\nhotstar.com\nhottopic.com\nhotukdeals.com\nhotwire.com\nhouse365.com\nhousemaster.com\nhouzz.com\nhowardforums.com\nhowstuffworks.com\nhowtogeek.com\nhp.com\nhrblock.com\nhref.li\nhrs.de\nhsbc.co.uk\nhsbc.com.hk\nhse.gov.uk\nhsn.com\nhuaban.com\nhuanqiu.com\nhuawei.com\nhubpages.com\nhubspot.com\nhud.gov\nhuffingtonpost.com\nhulu.com\nhulu.jp\nhumblebundle.com\nhupu.com\nhurriyet.com.tr\nhusqvarna.com\nhuya.com\nhyatt.com\ni.ua\ni2x.net\nibanking-services.com\nibba.org\niberia.com\nibm.com\nibnlive.com\nibtimes.co.in\nibtimes.co.uk\nibtimes.com\nicbc.com.cn\nicc.net\niciba.com\nicicibank.com\nicims.com\nicloud.com\nicolor.com.cn\niconosquare.com\nicook.tw\nid.net\nidata.com\nideafit.com\nidealista.com\nidealo.de\nidnes.cz\nifeng.com\niflscience.com\nifma.org\nig.com.br\nign.com\niheart.com\niherb.com\nihg.com\nihwy.com\nijreview.com\nikea.com\nilfattoquotidiano.it\nilmeteo.it\niltalehti.fi\niltasanomat.fi\nimagebam.com\nimages-amazon.com\nimaging-resource.com\nimamerchants.org\nimdb.com\nimgchili.net\nimgur.com\nimmobilienscout24.de\nimobile.com.cn\nimpress.co.jp\ninc.com\nindeed.co.in\nindeed.co.uk\nindeed.com\nindependent.co.uk\nindex.hu\nindia.com\nindiamart.com\nindianexpress.com\nindianrail.gov.in\nindiatimes.com\nindiegogo.com\ninfoaccess.net\ninfobae.com\ninformaticsinc.com\ninformer.com\ninfoseek.co.jp\ninfusionsoft.com\ning.nl\nink361.com\ninman.com\ninmotionhosting.com\ninnfrad.com\ninnotrac.com\ninnshopper.com\ninquirer.net\ninquisitr.com\ninspectionnews.net\ninspectorsjournal.com\ninspsearch.com\ninstacommerce.net\ninstagram.com\ninstituteonline.com\ninstructables.com\ninstructure.com\nintegrativenutrition.com\nintegro.com\nintel.com\ninteractivesoftware.co.uk\ninteria.pl\nintermountainhealthcare.org\ninternetdownloadmanager.com\ninternethaber.com\ninterpark.com\nintervalworld.com\nintoday.in\nintrabench.com\nintuit.com\ninvaluable.com\ninvesting.com\ninvestopedia.com\nioffer.com\nionidea.com\nipko.pl\niqiyi.com\nirctc.co.in\nirecommend.ru\nirei.com\nirem.org\nironplanet.com\nirpopup.ir\nirs.gov\nissuu.com\nistockphoto.com\nit4profit.com\nitasoftware.com\nitau.com.br\nitelligencegroup.com\nitmedia.co.jp\nivanhoecambridge.com\nivc.cn\nivc.com\nivenue.com\nivi.ru\niwillteachyoutoberich.com\niza.ne.jp\nj-cast.com\njabong.com\njagran.com\njal.co.jp\njalan.net\njalopnik.com\njamieoliver.com\njapanpost.jp\njav68.me\njava.com\njavedch.com\njb51.net\njbl.com\njcpenney.com\njcrew.com\njd.com\njet.com\njetairways.com\njetblue.com\njeuxvideo.com\njezebel.com\njia360.com\njimdo.com\njin115.com\njjwxc.net\njma.go.jp\njnj.com\njoann.com\njobrapido.com\njohnlewis.com\njoins.com\njomashop.com\njoneslanglasalle.com\njournaldesfemmes.com\njquery.com\njqw.com\njrj.com.cn\njsfiddle.net\njugem.jp\njumeirah.com\njumia.com.ng\njunbi-tracker.com\njustdial.com\njw.org\nk618.cn\nkaiserpermanente.org\nkakaku.com\nkana.com\nkao.com\nkapook.com\nkapre.com\nkaskus.co.id\nkaspersky.com\nkat.cr\nkay.com\nkayak.com\nkbb.com\nkddi.com\nkdnet.net\nkeenfootwear.com\nkeepvid.com\nkenh14.vn\nkenwood.com\nkevinmd.com\nkhabaronline.ir\nkhanacademy.org\nkicker.de\nkickstarter.com\nkidshealth.org\nkidspot.com.au\nkienthuc.net.vn\nkijiji.ca\nkimberamerica.com\nkinja.com\nkinogo.co\nkinopoisk.ru\nkinox.to\nkinozal.tv\nkiplinger.com\nkissanime.to\nkizi.com\nkizlarsoruyor.com\nklikbca.com\nklipsch.com\nklm.com\nkmart.com\nkmp.co.uk\nkohls.com\nkompas.com\nkompasiana.com\nkonga.com\nkongregate.com\nkooora.com\nkorabia.com\nkoreanair.com\nkotaku.com\nkouclo.com\nkp.ru\nkraftrecipes.com\nksl.com\nkt9267.com\nkuronekoyamato.co.jp\nkuwo.cn\nkyosho.com\nla-z-boy.com\nlabanquepostale.fr\nlabcorp.com\nlabtestsonline.org\nlacaixa.es\nlady8844.com\nlagaceta.com.ar\nlamoda.ru\nlan.com\nlanacion.com.ar\nlancome-usa.com\nlandcentral.com\nlandflip.com\nlandlords.org.uk\nlandsend.com\nlandwatch.com\nlapatilla.com\nlaposte.net\nlaredoute.fr\nlast.fm\nlastpass.com\nlaterooms.com\nlatimes.com\nlayalina.com\nlazada.co.id\nldblog.jp\nlds.com\nlds.org\nleadzupc.com\nleagueoflegends.com\nleboncoin.fr\nlefigaro.fr\nlegacy.com\nlego.com\nlemonde.fr\nlenovo.com\nlenscrafters.com\nlenta.ru\nleo.org\nlequipe.fr\nletv.com\nlg.com\nlibero.it\nlifebuzz.com\nlifehack.org\nlifehacker.com\nlifenews.ru\nlightinthebox.com\nlightwellinc.com\nlikemag.com\nlikes.com\nlindaikejisblog.com\nline.me\nlinkedin.com\nlinkwithin.com\nlinternaute.com\nliputan6.com\nliquor.com\nlist-manage.com\nlist-manage1.com\nlittlethings.com\nlive.com\nliveadexchanger.com\nlivedoor.biz\nlivedoor.com\nlivedoor.jp\nlivehelper.com\nliveinternet.ru\nlivejournal.com\nliveleak.com\nliveperson.net\nlivescore.com\nlivestrong.com\nlivetv.sx\nlivingsocial.com\nllbean.com\nlloydsbank.co.uk\nloading-delivery2.com\nlockerdome.com\nlogicsoftware.co.uk\nlogitech.com\nlohaco.jp\nlolesports.com\nlonelyplanet.com\nlongchamp.com\nloopnet.com\nlordandtaylor.com\nlorealparisusa.com\nlostfilm.tv\nlot.com\nlotterypost.com\nlowensign.com\nlowes.com\nltn.com.tw\nlufthansa.com\nlululemon.com\nlun.com\nluxtarget.com\nluxuryhomemarketing.com\nluxuryhomes.com\nluxuryrealestate.com\nlvmama.com\nlynda.com\nmackeeper.com\nmackolik.com\nmacromill.com\nmacrumors.com\nmacys.com\nmade-in-china.com\nmail.com\nmail.ru\nmailchimp.com\nmainichi.jp\nmakeleio.gr\nmakemytrip.com\nmakeupalley.com\nmakeuseof.com\nmama.cn\nmangafox.me\nmangahere.co\nmangareader.net\nmango.com\nmanoramaonline.com\nmanta.com\nmanualsonline.com\nmanufacturing.net\nmapquest.com\nmapsofindia.com\nmarc-o-polo.com\nmarca.com\nmarketgid.com\nmarketwatch.com\nmarksandspencer.com\nmarktplaats.nl\nmarmiton.org\nmarmot.com\nmarriott.com\nmarthastewart.com\nmarykay.com\nmashable.com\nmashreghnews.ir\nmasralarabia.com\nmasrawy.com\nmassageenvy.com\nmatch.com\nmattel.com\nmaxtalk.com\nmaybank2u.com.my\nmaybelline.com\nmayoclinic.org\nmbank.pl\nmbc.net\nmcafee.com\nmcfadyen.com\nmckesson.com\nmckissock.com\nmdanderson.org\nmeaww.com\nmeb.gov.tr\nmec.gov.br\nmediafire.com\nmediaplex.com\nmediaset.it\nmedicare.gov\nmedicinenet.com\nmedium.com\nmedscape.com\nmeetup.com\nmega.co.nz\nmega.nz\nmegapopads.com\nmeituan.com\nmelia.com\nmemecats.com\nmensfitness.com\nmenshealth.co.uk\nmenshealth.com\nmentalfloss.com\nmeowshare.com\nmercadolibre.com.ar\nmercadolibre.com.mx\nmercadolibre.com.ve\nmercadolivre.com.br\nmercola.com\nmerdeka.com\nmergent.com\nmerriam-webster.com\nmessenger.com\nmesteel.com\nmetacritic.com\nmeteofrance.com\nmetro.co.uk\nmetroer.com\nmetrolyrics.com\nmetrotvnews.com\nmgid.com\nmheducation.com\nmi.com\nmic.com\nmicrosoft.com\nmicrosoftonline.com\nmicrosoftstore.com\nmidwayusa.com\nmihanblog.com\nmikeferry.com\nmilanuncios.com\nmileroticos.com\nmilitary.com\nmillersamuel.com\nmilliyet.com.tr\nminecraft.net\nminiclip.com\nmint.com\nmirror.co.uk\nmirtesen.ru\nmisr5.com\nmit.edu\nmiui.com\nmixi.jp\nmizuhobank.co.jp\nmlb.com\nmobafire.com\nmobfactory.info\nmobikwik.com\nmobile.de\nmobile01.com\nmobile9.com\nmodcloth.com\nmomoshop.com.tw\nmonarch.co.uk\nmoneycontrol.com\nmoneysavingexpert.com\nmoneytalksnews.com\nmonotaro.com\nmonster.com\nmontblanc.com\nmontgomerycountymd.gov\nmos.ru\nmoseley.org\nmotherearthnews.com\nmotortrend.com\nmoudamepo.com\nmountainhardwear.com\nmouthshut.com\nmoveandstay.com\nmovoto.com\nmoz.com\nmozilla.org\nmr-johal.com\nmskcc.org\nmsn.com\nmsnbc.com\nmts.ru\nmtsindia.in\nmtv.com\nmufg.jp\nmultihousingnews.com\nmundo.com\nmundosexanuncio.com\nmunrvscurlms.com\nmusica.com\nmusiciansfriend.com\nmvideo.ru\nmxttrf.com\nmy-hit.org\nmyanimelist.net\nmydala.com\nmydomainadvisor.com\nmydrivers.com\nmyfico.com\nmyfitnesspal.com\nmynavi.jp\nmynet.com\nmypearson.com\nmyshopify.com\nmysmartprice.com\nmyspace.com\nmysql.com\nmystart.com\nmystartsearch.com\nmyway.com\nn-tv.de\nn11.com\nn121adserv.com\nnaahq.org\nnachi.org\nnacion.com\nnahi.org\nnaiglobal.com\nnaiop.org\nnairaland.com\nnamecheap.com\nnametests.com\nnamu.wiki\nnarod.ru\nnarpm.org\nnasa.gov\nnat.com\nnate.com\nnational-lottery.co.uk\nnationalgeographic.com\nnationalmssociety.org\nnaturallycurly.com\nnature.com\nnaukri.com\nnautica.com\nnaver.com\nnaver.jp\nnavyfederal.org\nnba.com\nnbc.com\nnbcnews.com\nnbcsports.com\nncl.com\nncsha.org\nndtv.com\nneimanmarcus.com\nnejm.org\nneobux.com\nnesn.com\nnespresso.com\nnet-a-porter.com\nnetcasters.com\nnetdna-cdn.com\nnetdoctor.co.uk\nnetflix.com\nnetpartnering.com\nnetshoes.com.br\nnetsuite.com\nnetteller.com\nnetx.net\nnewbalance.com\nnewegg.com\nnewhomesdirectory.com\nnews-us.jp\nnews.com.au\nnews24.com\nnewsmth.net\nnewsnow.co.uk\nnewtab-tv.com\nnewyorker.com\nnextag.com\nnextdoor.com\nnexternal.com\nnextmedia.com\nnexusmods.com\nnfl.com\nngacn.cc\nnguoiduatin.vn\nnguyentandung.org\nnhadatso.com\nnhk.or.jp\nnhl.com\nnicovideo.jp\nnifty.com\nnih.gov\nniiz.info\nnike.com\nnikkansports.com\nnikkei.com\nnikkeibp.co.jp\nning.com\nnipic.com\nnitroflare.com\nniuche.com\nnj.com\nnlihc.org\nnmhc.org\nnmisr.com\nnnm-club.me\nnoaa.gov\nnocookie.net\nnomadicmatt.com\nnordictrack.com\nnordstrom.com\nnorton.com\nnosub.tv\nnouvelobs.com\nnovinky.cz\nnownews.com\nnowtv.de\nnowvideo.sx\nnpr.org\nnreionline.com\nnrk.no\nns5n.com\nnta.go.jp\nntdtv.com\nnttdocomo.co.jp\nnu.nl\nnuance.com\nnur.kz\nnutrition.org\nny.gov\nnyaa.eu\nnyaa.se\nnydailynews.com\nnymag.com\nnypost.com\nnyrei.com\nnytimes.com\no2.pl\noakley.com\noakwood.com\nobeo.com\nocn.ne.jp\noeeee.com\noffice.com\noffice365.com\nofficedepot.com\nok.ru\nokcupid.com\nokezone.com\nokta.com\nokwave.jp\noldnavy.com\noldrepublictitle.com\nolx.co.id\nolx.com.br\nolx.in\nolx.kz\nolx.pl\nolx.ro\nolx.ua\nomegawatches.com\nonclickads.net\nonclicktop.com\nonedio.com\noneindia.com\nonestopclick.com\nonet.pl\nonetravel.com\nonkyo.com\nonline-convert.com\nonlinecreditcenter6.com\nonlinekhabar.com\nonliner.by\nonlinesbi.com\nontests.me\nopencrm.co.uk\nopendns.com\nopenload.co\nopensooq.com\nopensubtitles.org\nopentable.com\noracle.com\norange.fr\norbitz.com\norf.at\norientaltrading.com\norigin.com\noschina.net\nosha.gov\notomoto.pl\notto.de\nouo.io\noutbrain.com\nouteredgeuk.com\nover-blog.com\noverdrive.com\noverstock.com\nownersdirect.co.uk\noyaide.com\nozon.ru\np30download.com\np5w.net\npadsdel.com\npagesjaunes.fr\npaidverts.com\npampers.com\npanasonic.com\npanasonic.jp\npanda.tv\npandora.com\npandora.net\npanet.co.il\npantip.com\npapajohns.com\nparenting.com\nparents.com\npartners.org\npastebin.com\npatagonia.com\npatch.com\npatheos.com\npatient.info\npaulaschoice.com\npayoneer.com\npaypal.com\npayseal.com\npaytm.com\npaytm.in\npbs.org\npcadvisor.co.uk\npcgamer.com\npch.com\npchome.com.tw\npchome.net\npcmag.com\npcworld.com\npearsoncmg.com\npelis24.com\npendleton-usa.com\npeople.com\nperfect.com\nperiscope.tv\npersianblog.ir\npetco.com\npetmd.com\npetsafe.net\npetsmart.com\npex.jp\npeyvandha.ir\npeza.gov.ph\nphilippineairlines.com\nphilips.com\nphoenixads.co.in\nphonearena.com\nphotobucket.com\nphotoplan.co.uk\nphotosigns.com.au\nphp.net\npiac.com.pk\npicmonkey.com\npicofile.com\npier1.com\npikabu.ru\npillsbury.com\npinimg.com\npinterest.com\npioneerelectronics.com\npiriform.com\npissedconsumer.com\npitchfork.com\npixabay.com\npixelplanet.com\npixiv.net\npixlr.com\npixnet.net\npizzahut.com\nplanetecomsolutions.com\nplanetromeo.com\nplannedparenthood.org\nplarium.com\nplay1topgame.com\nplaystation.com\nplurk.com\nplurotech.com\nplymouth.ac.uk\npnc.com\npo.st\npochta.ru\npof.com\npogo.com\npolar.com\npole-emploi.fr\npolitico.com\npolyvore.com\nponparemall.com\npopads.net\npopcash.net\npopmaster.ir\npopmyads.com\npopped.biz\npopsugar.com\npopundertotal.com\nportplus.com\npostbank.de\nposte.it\npostimg.org\npotterybarn.com\npowerball.com\nppomppu.co.kr\npps.tv\nprana.com\npredictivadvertising.com\npremierleague.com\npretty52.com\nprevention.com\nprezi.com\npricegrabber.com\npriceline.com\npriceminister.com\nprimewire.ag\nprincess.com\nprinciplevaluation.com\nprivatbank.ua\nprivateislandsonline.com\nprntscr.com\nproboards.com\nprodapt.com\nprogramme-tv.net\nprojectfreetv.so\nprologis.com\nprom.ua\npronto.com\npropertypanorama.com\nproschools.com\nprospectsoft.com\nprotect0r.com\nprothom-alo.com\nprotothema.gr\nprou.net\nprovidence.org\nprpops.com\npsu.edu\npsychologytoday.com\nptt.cc\npubcon.com\npudelek.pl\npurdue.edu\npureadexchange.com\nputlocker.is\npython.org\nqantas.com.au\nqatarairways.com\nqidian.com\nqingdaonews.com\nqiwi.com\nqq.com\nqualtrics.com\nquanjing.com\nquikr.com\nquiksilver.com\nquizlet.com\nquora.com\nqvc.com\nqz.com\nr10.net\nrabobank.nl\nrackcdn.com\nrackspace.com\nradikal.com.tr\nrakuten-bank.co.jp\nrakuten-card.co.jp\nrakuten.co.jp\nrakuten.com\nrakuten.ne.jp\nralphlauren.com\nrambler.ru\nranker.com\nrapidgator.net\nrappler.com\nrarbg.to\nravelry.com\nrbc.ru\nrbcroyalbank.com\nrci.com\nrdsa2012.com\nrealclearpolitics.com\nrealestate.com.au\nrealestatece.com\nrealestateexpress.com\nrealestatetomato.com\nrealogy.com\nrealsimple.com\nrealstar.ca\nrealtor.com\nrealtourvision.com\nrealtyexecutives.com\nrealtytimes.com\nrebac.net\nreclameaqui.com.br\nredappleapartments.com\nredbox.com\nredbubble.com\nredcrossblood.org\nreddit.com\nredfin.com\nrediff.com\nredirectvoluum.com\nrednet.cn\nreduxmediia.com\nredwingshoes.com\nreference.com\nrefinery29.com\nrei.com\nreimageplus.com\nreis.com\nrejournals.com\nrelated.com\nremax.com\nremedi.com\nremington.com\nremonline.com\nrenren.com\nrentalsite.com\nrepelis.tv\nrepubblica.it\nrepublika.co.id\nresearchgate.net\nresellerratings.com\nresultados-futbol.com\nretailmenot.com\nreuters.com\nrevacomm.com\nreverb.com\nreverso.net\nrfptemplates.org\nria.ru\nricksteves.com\nricoh-usa.com\nrightmove.co.uk\nrimanews.com\nripoffreport.com\nrismedia.com\nrivals.com\nroblox.com\nrockanddirt.com\nrocketnews24.com\nrockport.com\nrockwellinstitute.com\nrodalesorganiclife.com\nroku.com\nrolex.com\nrollingstone.com\nrottentomatoes.com\nroughguides.com\nroyalbank.com\nroyalcaribbean.com\nrozblog.com\nrozetka.com.ua\nrr.com\nrrbonlinereg.in\nrrbonlinereg.net\nrt.com\nruger.com\nrusbiz.com\nrush.edu\nruten.com.tw\nrutracker.org\nrutube.ru\nrxlist.com\nryanair.com\nsabah.com.tr\nsabq.org\nsahibinden.com\nsaisoncard.co.jp\nsaksfifthavenue.com\nsakura.ne.jp\nsalesforce.com\nsalespage.com\nsalon.com\nsamhsa.gov\nsammydress.com\nsamplicio.us\nsamsclub.com\nsamsung.com\nsankei.com\nsanook.com\nsanspo.com\nsantander.co.uk\nsap.com\nsapient.com\nsapo.pt\nsaramin.co.kr\nsasontnwc.net\nsaudiairlines.com\nsavefrom.net\nsaveur.com\nsavills.com\nsavingstar.com\nsberbank.ru\nsbisec.co.jp\nsbnation.com\nscarymommy.com\nscholastic.com\nschwab.com\nsciencedirect.com\nscoop.it\nscoopwhoop.com\nscotiabank.com\nscribd.com\nsdsu.edu\nsearchalgo.com\nsearchengineland.com\nsearchlock.com\nsears.com\nseasonvar.ru\nseatguru.com\nsecureinternetbank.com\nsecureserver.net\nseek.com.au\nseekingalpha.com\nseesaa.net\nselectleaders.com\nself.com\nseniorsrealestate.com\nsennheiser.com\nsephora.com\nserverfault.com\nserving-sys.com\nsesconference.com\nsetn.com\nseznam.cz\nsfgate.com\nsfr.fr\nsh.st\nshaadi.com\nshahrekhabar.com\nshangri-la.com\nshaparak.ir\nshareasale.com\nsharecare.com\nsharepoint.com\nsharpusa.com\nshasha.ps\nshermanstravel.com\nshimano.com\nshine.com\nshiseido.co.jp\nshoecarnival.com\nshop-pro.jp\nshopathome.com\nshopbop.com\nshopclues.com\nshopify.com\nshopping.com\nshoutmeloud.com\nshufoo.net\nshutterfly.com\nshutterstock.com\nsi.com\nsierratradingpost.com\nsigsauer.com\nsimilarweb.com\nsimplyrecipes.com\nsina.com.cn\nsinaimg.cn\nsindonews.com\nsiriusxm.com\nsiteadvisor.com\nsitepoint.com\nskroutz.gr\nsky.com\nskycn.com\nskype.com\nskyscanner.com\nskyscanner.net\nskysports.com\nslack.com\nslashdot.org\nslate.com\nslickdeals.net\nslideshare.net\nslidesharecdn.com\nslimspots.com\nslopeaota.com\nsmallpdf.com\nsmallseotools.com\nsmartnewtab.com\nsmartshopping.com\nsmbc-card.com\nsmh.com.au\nsmi2.ru\nsmith-wesson.com\nsmittenkitchen.com\nsmzdm.com\nsnapdeal.com\nsnopes.com\nso-net.ne.jp\nso.com\nsocialsecurity.gov\nsocietegenerale.fr\nsoftbank.jp\nsoftcare.com\nsoftonic.com\nsoftpedia.com\nsoftwarefolks.com\nsogou.com\nsohu.com\nsolarmovie.ph\nsonos.com\nsony.com\nsony.jp\nsonyentertainmentnetwork.com\nsopitas.com\nsoso.com\nsothebysrealty.com\nsou300.com\nsoundcloud.com\nsouq.com\nsourceforge.net\nsouthernliving.com\nsouthwest.com\nsozcu.com.tr\nsp.gov.br\nspaceshipads.com\nspanishdict.com\nspecialized.com\nspeedtest.net\nspicejet.com\nspiegel.de\nspine-health.com\nspirit.com\nsponichi.co.jp\nsport1.de\nsports.ru\nsportsauthority.com\nsportsdirect.com\nsportsmansguide.com\nspotify.com\nspringer.com\nspringfield-armory.com\nsprint.com\nspscommerce.com\nsputniknews.com\nsq.cn\nsquare-enix.com\nsquarespace.com\nsquaretrade.com\nsquareup.com\nsquiz.net\nsram.com\nsrilankan.com\nsrv2trking.com\nssa.gov\nssisurveys.com\nstackexchange.com\nstackoverflow.com\nstandardmedia.co.ke\nstanford.edu\nstaples.com\nstarbucks.com\nstarsports.com\nstartimes.com\nstarwoodhotels.com\nstatcounter.com\nstate.gov\nstate.tx.us\nstaticflickr.com\nstaticwebdom.com\nsteamcommunity.com\nsteampowered.com\nsterlingcommerce.com\nstjude.org\nstockstar.com\nstokke.com\nstorebuilder.co.uk\nstorefrontconsulting.com\nstraightsell.com.au\nstrava.com\nstreamcloud.eu\nstreamin.to\nstubhub.com\nstudentdoctor.net\nstudentuniverse.com\nstudy.com\nstuff.tv\nstumbleupon.com\nstyleforum.net\nsuara.com\nsubito.it\nsubmarino.com.br\nsubscene.com\nsueddeutsche.de\nsugarcrm.com\nsulekha.com\nsunbeltnetwork.com\nsuning.com\nsunset.com\nsuntrust.com\nsuper.cz\nsuperuser.com\nsurplusglobal.com\nsuruga-ya.jp\nsurveymonkey.com\nsutterhealth.org\nsuumo.jp\nsuunto.com\nsvmsolutions.com\nswagbucks.com\nswarovski.com\nswatch.com\nsweetwater.com\nswiss.com\nsynchronycredit.com\nsyosetu.com\nszn.cz\nsznews.com\nt-mobile.com\nt-online.de\nt.co\nt411.in\ntabelog.com\ntabnak.ir\ntaboola.com\ntagged.com\ntagheuer.com\ntagonline.com\ntahrirnews.com\ntaimienphi.vn\ntakungpao.com\ntaleo.net\ntalk.tw\ntamiya.com\ntaobao.com\ntarget.com\ntaringa.net\ntarladalal.com\ntasify.com\ntattoodo.com\ntaxact.com\ntd.com\ntdbank.com\nteacherspayteachers.com\nteamviewer.com\ntebyan.net\ntechbang.com\ntechcrunch.com\ntechindia.com\ntechradar.com\ntechtarget.com\ntechtudo.com.br\nted.com\nteepr.com\ntelegraaf.nl\ntelegraf.com.ua\ntelegrafi.com\ntelegram.me\ntelegram.org\ntelegraph.co.uk\ntelekom.com\ntemplatemonster.com\ntempo.co\ntenki.jp\nterra.com.br\nterraclicks.com\ntesco.com\ntf1.fr\ntgbus.com\nthaiairways.com\ntheatlantic.com\ntheblaze.com\nthechive.com\nthedailybeast.com\nthefreedictionary.com\ntheguardian.com\nthehill.com\nthehindu.com\nthekitchn.com\nthekrazycouponlady.com\ntheladbible.com\nthelancet.com\nthemeforest.net\nthemoneyconverter.com\nthemovechannel.com\nthenextweb.com\nthenorthface.com\ntheonion.com\nthepennyhoarder.com\nthepiratebay.se\nthesaurus.com\nthesimpledollar.com\nthesportbible.com\nthestreet.com\ntheverge.com\nthevideo.me\nthewatchseries.to\ntheweathernetwork.com\nthewhizmarketing.com\nthisav.com\nthomascook.com\nthriftyfun.com\nthrillist.com\ntianya.cn\nticketmaster.com\nticketmonster.co.kr\ntigerdirect.com\ntilestwra.com\ntim.it\ntimberland.com\ntime.com\ntimeanddate.com\ntimeout.com\ntimewarnercable.com\ntinypic.com\ntirerack.com\ntiscali.it\ntistory.com\ntitleonecorp.com\ntiu.ru\ntlbb8.com\ntmall.com\ntmz.com\nto8to.com\ntoday.com\ntodayhumor.co.kr\ntogetter.com\ntokopedia.com\ntomsguide.com\ntomshardware.com\ntomtop.com\ntonyrobbins.com\ntop10homeremedies.com\ntopix.com\ntoptenreviews.com\ntoranoana.jp\ntorcache.net\ntorrentkim1.net\ntorrentz.com\ntorrentz.eu\ntorrentz.in\ntoshiba.com\ntotalbeauty.com\ntourfactory.com\ntoyokeizai.net\ntoysrus.com\ntpmco.com\ntrackingclick.net\ntrackvoluum.com\ntradeadexchange.com\ntradedoubler.com\ntrademe.co.nz\ntraffichunt.com\ntrafficmonsoon.com\ntrafficserving.com\ntraidnt.net\ntravelandleisure.com\ntravelocity.com\ntravelzoo.com\ntraxxas.com\ntreasury.gov\ntrekbikes.com\ntrello.com\ntribunnews.com\ntrinityinsight.com\ntripadvisor.co.uk\ntripadvisor.com\ntripadvisor.in\ntriseptsolutions.com\ntriumph.com\ntrklnks.com\ntrkute.com\ntruecaller.com\ntrulia.com\ntruste.com\ntrustpilot.com\ntsite.jp\ntubepatrol.net\ntudou.com\ntumblr.com\ntums.ac.ir\ntunein.com\nturbobit.net\nturkishairlines.com\ntut.by\ntutorialspoint.com\ntutsplus.com\ntv.com\ntvguide.com\ntvn24.pl\ntwimg.com\ntwitch.tv\ntwitter.com\ntwoo.com\ntxxx.com\ntypepad.com\nuber.com\nubergizmo.com\nubi.com\nubuntu.com\nucla.edu\nucoz.ru\nudacity.com\nudemy.com\nudn.com\nukr.net\nulmart.ru\nulta.com\nultimate-guitar.com\numblr.com\numich.edu\nunam.mx\nunblocked.li\nunderarmour.com\nunicredit.it\nunikron.com\nunilever.com\nuniqlo.com\nuniquehomes.com\nunited.com\nunity3d.com\nuol.com.br\nuploaded.net\nupmc.com\nupornia.com\nuproxx.com\nups.com\nuptobox.com\nuptodown.com\nupwork.com\nurbandictionary.com\nurbanoutfitters.com\nurdupoint.com\nusaa.com\nusatoday.com\nusbank.com\nuserscloud.com\nusmagazine.com\nusnews.com\nusps.com\nustream.tv\nutexas.edu\nutorrent.com\nuzone.id\nv1.cn\nvacationstogo.com\nvagalume.com.br\nvaned.com\nvanguard.com\nvariety.com\nvarzesh3.com\nvcommission.com\nvector.co.jp\nvegas.com\nvegrecipesofindia.com\nvendormanagedinventory.com\nvenere.com\nvente-privee.com\nventuread.com\nverizon.com\nverizonwireless.com\nvesti.ru\nvetogate.com\nvg.no\nviamichelin.com\nvice.com\nvictoriassecret.com\nvid.me\nvideodownloadconverter.com\nvideomega.tv\nvideoyoum7.com\nvidto.me\nvidzi.tv\nvikingrivercruises.com\nvimeo.com\nvine.co\nvip.com\nviralands.com\nviralthread.com\nvirgilio.it\nvirgin-atlantic.com\nvirginaustralia.com\nvirginmedia.com\nvirtualtourist.com\nvisahq.com\nvistaprint.com\nvisualtour.com\nvitacost.com\nvitals.com\nvitamix.com\nviva.co.id\nviviun.com\nvk.com\nvk.me\nvmware.com\nvnexpress.net\nvoc.com.cn\nvodafone.in\nvodlocker.com\nvoluumtrk.com\nvox.com\nvrbo.com\nvsuch.com\nvulture.com\nw3.org\nw3schools.com\nwalgreens.com\nwalmart.com\nwanyh.com\nwargaming.net\nwarmportrait.com\nwarriorforum.com\nwashington.edu\nwashingtonpost.com\nwatchfree.to\nwatchseries.li\nwattpad.com\nwav.tv\nway2sms.com\nwayfair.com\nwbresearch.com\nwcr.org\nweather.com\nweather.com.cn\nweather.gov\nweathernews.jp\nweb.de\nwebcrawler.com\nweber.com\nwebex.com\nwebjaguar.com\nwebkinz.com\nweblio.jp\nwebmd.com\nwebmoney.ru\nwebmonkey.com\nwebpro.com\nwebssearches.com\nwebsta.me\nwebtretho.com\nweebly.com\nweheartit.com\nweibo.com\nweightwatchers.com\nwellness.com\nwellsfargo.com\nwelt.de\nwestelm.com\nwesternjournalism.com\nwestjet.com\nwestmonroepartners.com\nwetalk.tw\nwetransfer.com\nwetter.com\nwgsn.com\nwhat-character-are-you.com\nwhatsapp.com\nwhfoods.com\nwhirlpool.com\nwhitecapcanada.com\nwhitepages.com\nwho.int\nwho.is\nwholesalesuppliesplus.com\nwikia.com\nwikihow.com\nwikimedia.org\nwikipedia.org\nwikispaces.com\nwikitravel.org\nwikivoyage.org\nwikiwiki.jp\nwiktionary.org\nwildberries.ru\nwiley.com\nwilliamhill.com\nwilliams-sonoma.com\nwindows.com\nwindows.net\nwingtaiasia.com.sg\nwiocha.pl\nwipro.com\nwired.com\nwisc.edu\nwish.com\nwitiger.com\nwittyfeed.com\nwix.com\nwmaraci.com\nwmpoweruser.com\nwnd.com\nwolframalpha.com\nwoot.com\nwordpress.com\nwordpress.org\nwordreference.com\nworkercn.cn\nworkingre.com\nworldfirst.com\nworldlifestyle.com\nworldmarket.com\nworldoftanks.ru\nworldstarhiphop.com\nworldweb.com\nworldwise.net\nwotif.com\nwowhead.com\nwp.com\nwp.pl\nwpbeginner.com\nwsj.com\nwunderground.com\nwunderlist.com\nwuxiaworld.com\nwwe.com\nwww.gov.uk\nwww.nhs.uk\nwyborcza.pl\nwyndham.com\nx-rates.com\nxbox.com\nxda-developers.com\nxe.com\nxerox.com\nxfinity.com\nxiami.com\nxiaomi.com\nxing.com\nxinhuanet.com\nxkcd.com\nxl415.com\nxmediaserve.com\nxn--igbhe7b5a3d5a.com\nxoriant.com\nxuite.net\nxunlei.com\nxywy.com\ny8.com\nyadi.sk\nyahoo-mbga.jp\nyahoo.co.jp\nyahoo.com\nyallakora.com\nyam.com\nyandex.by\nyandex.com\nyandex.com.tr\nyandex.kz\nyandex.ru\nyandex.ua\nyaolan.com\nyaplakal.com\nyaraon-blog.com\nyellowpages.com\nyelp.com\nyenisafak.com\nyesky.com\nyhd.com\nyjc.ir\nynet.co.il\nyodobashi.com\nyomiuri.co.jp\nyoo.com\nyoox.com\nyouboy.com\nyoudao.com\nyouku.com\nyoum7.com\nyouneedabudget.com\nyouradexchange.com\nyourdictionary.com\nyouth.cn\nyoutube-mp3.org\nyoutube.com\nyr.no\nytimg.com\nyts.ag\nzalando.de\nzalukaj.tv\nzaman.com.tr\nzappos.com\nzara.com\nzazzle.com\nzdf.de\nzdnet.com\nzedo.com\nzeit.de\nzendesk.com\nzergnet.com\nzerohedge.com\nzeroredirect1.com\nzhaopin.com\nzhihu.com\nzillow.com\nzimbio.com\nzimuzu.tv\nzing.vn\nzippyshare.com\nzocdoc.com\nzoho.com\nzol.com.cn\nzomato.com\nzone-telechargement.com\nzoopla.co.uk\nzougla.gr\nzozo.jp\nzulily.com\nzwaar.net\nzybang.com\n".
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-trim().split("\n"));
deleted file mode 100644
--- a/browser/extensions/loop/chrome/content/modules/LoopRooms.jsm
+++ /dev/null
@@ -1,1268 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-"use strict";var _slicedToArray = function () {function sliceIterator(arr, i) {var _arr = [];var _n = true;var _d = false;var _e = undefined;try {for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {_arr.push(_s.value);if (i && _arr.length === i) break;}} catch (err) {_d = true;_e = err;} finally {try {if (!_n && _i["return"]) _i["return"]();} finally {if (_d) throw _e;}}return _arr;}return function (arr, i) {if (Array.isArray(arr)) {return arr;} else if (Symbol.iterator in Object(arr)) {return sliceIterator(arr, i);} else {throw new TypeError("Invalid attempt to destructure non-iterable instance");}};}();function _toConsumableArray(arr) {if (Array.isArray(arr)) {for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {arr2[i] = arr[i];}return arr2;} else {return Array.from(arr);}}var _Components = 
-
-Components;var Cu = _Components.utils;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/Task.jsm");
-Cu.import("resource://gre/modules/Timer.jsm");var _Cu$import = 
-
-Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});var MozLoopService = _Cu$import.MozLoopService;var LOOP_SESSION_TYPE = _Cu$import.LOOP_SESSION_TYPE;
-XPCOMUtils.defineLazyModuleGetter(this, "Promise", 
-"resource://gre/modules/Promise.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils", 
-"resource://services-common/utils.js");
-XPCOMUtils.defineLazyModuleGetter(this, "WebChannel", 
-"resource://gre/modules/WebChannel.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "eventEmitter", function () {var _Cu$import2 = 
-  Cu.import("resource://devtools/shared/event-emitter.js", {});var EventEmitter = _Cu$import2.EventEmitter;
-  return new EventEmitter();});
-
-
-XPCOMUtils.defineLazyModuleGetter(this, "DomainWhitelist", 
-"chrome://loop/content/modules/DomainWhitelist.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "LoopRoomsCache", 
-"chrome://loop/content/modules/LoopRoomsCache.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "loopUtils", 
-"chrome://loop/content/modules/utils.js", "utils");
-XPCOMUtils.defineLazyModuleGetter(this, "loopCrypto", 
-"chrome://loop/content/shared/js/crypto.js", "LoopCrypto");
-XPCOMUtils.defineLazyModuleGetter(this, "ObjectUtils", 
-"resource://gre/modules/ObjectUtils.jsm");
-
-/* exported LoopRooms, roomsPushNotification */
-
-this.EXPORTED_SYMBOLS = ["LoopRooms", "roomsPushNotification"];
-
-// The maximum number of clients that we support currently.
-var CLIENT_MAX_SIZE = 2;
-
-// Wait at least 5 seconds before doing opportunistic encryption.
-var MIN_TIME_BEFORE_ENCRYPTION = 5 * 1000;
-// Wait at maximum of 30 minutes before doing opportunistic encryption.
-var MAX_TIME_BEFORE_ENCRYPTION = 30 * 60 * 1000;
-// Wait time between individual re-encryption cycles (1 second).
-var TIME_BETWEEN_ENCRYPTIONS = 1000;
-
-// This is the pref name for the url of the standalone pages.
-var LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
-
-var roomsPushNotification = function roomsPushNotification(version, channelID) {
-  return LoopRoomsInternal.onNotification(version, channelID);};
-
-
-// Since the LoopRoomsInternal.rooms map as defined below is a local cache of
-// room objects that are retrieved from the server, this is list may become out
-// of date. The Push server may notify us of this event, which will set the global
-// 'dirty' flag to TRUE.
-var gDirty = true;
-// Global variable that keeps track of the currently used account.
-var gCurrentUser = null;
-// Global variable that keeps track of the room cache.
-var gRoomsCache = null;
-// Global variable that keeps track of the link clicker channel.
-var gLinkClickerChannel = null;
-// Global variable that keeps track of in-progress getAll requests.
-var gGetAllPromise = null;
-
-/**
- * Extend a `target` object with the properties defined in `source`.
- *
- * @param {Object} target The target object to receive properties defined in `source`
- * @param {Object} source The source object to copy properties from
- */
-var extend = function extend(target, source) {var _iteratorNormalCompletion = true;var _didIteratorError = false;var _iteratorError = undefined;try {
-    for (var _iterator = Object.getOwnPropertyNames(source)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {var key = _step.value;
-      target[key] = source[key];}} catch (err) {_didIteratorError = true;_iteratorError = err;} finally {try {if (!_iteratorNormalCompletion && _iterator.return) {_iterator.return();}} finally {if (_didIteratorError) {throw _iteratorError;}}}
-
-  return target;};
-
-
-/**
- * Checks whether a participant is already part of a room.
- *
- * @see https://wiki.mozilla.org/Loop/Architecture/Rooms#User_Identification_in_a_Room
- *
- * @param {Object} room        A room object that contains a list of current participants
- * @param {Object} participant Participant to check if it's already there
- * @returns {Boolean} TRUE when the participant is already a member of the room,
- *                    FALSE when it's not.
- */
-var containsParticipant = function containsParticipant(room, participant) {var _iteratorNormalCompletion2 = true;var _didIteratorError2 = false;var _iteratorError2 = undefined;try {
-    for (var _iterator2 = room.participants[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {var user = _step2.value;
-      if (user.roomConnectionId == participant.roomConnectionId) {
-        return true;}}} catch (err) {_didIteratorError2 = true;_iteratorError2 = err;} finally {try {if (!_iteratorNormalCompletion2 && _iterator2.return) {_iterator2.return();}} finally {if (_didIteratorError2) {throw _iteratorError2;}}}
-
-
-  return false;};
-
-
-/**
- * Compares the list of participants of the room currently in the cache and an
- * updated version of that room. When a new participant is found, the 'joined'
- * event is emitted. When a participant is not found in the update, it emits a
- * 'left' event.
- *
- * @param {Object} room        A room object to compare the participants list
- *                             against
- * @param {Object} updatedRoom A room object that contains the most up-to-date
- *                             list of participants
- */
-var checkForParticipantsUpdate = function checkForParticipantsUpdate(room, updatedRoom) {
-  // Partially fetched rooms don't contain the participants list yet. Skip the
-  // check for now.
-  if (!("participants" in room)) {
-    return;}
-
-
-  var participant = void 0;
-  // Check for participants that joined.
-  var _iteratorNormalCompletion3 = true;var _didIteratorError3 = false;var _iteratorError3 = undefined;try {for (var _iterator3 = updatedRoom.participants[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {participant = _step3.value;
-      if (!containsParticipant(room, participant)) {
-        eventEmitter.emit("joined", room, participant);
-        eventEmitter.emit("joined:" + room.roomToken, participant);}}
-
-
-
-    // Check for participants that left.
-  } catch (err) {_didIteratorError3 = true;_iteratorError3 = err;} finally {try {if (!_iteratorNormalCompletion3 && _iterator3.return) {_iterator3.return();}} finally {if (_didIteratorError3) {throw _iteratorError3;}}}var _iteratorNormalCompletion4 = true;var _didIteratorError4 = false;var _iteratorError4 = undefined;try {for (var _iterator4 = room.participants[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {participant = _step4.value;
-      if (!containsParticipant(updatedRoom, participant)) {
-        eventEmitter.emit("left", room, participant);
-        eventEmitter.emit("left:" + room.roomToken, participant);}}} catch (err) {_didIteratorError4 = true;_iteratorError4 = err;} finally {try {if (!_iteratorNormalCompletion4 && _iterator4.return) {_iterator4.return();}} finally {if (_didIteratorError4) {throw _iteratorError4;}}}};
-
-
-
-
-/**
- * These are wrappers which can be overriden by tests to allow us to manually
- * handle the timeouts.
- */
-var timerHandlers = { 
-  /**
-   * Wrapper for setTimeout.
-   *
-   * @param  {Function} callback The callback function.
-   * @param  {Number}   delay    The delay in milliseconds.
-   * @return {Number}            The timer identifier.
-   */
-  startTimer: function startTimer(callback, delay) {
-    return setTimeout(callback, delay);} };
-
-
-
-/**
- * The Rooms class.
- *
- * Each method that is a member of this class requires the last argument to be a
- * callback Function. MozLoopAPI will cause things to break if this invariant is
- * violated. You'll notice this as well in the documentation for each method.
- */
-var LoopRoomsInternal = { 
-  /**
-   * @var {Map} rooms Collection of rooms currently in cache.
-   */
-  rooms: new Map(), 
-
-  get roomsCache() {
-    if (!gRoomsCache) {
-      gRoomsCache = new LoopRoomsCache();}
-
-    return gRoomsCache;}, 
-
-
-  /**
-   * @var {Object} encryptionQueue  This stores the list of rooms awaiting
-   *                                encryption and associated timers.
-   */
-  encryptionQueue: { 
-    queue: [], 
-    timer: null, 
-    reset: function reset() {
-      this.queue = [];
-      this.timer = null;} }, 
-
-
-
-  /**
-   * Initialises the rooms, sets up the link clicker listener.
-   */
-  init: function init() {
-    Services.prefs.addObserver(LINKCLICKER_URL_PREFNAME, 
-    this.setupLinkClickerListener.bind(this), false);
-
-    this.setupLinkClickerListener();}, 
-
-
-  /**
-   * Sets up a WebChannel listener for the link clicker so that we can open
-   * rooms in the Firefox UI.
-   */
-  setupLinkClickerListener: function setupLinkClickerListener() {
-    // Ensure any existing channel is tidied up.
-    if (gLinkClickerChannel) {
-      gLinkClickerChannel.stopListening();
-      gLinkClickerChannel = null;}
-
-
-    var linkClickerUrl = Services.prefs.getCharPref(LINKCLICKER_URL_PREFNAME);
-
-    // Don't do anything if there's no url.
-    if (!linkClickerUrl) {
-      return;}
-
-
-    var uri = Services.io.newURI(linkClickerUrl, null, null);
-
-    gLinkClickerChannel = new WebChannel("loop-link-clicker", uri);
-    gLinkClickerChannel.listen(this._handleLinkClickerMessage.bind(this));}, 
-
-
-  /**
-   * @var {String} sessionType The type of user session. May be 'FXA' or 'GUEST'.
-   */
-  get sessionType() {
-    return MozLoopService.userProfile ? LOOP_SESSION_TYPE.FXA : 
-    LOOP_SESSION_TYPE.GUEST;}, 
-
-
-  /**
-   * @var {Number} participantsCount The total amount of participants currently
-   *                                 inside all rooms.
-   */
-  get participantsCount() {
-    var count = 0;var _iteratorNormalCompletion5 = true;var _didIteratorError5 = false;var _iteratorError5 = undefined;try {
-      for (var _iterator5 = this.rooms.values()[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {var room = _step5.value;
-        if (room.deleted || !("participants" in room)) {
-          continue;}
-
-        count += room.participants.length;}} catch (err) {_didIteratorError5 = true;_iteratorError5 = err;} finally {try {if (!_iteratorNormalCompletion5 && _iterator5.return) {_iterator5.return();}} finally {if (_didIteratorError5) {throw _iteratorError5;}}}
-
-    return count;}, 
-
-
-  /**
-   * Processes the encryption queue. Takes the next item off the queue,
-   * restarts the timer if necessary.
-   *
-   * Although this is only called from a timer callback, it is an async function
-   * so that tests can call it and be deterministic.
-   */
-  processEncryptionQueue: Task.async(function* () {
-    var roomToken = this.encryptionQueue.queue.shift();
-
-    // Performed in sync fashion so that we don't queue a timer until it has
-    // completed, and to make it easier to run tests.
-    var roomData = this.rooms.get(roomToken);
-
-    if (roomData) {
-      try {
-        // Passing the empty object for roomData is enough for the room to be
-        // re-encrypted.
-        yield LoopRooms.promise("update", roomToken, {});} 
-      catch (error) {
-        MozLoopService.log.error("Upgrade encryption of room failed", error);
-        // No need to remove the room from the list as that's done in the shift above.
-      }}
-
-
-    if (this.encryptionQueue.queue.length) {
-      this.encryptionQueue.timer = 
-      timerHandlers.startTimer(this.processEncryptionQueue.bind(this), TIME_BETWEEN_ENCRYPTIONS);} else 
-    {
-      this.encryptionQueue.timer = null;}}), 
-
-
-
-  /**
-   * Queues a room for encryption sometime in the future. This is done so as
-   * not to overload the server or the browser when we initially request the
-   * list of rooms.
-   *
-   * @param {String} roomToken The token for the room that needs encrypting.
-   */
-  queueForEncryption: function queueForEncryption(roomToken) {
-    if (this.encryptionQueue.queue.indexOf(roomToken) == -1) {
-      this.encryptionQueue.queue.push(roomToken);}
-
-
-    // Set up encryption to happen at a random time later. There's a minimum
-    // wait time - we don't need to do this straight away, so no need if the user
-    // is starting up. We then add a random factor on top of that. This is to
-    // try and avoid any potential with a set of clients being restarted at the
-    // same time and flooding the server.
-    if (!this.encryptionQueue.timer) {
-      var waitTime = (MAX_TIME_BEFORE_ENCRYPTION - MIN_TIME_BEFORE_ENCRYPTION) * 
-      Math.random() + MIN_TIME_BEFORE_ENCRYPTION;
-      this.encryptionQueue.timer = 
-      timerHandlers.startTimer(this.processEncryptionQueue.bind(this), waitTime);}}, 
-
-
-
-  /**
-   * Gets or creates a room key for a room.
-   *
-   * It assumes that the room data is decrypted.
-   *
-   * @param {Object} roomData The roomData to get the key for.
-   * @return {Promise} A promise that is resolved whith the room key.
-   */
-  promiseGetOrCreateRoomKey: Task.async(function* (roomData) {
-    if (roomData.roomKey) {
-      return roomData.roomKey;}
-
-
-    return yield loopCrypto.generateKey();}), 
-
-
-  /**
-   * Encrypts a room key for sending to the server using the profile encryption
-   * key.
-   *
-   * @param {String} key The JSON web key to encrypt.
-   * @return {Promise} A promise that is resolved with the encrypted room key.
-   */
-  promiseEncryptedRoomKey: Task.async(function* (key) {
-    var profileKey = yield MozLoopService.promiseProfileEncryptionKey();
-
-    var encryptedRoomKey = yield loopCrypto.encryptBytes(profileKey, key);
-    return encryptedRoomKey;}), 
-
-
-  /**
-   * Decryptes a room key from the server using the profile encryption key.
-   *
-   * @param  {String} encryptedKey The room key to decrypt.
-   * @return {Promise} A promise that is resolved with the decrypted room key.
-   */
-  promiseDecryptRoomKey: Task.async(function* (encryptedKey) {
-    var profileKey = yield MozLoopService.promiseProfileEncryptionKey();
-
-    var decryptedRoomKey = yield loopCrypto.decryptBytes(profileKey, encryptedKey);
-    return decryptedRoomKey;}), 
-
-
-  /**
-   * Updates a roomUrl to add a new key onto the end of the url.
-   *
-   * @param  {String} roomUrl The existing url that may or may not have a key
-   *                          on the end.
-   * @param  {String} roomKey The new key to put on the url.
-   * @return {String}         The revised url.
-   */
-  refreshRoomUrlWithNewKey: function refreshRoomUrlWithNewKey(roomUrl, roomKey) {
-    // Strip any existing key from the url.
-    roomUrl = roomUrl.split("#")[0];
-    // Now add the key to the url.
-    return roomUrl + "#" + roomKey;}, 
-
-
-  /**
-   * Encrypts room data in a format appropriate to sending to the loop
-   * server.
-   *
-   * @param  {Object} roomData The room data to encrypt.
-   * @return {Promise} A promise that is resolved with an object containing
-   *                   two objects:
-   *                   - encrypted: The encrypted data to send. This excludes
-   *                                any decrypted data.
-   *                   - all: The roomData with both encrypted and decrypted
-   *                          information.
-   *                   If rejected, encryption has failed. This could be due to
-   *                   missing keys for FxA, which this process can't manage. It
-   *                   is generally expected the panel will prompt the user for
-   *                   re-auth if the FxA keys are missing.
-   */
-  promiseEncryptRoomData: Task.async(function* (roomData) {
-    var newRoomData = extend({}, roomData);
-
-    if (!newRoomData.context) {
-      newRoomData.context = {};}
-
-
-    // First get the room key.
-    var key = yield this.promiseGetOrCreateRoomKey(newRoomData);
-
-    newRoomData.context.wrappedKey = yield this.promiseEncryptedRoomKey(key);
-
-    // Now encrypt the actual data.
-    newRoomData.context.value = yield loopCrypto.encryptBytes(key, 
-    JSON.stringify(newRoomData.decryptedContext));
-
-    // The algorithm is currently hard-coded as AES-GCM, in case of future
-    // changes.
-    newRoomData.context.alg = "AES-GCM";
-    newRoomData.roomKey = key;
-
-    var serverRoomData = extend({}, newRoomData);
-
-    // We must not send these items to the server.
-    delete serverRoomData.decryptedContext;
-    delete serverRoomData.roomKey;
-
-    return { 
-      encrypted: serverRoomData, 
-      all: newRoomData };}), 
-
-
-
-  /**
-   * Decrypts room data recevied from the server.
-   *
-   * @param  {Object} roomData The roomData with encrypted context.
-   * @return {Promise} A promise that is resolved with the decrypted room data.
-   */
-  promiseDecryptRoomData: Task.async(function* (roomData) {
-    if (!roomData.context) {
-      return roomData;}
-
-
-    if (!roomData.context.wrappedKey) {
-      throw new Error("Missing wrappedKey");}
-
-
-    var savedRoomKey = yield this.roomsCache.getKey(this.sessionType, roomData.roomToken);
-    var fallback = false;
-    var key = void 0;
-
-    try {
-      key = yield this.promiseDecryptRoomKey(roomData.context.wrappedKey);} 
-    catch (error) {
-      // If we don't have a key saved, then we can't do anything.
-      if (!savedRoomKey) {
-        throw error;}
-
-
-      // We failed to decrypt the room key, so has our FxA key changed?
-      // If so, we fall-back to the saved room key.
-      key = savedRoomKey;
-      fallback = true;}
-
-
-    var decryptedData = yield loopCrypto.decryptBytes(key, roomData.context.value);
-
-    if (fallback) {
-      // Fallback decryption succeeded, so we need to re-encrypt the room key and
-      // save the data back again.
-      MozLoopService.log.debug("Fell back to saved key, queuing for encryption", 
-      roomData.roomToken);
-      this.queueForEncryption(roomData.roomToken);} else 
-    if (!savedRoomKey || key != savedRoomKey) {
-      // Decryption succeeded, but we don't have the right key saved.
-      try {
-        yield this.roomsCache.setKey(this.sessionType, roomData.roomToken, key);} 
-
-      catch (error) {
-        MozLoopService.log.error("Failed to save room key:", error);}}
-
-
-
-    roomData.roomKey = key;
-    roomData.decryptedContext = JSON.parse(decryptedData);
-
-    roomData.roomUrl = this.refreshRoomUrlWithNewKey(roomData.roomUrl, roomData.roomKey);
-
-    return roomData;}), 
-
-
-  /**
-   * Saves room information and notifies updates to any listeners.
-   *
-   * @param {Object}  roomData The new room data to save.
-   * @param {Boolean} isUpdate true if this is an update, false if its an add.
-   */
-  saveAndNotifyUpdate: function saveAndNotifyUpdate(roomData, isUpdate) {
-    this.rooms.set(roomData.roomToken, roomData);
-
-    var eventName = isUpdate ? "update" : "add";
-    eventEmitter.emit(eventName, roomData);
-    eventEmitter.emit(eventName + ":" + roomData.roomToken, roomData);}, 
-
-
-  /**
-   * Either adds or updates the room to the room store and notifies to any
-   * listeners.
-   *
-   * This will decrypt information if necessary, and adjust for the legacy
-   * "roomName" field.
-   *
-   * @param {Object}  room     The new room to add.
-   * @param {Boolean} isUpdate true if this is an update to an existing room.
-   */
-  addOrUpdateRoom: Task.async(function* (room, isUpdate) {
-    if (!room.context) {
-      // We don't do anything with roomUrl here as it doesn't need a key
-      // string adding at this stage.
-
-      // No encrypted data yet, use the old roomName field.
-      room.decryptedContext = { 
-        roomName: room.roomName };
-
-      delete room.roomName;
-
-      // This room doesn't have context, so we'll save it for a later encryption
-      // cycle.
-      this.queueForEncryption(room.roomToken);
-
-      this.saveAndNotifyUpdate(room, isUpdate);} else 
-    {
-      // We could potentially optimise this later by not decrypting if the
-      // encrypted context hasn't already changed. However perf doesn't seem
-      // to be too bigger an issue at the moment, so we just decrypt for now.
-      // If we do change this, then we need to make sure we get the new room
-      // data setup properly, as happens at the end of promiseDecryptRoomData.
-      try {
-        var roomData = yield this.promiseDecryptRoomData(room);
-
-        this.saveAndNotifyUpdate(roomData, isUpdate);} 
-      catch (error) {
-        MozLoopService.log.error("Failed to decrypt room data: ", error);
-        // Do what we can to save the room data.
-        room.decryptedContext = {};
-        this.saveAndNotifyUpdate(room, isUpdate);}}}), 
-
-
-
-
-  /**
-   * Fetch a list of rooms that the currently registered user is a member of.
-   *
-   * @param {String}   [version] If set, we will fetch a list of changed rooms since
-   *                             `version`. Optional.
-   * @return {Promise}           A promise that is resolved with a list of rooms
-   *                             on success, or rejected with an error if it fails.
-   */
-  getAll: function getAll(version) {
-    if (gGetAllPromise && !version) {
-      return gGetAllPromise;}
-
-
-    if (!gDirty) {
-      return Promise.resolve([].concat(_toConsumableArray(this.rooms.values())));}
-
-
-    gGetAllPromise = Task.spawn(function* () {
-      // Fetch the rooms from the server.
-      var url = "/rooms" + (version ? "?version=" + encodeURIComponent(version) : "");
-      var response = yield MozLoopService.hawkRequest(this.sessionType, url, "GET");
-      var roomsList = JSON.parse(response.body);
-      if (!Array.isArray(roomsList)) {
-        throw new Error("Missing array of rooms in response.");}var _iteratorNormalCompletion6 = true;var _didIteratorError6 = false;var _iteratorError6 = undefined;try {
-
-
-        for (var _iterator6 = roomsList[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {var room = _step6.value;
-          // See if we already have this room in our cache.
-          var orig = this.rooms.get(room.roomToken);
-
-          if (room.deleted) {
-            // If this client deleted the room, then we'll already have
-            // deleted the room in the function below.
-            if (orig) {
-              this.rooms.delete(room.roomToken);}
-
-
-            eventEmitter.emit("delete", room);
-            eventEmitter.emit("delete:" + room.roomToken, room);} else 
-          {
-            yield this.addOrUpdateRoom(room, !!orig);
-
-            if (orig) {
-              checkForParticipantsUpdate(orig, room);}}}
-
-
-
-
-        // If there's no rooms in the list, remove the guest created room flag, so that
-        // we don't keep registering for guest when we don't need to.
-      } catch (err) {_didIteratorError6 = true;_iteratorError6 = err;} finally {try {if (!_iteratorNormalCompletion6 && _iterator6.return) {_iterator6.return();}} finally {if (_didIteratorError6) {throw _iteratorError6;}}}if (this.sessionType == LOOP_SESSION_TYPE.GUEST && !this.rooms.size) {
-        this.setGuestCreatedRoom(false);}
-
-
-      // Set the 'dirty' flag back to FALSE, since the list is as fresh as can be now.
-      gDirty = false;
-      gGetAllPromise = null;
-      return [].concat(_toConsumableArray(this.rooms.values()));}.
-    bind(this)).catch(function (error) {
-      gGetAllPromise = null;
-      // Re-throw error so callers get notified.
-      throw error;});
-
-
-    return gGetAllPromise;}, 
-
-
-  /**
-   * Request information about number of participants joined to a specific room from the server.
-   * Used to determine infobar message state on the number of participants in the room.
-   *
-   * @param {String}  roomToken Room identifier
-   * @return {Number} Count of participants in the room.
-   */
-  getNumParticipants: function getNumParticipants(roomToken) {
-    try {
-      if (this.rooms && this.rooms.has(roomToken)) {
-        return this.rooms.get(roomToken).participants.length;}
-
-      return 0;} 
-
-    catch (ex) {
-      // No room, log error and send back 0 to indicate none in room.
-      MozLoopService.log.error("No room found in current session: ", ex);
-      return 0;}}, 
-
-
-
-  /**
-   * Request information about a specific room from the server. It will be
-   * returned from the cache if it's already in it.
-   *
-   * @param {String}   roomToken Room identifier
-   * @param {Function} callback  Function that will be invoked once the operation
-   *                             finished. The first argument passed will be an
-   *                             `Error` object or `null`. The second argument will
-   *                             be the list of rooms, if it was fetched successfully.
-   */
-  get: function get(roomToken, callback) {
-    var room = this.rooms.has(roomToken) ? this.rooms.get(roomToken) : {};
-    // Check if we need to make a request to the server to collect more room data.
-    var needsUpdate = !("participants" in room);
-    if (!gDirty && !needsUpdate) {
-      // Dirty flag is not set AND the necessary data is available, so we can
-      // simply return the room.
-      callback(null, room);
-      return;}
-
-
-    Task.spawn(function* () {
-      var response = yield MozLoopService.hawkRequest(this.sessionType, 
-      "/rooms/" + encodeURIComponent(roomToken), "GET");
-
-      var data = JSON.parse(response.body);
-
-      room.roomToken = roomToken;
-
-      if (data.deleted) {
-        this.rooms.delete(room.roomToken);
-
-        extend(room, data);
-        eventEmitter.emit("delete", room);
-        eventEmitter.emit("delete:" + room.roomToken, room);} else 
-      {
-        yield this.addOrUpdateRoom(data, !needsUpdate);
-
-        checkForParticipantsUpdate(room, data);}
-
-      callback(null, room);}.
-    bind(this)).catch(callback);}, 
-
-
-  /**
-   * Create a room.
-   *
-   * @param {Object}   room     Properties to be sent to the LoopServer
-   * @param {Function} callback Function that will be invoked once the operation
-   *                            finished. The first argument passed will be an
-   *                            `Error` object or `null`. The second argument will
-   *                            be the room, if it was created successfully.
-   */
-  create: function create(room, callback) {
-    if (!("decryptedContext" in room) || !("maxSize" in room)) {
-      callback(new Error("Missing required property to create a room"));
-      return;}
-
-
-    if (!("roomOwner" in room)) {
-      room.roomOwner = "-";}
-
-
-    Task.spawn(function* () {var _ref = 
-      yield this.promiseEncryptRoomData(room);var all = _ref.all;var encrypted = _ref.encrypted;
-
-      // Save both sets of data...
-      room = all;
-      // ...but only send the encrypted data.
-      var response = yield MozLoopService.hawkRequest(this.sessionType, "/rooms", 
-      "POST", encrypted);
-
-      extend(room, JSON.parse(response.body));
-      // Do not keep this value - it is a request to the server.
-      delete room.expiresIn;
-      // Make sure the url has the key on it.
-      room.roomUrl = this.refreshRoomUrlWithNewKey(room.roomUrl, room.roomKey);
-      this.rooms.set(room.roomToken, room);
-
-      if (this.sessionType == LOOP_SESSION_TYPE.GUEST) {
-        this.setGuestCreatedRoom(true);}
-
-
-      // Now we've got the room token, we can save the key to disk.
-      yield this.roomsCache.setKey(this.sessionType, room.roomToken, room.roomKey);
-
-      eventEmitter.emit("add", room);
-      callback(null, room);}.
-    bind(this)).catch(callback);}, 
-
-
-  /**
-   * Sets whether or not the user has created a room in guest mode.
-   *
-   * @param {Boolean} created If the user has created the room.
-   */
-  setGuestCreatedRoom: function setGuestCreatedRoom(created) {
-    if (created) {
-      Services.prefs.setBoolPref("loop.createdRoom", created);} else 
-    {
-      Services.prefs.clearUserPref("loop.createdRoom");}}, 
-
-
-
-  /**
-   * Returns true if the user has a created room in guest mode.
-   */
-  getGuestCreatedRoom: function getGuestCreatedRoom() {
-    try {
-      return Services.prefs.getBoolPref("loop.createdRoom");} 
-    catch (x) {
-      return false;}}, 
-
-
-
-  open: function open(roomToken) {
-    var windowData = { 
-      roomToken: roomToken, 
-      type: "room" };
-
-
-    eventEmitter.emit("open", roomToken);
-
-    return MozLoopService.openChatWindow(windowData, function () {
-      eventEmitter.emit("close");});}, 
-
-
-
-  /**
-   * Deletes a room.
-   *
-   * @param {String}   roomToken The room token.
-   * @param {Function} callback  Function that will be invoked once the operation
-   *                             finished. The first argument passed will be an
-   *                             `Error` object or `null`.
-   */
-  delete: function _delete(roomToken, callback) {var _this = this;
-    // XXX bug 1092954: Before deleting a room, the client should check room
-    //     membership and forceDisconnect() all current participants.
-    var room = this.rooms.get(roomToken);
-    var url = "/rooms/" + encodeURIComponent(roomToken);
-    MozLoopService.hawkRequest(this.sessionType, url, "DELETE").
-    then(function () {
-      _this.rooms.delete(roomToken);
-      eventEmitter.emit("delete", room);
-      eventEmitter.emit("delete:" + room.roomToken, room);
-      callback(null, room);}, 
-    function (error) {return callback(error);}).catch(function (error) {return callback(error);});}, 
-
-
-  /**
-   * Internal function to handle POSTs to a room.
-   *
-   * @param {String} roomToken  The room token.
-   * @param {Object} postData   The data to post to the room.
-   * @param {Function} callback Function that will be invoked once the operation
-   *                            finished. The first argument passed will be an
-   *                            `Error` object or `null`.
-   */
-  _postToRoom: function _postToRoom(roomToken, postData, callback) {var _this2 = this;
-    var url = "/rooms/" + encodeURIComponent(roomToken);
-    MozLoopService.hawkRequest(this.sessionType, url, "POST", postData).then(function (response) {
-      // Delete doesn't have a body return.
-      var joinData = response.body ? JSON.parse(response.body) : {};
-      if ("sessionToken" in joinData) {
-        var room = _this2.rooms.get(roomToken);
-        room.sessionToken = joinData.sessionToken;}
-
-      callback(null, joinData);}, 
-    function (error) {return callback(error);}).catch(function (error) {return callback(error);});}, 
-
-
-  /**
-   * Joins a room. The sessionToken that is returned by the server will be stored
-   * locally, for future use.
-   *
-   * @param {String} roomToken   The room token.
-   * @param {String} displayName The user's display name.
-   * @param {Function} callback  Function that will be invoked once the operation
-   *                             finished. The first argument passed will be an
-   *                             `Error` object or `null`.
-   */
-  join: function join(roomToken, displayName, callback) {
-    this._postToRoom(roomToken, { 
-      action: "join", 
-      displayName: displayName, 
-      clientMaxSize: CLIENT_MAX_SIZE }, 
-    callback);}, 
-
-
-  /**
-   * Refreshes a room
-   *
-   * @param {String} roomToken    The room token.
-   * @param {String} sessionToken The session token for the session that has been
-   *                              joined.
-   * @param {Function} callback   Function that will be invoked once the operation
-   *                              finished. The first argument passed will be an
-   *                              `Error` object or `null`.
-   */
-  refreshMembership: function refreshMembership(roomToken, sessionToken, callback) {
-    this._postToRoom(roomToken, { 
-      action: "refresh", 
-      sessionToken: sessionToken }, 
-    callback);}, 
-
-
-  /**
-   * Leaves a room. Although this is an sync function, no data is returned
-   * from the server.
-   *
-   * @param {String} roomToken    The room token.
-   * @param {String} sessionToken The session token for the session that has been
-   *                              joined
-   * @param {Function} callback   Optional. Function that will be invoked once the operation
-   *                              finished. The first argument passed will be an
-   *                              `Error` object or `null`.
-   */
-  leave: function leave(roomToken, sessionToken, callback) {
-    if (!callback) {
-      callback = function callback(error) {
-        if (error) {
-          MozLoopService.log.error(error);}};}
-
-
-
-    var room = this.rooms.get(roomToken);
-    if (!sessionToken && room && room.sessionToken) {
-      if (!room || !room.sessionToken) {
-        return;}
-
-      sessionToken = room.sessionToken;
-      delete room.sessionToken;}
-
-    this._postToRoom(roomToken, { 
-      action: "leave", 
-      sessionToken: sessionToken }, 
-    callback);}, 
-
-
-  /**
-   * Forwards connection status to the server.
-   *
-   * @param {String}                  roomToken The room token.
-   * @param {String}                  sessionToken The session token for the
-   *                                               session that has been
-   *                                               joined.
-   * @param {sharedActions.SdkStatus} status The connection status.
-   * @param {Function} callback Optional. Function that will be invoked once
-   *                            the operation finished. The first argument
-   *                            passed will be an `Error` object or `null`.
-   */
-  sendConnectionStatus: function sendConnectionStatus(roomToken, sessionToken, status, callback) {
-    if (!callback) {
-      callback = function callback(error) {
-        if (error) {
-          MozLoopService.log.error(error);}};}
-
-
-
-    this._postToRoom(roomToken, { 
-      action: "status", 
-      event: status.event, 
-      state: status.state, 
-      connections: status.connections, 
-      sendStreams: status.sendStreams, 
-      recvStreams: status.recvStreams, 
-      sessionToken: sessionToken }, 
-    callback);}, 
-
-
-  _domainLog: { 
-    domainMap: new Map(), 
-    roomToken: null }, 
-
-
-  /**
-   * Record a url associated to a room for batch submission if whitelisted.
-   *
-   * @param {String} roomToken The room token
-   * @param {String} url       Url with the domain to record
-   */
-  _recordUrl: function _recordUrl(roomToken, url) {
-    // Reset the log of domains if somehow we changed room tokens.
-    if (this._domainLog.roomToken !== roomToken) {
-      this._domainLog.roomToken = roomToken;
-      this._domainLog.domainMap.clear();}
-
-
-    var domain = void 0;
-    try {
-      domain = Services.eTLD.getBaseDomain(Services.io.newURI(url, null, null));} 
-
-    catch (ex) {
-      // Failed to extract domain, so don't record it.
-      return;}
-
-
-    // Only record domains that are whitelisted.
-    if (!DomainWhitelist.check(domain)) {
-      return;}
-
-
-    // Increment the count for previously recorded domains.
-    if (this._domainLog.domainMap.has(domain)) {
-      this._domainLog.domainMap.get(domain).count++;}
-
-    // Initialize the map for the domain with a value that can be submitted.
-    else {
-        this._domainLog.domainMap.set(domain, { count: 1, domain: domain });}}, 
-
-
-
-  /**
-   * Log the domains associated to a room token.
-   *
-   * @param {String} roomToken  The room token
-   * @param {Function} callback Function that will be invoked once the operation
-   *                            finished. The first argument passed will be an
-   *                            `Error` object or `null`.
-   */
-  logDomains: function logDomains(roomToken, callback) {
-    if (!callback) {
-      callback = function callback(error) {
-        if (error) {