merge fx-team to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 30 Nov 2015 13:15:40 +0100
changeset 308649 2d385f1302a2a86dc22f6d10dad2ac15b03d5f3d
parent 308599 a68027fa23fc05f97e0bb68350817037a5c89cd1 (current diff)
parent 308648 29ce9059dc2c97f2403f4e694eff3bcf611a8fc5 (diff)
child 308755 a18630f9ab42ddfde03ba8c7757a42069c48c7ed
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.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 fx-team to mozilla-central a=merge
browser/base/content/browser-loop.js
browser/components/loop/.eslintignore
browser/components/loop/.eslintrc
browser/components/loop/.eslintrc-gecko
browser/components/loop/.gitignore
browser/components/loop/README.txt
browser/components/loop/build-jsx
browser/components/loop/content/conversation.html
browser/components/loop/content/css/panel.css
browser/components/loop/content/js/.eslintrc
browser/components/loop/content/js/conversation.js
browser/components/loop/content/js/conversation.jsx
browser/components/loop/content/js/conversationAppStore.js
browser/components/loop/content/js/feedbackViews.js
browser/components/loop/content/js/feedbackViews.jsx
browser/components/loop/content/js/otconfig.js
browser/components/loop/content/js/panel.js
browser/components/loop/content/js/panel.jsx
browser/components/loop/content/js/roomStore.js
browser/components/loop/content/js/roomViews.js
browser/components/loop/content/js/roomViews.jsx
browser/components/loop/content/libs/l10n.js
browser/components/loop/content/panel.html
browser/components/loop/content/shared/README.md
browser/components/loop/content/shared/css/common.css
browser/components/loop/content/shared/css/conversation.css
browser/components/loop/content/shared/css/reset.css
browser/components/loop/content/shared/img/02.png
browser/components/loop/content/shared/img/02@2x.png
browser/components/loop/content/shared/img/animated-spinner.svg
browser/components/loop/content/shared/img/audio-call-avatar.svg
browser/components/loop/content/shared/img/audio-default-16x16@1.5x.png
browser/components/loop/content/shared/img/audio-default-16x16@2x.png
browser/components/loop/content/shared/img/avatars.svg
browser/components/loop/content/shared/img/beta-ribbon.svg
browser/components/loop/content/shared/img/chatbubble-arrow-left.svg
browser/components/loop/content/shared/img/chatbubble-arrow-right.svg
browser/components/loop/content/shared/img/check.svg
browser/components/loop/content/shared/img/ellipsis-v.svg
browser/components/loop/content/shared/img/empty_conversations.svg
browser/components/loop/content/shared/img/empty_search.svg
browser/components/loop/content/shared/img/facemute-14x14.png
browser/components/loop/content/shared/img/facemute-14x14@2x.png
browser/components/loop/content/shared/img/firefox-avatar.svg
browser/components/loop/content/shared/img/firefox-logo.png
browser/components/loop/content/shared/img/hangup-inverse-14x14.png
browser/components/loop/content/shared/img/hangup-inverse-14x14@2x.png
browser/components/loop/content/shared/img/happy.png
browser/components/loop/content/shared/img/hello_logo.svg
browser/components/loop/content/shared/img/helloicon.svg
browser/components/loop/content/shared/img/icon_32.png
browser/components/loop/content/shared/img/icon_64.png
browser/components/loop/content/shared/img/icons-10x10.svg
browser/components/loop/content/shared/img/icons-14x14.svg
browser/components/loop/content/shared/img/icons-16x16.svg
browser/components/loop/content/shared/img/movistar.png
browser/components/loop/content/shared/img/movistar@2x.png
browser/components/loop/content/shared/img/mute-inverse-14x14.png
browser/components/loop/content/shared/img/mute-inverse-14x14@2x.png
browser/components/loop/content/shared/img/pause-12x12.svg
browser/components/loop/content/shared/img/play-12x12.svg
browser/components/loop/content/shared/img/sad.png
browser/components/loop/content/shared/img/sad_hello_icon_64x64.svg
browser/components/loop/content/shared/img/spinner.png
browser/components/loop/content/shared/img/spinner.svg
browser/components/loop/content/shared/img/spinner@2x.png
browser/components/loop/content/shared/img/stop-12x12.svg
browser/components/loop/content/shared/img/svg/audio-hover.svg
browser/components/loop/content/shared/img/svg/audio-mute-hover.svg
browser/components/loop/content/shared/img/svg/audio-mute.svg
browser/components/loop/content/shared/img/svg/audio.svg
browser/components/loop/content/shared/img/svg/cam_audio-no.svg
browser/components/loop/content/shared/img/svg/cam_audio.svg
browser/components/loop/content/shared/img/svg/cam_audio_h.svg
browser/components/loop/content/shared/img/svg/exit.svg
browser/components/loop/content/shared/img/svg/glyph-email-16x16.svg
browser/components/loop/content/shared/img/svg/glyph-facebook-16x16.svg
browser/components/loop/content/shared/img/svg/glyph-help-16x16.svg
browser/components/loop/content/shared/img/svg/glyph-link-16x16.svg
browser/components/loop/content/shared/img/svg/glyph-user-16x16.svg
browser/components/loop/content/shared/img/svg/media-group-left-hover.svg
browser/components/loop/content/shared/img/svg/media-group-right-hover.svg
browser/components/loop/content/shared/img/svg/media-group.svg
browser/components/loop/content/shared/img/svg/settings-hover.svg
browser/components/loop/content/shared/img/svg/settings.svg
browser/components/loop/content/shared/img/svg/sharing-active.svg
browser/components/loop/content/shared/img/svg/sharing-hover.svg
browser/components/loop/content/shared/img/svg/sharing-pending.svg
browser/components/loop/content/shared/img/svg/sharing.svg
browser/components/loop/content/shared/img/svg/video-hover.svg
browser/components/loop/content/shared/img/svg/video-mute-hover.svg
browser/components/loop/content/shared/img/svg/video-mute.svg
browser/components/loop/content/shared/img/svg/video.svg
browser/components/loop/content/shared/img/telefonica.png
browser/components/loop/content/shared/img/telefonica@2x.png
browser/components/loop/content/shared/img/vivo.png
browser/components/loop/content/shared/img/vivo@2x.png
browser/components/loop/content/shared/js/actions.js
browser/components/loop/content/shared/js/activeRoomStore.js
browser/components/loop/content/shared/js/crypto.js
browser/components/loop/content/shared/js/dispatcher.js
browser/components/loop/content/shared/js/linkifiedTextView.js
browser/components/loop/content/shared/js/linkifiedTextView.jsx
browser/components/loop/content/shared/js/loopapi-client.js
browser/components/loop/content/shared/js/mixins.js
browser/components/loop/content/shared/js/models.js
browser/components/loop/content/shared/js/otSdkDriver.js
browser/components/loop/content/shared/js/store.js
browser/components/loop/content/shared/js/textChatStore.js
browser/components/loop/content/shared/js/textChatView.js
browser/components/loop/content/shared/js/textChatView.jsx
browser/components/loop/content/shared/js/urlRegExps.js
browser/components/loop/content/shared/js/utils.js
browser/components/loop/content/shared/js/validate.js
browser/components/loop/content/shared/js/views.js
browser/components/loop/content/shared/js/views.jsx
browser/components/loop/content/shared/libs/backbone-1.2.1.js
browser/components/loop/content/shared/libs/classnames-2.2.0.js
browser/components/loop/content/shared/libs/lodash-3.9.3.js
browser/components/loop/content/shared/libs/react-0.13.3-prod.js
browser/components/loop/content/shared/libs/react-0.13.3.js
browser/components/loop/content/shared/libs/sdk-content/css/ot.css
browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-denied-chrome.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-denied-copy-firefox.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-denied-firefox.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-predenied-chrome.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-prompt-chrome.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/audioonly-publisher.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/audioonly-subscriber.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/buttons.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/loader.gif
browser/components/loop/content/shared/libs/sdk-content/images/rtc/mic-off.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/mic-on.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/speaker-off.png
browser/components/loop/content/shared/libs/sdk-content/images/rtc/speaker-on.png
browser/components/loop/content/shared/libs/sdk-content/js/dynamic_config.min.js
browser/components/loop/content/shared/libs/sdk.js
browser/components/loop/content/shared/sounds/connected.ogg
browser/components/loop/content/shared/sounds/connecting.ogg
browser/components/loop/content/shared/sounds/failure.ogg
browser/components/loop/content/shared/sounds/message.ogg
browser/components/loop/content/shared/sounds/ringtone.ogg
browser/components/loop/content/shared/sounds/room-joined-in.ogg
browser/components/loop/content/shared/sounds/room-joined.ogg
browser/components/loop/content/shared/sounds/room-left.ogg
browser/components/loop/content/shared/sounds/terminated.ogg
browser/components/loop/jar.mn
browser/components/loop/manifest.ini
browser/components/loop/modules/.eslintrc
browser/components/loop/modules/LoopRooms.jsm
browser/components/loop/modules/LoopRoomsCache.jsm
browser/components/loop/modules/MozLoopAPI.jsm
browser/components/loop/modules/MozLoopPushHandler.jsm
browser/components/loop/modules/MozLoopService.jsm
browser/components/loop/modules/MozLoopWorker.js
browser/components/loop/moz.build
browser/components/loop/run-all-loop-tests.sh
browser/components/loop/standalone/.gitignore
browser/components/loop/standalone/Makefile
browser/components/loop/standalone/README.md
browser/components/loop/standalone/content/css/webapp.css
browser/components/loop/standalone/content/favicon.ico
browser/components/loop/standalone/content/img/gum-chrome.svg
browser/components/loop/standalone/content/img/gum-firefox.svg
browser/components/loop/standalone/content/img/gum-opera.svg
browser/components/loop/standalone/content/img/gum-others.svg
browser/components/loop/standalone/content/img/hello-logo-text.svg
browser/components/loop/standalone/content/img/logo.png
browser/components/loop/standalone/content/img/mozilla-logo.svg
browser/components/loop/standalone/content/index.html
browser/components/loop/standalone/content/js/standaloneAppStore.js
browser/components/loop/standalone/content/js/standaloneMetricsStore.js
browser/components/loop/standalone/content/js/standaloneMozLoop.js
browser/components/loop/standalone/content/js/standaloneRoomViews.js
browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
browser/components/loop/standalone/content/js/webapp.js
browser/components/loop/standalone/content/js/webapp.jsx
browser/components/loop/standalone/content/l10n/en-US/loop.properties
browser/components/loop/standalone/content/libs/l10n-gaia-02ca67948fe8.js
browser/components/loop/standalone/content/libs/l10n-gaia-upstream.txt
browser/components/loop/standalone/content/robots.txt
browser/components/loop/standalone/content/webappEntryPoint.js
browser/components/loop/standalone/package.json
browser/components/loop/standalone/server.js
browser/components/loop/standalone/webpack.config.js
browser/components/loop/test/.eslintrc
browser/components/loop/test/coverage/index.html
browser/components/loop/test/desktop-local/.eslintrc
browser/components/loop/test/desktop-local/README.md
browser/components/loop/test/desktop-local/conversationAppStore_test.js
browser/components/loop/test/desktop-local/conversation_test.js
browser/components/loop/test/desktop-local/feedbackViews_test.js
browser/components/loop/test/desktop-local/index.html
browser/components/loop/test/desktop-local/l10n_test.js
browser/components/loop/test/desktop-local/panel_test.js
browser/components/loop/test/desktop-local/roomStore_test.js
browser/components/loop/test/desktop-local/roomViews_test.js
browser/components/loop/test/desktop-local/test_desktop_all.py
browser/components/loop/test/functional/config.py
browser/components/loop/test/functional/hanging_threads.py
browser/components/loop/test/functional/manifest.ini
browser/components/loop/test/functional/serversetup.py
browser/components/loop/test/functional/test_1_browser_call.py
browser/components/loop/test/index.html
browser/components/loop/test/karma/head.js
browser/components/loop/test/karma/karma.conf.base.js
browser/components/loop/test/karma/karma.coverage.desktop.js
browser/components/loop/test/karma/karma.coverage.shared_standalone.js
browser/components/loop/test/mochitest/.eslintrc
browser/components/loop/test/mochitest/browser.ini
browser/components/loop/test/mochitest/browser_LoopRooms_channel.js
browser/components/loop/test/mochitest/browser_fxa_login.js
browser/components/loop/test/mochitest/browser_loop_fxa_server.js
browser/components/loop/test/mochitest/browser_mozLoop_appVersionInfo.js
browser/components/loop/test/mochitest/browser_mozLoop_chat.js
browser/components/loop/test/mochitest/browser_mozLoop_context.js
browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
browser/components/loop/test/mochitest/browser_mozLoop_socialShare.js
browser/components/loop/test/mochitest/browser_mozLoop_telemetry.js
browser/components/loop/test/mochitest/browser_toolbarbutton.js
browser/components/loop/test/mochitest/head.js
browser/components/loop/test/mochitest/loop_fxa.sjs
browser/components/loop/test/mochitest/test_loopLinkClicker_channel.html
browser/components/loop/test/package.json
browser/components/loop/test/shared/activeRoomStore_test.js
browser/components/loop/test/shared/crypto_test.js
browser/components/loop/test/shared/dispatcher_test.js
browser/components/loop/test/shared/frontend_tester.py
browser/components/loop/test/shared/index.html
browser/components/loop/test/shared/linkifiedTextView_test.js
browser/components/loop/test/shared/loop_mocha_utils.js
browser/components/loop/test/shared/loopapi-client_test.js
browser/components/loop/test/shared/mixins_test.js
browser/components/loop/test/shared/models_test.js
browser/components/loop/test/shared/otSdkDriver_test.js
browser/components/loop/test/shared/sdk_mock.js
browser/components/loop/test/shared/store_test.js
browser/components/loop/test/shared/test_shared_all.py
browser/components/loop/test/shared/textChatStore_test.js
browser/components/loop/test/shared/textChatView_test.js
browser/components/loop/test/shared/utils_test.js
browser/components/loop/test/shared/validate_test.js
browser/components/loop/test/shared/vendor/chai-3.0.0.js
browser/components/loop/test/shared/vendor/chai-as-promised-5.1.0.js
browser/components/loop/test/shared/vendor/mocha-2.2.5.css
browser/components/loop/test/shared/vendor/mocha-2.2.5.js
browser/components/loop/test/shared/vendor/sinon-1.16.1.js
browser/components/loop/test/shared/views_test.js
browser/components/loop/test/standalone/index.html
browser/components/loop/test/standalone/standaloneAppStore_test.js
browser/components/loop/test/standalone/standaloneMetricsStore_test.js
browser/components/loop/test/standalone/standaloneMozLoop_test.js
browser/components/loop/test/standalone/standaloneRoomViews_test.js
browser/components/loop/test/standalone/test_standalone_all.py
browser/components/loop/test/standalone/webapp_test.js
browser/components/loop/test/ui-showcase/test_ui-showcase.py
browser/components/loop/test/visual-regression/README.md
browser/components/loop/test/visual-regression/screenshot
browser/components/loop/test/xpcshell/.eslintrc
browser/components/loop/test/xpcshell/head.js
browser/components/loop/test/xpcshell/test_loopapi_doNotDisturb.js
browser/components/loop/test/xpcshell/test_loopapi_internal.js
browser/components/loop/test/xpcshell/test_loopapi_prefs.js
browser/components/loop/test/xpcshell/test_looppush_initialize.js
browser/components/loop/test/xpcshell/test_looprooms.js
browser/components/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
browser/components/loop/test/xpcshell/test_looprooms_first_notification.js
browser/components/loop/test/xpcshell/test_looprooms_getall.js
browser/components/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
browser/components/loop/test/xpcshell/test_loopservice_dnd.js
browser/components/loop/test/xpcshell/test_loopservice_encryptionkey.js
browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js
browser/components/loop/test/xpcshell/test_loopservice_hawk_request.js
browser/components/loop/test/xpcshell/test_loopservice_initialize.js
browser/components/loop/test/xpcshell/test_loopservice_locales.js
browser/components/loop/test/xpcshell/test_loopservice_loop_prefs.js
browser/components/loop/test/xpcshell/test_loopservice_registration.js
browser/components/loop/test/xpcshell/test_loopservice_registration_retry.js
browser/components/loop/test/xpcshell/test_loopservice_restart.js
browser/components/loop/test/xpcshell/test_loopservice_token_invalid.js
browser/components/loop/test/xpcshell/test_loopservice_token_save.js
browser/components/loop/test/xpcshell/test_loopservice_token_send.js
browser/components/loop/test/xpcshell/test_loopservice_token_validation.js
browser/components/loop/test/xpcshell/xpcshell.ini
browser/components/loop/ui/README.md
browser/components/loop/ui/fake-l10n.js
browser/components/loop/ui/fake-mozLoop.js
browser/components/loop/ui/index.html
browser/components/loop/ui/react-frame-component.js
browser/components/loop/ui/sample-img/video-screen-local.png
browser/components/loop/ui/sample-img/video-screen-remote.png
browser/components/loop/ui/sample-img/video-screen-terminal.png
browser/components/loop/ui/ui-showcase.css
browser/components/loop/ui/ui-showcase.js
browser/components/loop/ui/ui-showcase.jsx
browser/themes/linux/loop/menuPanel.png
browser/themes/linux/loop/menuPanel@2x.png
browser/themes/linux/loop/toolbar-inverted.png
browser/themes/linux/loop/toolbar-inverted@2x.png
browser/themes/linux/loop/toolbar.png
browser/themes/linux/loop/toolbar@2x.png
browser/themes/osx/loop/menuPanel-yosemite.png
browser/themes/osx/loop/menuPanel-yosemite@2x.png
browser/themes/osx/loop/menuPanel.png
browser/themes/osx/loop/menuPanel@2x.png
browser/themes/osx/loop/toolbar-inverted.png
browser/themes/osx/loop/toolbar-inverted@2x.png
browser/themes/osx/loop/toolbar-yosemite.png
browser/themes/osx/loop/toolbar-yosemite@2x.png
browser/themes/osx/loop/toolbar.png
browser/themes/osx/loop/toolbar@2x.png
browser/themes/windows/loop/menuPanel-aero.png
browser/themes/windows/loop/menuPanel-aero@2x.png
browser/themes/windows/loop/menuPanel.png
browser/themes/windows/loop/menuPanel@2x.png
browser/themes/windows/loop/toolbar-XP.png
browser/themes/windows/loop/toolbar-XP@2x.png
browser/themes/windows/loop/toolbar-aero.png
browser/themes/windows/loop/toolbar-aero@2x.png
browser/themes/windows/loop/toolbar-inverted.png
browser/themes/windows/loop/toolbar-inverted@2x.png
browser/themes/windows/loop/toolbar-lunaSilver.png
browser/themes/windows/loop/toolbar-lunaSilver@2x.png
browser/themes/windows/loop/toolbar-win8.png
browser/themes/windows/loop/toolbar-win8@2x.png
browser/themes/windows/loop/toolbar.png
browser/themes/windows/loop/toolbar@2x.png
devtools/.eslintignore
mobile/android/.eslintignore
mobile/android/base/RestrictedProfiles.java
mobile/android/base/build.gradle
mobile/android/base/restrictions/Restriction.java
mobile/android/base/sync/setup/activities/ClientRecordArrayAdapter.java
mobile/android/base/sync/setup/activities/SendTabActivity.java
new file mode 100644
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,157 @@
+# Always ignore node_modules.
+**/node_modules/**/*.*
+
+# Exclude expected objdirs.
+obj*/**
+
+# We ignore all these directories by default, until we get them enabled.
+# If you are enabling a directory, please add directory specific exclusions
+# below.
+accessible/**
+addon-sdk/**
+b2g/**
+build/**
+caps/**
+chrome/**
+config/**
+db/**
+docshell/**
+dom/**
+editor/**
+embedding/**
+extensions/**
+gfx/**
+gradle/**
+hal/**
+image/**
+intl/**
+ipc/**
+js/**
+layout/**
+media/**
+memory/**
+mfbt/**
+modules/**
+mozglue/**
+netwerk/**
+nsprpub/**
+other-licenses/**
+parser/**
+probes/**
+python/**
+rdf/**
+security/**
+services/**
+startupcache/**
+storage/**
+testing/**
+toolkit/**
+tools/**
+uriloader/**
+view/**
+webapprt/**
+widget/**
+xpcom/**
+xpfe/**
+xulrunner/**
+
+# browser/ exclusions
+browser/app/**
+browser/base/**
+browser/branding/**
+browser/components/**
+browser/config/**
+browser/docs/**
+browser/experiments/**
+browser/extensions/pdfjs/**
+browser/extensions/shumway/**
+browser/fuel/**
+browser/installer/**
+browser/locales/**
+browser/modules/**
+browser/themes/**
+
+# Loop specific exclusions
+
+# This file currently uses a non-standard (and not on a standards track)
+# if statement within catch.
+browser/extensions/loop/content/modules/MozLoopWorker.js
+# This file currently uses es7 features eslint issue:
+# https://github.com/eslint/espree/issues/125
+browser/extensions/loop/content/modules/MozLoopAPI.jsm
+# Need to fix the configuration for this.
+browser/extensions/loop/bootstrap.js
+# Need to drop the preprocessing (bug 1212428)
+browser/extensions/loop/content/preferences/prefs.js
+# Libs we don't need to check
+browser/extensions/loop/content/panels/vendor
+browser/extensions/loop/content/shared/vendor
+browser/extensions/loop/standalone/content/libs
+# Libs we don't need to check
+browser/extensions/loop/test/shared/vendor
+# Coverage files
+browser/extensions/loop/test/coverage
+# These are generated react files that we don't need to check
+browser/extensions/loop/content/panels/js/conversation.js
+browser/extensions/loop/content/panels/js/conversationViews.js
+browser/extensions/loop/content/panels/js/panel.js
+browser/extensions/loop/content/panels/js/roomViews.js
+browser/extensions/loop/content/panels/js/feedbackViews.js
+browser/extensions/loop/content/shared/js/textChatView.js
+browser/extensions/loop/content/shared/js/linkifiedTextView.js
+browser/extensions/loop/content/shared/js/views.js
+browser/extensions/loop/standalone/content/js/standaloneRoomViews.js
+browser/extensions/loop/standalone/content/js/webapp.js
+browser/extensions/loop/ui/ui-showcase.js
+# Don't need to check the built tree
+browser/extensions/loop/standalone/dist
+
+# devtools/ exclusions
+# Ignore d3
+devtools/client/shared/d3.js
+devtools/client/webaudioeditor/lib/dagre-d3.js
+
+# Ignore codemirror
+devtools/client/sourceeditor/codemirror/*.js
+devtools/client/sourceeditor/codemirror/**/*.js
+
+# Ignore jquery test libs
+devtools/client/markupview/test/lib_*
+
+# Ignore pre-processed files
+devtools/client/framework/toolbox-process-window.js
+devtools/client/performance/system.js
+devtools/client/webide/webide-prefs.js
+
+# Ignore various libs
+devtools/shared/jsbeautify/*
+devtools/shared/acorn/*
+devtools/shared/tern/*
+devtools/shared/pretty-fast/*
+devtools/shared/sourcemap/*
+
+# mobile/android/ exclusions
+mobile/android/chrome/content
+mobile/android/tests/
+
+# Uses `#filter substitution`
+mobile/android/b2gdroid/app/b2gdroid.js
+mobile/android/app/mobile.js
+mobile/android/chrome/content/healthreport-prefs.js
+
+# Uses `#expand`
+mobile/android/chrome/content/about.js
+
+# Not much JS to lint and non-standard at that
+mobile/android/installer/
+mobile/android/locales/
+
+# Pretty sure we're disabling this one anyway
+mobile/android/modules/ContactService.jsm
+
+# es7 proposed: array comprehensions
+#   https://github.com/eslint/espree/issues/125
+mobile/android/modules/WebappManager.jsm
+
+# Non-standard `(catch ex if ...)`
+mobile/android/components/Snippets.js
--- a/.hgignore
+++ b/.hgignore
@@ -75,19 +75,25 @@
 ^media/libstagefright/android$
 
 # Tag files generated by GNU Global
 GTAGS
 GRTAGS
 GSYMS
 GPATH
 
-# Unit tests for Loop
+# Various items for Loop
 ^browser/components/loop/standalone/content/config\.js$
-^browser/components/loop/standalone/node_modules/
+^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
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1371,52 +1371,16 @@ pref("pdfjs.previousHandler.alwaysAskBef
 pref("shumway.disabled", true);
 #endif
 
 // The maximum amount of decoded image data we'll willingly keep around (we
 // might keep around more than this, but we'll try to get down to this value).
 // (This is intentionally on the high side; see bug 746055.)
 pref("image.mem.max_decoded_image_kb", 256000);
 
-pref("loop.enabled", true);
-pref("loop.textChat.enabled", true);
-pref("loop.server", "https://loop.services.mozilla.com/v0");
-pref("loop.linkClicker.url", "https://hello.firefox.com/");
-pref("loop.gettingStarted.seen", false);
-pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
-pref("loop.gettingStarted.resumeOnFirstJoin", false);
-pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
-pref("loop.legal.ToS_url", "https://www.mozilla.org/about/legal/terms/firefox-hello/");
-pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/firefox-hello/");
-pref("loop.do_not_disturb", false);
-pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/ringtone.ogg");
-pref("loop.retry_delay.start", 60000);
-pref("loop.retry_delay.limit", 300000);
-pref("loop.ping.interval", 1800000);
-pref("loop.ping.timeout", 10000);
-pref("loop.feedback.baseUrl", "https://input.mozilla.org/api/v1/feedback");
-pref("loop.feedback.product", "Loop");
-pref("loop.debug.loglevel", "Error");
-pref("loop.debug.dispatcher", false);
-pref("loop.debug.sdk", false);
-pref("loop.debug.twoWayMediaTelemetry", false);
-pref("loop.feedback.dateLastSeenSec", 0);
-pref("loop.feedback.periodSec", 15770000); // 6 months.
-pref("loop.feedback.formURL", "https://www.mozilla.org/firefox/hello/npssurvey/");
-pref("loop.feedback.manualFormURL", "https://www.mozilla.org/firefox/hello/feedbacksurvey/");
-#ifdef DEBUG
-pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
-#else
-pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
-#endif
-pref("loop.fxa_oauth.tokendata", "");
-pref("loop.fxa_oauth.profile", "");
-pref("loop.support_url", "https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc");
-pref("loop.browserSharing.showInfoBar", true);
-
 pref("social.sidebar.unload_timeout_ms", 10000);
 
 // Activation from inside of share panel is possible if activationPanelEnabled
 // is true. Pref'd off for release while usage testing is done through beta.
 pref("social.share.activationPanelEnabled", true);
 pref("social.shareDirectory", "https://activations.cdn.mozilla.net/sharePanel.html");
 
 pref("dom.identity.enabled", false);
deleted file mode 100644
--- a/browser/base/content/browser-loop.js
+++ /dev/null
@@ -1,613 +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/.
-
-// the "exported" symbols
-var LoopUI;
-
-(function() {
-  const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
-  const kBrowserSharingNotificationId = "loop-sharing-notification";
-  const kPrefBrowserSharingInfoBar = "browserSharing.showInfoBar";
-
-  LoopUI = {
-    /**
-     * @var {XULWidgetSingleWrapper} toolbarButton Getter for the Loop toolbarbutton
-     *                                             instance for this 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() {
-      let browser = document.querySelector("#loop-notification-panel > #loop-panel-iframe");
-      if (browser) {
-        delete this.browser;
-        this.browser = browser;
-      }
-      return browser;
-    },
-
-    /**
-     * @var {String|null} selectedTab Getter for the name of the currently selected
-     *                                tab inside the Loop panel. Will be NULL if
-     *                                the panel hasn't loaded yet.
-     */
-    get selectedTab() {
-      if (!this.browser) {
-        return null;
-      }
-
-      let selectedTab = this.browser.contentDocument.querySelector(".tab-view > .selected");
-      return selectedTab && selectedTab.getAttribute("data-tab-name");
-    },
-
-    /**
-     * @return {Promise}
-     */
-    promiseDocumentVisible(aDocument) {
-      if (!aDocument.hidden) {
-        return Promise.resolve(aDocument);
-      }
-
-      return new Promise((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.
-     * @param {String}   [tabId] Optional name of the tab to select after the panel
-     *                           has opened. Does nothing when the panel is hidden.
-     * @return {Promise}
-     */
-    togglePanel: function(event, tabId = null) {
-      if (!this.panel) {
-        // We're on the hidden window! What fun!
-        let obs = win => {
-          Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
-          win.LoopUI.togglePanel(event, tabId);
-        };
-        Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
-        return OpenBrowserWindow();
-      }
-      if (this.panel.state == "open") {
-        return new Promise(resolve => {
-          this.panel.hidePopup();
-          resolve();
-        });
-      }
-
-      return this.openCallPanel(event, tabId).then(doc => {
-        let fm = Services.focus;
-        fm.moveFocus(doc.defaultView, null, fm.MOVEFOCUS_FIRST, fm.FLAG_NOSCROLL);
-      }).catch(err => {
-        Cu.reportError(x);
-      });
-    },
-
-    /**
-     * 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.
-     * @param {String} [tabId] Identifier of the tab to select when the panel is
-     *                         opened. Example: 'rooms', 'contacts', etc.
-     * @return {Promise}
-     */
-    openCallPanel: function(event, tabId = null) {
-      return new Promise((resolve) => {
-        let callback = iframe => {
-          // Helper function to show a specific tab view in the panel.
-          function showTab() {
-            if (!tabId) {
-              resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
-              return;
-            }
-
-            let win = iframe.contentWindow;
-            let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
-              detail: {
-                action: "selectTab",
-                tab: tabId
-              }
-            }, win));
-            win.dispatchEvent(ev);
-            resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
-          }
-
-          // If the panel has been opened and initialized before, we can skip waiting
-          // for the content to load - because it's already there.
-          if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
-            showTab();
-            return;
-          }
-
-          let documentDOMLoaded = () => {
-            iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
-            // Handle window.close correctly on the panel.
-            this.hookWindowCloseForPanelClose(iframe.contentWindow);
-            iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
-              iframe.contentWindow.removeEventListener("loopPanelInitialized",
-                loopPanelInitialized);
-              showTab();
-            });
-          };
-          iframe.addEventListener("DOMContentLoaded", documentDOMLoaded, true);
-        };
-
-        // Used to clear the temporary "login" state from the button.
-        Services.obs.notifyObservers(null, "loop-status-changed", null);
-
-        this.shouldResumeTour().then((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.
-            this.MozLoopService.resumeTour("waiting");
-            resolve();
-            return;
-          }
-
-          this.LoopAPI.initialize();
-
-          let anchor = event ? event.target : this.toolbarButton.anchor;
-          let setHeight = 410;
-          if (gBrowser.selectedBrowser.getAttribute("remote") === "true") {
-            setHeight = 262;
-          }
-          this.PanelFrame.showPopup(window, anchor,
-            "loop", null, "about:looppanel",
-            // Loop wants a fixed size for the panel. This also stops it dynamically resizing.
-            { width: 330, height: setHeight },
-            callback);
-        });
-      });
-    },
-
-    /**
-     * 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;
-      }
-
-      let 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() {
-      return new Promise(resolve => {
-        this.LoopRooms.getAll((error, rooms) => {
-          let roomsWithNonOwners = [];
-          for (let room of rooms) {
-            if (!("participants" in room)) {
-              continue;
-            }
-            let numNonOwners = room.participants.filter(participant => !participant.owner).length;
-            if (!numNonOwners) {
-              continue;
-            }
-            roomsWithNonOwners.push(room);
-          }
-          resolve(roomsWithNonOwners);
-        });
-      });
-    },
-
-    /**
-     * Triggers the initialization of the loop service.  Called by
-     * delayedStartup.
-     */
-    init: function() {
-      // Add observer notifications before the service is initialized
-      Services.obs.addObserver(this, "loop-status-changed", false);
-
-      // 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().catch(ex => {
-        if (!ex.message ||
-            (!ex.message.contains("not enabled") &&
-             !ex.message.contains("not needed"))) {
-          console.error(ex);
-        }
-      });
-      this.updateToolbarState();
-    },
-
-    uninit: function() {
-      Services.obs.removeObserver(this, "loop-status-changed");
-    },
-
-    // Implements nsIObserver
-    observe: function(subject, topic, data) {
-      if (topic != "loop-status-changed") {
-        return;
-      }
-      this.updateToolbarState(data);
-    },
-
-    /**
-     * Updates the toolbar/menu-button state to reflect Loop status.
-     *
-     * @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(aReason = null) {
-      if (!this.toolbarButton.node) {
-        return;
-      }
-      let state = "";
-      let mozL10nId = "loop-call-button3";
-      let suffix = ".tooltiptext";
-      if (this.MozLoopService.errors.size) {
-        state = "error";
-        mozL10nId += "-error";
-      } 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(roomsWithNonOwners => {
-          if (roomsWithNonOwners.length > 0) {
-            mozL10nId += "-participantswaiting";
-          } else {
-            mozL10nId += "-active";
-          }
-
-          suffix += "2";
-          this.updateTooltiptext(mozL10nId + suffix);
-          this.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.
-     *
-     * @param {string} [mozL10nId] l10n ID that refelct the current
-     *                           Loop status.
-     */
-    updateTooltiptext: function(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(options) {
-      if (this.MozLoopService.doNotDisturb) {
-        return;
-      }
-
-      if (!options.title) {
-        throw new Error("Missing title, can not display notification");
-      }
-
-      let 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);
-      }
-
-      let notification = new window.Notification(options.title, notificationOptions);
-      notification.addEventListener("click", e => {
-        if (window.closed) {
-          return;
-        }
-
-        try {
-          window.focus();
-        } catch (ex) {}
-
-        // We need a setTimeout here, otherwise the panel won't show after the
-        // window received focus.
-        window.setTimeout(() => {
-          if (typeof options.onclick == "function") {
-            options.onclick();
-          } else {
-            // Open the Loop panel as a default action.
-            this.openCallPanel(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(name) {
-      if (this.ActiveSound || this.MozLoopService.doNotDisturb) {
-        return;
-      }
-
-      this.activeSound = new window.Audio();
-      this.activeSound.src = `chrome://browser/content/loop/shared/sounds/${name}.ogg`;
-      this.activeSound.load();
-      this.activeSound.play();
-
-      this.activeSound.addEventListener("ended", () => this.activeSound = undefined, false);
-    },
-
-    /**
-     * Start listening to selected tab changes and notify any content page that's
-     * listening to 'BrowserSwitch' push messages.
-     *
-     * Push message parameters:
-     * - {Integer} windowId  The new windowId for the browser.
-     */
-    startBrowserSharing: function() {
-      if (!this._listeningToTabSelect) {
-        gBrowser.tabContainer.addEventListener("TabSelect", this);
-        this._listeningToTabSelect = true;
-      }
-
-      this._maybeShowBrowserSharingInfoBar();
-
-      // Get the first window Id for the listener.
-      this.LoopAPI.broadcastPushMessage("BrowserSwitch",
-        gBrowser.selectedBrowser.outerWindowID);
-    },
-
-    /**
-     * Stop listening to selected tab changes.
-     */
-    stopBrowserSharing: function() {
-      if (!this._listeningToTabSelect) {
-        return;
-      }
-
-      this._hideBrowserSharingInfoBar();
-      gBrowser.tabContainer.removeEventListener("TabSelect", this);
-      this._listeningToTabSelect = false;
-    },
-
-    /**
-     * 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(key) {
-      let str = this.MozLoopService.getStrings(key);
-      if (str) {
-        str = JSON.parse(str).textContent;
-      }
-      return str;
-    },
-
-    /**
-     * 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.
-     */
-    _maybeShowBrowserSharingInfoBar: function() {
-      this._hideBrowserSharingInfoBar();
-
-      // Don't show the infobar if it's been permanently disabled from the menu.
-      if (!this.MozLoopService.getLoopPref(kPrefBrowserSharingInfoBar)) {
-        return;
-      }
-
-      let box = gBrowser.getNotificationBox();
-      let paused = false;
-      let bar = box.appendNotification(
-        this._getString("infobar_screenshare_browser_message"),
-        kBrowserSharingNotificationId,
-        // Icon is defined in browser theme CSS.
-        null,
-        box.PRIORITY_WARNING_LOW,
-        [{
-          label: this._getString("infobar_button_pause_label"),
-          accessKey: this._getString("infobar_button_pause_accesskey"),
-          isDefault: false,
-          callback: (event, buttonInfo, buttonNode) => {
-            paused = !paused;
-            bar.label = paused ? this._getString("infobar_screenshare_paused_browser_message") :
-              this._getString("infobar_screenshare_browser_message");
-            bar.classList.toggle("paused", paused);
-            buttonNode.label = paused ? this._getString("infobar_button_resume_label") :
-              this._getString("infobar_button_pause_label");
-            buttonNode.accessKey = paused ? this._getString("infobar_button_resume_accesskey") :
-              this._getString("infobar_button_pause_accesskey");
-            return true;
-          }
-        },
-        {
-          label: this._getString("infobar_button_stop_label"),
-          accessKey: this._getString("infobar_button_stop_accesskey"),
-          isDefault: true,
-          callback: () => {
-            this._hideBrowserSharingInfoBar();
-            LoopUI.MozLoopService.hangupAllChatWindows();
-          }
-        }]
-      );
-
-      // Keep showing the notification bar until the user explicitly closes it.
-      bar.persistence = -1;
-    },
-
-    /**
-     * Hides the infobar, permanantly if requested.
-     *
-     * @param {Boolean} permanently Flag that determines if the infobar will never
-     *                              been shown again. Defaults to `false`.
-     * @return {Boolean} |true| if the infobar was hidden here.
-     */
-    _hideBrowserSharingInfoBar: function(permanently = false, browser) {
-      browser = browser || gBrowser.selectedBrowser;
-      let box = gBrowser.getNotificationBox(browser);
-      let notification = box.getNotificationWithValue(kBrowserSharingNotificationId);
-      let removed = false;
-      if (notification) {
-        box.removeNotification(notification);
-        removed = true;
-      }
-
-      if (permanently) {
-        this.MozLoopService.setLoopPref(kPrefBrowserSharingInfoBar, false);
-      }
-
-      return removed;
-    },
-
-    /**
-     * Handles events from gBrowser.
-     */
-    handleEvent: function(event) {
-      // We only should get "select" events.
-      if (event.type != "TabSelect") {
-        return;
-      }
-
-      let wasVisible = false;
-      // Hide the infobar from the previous tab.
-      if (event.detail.previousTab) {
-        wasVisible = this._hideBrowserSharingInfoBar(false,
-          event.detail.previousTab.linkedBrowser);
-      }
-
-      // We've changed the tab, so get the new window id.
-      this.LoopAPI.broadcastPushMessage("BrowserSwitch",
-        gBrowser.selectedBrowser.outerWindowID);
-
-      if (wasVisible) {
-        // If the infobar was visible before, we should show it again after the
-        // switch.
-        this._maybeShowBrowserSharingInfoBar();
-      }
-    },
-
-    /**
-     * 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(callback) {
-      let 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(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.
-        let xhr = new XMLHttpRequest();
-        xhr.open("get", uri.spec, true);
-        xhr.responseType = "blob";
-        xhr.overrideMimeType("image/x-icon");
-        xhr.onload = () => {
-          if (xhr.status != 200) {
-            callback(new Error("Invalid status code received for favicon XHR: " + xhr.status));
-            return;
-          }
-
-          let reader = new FileReader();
-          reader.onload = reader.onload = () => callback(null, reader.result);
-          reader.onerror = callback;
-          reader.readAsDataURL(xhr.response);
-        };
-        xhr.onerror = callback;
-        xhr.send();
-      }).catch(err => {
-        callback(err || new Error("No favicon found"));
-      });
-    }
-  };
-})();
-
-XPCOMUtils.defineLazyModuleGetter(LoopUI, "hookWindowCloseForPanelClose", "resource://gre/modules/MozSocialAPI.jsm");
-XPCOMUtils.defineLazyModuleGetter(LoopUI, "LoopAPI", "resource:///modules/loop/MozLoopAPI.jsm");
-XPCOMUtils.defineLazyModuleGetter(LoopUI, "LoopRooms", "resource:///modules/loop/LoopRooms.jsm");
-XPCOMUtils.defineLazyModuleGetter(LoopUI, "MozLoopService", "resource:///modules/loop/MozLoopService.jsm");
-XPCOMUtils.defineLazyModuleGetter(LoopUI, "PanelFrame", "resource:///modules/PanelFrame.jsm");
-XPCOMUtils.defineLazyModuleGetter(LoopUI, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -897,21 +897,16 @@ panelview > .social-panel-frame {
   height: auto;
 }
 
 /* Translation */
 notification[value="translation"] {
   -moz-binding: url("chrome://browser/content/translation-infobar.xml#translationbar");
 }
 
-/* Loop/ Hello */
-notification[value="loop-sharing-notification"] .close-icon {
-  display: none;
-}
-
 /* Social */
 /* Note the chatbox 'width' values are duplicated in socialchat.xml */
 chatbox {
   -moz-binding: url("chrome://browser/content/socialchat.xml#chatbox");
   transition: height 150ms ease-out, width 150ms ease-out;
   height: 290px;
   width: 300px; /* CHAT_WIDTH_OPEN in socialchat.xml */
 }
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -269,17 +269,16 @@ var gInitialPages = [
 #include browser-ctrlTab.js
 #include browser-customization.js
 #include browser-devedition.js
 #include browser-eme.js
 #include browser-feeds.js
 #include browser-fullScreen.js
 #include browser-fullZoom.js
 #include browser-gestureSupport.js
-#include browser-loop.js
 #include browser-places.js
 #include browser-plugins.js
 #include browser-safebrowsing.js
 #include browser-sidebar.js
 #include browser-social.js
 #include browser-syncui.js
 #include browser-tabview.js
 #include browser-thumbnails.js
@@ -1352,18 +1351,16 @@ var gBrowserInit = {
     // initialize the sync UI
     gSyncUI.init();
     gFxAccounts.init();
 
 #ifdef MOZ_DATA_REPORTING
     gDataNotificationInfoBar.init();
 #endif
 
-    LoopUI.init();
-
     gBrowserThumbnails.init();
 
     // Add Devtools menuitems and listeners
     gDevToolsBrowser.registerBrowserWindow(window);
 
     gMenuButtonBadgeManager.init();
 
     gMenuButtonUpdateBadge.init();
@@ -1535,17 +1532,16 @@ var gBrowserInit = {
       if (Win7Features)
         Win7Features.onCloseWindow();
 
       gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
       ctrlTab.uninit();
       TabView.uninit();
       SocialUI.uninit();
       gBrowserThumbnails.uninit();
-      LoopUI.uninit();
       FullZoom.destroy();
 
       Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-origin-blocked");
       Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -103,22 +103,22 @@ static RedirEntry kRedirMap[] = {
 #endif
   { "accounts", "chrome://browser/content/aboutaccounts/aboutaccounts.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
   { "customizing", "chrome://browser/content/customizableui/aboutCustomizing.xul",
     nsIAboutModule::ALLOW_SCRIPT },
   {
     "debugging", "chrome://devtools/content/aboutdebugging/aboutdebugging.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
-  { "loopconversation", "chrome://browser/content/loop/conversation.html",
+  { "loopconversation", "chrome://loop/content/panels/conversation.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT |
     nsIAboutModule::ENABLE_INDEXED_DB },
-  { "looppanel", "chrome://browser/content/loop/panel.html",
+  { "looppanel", "chrome://loop/content/panels/panel.html",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::HIDE_FROM_ABOUTABOUT |
     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 |
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -946,52 +946,16 @@ const CustomizableWidgets = [
   }, {
     id: "email-link-button",
     tooltiptext: "email-link-button.tooltiptext3",
     onCommand: function(aEvent) {
       let win = aEvent.view;
       win.MailIntegration.sendLinkForBrowser(win.gBrowser.selectedBrowser)
     }
   }, {
-    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,
-    introducedInVersion: 4,
-    onBuild: function(aDocument) {
-      // If we're not supposed to see the button, return zip.
-      if (!Services.prefs.getBoolPref("loop.enabled")) {
-        return null;
-      }
-
-      let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView);
-
-      let 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");
-      let 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;
-    }
-  }, {
     id: "web-apps-button",
     label: "web-apps-button.label",
     tooltiptext: "web-apps-button.tooltiptext",
     onCommand: function(aEvent) {
       let win = aEvent.target &&
                 aEvent.target.ownerDocument &&
                 aEvent.target.ownerDocument.defaultView;
       if (win && typeof win.BrowserOpenApps == "function") {
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -1,8 +1,10 @@
+"use strict";
+
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
 
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
@@ -371,16 +373,24 @@ extensions.registerAPI((extension, conte
         }
       },
 
       get: function(tabId, callback) {
         let tab = TabManager.getTab(tabId);
         runSafe(context, callback, TabManager.convert(extension, tab));
       },
 
+      getCurrent(callback) {
+        let tab;
+        if (context.tabId) {
+          tab = TabManager.convert(extension, TabManager.getTab(context.tabId));
+        }
+        runSafe(context, callback, tab);
+      },
+
       getAllInWindow: function(...args) {
         let window, callback;
         if (args.length == 1) {
           callbacks = args[0];
         } else {
           window = WindowManager.getWindow(args[0]);
           callback = args[1];
         }
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -17,14 +17,15 @@ support-files =
 [browser_ext_pageAction_context.js]
 [browser_ext_pageAction_popup.js]
 [browser_ext_browserAction_popup.js]
 [browser_ext_popup_api_injection.js]
 [browser_ext_contextMenus.js]
 [browser_ext_getViews.js]
 [browser_ext_tabs_executeScript.js]
 [browser_ext_tabs_query.js]
+[browser_ext_tabs_getCurrent.js]
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_onUpdated.js]
 [browser_ext_tabs_sendMessage.js]
 [browser_ext_windows_update.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_tab_runtimeConnect.js]
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_disabled.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_disabled.js
@@ -33,42 +33,34 @@ add_task(function* testDisabled() {
 
         browser.test.sendMessage("next-test");
       });
 
       browser.test.sendMessage("ready");
     },
   });
 
-  let browserActionId = makeWidgetId(extension.id) + "-browser-action";
-
-  yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
-
-  let elem = document.getElementById(browserActionId);
+  yield extension.startup();
+  yield extension.awaitMessage("ready");
 
-  function click() {
-    EventUtils.synthesizeMouseAtCenter(elem, {}, window);
-    return new Promise(SimpleTest.executeSoon);
-  }
-
-  yield click();
+  yield clickBrowserAction(extension);
 
   extension.sendMessage("check-clicked", true);
   yield extension.awaitMessage("next-test");
 
   extension.sendMessage("disable");
   yield extension.awaitMessage("next-test");
 
-  yield click();
+  yield clickBrowserAction(extension);
 
   extension.sendMessage("check-clicked", false);
   yield extension.awaitMessage("next-test");
 
   extension.sendMessage("enable");
   yield extension.awaitMessage("next-test");
 
-  yield click();
+  yield clickBrowserAction(extension);
 
   extension.sendMessage("check-clicked", true);
   yield extension.awaitMessage("next-test");
 
   yield extension.unload();
 });
--- a/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_browserAction_popup.js
@@ -110,23 +110,20 @@ add_task(function* testPageActionPopup()
           }
         });
 
         browser.test.sendMessage("next-test");
       },
     },
   });
 
-  let browserActionId = makeWidgetId(extension.id) + "-browser-action";
   let panelId = makeWidgetId(extension.id) + "-panel";
 
   extension.onMessage("send-click", () => {
-    let button = document.getElementById(browserActionId);
-
-    EventUtils.synthesizeMouseAtCenter(button, {}, window);
+    clickBrowserAction(extension);
   });
 
   extension.onMessage("next-test", Task.async(function* () {
     let panel = document.getElementById(panelId);
     if (panel) {
       yield promisePopupShown(panel);
       panel.hidePopup();
 
--- a/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
+++ b/browser/components/extensions/test/browser/browser_ext_pageAction_popup.js
@@ -121,37 +121,35 @@ add_task(function* testPageActionPopup()
       },
     },
   });
 
   let pageActionId = makeWidgetId(extension.id) + "-page-action";
   let panelId = makeWidgetId(extension.id) + "-panel";
 
   extension.onMessage("send-click", () => {
-    let image = document.getElementById(pageActionId);
-
-    let evt = new MouseEvent("click", {});
-    image.dispatchEvent(evt);
+    clickPageAction(extension);
   });
 
   extension.onMessage("next-test", Task.async(function* () {
     let panel = document.getElementById(panelId);
     if (panel) {
       yield promisePopupShown(panel);
       panel.hidePopup();
 
       panel = document.getElementById(panelId);
       is(panel, undefined, "panel successfully removed from document after hiding");
     }
 
     extension.sendMessage("next-test");
   }));
 
 
-  yield Promise.all([extension.startup(), extension.awaitFinish("pageaction-tests-done")]);
+  yield extension.startup();
+  yield extension.awaitFinish("pageaction-tests-done");
 
   yield extension.unload();
 
   let node = document.getElementById(pageActionId);
   is(node, undefined, "pageAction image removed from document");
 
   let panel = document.getElementById(panelId);
   is(panel, undefined, "pageAction panel removed from document");
@@ -187,29 +185,23 @@ add_task(function* testPageActionSecurit
         var tabId = tabs[0].id;
 
         browser.pageAction.show(tabId);
         browser.test.sendMessage("ready");
       });
     },
   });
 
-  yield Promise.all([extension.startup(), extension.awaitMessage("ready")]);
-
-  let browserActionId = makeWidgetId(extension.id) + "-browser-action";
-  let pageActionId = makeWidgetId(extension.id) + "-page-action";
+  yield extension.startup();
+  yield extension.awaitMessage("ready");
 
-  let browserAction = document.getElementById(browserActionId);
-  let evt = new CustomEvent("command", {});
-  browserAction.dispatchEvent(evt);
-
-  let pageAction = document.getElementById(pageActionId);
-  evt = new MouseEvent("click", {});
-  pageAction.dispatchEvent(evt);
+  yield clickBrowserAction(extension);
+  yield clickPageAction(extension);
 
   yield extension.unload();
 
+  let pageActionId = makeWidgetId(extension.id) + "-page-action";
   let node = document.getElementById(pageActionId);
   is(node, undefined, "pageAction image removed from document");
 
   SimpleTest.endMonitorConsole();
   yield waitForConsole;
 });
new file mode 100644
--- /dev/null
+++ b/browser/components/extensions/test/browser/browser_ext_tabs_getCurrent.js
@@ -0,0 +1,66 @@
+"use strict";
+
+add_task(function* () {
+  let extension = ExtensionTestUtils.loadExtension({
+    manifest: {
+      "permissions": ["tabs"],
+
+      "browser_action": { "default_popup": "popup.html" },
+    },
+
+    files: {
+      "tab.js": function() {
+        let url = document.location.href;
+
+        browser.tabs.getCurrent(currentTab => {
+          browser.test.assertEq(currentTab.url, url, "getCurrent in non-active background tab");
+
+          // Activate the tab.
+          browser.tabs.onActivated.addListener(function listener({ tabId }) {
+            if (tabId == currentTab.id) {
+              browser.tabs.onActivated.removeListener(listener);
+
+              browser.tabs.getCurrent(currentTab => {
+                browser.test.assertEq(currentTab.id, tabId, "in active background tab");
+                browser.test.assertEq(currentTab.url, url, "getCurrent in non-active background tab");
+
+                browser.test.sendMessage("tab-finished");
+                browser.tabs.remove(tabId);
+              });
+            }
+          });
+          browser.tabs.update(currentTab.id, { active: true });
+        });
+      },
+
+      "popup.js": function() {
+        browser.tabs.getCurrent(tab => {
+          browser.test.assertEq(tab, undefined, "getCurrent in popup script");
+          browser.test.sendMessage("popup-finished");
+        });
+      },
+
+      "tab.html": `<head><meta charset="utf-8"><script src="tab.js"></script></head>`,
+      "popup.html": `<head><meta charset="utf-8"><script src="popup.js"></script></head>`,
+    },
+
+    background: function() {
+      browser.tabs.getCurrent(tab => {
+        browser.test.assertEq(tab, undefined, "getCurrent in background script");
+        browser.test.sendMessage("background-finished");
+      });
+
+      browser.tabs.create({ url: "tab.html", active: false });
+    },
+  });
+
+  yield extension.startup();
+
+  yield extension.awaitMessage("background-finished");
+  yield extension.awaitMessage("tab-finished");
+
+  clickBrowserAction(extension);
+  yield extension.awaitMessage("popup-finished");
+
+  yield extension.unload();
+});
--- a/browser/components/extensions/test/browser/head.js
+++ b/browser/components/extensions/test/browser/head.js
@@ -18,8 +18,32 @@ var focusWindow = Task.async(function* f
       win.removeEventListener("focus", listener, true);
       resolve();
     }, true);
   });
 
   win.focus();
   yield promise;
 });
+
+function clickBrowserAction(extension, win = window) {
+  let browserActionId = makeWidgetId(extension.id) + "-browser-action";
+  let elem = win.document.getElementById(browserActionId);
+
+  EventUtils.synthesizeMouseAtCenter(elem, {}, win);
+  return new Promise(SimpleTest.executeSoon);
+}
+
+function clickPageAction(extension, win = window) {
+  // This would normally be set automatically on navigation, and cleared
+  // when the user types a value into the URL bar, to show and hide page
+  // identity info and icons such as page action buttons.
+  //
+  // Unfortunately, that doesn't happen automatically in browser chrome
+  // tests.
+  SetPageProxyState("valid");
+
+  let pageActionId = makeWidgetId(extension.id) + "-page-action";
+  let elem = win.document.getElementById(pageActionId);
+
+  EventUtils.synthesizeMouse(elem, 8, 8, {}, win);
+  return new Promise(SimpleTest.executeSoon);
+}
deleted file mode 100644
--- a/browser/components/loop/.eslintignore
+++ /dev/null
@@ -1,31 +0,0 @@
-# This file currently uses a non-standard (and not on a standards track)
-# if statement within catch.
-modules/MozLoopWorker.js
-# This file currently uses es7 features eslint issue:
-# https://github.com/eslint/espree/issues/125
-modules/MozLoopAPI.jsm
-# Libs we don't need to check
-content/libs
-content/shared/libs
-standalone/content/libs
-standalone/node_modules
-# Libs we don't need to check
-test/shared/vendor
-# Coverage files
-test/coverage
-test/node_modules
-# These are generated react files that we don't need to check
-content/js/conversation.js
-content/js/conversationViews.js
-content/js/panel.js
-content/js/roomViews.js
-content/js/feedbackViews.js
-content/shared/js/textChatView.js
-content/shared/js/linkifiedTextView.js
-content/shared/js/views.js
-standalone/content/js/standaloneRoomViews.js
-standalone/content/js/webapp.js
-ui/ui-showcase.js
-# Don't need to check the built tree
-standalone/dist
-
--- a/browser/components/moz.build
+++ b/browser/components/moz.build
@@ -6,17 +6,16 @@
 
 DIRS += [
     'about',
     'customizableui',
     'dirprovider',
     'downloads',
     'extensions',
     'feeds',
-    'loop',
     'migration',
     'newtab',
     'places',
     'pocket',
     'preferences',
     'privatebrowsing',
     'search',
     'sessionstore',
--- a/browser/components/uitour/test/browser_UITour_loop.js
+++ b/browser/components/uitour/test/browser_UITour_loop.js
@@ -4,18 +4,18 @@
 "use strict";
 
 var gTestTab;
 var gContentAPI;
 var gContentWindow;
 var loopButton;
 var loopPanel = document.getElementById("loop-notification-panel");
 
-const { LoopRooms } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
-const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
+const { LoopRooms } = Components.utils.import("chrome://loop/content/modules/LoopRooms.jsm", {});
+const { MozLoopServiceInternal } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
 
 function test() {
   UITourTest();
 }
 
 function runOffline(fun) {
   return (done) => {
     Services.io.offline = true;
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/.eslintignore
@@ -0,0 +1,35 @@
+# This file currently uses a non-standard (and not on a standards track)
+# if statement within catch.
+content/modules/MozLoopWorker.js
+# This file currently uses es7 features eslint issue:
+# https://github.com/eslint/espree/issues/125
+content/modules/MozLoopAPI.jsm
+# Need to fix the configuration for this.
+bootstrap.js
+# Need to drop the preprocessing (bug 1212428)
+content/preferences/prefs.js
+# Libs we don't need to check
+content/panels/vendor
+content/shared/vendor
+standalone/content/libs
+standalone/node_modules
+# Libs we don't need to check
+test/shared/vendor
+# Coverage files
+test/coverage
+test/node_modules
+# These are generated react files that we don't need to check
+content/panels/js/conversation.js
+content/panels/js/conversationViews.js
+content/panels/js/panel.js
+content/panels/js/roomViews.js
+content/panels/js/feedbackViews.js
+content/shared/js/textChatView.js
+content/shared/js/linkifiedTextView.js
+content/shared/js/views.js
+standalone/content/js/standaloneRoomViews.js
+standalone/content/js/webapp.js
+ui/ui-showcase.js
+# Don't need to check the built tree
+standalone/dist
+
rename from browser/components/loop/.eslintrc
rename to browser/extensions/loop/.eslintrc
rename from browser/components/loop/.eslintrc-gecko
rename to browser/extensions/loop/.eslintrc-gecko
--- a/browser/components/loop/.eslintrc-gecko
+++ b/browser/extensions/loop/.eslintrc-gecko
@@ -2,19 +2,20 @@
 // This is applied on top of the basic .eslintrc for gecko specific directories
 // e.g. the modules directory.
 {
   "ecmaFeatures": {
     "arrowFunctions": true,
     "blockBindings": true,
     "destructuring": true,
     "generators": true,
+    "objectLiteralShorthandMethods": true,
     "restParams": true,
     "spread": true,
-    "objectLiteralShorthandMethods": true,
+    "templateStrings": true,
   },
   "globals": {
     // Gecko + Loop Globals.
     "Chat": false,
     "ChromeWorker": false,
     "CommonUtils": false,
     "Components": false,
     "convertToRTCStatsReport": false,
@@ -36,16 +37,17 @@
     "LoopAPI": true,
     "LoopCalls": true,
     "loopCrypto": false,
     "LoopRooms": true,
     "LoopRoomsCache": true,
     "MozLoopPushHandler": true,
     "MozLoopService": true,
     "OS": false,
+    "PrivateBrowsingUtils": false,
     "roomsPushNotification": true,
     "Services": false,
     "Social": false,
     "SocialShare": false,
     "Task": false,
     "UITour": false,
     "WebChannel": false,
     "XPCOMUtils": false,
rename from browser/components/loop/.gitignore
rename to browser/extensions/loop/.gitignore
rename from browser/components/loop/README.txt
rename to browser/extensions/loop/README.txt
--- a/browser/components/loop/README.txt
+++ b/browser/extensions/loop/README.txt
@@ -17,47 +17,47 @@ Working with React JSX files
 
 Our views use [React](http://facebook.github.io/react/) written in JSX files
 and transpiled to JS before we commit. You need to install the JSX compiler
 using npm in order to compile the .jsx files into regular .js ones:
 
     npm install -g react-tools@0.12.2
 
 Once installed, run build-jsx with the --watch option from
-browser/components/loop, eg.:
+browser/extensions/loop, eg.:
 
-    cd browser/components/loop
+    cd browser/extensions/loop
     ./build-jsx --watch
 
 build-jsx can also be do a one-time compile pass instead of watching if
 the --watch argument is omitted.  Be sure to commit any transpiled files
 at the same time as changes to their sources.
 
 
 Hacking
 =======
 Please be sure to execute
 
-  browser/components/loop/run-all-loop-tests.sh
+  browser/extensions/loop/run-all-loop-tests.sh
 
 from the top level before requesting review on a patch.
 
 Linting
 =======
 run-all-loop-tests.sh will take care of this for you automatically, after
 you've installed the dependencies by typing:
 
   ( cd standalone ; make install )
 
 If you install eslint and the react plugin globally:
 
   npm install -g eslint
   npm install -g eslint-plugin-react
 
-You can also run it by hand in the browser/components/loop directory:
+You can also run it by hand in the browser/extensions/loop directory:
 
   eslint --ext .js --ext .jsx --ext .jsm .
 
 Test coverage
 =============
 Initial setup
   cd test
   npm install
@@ -83,17 +83,17 @@ You can run these as part of the run-all
 Functional Tests
 ================
 These are currently a work in progress, but it's already possible to run a test
 if you have a [loop-server](https://github.com/mozilla-services/loop-server)
 install that is properly configured.  From the top-level gecko directory,
 execute:
 
   export LOOP_SERVER=/Users/larry/src/loop-server
-  ./mach marionette-test browser/components/loop/test/functional/manifest.ini
+  ./mach marionette-test browser/extensions/loop/test/functional/manifest.ini
 
 Once the automation is complete, we'll include this in run-all-loop-tests.sh
 as well.
 
 
 UI-Showcase
 ===========
 This is a tool giving the layouts for all the frontend views of Loop, allowing debugging and testing of css layouts and local component behavior.
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/bootstrap.js
@@ -0,0 +1,849 @@
+/* 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";
+
+const { interfaces: Ci, utils: Cu, classes: Cc } = Components;
+
+const kNSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+const kBrowserSharingNotificationId = "loop-sharing-notification";
+const kPrefBrowserSharingInfoBar = "browserSharing.showInfoBar";
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.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");
+
+/**
+ * This window listener gets loaded into each browser.xul window and is used
+ * to provide the required loop functions for the window.
+ */
+var WindowListener = {
+  /**
+   * Sets up the chrome integration within browser windows for Loop.
+   *
+   * @param {Object} window The window to inject the integration into.
+   */
+  setupBrowserUI: function(window) {
+    let document = window.document;
+    let gBrowser = window.gBrowser;
+    let xhrClass = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"];
+    let FileReader = window.FileReader;
+
+    // the "exported" symbols
+    var LoopUI = {
+      /**
+       * @var {XULWidgetSingleWrapper} toolbarButton Getter for the Loop toolbarbutton
+       *                                             instance for this 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() {
+        let browser = document.querySelector("#loop-notification-panel > #loop-panel-iframe");
+        if (browser) {
+          delete this.browser;
+          this.browser = browser;
+        }
+        return browser;
+      },
+
+      /**
+       * @var {String|null} selectedTab Getter for the name of the currently selected
+       *                                tab inside the Loop panel. Will be NULL if
+       *                                the panel hasn't loaded yet.
+       */
+      get selectedTab() {
+        if (!this.browser) {
+          return null;
+        }
+
+        let selectedTab = this.browser.contentDocument.querySelector(".tab-view > .selected");
+        return selectedTab && selectedTab.getAttribute("data-tab-name");
+      },
+
+      /**
+       * @return {Promise}
+       */
+      promiseDocumentVisible(aDocument) {
+        if (!aDocument.hidden) {
+          return Promise.resolve(aDocument);
+        }
+
+        return new Promise((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.
+       * @param {String}   [tabId] Optional name of the tab to select after the panel
+       *                           has opened. Does nothing when the panel is hidden.
+       * @return {Promise}
+       */
+      togglePanel: function(event, tabId = null) {
+        if (!this.panel) {
+          // We're on the hidden window! What fun!
+          let obs = win => {
+            Services.obs.removeObserver(obs, "browser-delayed-startup-finished");
+            win.LoopUI.togglePanel(event, tabId);
+          };
+          Services.obs.addObserver(obs, "browser-delayed-startup-finished", false);
+          return window.OpenBrowserWindow();
+        }
+        if (this.panel.state == "open") {
+          return new Promise(resolve => {
+            this.panel.hidePopup();
+            resolve();
+          });
+        }
+
+        return this.openCallPanel(event, tabId).then(doc => {
+          let fm = Services.focus;
+          fm.moveFocus(doc.defaultView, null, fm.MOVEFOCUS_FIRST, fm.FLAG_NOSCROLL);
+        }).catch(err => {
+          Cu.reportError(err);
+        });
+      },
+
+      /**
+       * 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.
+       * @param {String} [tabId] Identifier of the tab to select when the panel is
+       *                         opened. Example: 'rooms', 'contacts', etc.
+       * @return {Promise}
+       */
+      openCallPanel: function(event, tabId = null) {
+        return new Promise((resolve) => {
+          let callback = iframe => {
+            // Helper function to show a specific tab view in the panel.
+            function showTab() {
+              if (!tabId) {
+                resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
+                return;
+              }
+
+              let win = iframe.contentWindow;
+              let ev = new win.CustomEvent("UIAction", Cu.cloneInto({
+                detail: {
+                  action: "selectTab",
+                  tab: tabId
+                }
+              }, win));
+              win.dispatchEvent(ev);
+              resolve(LoopUI.promiseDocumentVisible(iframe.contentDocument));
+            }
+
+            // If the panel has been opened and initialized before, we can skip waiting
+            // for the content to load - because it's already there.
+            if (("contentWindow" in iframe) && iframe.contentWindow.document.readyState == "complete") {
+              showTab();
+              return;
+            }
+
+            let documentDOMLoaded = () => {
+              iframe.removeEventListener("DOMContentLoaded", documentDOMLoaded, true);
+              // Handle window.close correctly on the panel.
+              this.hookWindowCloseForPanelClose(iframe.contentWindow);
+              iframe.contentWindow.addEventListener("loopPanelInitialized", function loopPanelInitialized() {
+                iframe.contentWindow.removeEventListener("loopPanelInitialized",
+                  loopPanelInitialized);
+                showTab();
+              });
+            };
+            iframe.addEventListener("DOMContentLoaded", documentDOMLoaded, true);
+          };
+
+          // Used to clear the temporary "login" state from the button.
+          Services.obs.notifyObservers(null, "loop-status-changed", null);
+
+          this.shouldResumeTour().then((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.
+              this.MozLoopService.resumeTour("waiting");
+              resolve();
+              return;
+            }
+
+            this.LoopAPI.initialize();
+
+            let anchor = event ? event.target : this.toolbarButton.anchor;
+            let setHeight = 410;
+            if (gBrowser.selectedBrowser.getAttribute("remote") === "true") {
+              setHeight = 262;
+            }
+            this.PanelFrame.showPopup(window, anchor,
+              "loop", null, "about:looppanel",
+              // Loop wants a fixed size for the panel. This also stops it dynamically resizing.
+              { width: 330, height: setHeight },
+              callback);
+          });
+        });
+      },
+
+      /**
+       * 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;
+        }
+
+        let 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() {
+        return new Promise(resolve => {
+          this.LoopRooms.getAll((error, rooms) => {
+            let roomsWithNonOwners = [];
+            for (let room of rooms) {
+              if (!("participants" in room)) {
+                continue;
+              }
+              let numNonOwners = room.participants.filter(participant => !participant.owner).length;
+              if (!numNonOwners) {
+                continue;
+              }
+              roomsWithNonOwners.push(room);
+            }
+            resolve(roomsWithNonOwners);
+          });
+        });
+      },
+
+      /**
+       * Triggers the initialization of the loop service.  Called by
+       * delayedStartup.
+       */
+      init: function() {
+        // Cleanup when the window unloads.
+        window.addEventListener("unload", () => {
+          this.uninit();
+        });
+
+        // Add observer notifications before the service is initialized
+        Services.obs.addObserver(this, "loop-status-changed", false);
+
+        // 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().catch(ex => {
+          if (!ex.message ||
+              (!ex.message.contains("not enabled") &&
+               !ex.message.contains("not needed"))) {
+            console.error(ex);
+          }
+        });
+        this.updateToolbarState();
+      },
+
+      uninit: function() {
+        Services.obs.removeObserver(this, "loop-status-changed");
+      },
+
+      // Implements nsIObserver
+      observe: function(subject, topic, data) {
+        if (topic != "loop-status-changed") {
+          return;
+        }
+        this.updateToolbarState(data);
+      },
+
+      /**
+       * Updates the toolbar/menu-button state to reflect Loop status.
+       *
+       * @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(aReason = null) {
+        if (!this.toolbarButton.node) {
+          return;
+        }
+        let state = "";
+        let mozL10nId = "loop-call-button3";
+        let suffix = ".tooltiptext";
+        if (this.MozLoopService.errors.size) {
+          state = "error";
+          mozL10nId += "-error";
+        } 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(roomsWithNonOwners => {
+            if (roomsWithNonOwners.length > 0) {
+              mozL10nId += "-participantswaiting";
+            } else {
+              mozL10nId += "-active";
+            }
+
+            suffix += "2";
+            this.updateTooltiptext(mozL10nId + suffix);
+            this.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.
+       *
+       * @param {string} [mozL10nId] l10n ID that refelct the current
+       *                           Loop status.
+       */
+      updateTooltiptext: function(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(options) {
+        if (this.MozLoopService.doNotDisturb) {
+          return;
+        }
+
+        if (!options.title) {
+          throw new Error("Missing title, can not display notification");
+        }
+
+        let 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);
+        }
+
+        let notification = new window.Notification(options.title, notificationOptions);
+        notification.addEventListener("click", e => {
+          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(() => {
+            if (typeof options.onclick == "function") {
+              options.onclick();
+            } else {
+              // Open the Loop panel as a default action.
+              this.openCallPanel(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(name) {
+        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", () => this.activeSound = undefined, false);
+      },
+
+      /**
+       * Start listening to selected tab changes and notify any content page that's
+       * listening to 'BrowserSwitch' push messages.
+       *
+       * Push message parameters:
+       * - {Integer} windowId  The new windowId for the browser.
+       */
+      startBrowserSharing: function() {
+        if (!this._listeningToTabSelect) {
+          gBrowser.tabContainer.addEventListener("TabSelect", this);
+          this._listeningToTabSelect = true;
+        }
+
+        this._maybeShowBrowserSharingInfoBar();
+
+        // Get the first window Id for the listener.
+        this.LoopAPI.broadcastPushMessage("BrowserSwitch",
+          gBrowser.selectedBrowser.outerWindowID);
+      },
+
+      /**
+       * Stop listening to selected tab changes.
+       */
+      stopBrowserSharing: function() {
+        if (!this._listeningToTabSelect) {
+          return;
+        }
+
+        this._hideBrowserSharingInfoBar();
+        gBrowser.tabContainer.removeEventListener("TabSelect", this);
+        this._listeningToTabSelect = false;
+      },
+
+      /**
+       * 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(key) {
+        let str = this.MozLoopService.getStrings(key);
+        if (str) {
+          str = JSON.parse(str).textContent;
+        }
+        return str;
+      },
+
+      /**
+       * 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.
+       */
+      _maybeShowBrowserSharingInfoBar: function() {
+        this._hideBrowserSharingInfoBar();
+
+        // Don't show the infobar if it's been permanently disabled from the menu.
+        if (!this.MozLoopService.getLoopPref(kPrefBrowserSharingInfoBar)) {
+          return;
+        }
+
+        let box = gBrowser.getNotificationBox();
+        let paused = false;
+        let bar = box.appendNotification(
+          this._getString("infobar_screenshare_browser_message"),
+          kBrowserSharingNotificationId,
+          // Icon is defined in browser theme CSS.
+          null,
+          box.PRIORITY_WARNING_LOW,
+          [{
+            label: this._getString("infobar_button_pause_label"),
+            accessKey: this._getString("infobar_button_pause_accesskey"),
+            isDefault: false,
+            callback: (event, buttonInfo, buttonNode) => {
+              paused = !paused;
+              bar.label = paused ? this._getString("infobar_screenshare_paused_browser_message") :
+                this._getString("infobar_screenshare_browser_message");
+              bar.classList.toggle("paused", paused);
+              buttonNode.label = paused ? this._getString("infobar_button_resume_label") :
+                this._getString("infobar_button_pause_label");
+              buttonNode.accessKey = paused ? this._getString("infobar_button_resume_accesskey") :
+                this._getString("infobar_button_pause_accesskey");
+              return true;
+            }
+          },
+          {
+            label: this._getString("infobar_button_stop_label"),
+            accessKey: this._getString("infobar_button_stop_accesskey"),
+            isDefault: true,
+            callback: () => {
+              this._hideBrowserSharingInfoBar();
+              LoopUI.MozLoopService.hangupAllChatWindows();
+            }
+          }]
+        );
+
+        // Keep showing the notification bar until the user explicitly closes it.
+        bar.persistence = -1;
+      },
+
+      /**
+       * Hides the infobar, permanantly if requested.
+       *
+       * @param {Boolean} permanently Flag that determines if the infobar will never
+       *                              been shown again. Defaults to `false`.
+       * @return {Boolean} |true| if the infobar was hidden here.
+       */
+      _hideBrowserSharingInfoBar: function(permanently = false, browser) {
+        browser = browser || gBrowser.selectedBrowser;
+        let box = gBrowser.getNotificationBox(browser);
+        let notification = box.getNotificationWithValue(kBrowserSharingNotificationId);
+        let removed = false;
+        if (notification) {
+          box.removeNotification(notification);
+          removed = true;
+        }
+
+        if (permanently) {
+          this.MozLoopService.setLoopPref(kPrefBrowserSharingInfoBar, false);
+        }
+
+        return removed;
+      },
+
+      /**
+       * Handles events from gBrowser.
+       */
+      handleEvent: function(event) {
+        // We only should get "select" events.
+        if (event.type != "TabSelect") {
+          return;
+        }
+
+        let wasVisible = false;
+        // Hide the infobar from the previous tab.
+        if (event.detail.previousTab) {
+          wasVisible = this._hideBrowserSharingInfoBar(false,
+            event.detail.previousTab.linkedBrowser);
+        }
+
+        // We've changed the tab, so get the new window id.
+        this.LoopAPI.broadcastPushMessage("BrowserSwitch",
+          gBrowser.selectedBrowser.outerWindowID);
+
+        if (wasVisible) {
+          // If the infobar was visible before, we should show it again after the
+          // switch.
+          this._maybeShowBrowserSharingInfoBar();
+        }
+      },
+
+      /**
+       * 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(callback) {
+        let 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(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.
+          let xhr = xhrClass.createInstance(Ci.nsIXMLHttpRequest);
+          xhr.open("get", uri.spec, true);
+          xhr.responseType = "blob";
+          xhr.overrideMimeType("image/x-icon");
+          xhr.onload = () => {
+            if (xhr.status != 200) {
+              callback(new Error("Invalid status code received for favicon XHR: " + xhr.status));
+              return;
+            }
+
+            let reader = new FileReader();
+            reader.onload = reader.onload = () => callback(null, reader.result);
+            reader.onerror = callback;
+            reader.readAsDataURL(xhr.response);
+          };
+          xhr.onerror = callback;
+          xhr.send();
+        }).catch(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;
+  },
+
+  tearDownBrowserUI: function(window) {
+    let document = window.document;
+
+    // Take any steps to remove UI or anything from the browser window
+    // document.getElementById() etc. will work here
+    // XXX Add in tear-down of the panel.
+  },
+
+  // nsIWindowMediatorListener functions.
+  onOpenWindow: function(xulWindow) {
+    // A new window has opened.
+    let 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(xulWindow) {
+  },
+
+  onWindowTitleChange: function(xulWindow, newTitle) {
+  }
+};
+
+/**
+ * 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(aDocument) {
+      // If we're not supposed to see the button, return zip.
+      if (!Services.prefs.getBoolPref("loop.enabled")) {
+        return null;
+      }
+
+      let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(aDocument.defaultView);
+
+      let 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");
+      }
+      let 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: (key, val) => {
+      switch (typeof val) {
+        case "boolean":
+          branch.setBoolPref(key, val);
+          break;
+        case "number":
+          branch.setIntPref(key, val);
+          break;
+        case "string":
+          branch.setCharPref(key, val);
+          break;
+      }
+    }
+  });
+}
+
+/**
+ * Called when the add-on is started, e.g. when installed or when Firefox starts.
+ */
+function startup() {
+  loadDefaultPrefs();
+
+  createLoopButton();
+
+  // Attach to hidden window (for OS X).
+  try {
+    WindowListener.setupBrowserUI(Services.appShell.hiddenDOMWindow);
+  } catch (ex) {
+    // Hidden window didn't exist, so wait until startup is done.
+    let 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.
+  let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
+  let windows = wm.getEnumerator("navigator:browser");
+  while (windows.hasMoreElements()) {
+    let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
+    WindowListener.setupBrowserUI(domWindow);
+  }
+
+  // Wait for any new browser windows to open.
+  wm.addListener(WindowListener);
+
+  // Load our stylesheets.
+  let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
+    .getService(Components.interfaces.nsIStyleSheetService);
+  let sheets = ["chrome://loop-shared/skin/loop.css",
+                "chrome://loop/skin/platform.css"];
+  for (let sheet of sheets) {
+    let styleSheetURI = Services.io.newURI(sheet, null, null);
+    // XXX We would love to specify AUTHOR_SHEET here and in shutdown, however
+    // bug 1228542 prevents us from doing that as we'd cause a lot of assertions
+    // in debug mode for tests. Once that is fixed, we should be able to change
+    // this, and remove the !important attributes from our syle sheets.
+    styleSheetService.loadAndRegisterSheet(styleSheetURI,
+                                           styleSheetService.USER_SHEET);
+  }
+}
+
+/**
+ * Called when the add-on is shutting down, could be for re-installation
+ * or just uninstall.
+ */
+function shutdown() {
+  // Close any open chat windows
+  Cu.import("resource:///modules/Chat.jsm");
+  let isLoopURL = ({ src }) => /^about:loopconversation#/.test(src);
+  [...Chat.chatboxes].filter(isLoopURL).forEach(chatbox => {
+    chatbox.content.contentWindow.close();
+  });
+
+  // Detach from hidden window (for OS X).
+  WindowListener.tearDownBrowserUI(Services.appShell.hiddenDOMWindow);
+
+  // Detach from browser windows.
+  let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
+  let windows = wm.getEnumerator("navigator:browser");
+  while (windows.hasMoreElements()) {
+    let domWindow = windows.getNext().QueryInterface(Ci.nsIDOMWindow);
+    WindowListener.tearDownBrowserUI(domWindow);
+  }
+
+  // Stop waiting for browser windows to open.
+  wm.removeListener(WindowListener);
+
+  CustomizableUI.destroyWidget("loop-button");
+
+  // Unload stylesheets.
+  let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
+    .getService(Components.interfaces.nsIStyleSheetService);
+  let sheets = ["chrome://loop/content/addon/css/loop.css",
+                "chrome://loop/skin/platform.css"];
+  for (let sheet of sheets) {
+    let styleSheetURI = Services.io.newURI(sheet, null, null);
+    if (styleSheetService.sheetRegistered(styleSheetURI,
+                                          styleSheetService.USER_SHEET)) {
+      styleSheetService.unregisterSheet(styleSheetURI,
+                                        styleSheetService.USER_SHEET);
+    }
+  }
+
+  // Unload modules.
+  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() {}
rename from browser/components/loop/build-jsx
rename to browser/extensions/loop/build-jsx
--- a/browser/components/loop/build-jsx
+++ b/browser/extensions/loop/build-jsx
@@ -69,17 +69,17 @@ def find_react_command():
 
     # This is what node really wants to run.
     jsx_path = os.path.join(jsx_path,
                             "node_modules", "react-tools", "bin", "jsx")
 
     return [node, jsx_path]
 
 
-SHARED_LIBS_DIR=os.path.join(os.path.dirname(__file__), "content", "shared", "libs")
+SHARED_LIBS_DIR=os.path.join(os.path.dirname(__file__), "content", "shared", "vendor")
 REACT_VERSION=find_react_version(SHARED_LIBS_DIR)
 
 src_files = []  # files to be compiled
 
 run_command = find_react_command()
 
 if sys.platform == 'win32':
     print 'Please ensure you are running react-tools version %s' % REACT_VERSION
@@ -94,17 +94,17 @@ else:
     if not info == REACT_VERSION:
         print 'You have the wrong version of react-tools installed'
         print 'Please use version %s' % REACT_VERSION
         exit(1)
 
 # parse the CLI arguments
 description = 'Loop build tool for JSX files. ' + \
               'Will scan entire loop directory and compile them in place. ' + \
-              'Must be executed from browser/components/loop directory.'
+              'Must be executed from browser/extensions/loop directory.'
 
 parser = argparse.ArgumentParser(description=description)
 parser.add_argument('--watch', '-w', action='store_true', help='continuous' +
                     'build based on file changes (optional)')
 args = parser.parse_args()
 
 # loop through all tuples and get unique dirs only
 unique_jsx_dirs = []
rename from browser/components/loop/modules/.eslintrc
rename to browser/extensions/loop/content/modules/.eslintrc
--- a/browser/components/loop/modules/.eslintrc
+++ b/browser/extensions/loop/content/modules/.eslintrc
@@ -1,3 +1,3 @@
 {
-  "extends": "../.eslintrc-gecko"
+  "extends": "../../.eslintrc-gecko"
 }
rename from browser/components/loop/modules/LoopRooms.jsm
rename to browser/extensions/loop/content/modules/LoopRooms.jsm
--- a/browser/components/loop/modules/LoopRooms.jsm
+++ b/browser/extensions/loop/content/modules/LoopRooms.jsm
@@ -5,38 +5,38 @@
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 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");
 
-const { MozLoopService, LOOP_SESSION_TYPE } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
+const { MozLoopService, LOOP_SESSION_TYPE } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
 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() {
   const { EventEmitter } = Cu.import("resource://devtools/shared/event-emitter.js", {});
   return new EventEmitter();
 });
 XPCOMUtils.defineLazyGetter(this, "gLoopBundle", function() {
   return Services.strings.createBundle("chrome://browser/locale/loop/loop.properties");
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoopRoomsCache",
-  "resource:///modules/loop/LoopRoomsCache.jsm");
+  "chrome://loop/content/modules/LoopRoomsCache.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "loopUtils",
-  "resource:///modules/loop/utils.js", "utils");
+  "chrome://loop/content/modules/utils.js", "utils");
 XPCOMUtils.defineLazyModuleGetter(this, "loopCrypto",
-  "resource:///modules/loop/crypto.js", "LoopCrypto");
+  "chrome://loop/content/shared/js/crypto.js", "LoopCrypto");
 XPCOMUtils.defineLazyModuleGetter(this, "ObjectUtils",
   "resource://gre/modules/ObjectUtils.jsm");
 
 
 this.EXPORTED_SYMBOLS = ["LoopRooms", "roomsPushNotification"];
 
 // The maximum number of clients that we support currently.
 const CLIENT_MAX_SIZE = 2;
rename from browser/components/loop/modules/LoopRoomsCache.jsm
rename to browser/extensions/loop/content/modules/LoopRoomsCache.jsm
--- a/browser/components/loop/modules/LoopRoomsCache.jsm
+++ b/browser/extensions/loop/content/modules/LoopRoomsCache.jsm
@@ -4,17 +4,17 @@
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Task.jsm");
 const { MozLoopService, LOOP_SESSION_TYPE } =
-  Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
+  Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
 
 this.EXPORTED_SYMBOLS = ["LoopRoomsCache"];
 
 const LOOP_ROOMS_CACHE_FILENAME = "loopRoomsCache.json";
 XPCOMUtils.defineConstant(this, "LOOP_ROOMS_CACHE_FILENAME", LOOP_ROOMS_CACHE_FILENAME);
rename from browser/components/loop/modules/MozLoopAPI.jsm
rename to browser/extensions/loop/content/modules/MozLoopAPI.jsm
--- a/browser/components/loop/modules/MozLoopAPI.jsm
+++ b/browser/extensions/loop/content/modules/MozLoopAPI.jsm
@@ -4,18 +4,18 @@
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource:///modules/loop/MozLoopService.jsm");
-Cu.import("resource:///modules/loop/LoopRooms.jsm");
+Cu.import("chrome://loop/content/modules/MozLoopService.jsm");
+Cu.import("chrome://loop/content/modules/LoopRooms.jsm");
 Cu.importGlobalProperties(["Blob"]);
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
                                         "resource://gre/modules/PageMetadata.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
                                         "resource://gre/modules/PluralForm.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                         "resource://gre/modules/UpdateUtils.jsm");
rename from browser/components/loop/modules/MozLoopPushHandler.jsm
rename to browser/extensions/loop/content/modules/MozLoopPushHandler.jsm
--- a/browser/components/loop/modules/MozLoopPushHandler.jsm
+++ b/browser/extensions/loop/content/modules/MozLoopPushHandler.jsm
@@ -5,17 +5,17 @@
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 
-const { MozLoopService } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
+const { MozLoopService } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
 const consoleLog = MozLoopService.log;
 
 this.EXPORTED_SYMBOLS = ["MozLoopPushHandler"];
 
 const CONNECTION_STATE_CLOSED = 0;
 const CONNECTION_STATE_CONNECTING = 1;
 const CONNECTION_STATE_OPEN = 2;
 
rename from browser/components/loop/modules/MozLoopService.jsm
rename to browser/extensions/loop/content/modules/MozLoopService.jsm
--- a/browser/components/loop/modules/MozLoopService.jsm
+++ b/browser/extensions/loop/content/modules/MozLoopService.jsm
@@ -116,24 +116,24 @@ XPCOMUtils.defineConstant(this, "LOOP_SE
 XPCOMUtils.defineConstant(this, "TWO_WAY_MEDIA_CONN_LENGTH", TWO_WAY_MEDIA_CONN_LENGTH);
 XPCOMUtils.defineConstant(this, "SHARING_STATE_CHANGE", SHARING_STATE_CHANGE);
 XPCOMUtils.defineConstant(this, "SHARING_ROOM_URL", SHARING_ROOM_URL);
 XPCOMUtils.defineConstant(this, "ROOM_CREATE", ROOM_CREATE);
 XPCOMUtils.defineConstant(this, "ROOM_DELETE", ROOM_DELETE);
 XPCOMUtils.defineConstant(this, "ROOM_CONTEXT_ADD", ROOM_CONTEXT_ADD);
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoopAPI",
-  "resource:///modules/loop/MozLoopAPI.jsm");
+  "chrome://loop/content/modules/MozLoopAPI.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport",
   "resource://gre/modules/media/RTCStatsReport.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "loopUtils",
-  "resource:///modules/loop/utils.js", "utils");
+  "chrome://loop/content/modules/utils.js", "utils");
 XPCOMUtils.defineLazyModuleGetter(this, "loopCrypto",
-  "resource:///modules/loop/crypto.js", "LoopCrypto");
+  "chrome://loop/content/shared/js/crypto.js", "LoopCrypto");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
                                   "resource://services-common/utils.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CryptoUtils",
                                   "resource://services-crypto/utils.js");
@@ -146,23 +146,23 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials",
                                   "resource://services-common/hawkrequest.js");
 
 XPCOMUtils.defineLazyModuleGetter(this, "hookWindowCloseForPanelClose",
                                   "resource://gre/modules/MozSocialAPI.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoopRooms",
-                                  "resource:///modules/loop/LoopRooms.jsm");
+                                  "chrome://loop/content/modules/LoopRooms.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "roomsPushNotification",
-                                  "resource:///modules/loop/LoopRooms.jsm");
+                                  "chrome://loop/content/modules/LoopRooms.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
-                                  "resource:///modules/loop/MozLoopPushHandler.jsm");
+                                  "chrome://loop/content/modules/MozLoopPushHandler.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
                                   "resource:///modules/UITour.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
                                    "@mozilla.org/uuid-generator;1",
                                    "nsIUUIDGenerator");
 
rename from browser/components/loop/modules/MozLoopWorker.js
rename to browser/extensions/loop/content/modules/MozLoopWorker.js
rename from browser/components/loop/content/conversation.html
rename to browser/extensions/loop/content/panels/conversation.html
--- a/browser/components/loop/content/conversation.html
+++ b/browser/extensions/loop/content/panels/conversation.html
@@ -2,47 +2,48 @@
 <!-- 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/.  -->
 <html>
   <head>
     <meta charset="utf-8">
     <!-- Title is set in conversation.js -->
     <title></title>
-    <link rel="stylesheet" type="text/css" href="loop/shared/css/reset.css">
-    <link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
-    <link rel="stylesheet" type="text/css" href="loop/shared/css/conversation.css">
+    <base href="chrome://loop/content">
+    <link rel="stylesheet" type="text/css" href="shared/css/reset.css">
+    <link rel="stylesheet" type="text/css" href="shared/css/common.css">
+    <link rel="stylesheet" type="text/css" href="shared/css/conversation.css">
  </head>
   <body class="fx-embedded">
 
     <div id="messages"></div>
 
     <div id="main"></div>
 
-    <script type="text/javascript" src="loop/libs/l10n.js"></script>
-    <script type="text/javascript" src="loop/js/otconfig.js"></script>
-    <script type="text/javascript" src="loop/libs/sdk.js"></script>
-    <script type="text/javascript" src="loop/shared/libs/react-0.13.3.js"></script>
-    <script type="text/javascript" src="loop/shared/libs/lodash-3.9.3.js"></script>
-    <script type="text/javascript" src="loop/shared/libs/backbone-1.2.1.js"></script>
-    <script type="text/javascript" src="loop/shared/libs/classnames-2.2.0.js"></script>
+    <script type="text/javascript" src="panels/vendor/l10n.js"></script>
+    <script type="text/javascript" src="panels/js/otconfig.js"></script>
+    <script type="text/javascript" src="shared/vendor/sdk.js"></script>
+    <script type="text/javascript" src="shared/vendor/react-0.13.3.js"></script>
+    <script type="text/javascript" src="shared/vendor/lodash-3.9.3.js"></script>
+    <script type="text/javascript" src="shared/vendor/backbone-1.2.1.js"></script>
+    <script type="text/javascript" src="shared/vendor/classnames-2.2.0.js"></script>
 
-    <script type="text/javascript" src="loop/shared/js/loopapi-client.js"></script>
-    <script type="text/javascript" src="loop/shared/js/utils.js"></script>
-    <script type="text/javascript" src="loop/shared/js/mixins.js"></script>
-    <script type="text/javascript" src="loop/shared/js/actions.js"></script>
-    <script type="text/javascript" src="loop/shared/js/validate.js"></script>
-    <script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
-    <script type="text/javascript" src="loop/shared/js/otSdkDriver.js"></script>
-    <script type="text/javascript" src="loop/shared/js/store.js"></script>
-    <script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
-    <script type="text/javascript" src="loop/shared/js/views.js"></script>
-    <script type="text/javascript" src="loop/js/feedbackViews.js"></script>
-    <script type="text/javascript" src="loop/shared/js/textChatStore.js"></script>
-    <script type="text/javascript" src="loop/shared/js/textChatView.js"></script>
-    <script type="text/javascript" src="loop/shared/js/linkifiedTextView.js"></script>
-    <script type="text/javascript" src="loop/shared/js/urlRegExps.js"></script>
-    <script type="text/javascript" src="loop/js/conversationAppStore.js"></script>
-    <script type="text/javascript" src="loop/js/roomStore.js"></script>
-    <script type="text/javascript" src="loop/js/roomViews.js"></script>
-    <script type="text/javascript" src="loop/js/conversation.js"></script>
+    <script type="text/javascript" src="shared/js/loopapi-client.js"></script>
+    <script type="text/javascript" src="shared/js/utils.js"></script>
+    <script type="text/javascript" src="shared/js/mixins.js"></script>
+    <script type="text/javascript" src="shared/js/actions.js"></script>
+    <script type="text/javascript" src="shared/js/validate.js"></script>
+    <script type="text/javascript" src="shared/js/dispatcher.js"></script>
+    <script type="text/javascript" src="shared/js/otSdkDriver.js"></script>
+    <script type="text/javascript" src="shared/js/store.js"></script>
+    <script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
+    <script type="text/javascript" src="shared/js/views.js"></script>
+    <script type="text/javascript" src="shared/js/textChatStore.js"></script>
+    <script type="text/javascript" src="shared/js/textChatView.js"></script>
+    <script type="text/javascript" src="shared/js/linkifiedTextView.js"></script>
+    <script type="text/javascript" src="shared/js/urlRegExps.js"></script>
+    <script type="text/javascript" src="panels/js/conversationAppStore.js"></script>
+    <script type="text/javascript" src="panels/js/feedbackViews.js"></script>
+    <script type="text/javascript" src="panels/js/roomStore.js"></script>
+    <script type="text/javascript" src="panels/js/roomViews.js"></script>
+    <script type="text/javascript" src="panels/js/conversation.js"></script>
   </body>
 </html>
rename from browser/components/loop/content/css/panel.css
rename to browser/extensions/loop/content/panels/css/panel.css
--- a/browser/components/loop/content/css/panel.css
+++ b/browser/extensions/loop/content/panels/css/panel.css
@@ -7,17 +7,17 @@ html {
 }
 
 body {
   background: none;
 }
 
 /* Beta Ribbon */
 .beta-ribbon {
-  background: url("../shared/img/beta-ribbon.svg") no-repeat;
+  background: url("../../shared/img/beta-ribbon.svg") no-repeat;
   background-size: 30px;
   width: 30px;
   height: 30px;
 }
 
 /* Panel styles */
 
 .panel {
@@ -153,17 +153,17 @@ body {
   text-align: center;
   color: #4a4a4a;
   font-weight: lighter;
   position: relative;
   top: 50%;
   transform: translateY(-50%);
   padding-top: 11rem;
   padding-bottom: 1rem;
-  background-image: url("../shared/img/empty_conversations.svg");
+  background-image: url("../../shared/img/empty_conversations.svg");
   background-repeat: no-repeat;
   background-position: top center;
 }
 
 .panel-text-medium,
 .panel-text-large {
   margin: 3px 0;
 }
@@ -319,31 +319,31 @@ body {
 }
 
 .room-list > .room-entry:hover > h2 > button {
   animation: drop-and-fade-in 0.250s;
   animation-fill-mode: forwards;
 }
 
 .room-list > .room-entry:hover > h2 > .copy-link {
-  background-image: url(../shared/img/icons-16x16.svg#copy);
+  background-image: url(../../shared/img/icons-16x16.svg#copy);
 }
 
 .room-list > .room-entry:hover > h2 > .delete-link {
-  background-image: url(../shared/img/icons-16x16.svg#trash);
+  background-image: url(../../shared/img/icons-16x16.svg#trash);
 }
 
 /* scale this up to 1.1x and then back to the original size */
 @keyframes pulse {
   0%, 100% { transform: scale(1.0); }
   50%      { transform: scale(1.1); }
 }
 
 .room-list > .room-entry > h2 > .copy-link.checked {
-  background: transparent url(../shared/img/icons-16x16.svg#checkmark);
+  background: transparent url(../../shared/img/icons-16x16.svg#checkmark);
   animation: pulse .150s;
   animation-timing-function: ease-in-out;
   top: 0;
 }
 
 /* keep the various room-entry row pieces aligned with each other */
 .room-list > .room-entry > h2 > button,
 .room-list > .room-entry > h2 > span {
@@ -357,17 +357,17 @@ body {
 }
 
 .room-entry:hover .room-entry-context-actions {
   display: inline-block;
 }
 
 /* Room entry edit button */
 .room-entry-context-edit-btn {
-  background-image: url("../shared/img/icons-10x10.svg#edit-darkgrey");
+  background-image: url("../../shared/img/icons-10x10.svg#edit-darkgrey");
   background-position: center;
   background-repeat: no-repeat;
   background-size: 12px;
   cursor: pointer;
   display: inline-block;
   height: 24px;
   vertical-align: middle;
   width: 16px;
@@ -394,17 +394,17 @@ html[dir="rtl"] .room-entry-context-acti
 
 .room-entry-context-item > img,
 .room-entry-context-item > a > img {
   width: 16px;
 }
 
 .button-close {
   background-color: transparent;
-  background-image: url(../shared/img/icons-10x10.svg#close);
+  background-image: url(../../shared/img/icons-10x10.svg#close);
   background-repeat: no-repeat;
   background-size: 8px 8px;
   border: none;
   padding: 0;
   height: 8px;
   width: 8px;
 }
 
@@ -423,26 +423,26 @@ html[dir="rtl"] .room-entry-context-acti
 .spinner {
   width: 16px;
   height: 16px;
   background-repeat: no-repeat;
   background-size: 16px 16px;
 }
 
 .spinner.busy {
-  background-image: url(../shared/img/spinner.png);
+  background-image: url(../../shared/img/spinner.png);
   animation-name: spinnerRotate;
   animation-duration: 1s;
   animation-timing-function: linear;
   animation-iteration-count: infinite;
 }
 
 @media (min-resolution: 2dppx) {
   .spinner.busy {
-    background-image: url(../shared/img/spinner@2x.png);
+    background-image: url(../../shared/img/spinner@2x.png);
   }
 }
 
 /* Share tab */
 
 .generate-url-stack {
   margin: 14px 0;
   position: relative;
@@ -497,60 +497,60 @@ html[dir="rtl"] .generate-url-spinner {
   margin-top: 0;
 }
 
 #powered-by-logo {
   display: inline-block;
   margin-left: 10px;
   margin-right: 10px;
   vertical-align: middle;
-  background-image: url("../shared/img/telefonica.png");
+  background-image: url("../../shared/img/telefonica.png");
   background-size: 72px 20px;
   width: 72px;
   height: 20px;
 }
 
 #powered-by-logo.en-GB,
 #powered-by-logo.de {
-  background-image: url("../shared/img/02.png");
+  background-image: url("../../shared/img/02.png");
   background-size: 21px 20px;
   width: 21px;
   height: 20px;
 }
 
 #powered-by-logo.pt-BR {
-  background-image: url("../shared/img/vivo.png");
+  background-image: url("../../shared/img/vivo.png");
   background-size: 53px 26px;
   width: 53px;
   height: 26px;
 }
 
 #powered-by-logo[class^="es-"] {
-  background-image: url("../shared/img/movistar.png");
+  background-image: url("../../shared/img/movistar.png");
   background-size: 92px 20px;
   width: 92px;
   height: 20px;
 }
 
 @media (min-resolution: 2dppx) {
   #powered-by-logo {
-    background-image: url("../shared/img/telefonica@2x.png");
+    background-image: url("../../shared/img/telefonica@2x.png");
   }
 
   #powered-by-logo.en-GB,
   #powered-by-logo.de {
-    background-image: url("../shared/img/02@2x.png");
+    background-image: url("../../shared/img/02@2x.png");
   }
 
   #powered-by-logo.pt-BR {
-    background-image: url("../shared/img/vivo@2x.png");
+    background-image: url("../../shared/img/vivo@2x.png");
   }
 
   #powered-by-logo[class^="es-"] {
-    background-image: url("../shared/img/movistar@2x.png");
+    background-image: url("../../shared/img/movistar@2x.png");
   }
 }
 
 .terms-service {
   padding-left: 5rem;
   padding-right: 5rem;
   padding-bottom: 1rem;
   text-align: center;
@@ -589,17 +589,17 @@ html[dir="rtl"] .generate-url-spinner {
 .button-settings {
   width: 10px;
   height: 10px;
   margin: 0;
   padding: 0;
   border: none;
   cursor: pointer;
   vertical-align: middle;
-  background: transparent url("../shared/img/icons-10x10.svg#settings-cog");
+  background: transparent url("../../shared/img/icons-10x10.svg#settings-cog");
   background-position: center;
   background-repeat: no-repeat;
   background-size: cover;
   -moz-margin-start: .5em;
 }
 
 .user-details .dropdown-menu {
   bottom: 1.3rem; /* Just above the text. */
rename from browser/components/loop/content/js/.eslintrc
rename to browser/extensions/loop/content/panels/js/.eslintrc
rename from browser/components/loop/content/js/conversation.js
rename to browser/extensions/loop/content/panels/js/conversation.js
rename from browser/components/loop/content/js/conversation.jsx
rename to browser/extensions/loop/content/panels/js/conversation.jsx
rename from browser/components/loop/content/js/conversationAppStore.js
rename to browser/extensions/loop/content/panels/js/conversationAppStore.js
rename from browser/components/loop/content/js/feedbackViews.js
rename to browser/extensions/loop/content/panels/js/feedbackViews.js
rename from browser/components/loop/content/js/feedbackViews.jsx
rename to browser/extensions/loop/content/panels/js/feedbackViews.jsx
rename from browser/components/loop/content/js/otconfig.js
rename to browser/extensions/loop/content/panels/js/otconfig.js
rename from browser/components/loop/content/js/panel.js
rename to browser/extensions/loop/content/panels/js/panel.js
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/extensions/loop/content/panels/js/panel.js
@@ -24,17 +24,17 @@ loop.panel = (function(_, mozL10n) {
       window.dispatchEvent(event);
       this.closeWindow();
     },
 
     render: function() {
       return (
         React.createElement("div", {className: "fte-get-started-content"}, 
           React.createElement("header", {className: "fte-title"}, 
-            React.createElement("img", {src: "loop/shared/img/hello_logo.svg"}), 
+            React.createElement("img", {src: "shared/img/hello_logo.svg"}), 
             React.createElement("div", {className: "fte-subheader"}, 
               mozL10n.get("first_time_experience_subheading")
             )
           ), 
           React.createElement(Button, {additionalClass: "fte-get-started-button", 
                   caption: mozL10n.get("first_time_experience_button_label"), 
                   htmlId: "fte-button", 
                   onClick: this.handleButtonClick})
@@ -374,28 +374,28 @@ loop.panel = (function(_, mozL10n) {
         loop.request("OpenURL", event.currentTarget.href);
         this.closeWindow();
       }
     },
 
     _renderDefaultIcon: function() {
       return (
         React.createElement("div", {className: "room-entry-context-item"}, 
-          React.createElement("img", {src: "loop/shared/img/icons-16x16.svg#globe"})
+          React.createElement("img", {src: "shared/img/icons-16x16.svg#globe"})
         )
       );
     },
 
     _renderIcon: function(roomUrl) {
       return (
         React.createElement("div", {className: "room-entry-context-item"}, 
           React.createElement("a", {href: roomUrl.location, 
             onClick: this.handleClick, 
             title: roomUrl.description}, 
-            React.createElement("img", {src: roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"})
+            React.createElement("img", {src: roomUrl.thumbnail || "shared/img/icons-16x16.svg#globe"})
           )
         )
       );
     },
 
     render: function() {
       var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
       if (roomUrl && roomUrl.location) {
@@ -712,17 +712,17 @@ loop.panel = (function(_, mozL10n) {
      * Let the user know we're loading rooms
      * @returns {Object} React render
      */
     _renderLoadingRoomsView: function() {
       return (
         React.createElement("div", {className: "room-list"}, 
           this._renderNewRoomButton(), 
           React.createElement("div", {className: "room-list-loading"}, 
-            React.createElement("img", {src: "loop/shared/img/animated-spinner.svg"})
+            React.createElement("img", {src: "shared/img/animated-spinner.svg"})
           )
         )
       );
     },
 
     _renderNoRoomsView: function() {
       return (
         React.createElement("div", {className: "rooms"}, 
@@ -879,17 +879,17 @@ loop.panel = (function(_, mozL10n) {
     propTypes: {
       onClick: React.PropTypes.func.isRequired
     },
 
     render: function() {
       return (
         React.createElement("div", {className: "error-content"}, 
           React.createElement("header", {className: "error-title"}, 
-            React.createElement("img", {src: "loop/shared/img/sad_hello_icon_64x64.svg"}), 
+            React.createElement("img", {src: "shared/img/sad_hello_icon_64x64.svg"}), 
             React.createElement("p", {className: "error-subheader"}, 
               mozL10n.get("e10s_not_supported_subheading", {
                 brandShortname: mozL10n.get("clientShortname2")
               })
             )
           ), 
           React.createElement(Button, {additionalClass: "e10s-not-supported-button", 
                   caption: mozL10n.get("e10s_not_supported_button_label"), 
rename from browser/components/loop/content/js/panel.jsx
rename to browser/extensions/loop/content/panels/js/panel.jsx
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/extensions/loop/content/panels/js/panel.jsx
@@ -24,17 +24,17 @@ loop.panel = (function(_, mozL10n) {
       window.dispatchEvent(event);
       this.closeWindow();
     },
 
     render: function() {
       return (
         <div className="fte-get-started-content">
           <header className="fte-title">
-            <img src="loop/shared/img/hello_logo.svg" />
+            <img src="shared/img/hello_logo.svg" />
             <div className="fte-subheader">
               {mozL10n.get("first_time_experience_subheading")}
             </div>
           </header>
           <Button additionalClass="fte-get-started-button"
                   caption={mozL10n.get("first_time_experience_button_label")}
                   htmlId="fte-button"
                   onClick={this.handleButtonClick} />
@@ -374,28 +374,28 @@ loop.panel = (function(_, mozL10n) {
         loop.request("OpenURL", event.currentTarget.href);
         this.closeWindow();
       }
     },
 
     _renderDefaultIcon: function() {
       return (
         <div className="room-entry-context-item">
-          <img src="loop/shared/img/icons-16x16.svg#globe" />
+          <img src="shared/img/icons-16x16.svg#globe" />
         </div>
       );
     },
 
     _renderIcon: function(roomUrl) {
       return (
         <div className="room-entry-context-item">
           <a href={roomUrl.location}
             onClick={this.handleClick}
             title={roomUrl.description}>
-            <img src={roomUrl.thumbnail || "loop/shared/img/icons-16x16.svg#globe"} />
+            <img src={roomUrl.thumbnail || "shared/img/icons-16x16.svg#globe"} />
           </a>
         </div>
       );
     },
 
     render: function() {
       var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
       if (roomUrl && roomUrl.location) {
@@ -712,17 +712,17 @@ loop.panel = (function(_, mozL10n) {
      * Let the user know we're loading rooms
      * @returns {Object} React render
      */
     _renderLoadingRoomsView: function() {
       return (
         <div className="room-list">
           {this._renderNewRoomButton()}
           <div className="room-list-loading">
-            <img src="loop/shared/img/animated-spinner.svg" />
+            <img src="shared/img/animated-spinner.svg" />
           </div>
         </div>
       );
     },
 
     _renderNoRoomsView: function() {
       return (
         <div className="rooms">
@@ -879,17 +879,17 @@ loop.panel = (function(_, mozL10n) {
     propTypes: {
       onClick: React.PropTypes.func.isRequired
     },
 
     render: function() {
       return (
         <div className="error-content">
           <header className="error-title">
-            <img src="loop/shared/img/sad_hello_icon_64x64.svg" />
+            <img src="shared/img/sad_hello_icon_64x64.svg" />
             <p className="error-subheader">
               {mozL10n.get("e10s_not_supported_subheading", {
                 brandShortname: mozL10n.get("clientShortname2")
               })}
             </p>
           </header>
           <Button additionalClass="e10s-not-supported-button"
                   caption={mozL10n.get("e10s_not_supported_button_label")}
rename from browser/components/loop/content/js/roomStore.js
rename to browser/extensions/loop/content/panels/js/roomStore.js
rename from browser/components/loop/content/js/roomViews.js
rename to browser/extensions/loop/content/panels/js/roomViews.js
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/extensions/loop/content/panels/js/roomViews.js
@@ -341,24 +341,24 @@ loop.roomViews = (function(mozL10n) {
             hide: this.props.showEditContext
           })}, 
             React.createElement("div", {className: cx({
                 "btn-copy": true,
                 "invite-button": true,
                 "triggered": this.state.copiedUrl
               }), 
               onClick: this.handleCopyButtonClick}, 
-              React.createElement("img", {src: "loop/shared/img/svg/glyph-link-16x16.svg"}), 
+              React.createElement("img", {src: "shared/img/glyph-link-16x16.svg"}), 
               React.createElement("p", null, mozL10n.get(this.state.copiedUrl ?
                 "invite_copied_link_button" : "invite_copy_link_button"))
             ), 
             React.createElement("div", {className: "btn-email invite-button", 
               onClick: this.handleEmailButtonClick, 
               onMouseOver: this.resetTriggeredButtons}, 
-              React.createElement("img", {src: "loop/shared/img/svg/glyph-email-16x16.svg"}), 
+              React.createElement("img", {src: "shared/img/glyph-email-16x16.svg"}), 
               React.createElement("p", null, mozL10n.get("invite_email_link_button"))
             )
           ), 
           React.createElement(SocialShareDropdown, {
             dispatcher: this.props.dispatcher, 
             ref: "menu", 
             roomUrl: this.props.roomData.roomUrl, 
             show: this.state.showMenu, 
@@ -531,17 +531,17 @@ loop.roomViews = (function(mozL10n) {
     },
 
     render: function() {
       if (!this.state.show) {
         return null;
       }
 
       var url = this._getURL();
-      var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe";
+      var thumbnail = url && url.thumbnail || "shared/img/icons-16x16.svg#globe";
       var urlDescription = url && url.description || "";
       var location = url && url.location || "";
 
       var cx = classNames;
       var availableContext = this.state.availableContext;
       return (
         React.createElement("div", {className: "room-context"}, 
           React.createElement("p", {className: cx({ "error": !!this.props.error,
rename from browser/components/loop/content/js/roomViews.jsx
rename to browser/extensions/loop/content/panels/js/roomViews.jsx
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/extensions/loop/content/panels/js/roomViews.jsx
@@ -341,24 +341,24 @@ loop.roomViews = (function(mozL10n) {
             hide: this.props.showEditContext
           })}>
             <div className={cx({
                 "btn-copy": true,
                 "invite-button": true,
                 "triggered": this.state.copiedUrl
               })}
               onClick={this.handleCopyButtonClick}>
-              <img src="loop/shared/img/svg/glyph-link-16x16.svg" />
+              <img src="shared/img/glyph-link-16x16.svg" />
               <p>{mozL10n.get(this.state.copiedUrl ?
                 "invite_copied_link_button" : "invite_copy_link_button")}</p>
             </div>
             <div className="btn-email invite-button"
               onClick={this.handleEmailButtonClick}
               onMouseOver={this.resetTriggeredButtons}>
-              <img src="loop/shared/img/svg/glyph-email-16x16.svg" />
+              <img src="shared/img/glyph-email-16x16.svg" />
               <p>{mozL10n.get("invite_email_link_button")}</p>
             </div>
           </div>
           <SocialShareDropdown
             dispatcher={this.props.dispatcher}
             ref="menu"
             roomUrl={this.props.roomData.roomUrl}
             show={this.state.showMenu}
@@ -531,17 +531,17 @@ loop.roomViews = (function(mozL10n) {
     },
 
     render: function() {
       if (!this.state.show) {
         return null;
       }
 
       var url = this._getURL();
-      var thumbnail = url && url.thumbnail || "loop/shared/img/icons-16x16.svg#globe";
+      var thumbnail = url && url.thumbnail || "shared/img/icons-16x16.svg#globe";
       var urlDescription = url && url.description || "";
       var location = url && url.location || "";
 
       var cx = classNames;
       var availableContext = this.state.availableContext;
       return (
         <div className="room-context">
           <p className={cx({ "error": !!this.props.error,
rename from browser/components/loop/content/panel.html
rename to browser/extensions/loop/content/panels/panel.html
--- a/browser/components/loop/content/panel.html
+++ b/browser/extensions/loop/content/panels/panel.html
@@ -1,35 +1,36 @@
 <!DOCTYPE html>
 <!-- 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/.  -->
 <html>
   <head>
     <meta charset="utf-8">
-    <link rel="stylesheet" type="text/css" href="loop/shared/css/reset.css">
-    <link rel="stylesheet" type="text/css" href="loop/shared/css/common.css">
-    <link rel="stylesheet" type="text/css" href="loop/css/panel.css">
+    <base href="chrome://loop/content">
+    <link rel="stylesheet" type="text/css" href="shared/css/reset.css">
+    <link rel="stylesheet" type="text/css" href="shared/css/common.css">
+    <link rel="stylesheet" type="text/css" href="panels/css/panel.css">
   </head>
   <body class="panel">
 
     <div id="main"></div>
 
-    <script type="text/javascript" src="loop/shared/libs/react-0.13.3.js"></script>
-    <script type="text/javascript" src="loop/libs/l10n.js"></script>
-    <script type="text/javascript" src="loop/shared/libs/lodash-3.9.3.js"></script>
-    <script type="text/javascript" src="loop/shared/libs/backbone-1.2.1.js"></script>
-    <script type="text/javascript" src="loop/shared/libs/classnames-2.2.0.js"></script>
+    <script type="text/javascript" src="panels/vendor/l10n.js"></script>
+    <script type="text/javascript" src="shared/vendor/react-0.13.3.js"></script>
+    <script type="text/javascript" src="shared/vendor/lodash-3.9.3.js"></script>
+    <script type="text/javascript" src="shared/vendor/backbone-1.2.1.js"></script>
+    <script type="text/javascript" src="shared/vendor/classnames-2.2.0.js"></script>
 
-    <script type="text/javascript" src="loop/shared/js/loopapi-client.js"></script>
-    <script type="text/javascript" src="loop/shared/js/utils.js"></script>
-    <script type="text/javascript" src="loop/shared/js/models.js"></script>
-    <script type="text/javascript" src="loop/shared/js/mixins.js"></script>
-    <script type="text/javascript" src="loop/shared/js/views.js"></script>
-    <script type="text/javascript" src="loop/shared/js/validate.js"></script>
-    <script type="text/javascript" src="loop/shared/js/actions.js"></script>
-    <script type="text/javascript" src="loop/shared/js/dispatcher.js"></script>
-    <script type="text/javascript" src="loop/shared/js/store.js"></script>
-    <script type="text/javascript" src="loop/shared/js/activeRoomStore.js"></script>
-    <script type="text/javascript" src="loop/js/roomStore.js"></script>
-    <script type="text/javascript" src="loop/js/panel.js"></script>
+    <script type="text/javascript" src="shared/js/loopapi-client.js"></script>
+    <script type="text/javascript" src="shared/js/utils.js"></script>
+    <script type="text/javascript" src="shared/js/models.js"></script>
+    <script type="text/javascript" src="shared/js/mixins.js"></script>
+    <script type="text/javascript" src="shared/js/views.js"></script>
+    <script type="text/javascript" src="shared/js/validate.js"></script>
+    <script type="text/javascript" src="shared/js/actions.js"></script>
+    <script type="text/javascript" src="shared/js/dispatcher.js"></script>
+    <script type="text/javascript" src="shared/js/store.js"></script>
+    <script type="text/javascript" src="shared/js/activeRoomStore.js"></script>
+    <script type="text/javascript" src="panels/js/roomStore.js"></script>
+    <script type="text/javascript" src="panels/js/panel.js"></script>
  </body>
 </html>
rename from browser/components/loop/content/libs/l10n.js
rename to browser/extensions/loop/content/panels/vendor/l10n.js
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/content/preferences/prefs.js
@@ -0,0 +1,33 @@
+pref("loop.enabled", true);
+pref("loop.textChat.enabled", true);
+pref("loop.server", "https://loop.services.mozilla.com/v0");
+pref("loop.linkClicker.url", "https://hello.firefox.com/");
+pref("loop.gettingStarted.seen", false);
+pref("loop.gettingStarted.url", "https://www.mozilla.org/%LOCALE%/firefox/%VERSION%/hello/start/");
+pref("loop.gettingStarted.resumeOnFirstJoin", false);
+pref("loop.learnMoreUrl", "https://www.firefox.com/hello/");
+pref("loop.legal.ToS_url", "https://www.mozilla.org/about/legal/terms/firefox-hello/");
+pref("loop.legal.privacy_url", "https://www.mozilla.org/privacy/firefox-hello/");
+pref("loop.do_not_disturb", false);
+pref("loop.ringtone", "chrome://browser/content/loop/shared/sounds/ringtone.ogg");
+pref("loop.retry_delay.start", 60000);
+pref("loop.retry_delay.limit", 300000);
+pref("loop.ping.interval", 1800000);
+pref("loop.ping.timeout", 10000);
+pref("loop.debug.loglevel", "Error");
+pref("loop.debug.dispatcher", false);
+pref("loop.debug.sdk", false);
+pref("loop.debug.twoWayMediaTelemetry", false);
+pref("loop.feedback.dateLastSeenSec", 0);
+pref("loop.feedback.periodSec", 15770000); // 6 months.
+pref("loop.feedback.formURL", "https://www.mozilla.org/firefox/hello/npssurvey/");
+pref("loop.feedback.manualFormURL", "https://www.mozilla.org/firefox/hello/feedbacksurvey/");
+#ifdef DEBUG
+pref("loop.CSP", "default-src 'self' about: file: chrome: http://localhost:*; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net http://localhost:* ws://localhost:*; media-src blob:");
+#else
+pref("loop.CSP", "default-src 'self' about: file: chrome:; img-src * data:; font-src 'none'; connect-src wss://*.tokbox.com https://*.opentok.com https://*.tokbox.com wss://*.mozilla.com https://*.mozilla.org wss://*.mozaws.net; media-src blob:");
+#endif
+pref("loop.fxa_oauth.tokendata", "");
+pref("loop.fxa_oauth.profile", "");
+pref("loop.support_url", "https://support.mozilla.org/kb/group-conversations-firefox-hello-webrtc");
+pref("loop.browserSharing.showInfoBar", true);
rename from browser/components/loop/content/shared/README.md
rename to browser/extensions/loop/content/shared/README.md
rename from browser/components/loop/content/shared/css/common.css
rename to browser/extensions/loop/content/shared/css/common.css
rename from browser/components/loop/content/shared/css/conversation.css
rename to browser/extensions/loop/content/shared/css/conversation.css
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/extensions/loop/content/shared/css/conversation.css
@@ -60,127 +60,127 @@ html[dir="rtl"] .conversation-toolbar > 
      extra gap which appears to push the toolbar in further than necessary */
   width: 28px;
 }
 
 .conversation-toolbar-media-btn-group-box {
   background-position: center;
   background-repeat: no-repeat;
   background-color: transparent;
-  background-image: url("../img/svg/media-group.svg");
+  background-image: url("../img/media-group.svg");
   background-size: cover;
   height: 28px;
   width: 67px;
 }
 
 .conversation-toolbar-media-btn-group-box > button:last-child:active,
 .conversation-toolbar-media-btn-group-box > button:last-child:hover {
-  background-image: url("../img/svg/media-group-right-hover.svg");
+  background-image: url("../img/media-group-right-hover.svg");
   background-size: cover;
 }
 
 html[dir="rtl"] .conversation-toolbar-media-btn-group-box > button:last-child:active,
 html[dir="rtl"] .conversation-toolbar-media-btn-group-box > button:last-child:hover {
-  background-image: url("../img/svg/media-group-left-hover.svg");
+  background-image: url("../img/media-group-left-hover.svg");
   background-size: cover;
 }
 
 .conversation-toolbar-media-btn-group-box > button:first-child:active,
 .conversation-toolbar-media-btn-group-box > button:first-child:hover {
-  background-image: url("../img/svg/media-group-left-hover.svg");
+  background-image: url("../img/media-group-left-hover.svg");
   background-size: cover;
 }
 
 html[dir="rtl"] .conversation-toolbar-media-btn-group-box > button:first-child:active,
 html[dir="rtl"] .conversation-toolbar-media-btn-group-box > button:first-child:hover {
-  background-image: url("../img/svg/media-group-right-hover.svg");
+  background-image: url("../img/media-group-right-hover.svg");
   background-size: cover;
 }
 
 .conversation-toolbar-btn-box.btn-edit-entry {
   float: right;
 }
 
 html[dir="rtl"] .conversation-toolbar-btn-box.btn-edit-entry {
   float: left;
 }
 
 /* conversationViews.jsx */
 
 .conversation-toolbar .btn-hangup {
-  background-image: url("../img/svg/exit.svg");
+  background-image: url("../img/exit.svg");
   border: 0;
 }
 
 /* Audio mute button */
 .btn-mute-audio:after {
-  content: url("../img/svg/audio.svg");
+  content: url("../img/audio.svg");
 }
 
 .btn-mute-audio.muted:after {
-  content: url("../img/svg/audio-mute.svg");
+  content: url("../img/audio-mute.svg");
 }
 .btn-mute-audio:hover:after,
 .btn-mute-audio:active:after {
-  content: url("../img/svg/audio-hover.svg");
+  content: url("../img/audio-hover.svg");
 }
 
 .btn-mute-audio.muted:hover:after,
 .btn-mute-audio.muted:active:after {
-  content: url("../img/svg/audio-mute-hover.svg");
+  content: url("../img/audio-mute-hover.svg");
 }
 
 /* Video mute button */
 .btn-mute-video:after {
-  content: url("../img/svg/video.svg");
+  content: url("../img/video.svg");
 }
 
 .btn-mute-video:active:after,
 .btn-mute-video:hover:after {
-  content: url("../img/svg/video-hover.svg");
+  content: url("../img/video-hover.svg");
 }
 
 .btn-mute-video.muted:after {
-  content: url("../img/svg/video-mute.svg");
+  content: url("../img/video-mute.svg");
 }
 
 .btn-mute-video.muted:hover:after,
 .btn-mute-video.muted:active:after {
-  content: url("../img/svg/video-mute-hover.svg");
+  content: url("../img/video-mute-hover.svg");
 }
 
 .btn-settings {
   width: 28px;
   height: 28px;
   background-size: 28px;
-  background-image: url("../img/svg/settings.svg");
+  background-image: url("../img/settings.svg");
   background-color: transparent;
 }
 
 .btn-settings:hover,
 .btn-settings:active {
-  background-image: url("../img/svg/settings-hover.svg");
+  background-image: url("../img/settings-hover.svg");
 }
 
 .btn-screen-share {
-  background-image: url("../img/svg/sharing.svg");
+  background-image: url("../img/sharing.svg");
 }
 
 .btn-screen-share:hover,
 .btn-screen-share:active {
-  background-image: url("../img/svg/sharing-hover.svg");
+  background-image: url("../img/sharing-hover.svg");
 }
 
 .btn-screen-share.active {
-  background-image: url("../img/svg/sharing-active.svg");
+  background-image: url("../img/sharing-active.svg");
 }
 
 .btn-screen-share.disabled {
   /* The screen share button is in its pending state when its disabled. */
-  background-image: url("../img/svg/sharing-pending.svg");
+  background-image: url("../img/sharing-pending.svg");
 }
 
 /* General Call (incoming or outgoing). */
 
 .call-action-group {
   display: flex;
   padding: 0 4px;
   width: 100%;
rename from browser/components/loop/content/shared/css/reset.css
rename to browser/extensions/loop/content/shared/css/reset.css
rename from browser/components/loop/content/shared/img/02.png
rename to browser/extensions/loop/content/shared/img/02.png
rename from browser/components/loop/content/shared/img/02@2x.png
rename to browser/extensions/loop/content/shared/img/02@2x.png
rename from browser/components/loop/content/shared/img/animated-spinner.svg
rename to browser/extensions/loop/content/shared/img/animated-spinner.svg
rename from browser/components/loop/content/shared/img/audio-call-avatar.svg
rename to browser/extensions/loop/content/shared/img/audio-call-avatar.svg
rename from browser/components/loop/content/shared/img/audio-default-16x16@1.5x.png
rename to browser/extensions/loop/content/shared/img/audio-default-16x16@1.5x.png
rename from browser/components/loop/content/shared/img/audio-default-16x16@2x.png
rename to browser/extensions/loop/content/shared/img/audio-default-16x16@2x.png
rename from browser/components/loop/content/shared/img/svg/audio-hover.svg
rename to browser/extensions/loop/content/shared/img/audio-hover.svg
rename from browser/components/loop/content/shared/img/svg/audio-mute-hover.svg
rename to browser/extensions/loop/content/shared/img/audio-mute-hover.svg
rename from browser/components/loop/content/shared/img/svg/audio-mute.svg
rename to browser/extensions/loop/content/shared/img/audio-mute.svg
rename from browser/components/loop/content/shared/img/svg/audio.svg
rename to browser/extensions/loop/content/shared/img/audio.svg
rename from browser/components/loop/content/shared/img/avatars.svg
rename to browser/extensions/loop/content/shared/img/avatars.svg
rename from browser/components/loop/content/shared/img/beta-ribbon.svg
rename to browser/extensions/loop/content/shared/img/beta-ribbon.svg
rename from browser/components/loop/content/shared/img/svg/cam_audio-no.svg
rename to browser/extensions/loop/content/shared/img/cam_audio-no.svg
rename from browser/components/loop/content/shared/img/svg/cam_audio.svg
rename to browser/extensions/loop/content/shared/img/cam_audio.svg
rename from browser/components/loop/content/shared/img/svg/cam_audio_h.svg
rename to browser/extensions/loop/content/shared/img/cam_audio_h.svg
rename from browser/components/loop/content/shared/img/chatbubble-arrow-left.svg
rename to browser/extensions/loop/content/shared/img/chatbubble-arrow-left.svg
rename from browser/components/loop/content/shared/img/chatbubble-arrow-right.svg
rename to browser/extensions/loop/content/shared/img/chatbubble-arrow-right.svg
rename from browser/components/loop/content/shared/img/check.svg
rename to browser/extensions/loop/content/shared/img/check.svg
rename from browser/components/loop/content/shared/img/ellipsis-v.svg
rename to browser/extensions/loop/content/shared/img/ellipsis-v.svg
rename from browser/components/loop/content/shared/img/empty_conversations.svg
rename to browser/extensions/loop/content/shared/img/empty_conversations.svg
rename from browser/components/loop/content/shared/img/empty_search.svg
rename to browser/extensions/loop/content/shared/img/empty_search.svg
rename from browser/components/loop/content/shared/img/svg/exit.svg
rename to browser/extensions/loop/content/shared/img/exit.svg
rename from browser/components/loop/content/shared/img/facemute-14x14.png
rename to browser/extensions/loop/content/shared/img/facemute-14x14.png
rename from browser/components/loop/content/shared/img/facemute-14x14@2x.png
rename to browser/extensions/loop/content/shared/img/facemute-14x14@2x.png
rename from browser/components/loop/content/shared/img/firefox-avatar.svg
rename to browser/extensions/loop/content/shared/img/firefox-avatar.svg
rename from browser/components/loop/content/shared/img/firefox-logo.png
rename to browser/extensions/loop/content/shared/img/firefox-logo.png
rename from browser/components/loop/content/shared/img/svg/glyph-email-16x16.svg
rename to browser/extensions/loop/content/shared/img/glyph-email-16x16.svg
rename from browser/components/loop/content/shared/img/svg/glyph-facebook-16x16.svg
rename to browser/extensions/loop/content/shared/img/glyph-facebook-16x16.svg
rename from browser/components/loop/content/shared/img/svg/glyph-help-16x16.svg
rename to browser/extensions/loop/content/shared/img/glyph-help-16x16.svg
rename from browser/components/loop/content/shared/img/svg/glyph-link-16x16.svg
rename to browser/extensions/loop/content/shared/img/glyph-link-16x16.svg
rename from browser/components/loop/content/shared/img/svg/glyph-user-16x16.svg
rename to browser/extensions/loop/content/shared/img/glyph-user-16x16.svg
rename from browser/components/loop/content/shared/img/hangup-inverse-14x14.png
rename to browser/extensions/loop/content/shared/img/hangup-inverse-14x14.png
rename from browser/components/loop/content/shared/img/hangup-inverse-14x14@2x.png
rename to browser/extensions/loop/content/shared/img/hangup-inverse-14x14@2x.png
rename from browser/components/loop/content/shared/img/happy.png
rename to browser/extensions/loop/content/shared/img/happy.png
rename from browser/components/loop/content/shared/img/hello_logo.svg
rename to browser/extensions/loop/content/shared/img/hello_logo.svg
rename from browser/components/loop/content/shared/img/helloicon.svg
rename to browser/extensions/loop/content/shared/img/helloicon.svg
rename from browser/components/loop/content/shared/img/icon_32.png
rename to browser/extensions/loop/content/shared/img/icon_32.png
rename from browser/components/loop/content/shared/img/icon_64.png
rename to browser/extensions/loop/content/shared/img/icon_64.png
rename from browser/components/loop/content/shared/img/icons-10x10.svg
rename to browser/extensions/loop/content/shared/img/icons-10x10.svg
rename from browser/components/loop/content/shared/img/icons-14x14.svg
rename to browser/extensions/loop/content/shared/img/icons-14x14.svg
rename from browser/components/loop/content/shared/img/icons-16x16.svg
rename to browser/extensions/loop/content/shared/img/icons-16x16.svg
rename from browser/components/loop/content/shared/img/svg/media-group-left-hover.svg
rename to browser/extensions/loop/content/shared/img/media-group-left-hover.svg
rename from browser/components/loop/content/shared/img/svg/media-group-right-hover.svg
rename to browser/extensions/loop/content/shared/img/media-group-right-hover.svg
rename from browser/components/loop/content/shared/img/svg/media-group.svg
rename to browser/extensions/loop/content/shared/img/media-group.svg
rename from browser/components/loop/content/shared/img/movistar.png
rename to browser/extensions/loop/content/shared/img/movistar.png
rename from browser/components/loop/content/shared/img/movistar@2x.png
rename to browser/extensions/loop/content/shared/img/movistar@2x.png
rename from browser/components/loop/content/shared/img/mute-inverse-14x14.png
rename to browser/extensions/loop/content/shared/img/mute-inverse-14x14.png
rename from browser/components/loop/content/shared/img/mute-inverse-14x14@2x.png
rename to browser/extensions/loop/content/shared/img/mute-inverse-14x14@2x.png
rename from browser/components/loop/content/shared/img/pause-12x12.svg
rename to browser/extensions/loop/content/shared/img/pause-12x12.svg
rename from browser/components/loop/content/shared/img/play-12x12.svg
rename to browser/extensions/loop/content/shared/img/play-12x12.svg
rename from browser/components/loop/content/shared/img/sad.png
rename to browser/extensions/loop/content/shared/img/sad.png
rename from browser/components/loop/content/shared/img/sad_hello_icon_64x64.svg
rename to browser/extensions/loop/content/shared/img/sad_hello_icon_64x64.svg
rename from browser/components/loop/content/shared/img/svg/settings-hover.svg
rename to browser/extensions/loop/content/shared/img/settings-hover.svg
rename from browser/components/loop/content/shared/img/svg/settings.svg
rename to browser/extensions/loop/content/shared/img/settings.svg
rename from browser/components/loop/content/shared/img/svg/sharing-active.svg
rename to browser/extensions/loop/content/shared/img/sharing-active.svg
rename from browser/components/loop/content/shared/img/svg/sharing-hover.svg
rename to browser/extensions/loop/content/shared/img/sharing-hover.svg
rename from browser/components/loop/content/shared/img/svg/sharing-pending.svg
rename to browser/extensions/loop/content/shared/img/sharing-pending.svg
rename from browser/components/loop/content/shared/img/svg/sharing.svg
rename to browser/extensions/loop/content/shared/img/sharing.svg
rename from browser/components/loop/content/shared/img/spinner.png
rename to browser/extensions/loop/content/shared/img/spinner.png
rename from browser/components/loop/content/shared/img/spinner.svg
rename to browser/extensions/loop/content/shared/img/spinner.svg
rename from browser/components/loop/content/shared/img/spinner@2x.png
rename to browser/extensions/loop/content/shared/img/spinner@2x.png
rename from browser/components/loop/content/shared/img/stop-12x12.svg
rename to browser/extensions/loop/content/shared/img/stop-12x12.svg
rename from browser/components/loop/content/shared/img/telefonica.png
rename to browser/extensions/loop/content/shared/img/telefonica.png
rename from browser/components/loop/content/shared/img/telefonica@2x.png
rename to browser/extensions/loop/content/shared/img/telefonica@2x.png
rename from browser/components/loop/content/shared/img/svg/video-hover.svg
rename to browser/extensions/loop/content/shared/img/video-hover.svg
rename from browser/components/loop/content/shared/img/svg/video-mute-hover.svg
rename to browser/extensions/loop/content/shared/img/video-mute-hover.svg
rename from browser/components/loop/content/shared/img/svg/video-mute.svg
rename to browser/extensions/loop/content/shared/img/video-mute.svg
rename from browser/components/loop/content/shared/img/svg/video.svg
rename to browser/extensions/loop/content/shared/img/video.svg
rename from browser/components/loop/content/shared/img/vivo.png
rename to browser/extensions/loop/content/shared/img/vivo.png
rename from browser/components/loop/content/shared/img/vivo@2x.png
rename to browser/extensions/loop/content/shared/img/vivo@2x.png
rename from browser/components/loop/content/shared/js/actions.js
rename to browser/extensions/loop/content/shared/js/actions.js
rename from browser/components/loop/content/shared/js/activeRoomStore.js
rename to browser/extensions/loop/content/shared/js/activeRoomStore.js
rename from browser/components/loop/content/shared/js/crypto.js
rename to browser/extensions/loop/content/shared/js/crypto.js
--- a/browser/components/loop/content/shared/js/crypto.js
+++ b/browser/extensions/loop/content/shared/js/crypto.js
@@ -13,17 +13,17 @@ var inChrome = typeof Components != "und
   var sharedUtils;
   if (inChrome) {
     this.EXPORTED_SYMBOLS = ["LoopCrypto"];
     var Cu = Components.utils;
     Cu.importGlobalProperties(["crypto"]);
     rootObject = {
       crypto: crypto
     };
-    sharedUtils = Cu.import("resource:///modules/loop/utils.js", {}).utils;
+    sharedUtils = Cu.import("chrome://loop/content/shared/js/utils.js", {}).utils;
   } else {
     sharedUtils = this.shared.utils;
   }
 
   var ALGORITHM = "AES-GCM";
   var KEY_LENGTH = 128;
   // We use JSON web key formats for the generated keys.
   // https://tools.ietf.org/html/draft-ietf-jose-json-web-key-41
rename from browser/components/loop/content/shared/js/dispatcher.js
rename to browser/extensions/loop/content/shared/js/dispatcher.js
rename from browser/components/loop/content/shared/js/linkifiedTextView.js
rename to browser/extensions/loop/content/shared/js/linkifiedTextView.js
rename from browser/components/loop/content/shared/js/linkifiedTextView.jsx
rename to browser/extensions/loop/content/shared/js/linkifiedTextView.jsx
rename from browser/components/loop/content/shared/js/loopapi-client.js
rename to browser/extensions/loop/content/shared/js/loopapi-client.js
rename from browser/components/loop/content/shared/js/mixins.js
rename to browser/extensions/loop/content/shared/js/mixins.js
rename from browser/components/loop/content/shared/js/models.js
rename to browser/extensions/loop/content/shared/js/models.js
rename from browser/components/loop/content/shared/js/otSdkDriver.js
rename to browser/extensions/loop/content/shared/js/otSdkDriver.js
rename from browser/components/loop/content/shared/js/store.js
rename to browser/extensions/loop/content/shared/js/store.js
rename from browser/components/loop/content/shared/js/textChatStore.js
rename to browser/extensions/loop/content/shared/js/textChatStore.js
rename from browser/components/loop/content/shared/js/textChatView.js
rename to browser/extensions/loop/content/shared/js/textChatView.js
rename from browser/components/loop/content/shared/js/textChatView.jsx
rename to browser/extensions/loop/content/shared/js/textChatView.jsx
rename from browser/components/loop/content/shared/js/urlRegExps.js
rename to browser/extensions/loop/content/shared/js/urlRegExps.js
rename from browser/components/loop/content/shared/js/utils.js
rename to browser/extensions/loop/content/shared/js/utils.js
rename from browser/components/loop/content/shared/js/validate.js
rename to browser/extensions/loop/content/shared/js/validate.js
rename from browser/components/loop/content/shared/js/views.js
rename to browser/extensions/loop/content/shared/js/views.js
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/extensions/loop/content/shared/js/views.js
@@ -849,17 +849,17 @@ loop.shared.views = (function(_, mozL10n
       } catch (ex) {
         return null;
       }
 
       var thumbnail = this.props.thumbnail;
 
       if (!thumbnail) {
         thumbnail = this.props.useDesktopPaths ?
-          "loop/shared/img/icons-16x16.svg#globe" :
+          "shared/img/icons-16x16.svg#globe" :
           "shared/img/icons-16x16.svg#globe";
       }
 
       var wrapperClasses = classNames({
         "context-wrapper": true,
         "clicks-allowed": this.props.allowClick
       });
 
rename from browser/components/loop/content/shared/js/views.jsx
rename to browser/extensions/loop/content/shared/js/views.jsx
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/extensions/loop/content/shared/js/views.jsx
@@ -849,17 +849,17 @@ loop.shared.views = (function(_, mozL10n
       } catch (ex) {
         return null;
       }
 
       var thumbnail = this.props.thumbnail;
 
       if (!thumbnail) {
         thumbnail = this.props.useDesktopPaths ?
-          "loop/shared/img/icons-16x16.svg#globe" :
+          "shared/img/icons-16x16.svg#globe" :
           "shared/img/icons-16x16.svg#globe";
       }
 
       var wrapperClasses = classNames({
         "context-wrapper": true,
         "clicks-allowed": this.props.allowClick
       });
 
rename from browser/components/loop/content/shared/sounds/connected.ogg
rename to browser/extensions/loop/content/shared/sounds/connected.ogg
rename from browser/components/loop/content/shared/sounds/connecting.ogg
rename to browser/extensions/loop/content/shared/sounds/connecting.ogg
rename from browser/components/loop/content/shared/sounds/failure.ogg
rename to browser/extensions/loop/content/shared/sounds/failure.ogg
rename from browser/components/loop/content/shared/sounds/message.ogg
rename to browser/extensions/loop/content/shared/sounds/message.ogg
rename from browser/components/loop/content/shared/sounds/ringtone.ogg
rename to browser/extensions/loop/content/shared/sounds/ringtone.ogg
rename from browser/components/loop/content/shared/sounds/room-joined-in.ogg
rename to browser/extensions/loop/content/shared/sounds/room-joined-in.ogg
rename from browser/components/loop/content/shared/sounds/room-joined.ogg
rename to browser/extensions/loop/content/shared/sounds/room-joined.ogg
rename from browser/components/loop/content/shared/sounds/room-left.ogg
rename to browser/extensions/loop/content/shared/sounds/room-left.ogg
rename from browser/components/loop/content/shared/sounds/terminated.ogg
rename to browser/extensions/loop/content/shared/sounds/terminated.ogg
rename from browser/components/loop/content/shared/libs/backbone-1.2.1.js
rename to browser/extensions/loop/content/shared/vendor/backbone-1.2.1.js
rename from browser/components/loop/content/shared/libs/classnames-2.2.0.js
rename to browser/extensions/loop/content/shared/vendor/classnames-2.2.0.js
rename from browser/components/loop/content/shared/libs/lodash-3.9.3.js
rename to browser/extensions/loop/content/shared/vendor/lodash-3.9.3.js
rename from browser/components/loop/content/shared/libs/react-0.13.3-prod.js
rename to browser/extensions/loop/content/shared/vendor/react-0.13.3-prod.js
rename from browser/components/loop/content/shared/libs/react-0.13.3.js
rename to browser/extensions/loop/content/shared/vendor/react-0.13.3.js
rename from browser/components/loop/content/shared/libs/sdk-content/css/ot.css
rename to browser/extensions/loop/content/shared/vendor/sdk-content/css/ot.css
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-denied-chrome.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/access-denied-chrome.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-denied-copy-firefox.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/access-denied-copy-firefox.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-denied-firefox.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/access-denied-firefox.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-predenied-chrome.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/access-predenied-chrome.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/access-prompt-chrome.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/access-prompt-chrome.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/audioonly-publisher.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/audioonly-publisher.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/audioonly-subscriber.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/audioonly-subscriber.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/buttons.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/buttons.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/loader.gif
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/loader.gif
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/mic-off.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/mic-off.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/mic-on.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/mic-on.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/speaker-off.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/speaker-off.png
rename from browser/components/loop/content/shared/libs/sdk-content/images/rtc/speaker-on.png
rename to browser/extensions/loop/content/shared/vendor/sdk-content/images/rtc/speaker-on.png
rename from browser/components/loop/content/shared/libs/sdk-content/js/dynamic_config.min.js
rename to browser/extensions/loop/content/shared/vendor/sdk-content/js/dynamic_config.min.js
rename from browser/components/loop/content/shared/libs/sdk.js
rename to browser/extensions/loop/content/shared/vendor/sdk.js
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/install.rdf.in
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+#filter substitution
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>loop@mozilla.org</em:id>
+    <em:bootstrap>true</em:bootstrap>
+    <em:version>0.1</em:version>
+    <em:type>2</em:type>
+
+    <!-- Target Application this extension can install into,
+         with minimum and maximum supported versions. -->
+    <em:targetApplication>
+      <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>45.0a1</em:minVersion>
+        <em:maxVersion>@FIREFOX_VERSION@</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+    <!-- Used for unit tests only. -->
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>0</em:minVersion>
+        <em:maxVersion>10</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+    <!-- Front End MetaData -->
+    <em:name>Firefox Hello</em:name>
+    <em:description>Web sharing for Firefox</em:description>
+    <em:creator>Mozilla</em:creator>
+  </Description>
+</RDF>
rename from browser/components/loop/jar.mn
rename to browser/extensions/loop/jar.mn
--- a/browser/components/loop/jar.mn
+++ b/browser/extensions/loop/jar.mn
@@ -1,136 +1,164 @@
 # 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/.
 
-browser.jar:
+[.] chrome.jar:
+% content loop %content/ contentaccessible=yes
+% skin loop classic/1.0 %skin/linux/ os=Linux
+% skin loop classic/1.0 %skin/osx/ os=Darwin
+% skin loop classic/1.0 %skin/windows/ os=WINNT
+% skin loop-shared classic/1.0 %skin/shared/
+% override chrome://loop/skin/menuPanel.png       chrome://loop/skin/menuPanel-yosemite.png       os=Darwin osversion>=10.10
+% override chrome://loop/skin/menuPanel@2x.png    chrome://loop/skin/menuPanel-yosemite@2x.png    os=Darwin osversion>=10.10
+% override chrome://loop/skin/toolbar.png         chrome://loop/skin/toolbar-yosemite.png         os=Darwin osversion>=10.10
+% override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-yosemite@2x.png      os=Darwin osversion>=10.10
+% override chrome://loop/skin/menuPanel.png       chrome://loop/skin/menuPanel-aero.png           os=WINNT osversion=6
+% override chrome://loop/skin/menuPanel.png       chrome://loop/skin/menuPanel-aero.png           os=WINNT osversion=6.1
+% override chrome://loop/skin/menuPanel@2x.png    chrome://loop/skin/menuPanel-aero@2x.png        os=WINNT osversion=6
+% override chrome://loop/skin/menuPanel@2x.png    chrome://loop/skin/menuPanel-aero@2x.png        os=WINNT osversion=6.1
+% override chrome://loop/skin/toolbar.png         chrome://loop/skin/toolbar-XP.png               os=WINNT osversion<6
+% override chrome://loop/skin/toolbar.png         chrome://loop/skin/toolbar-aero.png             os=WINNT osversion=6
+% override chrome://loop/skin/toolbar.png         chrome://loop/skin/toolbar-aero.png             os=WINNT osversion=6.1
+% override chrome://loop/skin/toolbar.png         chrome://loop/skin/toolbar-win8.png             os=WINNT osversion=6.2
+% override chrome://loop/skin/toolbar.png         chrome://loop/skin/toolbar-win8.png             os=WINNT osversion=6.3
+% override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-XP@2x.png            os=WINNT osversion<6
+% override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-aero@2x.png          os=WINNT osversion=6
+% override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-aero@2x.png          os=WINNT osversion=6.1
+% override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-win8@2x.png          os=WINNT osversion=6.2
+% override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-win8@2x.png          os=WINNT osversion=6.3
+  skin/  (skin/*)
+  content/modules/ (content/modules/*)
+* content/preferences/prefs.js (content/preferences/prefs.js)
+
   # Desktop html files
-  content/browser/loop/conversation.html            (content/conversation.html)
-  content/browser/loop/panel.html                   (content/panel.html)
+  content/panels/conversation.html                 (content/panels/conversation.html)
+  content/panels/panel.html                        (content/panels/panel.html)
 
-  # Desktop libs (see bottom of this file for TokBox sdk assets)
-  content/browser/loop/libs/l10n.js                 (content/libs/l10n.js)
+  # Desktop vendor (see bottom of this file for TokBox sdk assets)
+  content/panels/vendor/l10n.js                    (content/panels/vendor/l10n.js)
 
   # Desktop script
-  content/browser/loop/js/conversation.js           (content/js/conversation.js)
-  content/browser/loop/js/conversationAppStore.js   (content/js/conversationAppStore.js)
-  content/browser/loop/js/otconfig.js               (content/js/otconfig.js)
-  content/browser/loop/js/panel.js                  (content/js/panel.js)
-  content/browser/loop/js/roomStore.js              (content/js/roomStore.js)
-  content/browser/loop/js/roomViews.js              (content/js/roomViews.js)
-  content/browser/loop/js/feedbackViews.js          (content/js/feedbackViews.js)
+  content/panels/js/conversation.js                (content/panels/js/conversation.js)
+  content/panels/js/conversationAppStore.js        (content/panels/js/conversationAppStore.js)
+  content/panels/js/otconfig.js                    (content/panels/js/otconfig.js)
+  content/panels/js/panel.js                       (content/panels/js/panel.js)
+  content/panels/js/roomStore.js                   (content/panels/js/roomStore.js)
+  content/panels/js/roomViews.js                   (content/panels/js/roomViews.js)
+  content/panels/js/feedbackViews.js               (content/panels/js/feedbackViews.js)
 
   # Desktop styles
-  content/browser/loop/css/panel.css                (content/css/panel.css)
+  content/panels/css/panel.css                     (content/panels/css/panel.css)
 
   # Shared styles
-  content/browser/loop/shared/css/reset.css         (content/shared/css/reset.css)
-  content/browser/loop/shared/css/common.css        (content/shared/css/common.css)
-  content/browser/loop/shared/css/conversation.css  (content/shared/css/conversation.css)
+  content/shared/css/reset.css                     (content/shared/css/reset.css)
+  content/shared/css/common.css                    (content/shared/css/common.css)
+  content/shared/css/conversation.css              (content/shared/css/conversation.css)
 
   # Shared images
-  content/browser/loop/shared/img/helloicon.svg                 (content/shared/img/helloicon.svg)
-  content/browser/loop/shared/img/icon_32.png                   (content/shared/img/icon_32.png)
-  content/browser/loop/shared/img/icon_64.png                   (content/shared/img/icon_64.png)
-  content/browser/loop/shared/img/spinner.svg                   (content/shared/img/spinner.svg)
+  content/shared/img/helloicon.svg                 (content/shared/img/helloicon.svg)
+  content/shared/img/icon_32.png                   (content/shared/img/icon_32.png)
+  content/shared/img/icon_64.png                   (content/shared/img/icon_64.png)
+  content/shared/img/spinner.svg                   (content/shared/img/spinner.svg)
   # XXX could get rid of the png spinner usages and replace them with the svg
   # one?
-  content/browser/loop/shared/img/spinner.png                   (content/shared/img/spinner.png)
-  content/browser/loop/shared/img/spinner@2x.png                (content/shared/img/spinner@2x.png)
-  content/browser/loop/shared/img/sad_hello_icon_64x64.svg      (content/shared/img/sad_hello_icon_64x64.svg)
-  content/browser/loop/shared/img/chatbubble-arrow-left.svg     (content/shared/img/chatbubble-arrow-left.svg)
-  content/browser/loop/shared/img/chatbubble-arrow-right.svg    (content/shared/img/chatbubble-arrow-right.svg)
-  content/browser/loop/shared/img/facemute-14x14.png            (content/shared/img/facemute-14x14.png)
-  content/browser/loop/shared/img/facemute-14x14@2x.png         (content/shared/img/facemute-14x14@2x.png)
-  content/browser/loop/shared/img/hangup-inverse-14x14.png      (content/shared/img/hangup-inverse-14x14.png)
-  content/browser/loop/shared/img/hangup-inverse-14x14@2x.png   (content/shared/img/hangup-inverse-14x14@2x.png)
-  content/browser/loop/shared/img/mute-inverse-14x14.png        (content/shared/img/mute-inverse-14x14.png)
-  content/browser/loop/shared/img/mute-inverse-14x14@2x.png     (content/shared/img/mute-inverse-14x14@2x.png)
-  content/browser/loop/shared/img/svg/glyph-email-16x16.svg     (content/shared/img/svg/glyph-email-16x16.svg)
-  content/browser/loop/shared/img/svg/glyph-facebook-16x16.svg  (content/shared/img/svg/glyph-facebook-16x16.svg)
-  content/browser/loop/shared/img/svg/glyph-help-16x16.svg      (content/shared/img/svg/glyph-help-16x16.svg)
-  content/browser/loop/shared/img/svg/glyph-link-16x16.svg      (content/shared/img/svg/glyph-link-16x16.svg)
-  content/browser/loop/shared/img/svg/glyph-user-16x16.svg      (content/shared/img/svg/glyph-user-16x16.svg)
-  content/browser/loop/shared/img/svg/exit.svg                  (content/shared/img/svg/exit.svg)
-  content/browser/loop/shared/img/svg/audio.svg                 (content/shared/img/svg/audio.svg)
-  content/browser/loop/shared/img/svg/audio-hover.svg           (content/shared/img/svg/audio-hover.svg)
-  content/browser/loop/shared/img/svg/audio-mute.svg            (content/shared/img/svg/audio-mute.svg)
-  content/browser/loop/shared/img/svg/audio-mute-hover.svg      (content/shared/img/svg/audio-mute-hover.svg)
-  content/browser/loop/shared/img/svg/video.svg                 (content/shared/img/svg/video.svg)
-  content/browser/loop/shared/img/svg/video-hover.svg           (content/shared/img/svg/video-hover.svg)
-  content/browser/loop/shared/img/svg/video-mute.svg            (content/shared/img/svg/video-mute.svg)
-  content/browser/loop/shared/img/svg/video-mute-hover.svg      (content/shared/img/svg/video-mute-hover.svg)
-  content/browser/loop/shared/img/svg/settings.svg              (content/shared/img/svg/settings.svg)
-  content/browser/loop/shared/img/svg/settings-hover.svg        (content/shared/img/svg/settings-hover.svg)
-  content/browser/loop/shared/img/svg/sharing.svg               (content/shared/img/svg/sharing.svg)
-  content/browser/loop/shared/img/svg/sharing-active.svg        (content/shared/img/svg/sharing-active.svg)
-  content/browser/loop/shared/img/svg/sharing-pending.svg       (content/shared/img/svg/sharing-pending.svg)
-  content/browser/loop/shared/img/svg/sharing-hover.svg         (content/shared/img/svg/sharing-hover.svg)
-  content/browser/loop/shared/img/svg/media-group.svg           (content/shared/img/svg/media-group.svg)
-  content/browser/loop/shared/img/svg/media-group-left-hover.svg (content/shared/img/svg/media-group-left-hover.svg)
-  content/browser/loop/shared/img/svg/media-group-right-hover.svg (content/shared/img/svg/media-group-right-hover.svg)
-  content/browser/loop/shared/img/audio-call-avatar.svg         (content/shared/img/audio-call-avatar.svg)
-  content/browser/loop/shared/img/beta-ribbon.svg               (content/shared/img/beta-ribbon.svg)
-  content/browser/loop/shared/img/check.svg                     (content/shared/img/check.svg)
-  content/browser/loop/shared/img/icons-10x10.svg               (content/shared/img/icons-10x10.svg)
-  content/browser/loop/shared/img/icons-14x14.svg               (content/shared/img/icons-14x14.svg)
-  content/browser/loop/shared/img/icons-16x16.svg               (content/shared/img/icons-16x16.svg)
-  content/browser/loop/shared/img/movistar.png                  (content/shared/img/movistar.png)
-  content/browser/loop/shared/img/movistar@2x.png               (content/shared/img/movistar@2x.png)
-  content/browser/loop/shared/img/vivo.png                      (content/shared/img/vivo.png)
-  content/browser/loop/shared/img/vivo@2x.png                   (content/shared/img/vivo@2x.png)
-  content/browser/loop/shared/img/02.png                        (content/shared/img/02.png)
-  content/browser/loop/shared/img/02@2x.png                     (content/shared/img/02@2x.png)
-  content/browser/loop/shared/img/telefonica.png                (content/shared/img/telefonica.png)
-  content/browser/loop/shared/img/hello_logo.svg                (content/shared/img/hello_logo.svg)
-  content/browser/loop/shared/img/telefonica@2x.png             (content/shared/img/telefonica@2x.png)
-  content/browser/loop/shared/img/ellipsis-v.svg                (content/shared/img/ellipsis-v.svg)
-  content/browser/loop/shared/img/empty_conversations.svg       (content/shared/img/empty_conversations.svg)
-  content/browser/loop/shared/img/empty_search.svg              (content/shared/img/empty_search.svg)
-  content/browser/loop/shared/img/animated-spinner.svg          (content/shared/img/animated-spinner.svg)
-  content/browser/loop/shared/img/avatars.svg                   (content/shared/img/avatars.svg)
-  content/browser/loop/shared/img/firefox-avatar.svg            (content/shared/img/firefox-avatar.svg)
-  content/browser/loop/shared/img/pause-12x12.svg               (content/shared/img/pause-12x12.svg)
-  content/browser/loop/shared/img/play-12x12.svg                (content/shared/img/play-12x12.svg)
-  content/browser/loop/shared/img/stop-12x12.svg                (content/shared/img/stop-12x12.svg)
+  content/shared/img/spinner.png                   (content/shared/img/spinner.png)
+  content/shared/img/spinner@2x.png                (content/shared/img/spinner@2x.png)
+  content/shared/img/sad_hello_icon_64x64.svg      (content/shared/img/sad_hello_icon_64x64.svg)
+  content/shared/img/chatbubble-arrow-left.svg     (content/shared/img/chatbubble-arrow-left.svg)
+  content/shared/img/chatbubble-arrow-right.svg    (content/shared/img/chatbubble-arrow-right.svg)
+  content/shared/img/facemute-14x14.png            (content/shared/img/facemute-14x14.png)
+  content/shared/img/facemute-14x14@2x.png         (content/shared/img/facemute-14x14@2x.png)
+  content/shared/img/hangup-inverse-14x14.png      (content/shared/img/hangup-inverse-14x14.png)
+  content/shared/img/hangup-inverse-14x14@2x.png   (content/shared/img/hangup-inverse-14x14@2x.png)
+  content/shared/img/mute-inverse-14x14.png        (content/shared/img/mute-inverse-14x14.png)
+  content/shared/img/mute-inverse-14x14@2x.png     (content/shared/img/mute-inverse-14x14@2x.png)
+  content/shared/img/glyph-email-16x16.svg         (content/shared/img/glyph-email-16x16.svg)
+  content/shared/img/glyph-facebook-16x16.svg      (content/shared/img/glyph-facebook-16x16.svg)
+  content/shared/img/glyph-help-16x16.svg          (content/shared/img/glyph-help-16x16.svg)
+  content/shared/img/glyph-link-16x16.svg          (content/shared/img/glyph-link-16x16.svg)
+  content/shared/img/glyph-user-16x16.svg          (content/shared/img/glyph-user-16x16.svg)
+  content/shared/img/exit.svg                      (content/shared/img/exit.svg)
+  content/shared/img/audio.svg                     (content/shared/img/audio.svg)
+  content/shared/img/audio-hover.svg               (content/shared/img/audio-hover.svg)
+  content/shared/img/audio-mute.svg                (content/shared/img/audio-mute.svg)
+  content/shared/img/audio-mute-hover.svg          (content/shared/img/audio-mute-hover.svg)
+  content/shared/img/video.svg                     (content/shared/img/video.svg)
+  content/shared/img/video-hover.svg               (content/shared/img/video-hover.svg)
+  content/shared/img/video-mute.svg                (content/shared/img/video-mute.svg)
+  content/shared/img/video-mute-hover.svg          (content/shared/img/video-mute-hover.svg)
+  content/shared/img/settings.svg                  (content/shared/img/settings.svg)
+  content/shared/img/settings-hover.svg            (content/shared/img/settings-hover.svg)
+  content/shared/img/sharing.svg                   (content/shared/img/sharing.svg)
+  content/shared/img/sharing-active.svg            (content/shared/img/sharing-active.svg)
+  content/shared/img/sharing-pending.svg           (content/shared/img/sharing-pending.svg)
+  content/shared/img/sharing-hover.svg             (content/shared/img/sharing-hover.svg)
+  content/shared/img/media-group.svg               (content/shared/img/media-group.svg)
+  content/shared/img/media-group-left-hover.svg    (content/shared/img/media-group-left-hover.svg)
+  content/shared/img/media-group-right-hover.svg   (content/shared/img/media-group-right-hover.svg)
+  content/shared/img/audio-call-avatar.svg         (content/shared/img/audio-call-avatar.svg)
+  content/shared/img/beta-ribbon.svg               (content/shared/img/beta-ribbon.svg)
+  content/shared/img/check.svg                     (content/shared/img/check.svg)
+  content/shared/img/icons-10x10.svg               (content/shared/img/icons-10x10.svg)
+  content/shared/img/icons-14x14.svg               (content/shared/img/icons-14x14.svg)
+  content/shared/img/icons-16x16.svg               (content/shared/img/icons-16x16.svg)
+  content/shared/img/movistar.png                  (content/shared/img/movistar.png)
+  content/shared/img/movistar@2x.png               (content/shared/img/movistar@2x.png)
+  content/shared/img/vivo.png                      (content/shared/img/vivo.png)
+  content/shared/img/vivo@2x.png                   (content/shared/img/vivo@2x.png)
+  content/shared/img/02.png                        (content/shared/img/02.png)
+  content/shared/img/02@2x.png                     (content/shared/img/02@2x.png)
+  content/shared/img/telefonica.png                (content/shared/img/telefonica.png)
+  content/shared/img/hello_logo.svg                (content/shared/img/hello_logo.svg)
+  content/shared/img/telefonica@2x.png             (content/shared/img/telefonica@2x.png)
+  content/shared/img/ellipsis-v.svg                (content/shared/img/ellipsis-v.svg)
+  content/shared/img/empty_conversations.svg       (content/shared/img/empty_conversations.svg)
+  content/shared/img/empty_search.svg              (content/shared/img/empty_search.svg)
+  content/shared/img/animated-spinner.svg          (content/shared/img/animated-spinner.svg)
+  content/shared/img/avatars.svg                   (content/shared/img/avatars.svg)
+  content/shared/img/firefox-avatar.svg            (content/shared/img/firefox-avatar.svg)
+  content/shared/img/pause-12x12.svg               (content/shared/img/pause-12x12.svg)
+  content/shared/img/play-12x12.svg                (content/shared/img/play-12x12.svg)
+  content/shared/img/stop-12x12.svg                (content/shared/img/stop-12x12.svg)
 
   # Shared scripts
-  content/browser/loop/shared/js/actions.js             (content/shared/js/actions.js)
-  content/browser/loop/shared/js/store.js               (content/shared/js/store.js)
-  content/browser/loop/shared/js/activeRoomStore.js     (content/shared/js/activeRoomStore.js)
-  content/browser/loop/shared/js/dispatcher.js          (content/shared/js/dispatcher.js)
-  content/browser/loop/shared/js/linkifiedTextView.js   (content/shared/js/linkifiedTextView.js)
-  content/browser/loop/shared/js/loopapi-client.js      (content/shared/js/loopapi-client.js)
-  content/browser/loop/shared/js/models.js              (content/shared/js/models.js)
-  content/browser/loop/shared/js/mixins.js              (content/shared/js/mixins.js)
-  content/browser/loop/shared/js/otSdkDriver.js         (content/shared/js/otSdkDriver.js)
-  content/browser/loop/shared/js/views.js               (content/shared/js/views.js)
-  content/browser/loop/shared/js/textChatStore.js       (content/shared/js/textChatStore.js)
-  content/browser/loop/shared/js/textChatView.js        (content/shared/js/textChatView.js)
-  content/browser/loop/shared/js/urlRegExps.js          (content/shared/js/urlRegExps.js)
-  content/browser/loop/shared/js/utils.js               (content/shared/js/utils.js)
-  content/browser/loop/shared/js/validate.js            (content/shared/js/validate.js)
+  content/shared/js/actions.js                     (content/shared/js/actions.js)
+  content/shared/js/crypto.js                      (content/shared/js/crypto.js)
+  content/shared/js/store.js                       (content/shared/js/store.js)
+  content/shared/js/activeRoomStore.js             (content/shared/js/activeRoomStore.js)
+  content/shared/js/dispatcher.js                  (content/shared/js/dispatcher.js)
+  content/shared/js/linkifiedTextView.js           (content/shared/js/linkifiedTextView.js)
+  content/shared/js/loopapi-client.js              (content/shared/js/loopapi-client.js)
+  content/shared/js/models.js                      (content/shared/js/models.js)
+  content/shared/js/mixins.js                      (content/shared/js/mixins.js)
+  content/shared/js/otSdkDriver.js                 (content/shared/js/otSdkDriver.js)
+  content/shared/js/views.js                       (content/shared/js/views.js)
+  content/shared/js/textChatStore.js               (content/shared/js/textChatStore.js)
+  content/shared/js/textChatView.js                (content/shared/js/textChatView.js)
+  content/shared/js/urlRegExps.js                  (content/shared/js/urlRegExps.js)
+  content/shared/js/utils.js                       (content/shared/js/utils.js)
+  content/shared/js/validate.js                    (content/shared/js/validate.js)
 
   # Shared libs
 #ifdef DEBUG
-  content/browser/loop/shared/libs/react-0.13.3.js    (content/shared/libs/react-0.13.3.js)
+  content/shared/vendor/react-0.13.3.js            (content/shared/vendor/react-0.13.3.js)
 #else
-  content/browser/loop/shared/libs/react-0.13.3.js    (content/shared/libs/react-0.13.3-prod.js)
+  content/shared/vendor/react-0.13.3.js            (content/shared/vendor/react-0.13.3-prod.js)
 #endif
-  content/browser/loop/shared/libs/lodash-3.9.3.js    (content/shared/libs/lodash-3.9.3.js)
-  content/browser/loop/shared/libs/backbone-1.2.1.js  (content/shared/libs/backbone-1.2.1.js)
-  content/browser/loop/shared/libs/classnames-2.2.0.js      (content/shared/libs/classnames-2.2.0.js)
+  content/shared/vendor/lodash-3.9.3.js            (content/shared/vendor/lodash-3.9.3.js)
+  content/shared/vendor/backbone-1.2.1.js          (content/shared/vendor/backbone-1.2.1.js)
+  content/shared/vendor/classnames-2.2.0.js        (content/shared/vendor/classnames-2.2.0.js)
 
   # Shared sounds
-  content/browser/loop/shared/sounds/ringtone.ogg       (content/shared/sounds/ringtone.ogg)
-  content/browser/loop/shared/sounds/connecting.ogg     (content/shared/sounds/connecting.ogg)
-  content/browser/loop/shared/sounds/connected.ogg      (content/shared/sounds/connected.ogg)
-  content/browser/loop/shared/sounds/terminated.ogg     (content/shared/sounds/terminated.ogg)
-  content/browser/loop/shared/sounds/room-joined.ogg    (content/shared/sounds/room-joined.ogg)
-  content/browser/loop/shared/sounds/room-joined-in.ogg (content/shared/sounds/room-joined-in.ogg)
-  content/browser/loop/shared/sounds/room-left.ogg      (content/shared/sounds/room-left.ogg)
-  content/browser/loop/shared/sounds/failure.ogg        (content/shared/sounds/failure.ogg)
-  content/browser/loop/shared/sounds/message.ogg        (content/shared/sounds/message.ogg)
+  content/shared/sounds/ringtone.ogg               (content/shared/sounds/ringtone.ogg)
+  content/shared/sounds/connecting.ogg             (content/shared/sounds/connecting.ogg)
+  content/shared/sounds/connected.ogg              (content/shared/sounds/connected.ogg)
+  content/shared/sounds/terminated.ogg             (content/shared/sounds/terminated.ogg)
+  content/shared/sounds/room-joined.ogg            (content/shared/sounds/room-joined.ogg)
+  content/shared/sounds/room-joined-in.ogg         (content/shared/sounds/room-joined-in.ogg)
+  content/shared/sounds/room-left.ogg              (content/shared/sounds/room-left.ogg)
+  content/shared/sounds/failure.ogg                (content/shared/sounds/failure.ogg)
+  content/shared/sounds/message.ogg                (content/shared/sounds/message.ogg)
 
   # Partner SDK assets
-  content/browser/loop/libs/sdk.js                                                    (content/shared/libs/sdk.js)
-  content/browser/loop/sdk-content/js/dynamic_config.min.js                   (content/shared/libs/sdk-content/js/dynamic_config.min.js)
+  content/shared/vendor/sdk.js                     (content/shared/vendor/sdk.js)
+  content/sdk-content/js/dynamic_config.min.js     (content/shared/vendor/sdk-content/js/dynamic_config.min.js)
rename from browser/components/loop/manifest.ini
rename to browser/extensions/loop/manifest.ini
rename from browser/components/loop/moz.build
rename to browser/extensions/loop/moz.build
--- a/browser/components/loop/moz.build
+++ b/browser/extensions/loop/moz.build
@@ -1,27 +1,26 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
+FINAL_TARGET = 'dist/bin/browser/features/loop@mozilla.org'
+
+FINAL_TARGET_FILES += [
+ 'bootstrap.js'
+]
+
+DIST_FILES += [
+  'install.rdf.in'
+]
+
 JAR_MANIFESTS += ['jar.mn']
 
 XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
 
 BROWSER_CHROME_MANIFESTS += [
     'test/mochitest/browser.ini',
 ]
 
-EXTRA_JS_MODULES.loop += [
-    'content/shared/js/crypto.js',
-    'content/shared/js/utils.js',
-    'modules/LoopRooms.jsm',
-    'modules/LoopRoomsCache.jsm',
-    'modules/MozLoopAPI.jsm',
-    'modules/MozLoopPushHandler.jsm',
-    'modules/MozLoopService.jsm',
-    'modules/MozLoopWorker.js',
-]
-
 with Files('**'):
     BUG_COMPONENT = ('Loop', 'Client')
rename from browser/components/loop/run-all-loop-tests.sh
rename to browser/extensions/loop/run-all-loop-tests.sh
--- a/browser/components/loop/run-all-loop-tests.sh
+++ b/browser/extensions/loop/run-all-loop-tests.sh
@@ -7,30 +7,30 @@ if [ "$1" == "--help" ]; then
   exit 0;
 fi
 
 # Causes script to abort immediately if error code is not checked.
 set -e
 
 # Main tests
 
-LOOPDIR=browser/components/loop
+LOOPDIR=browser/extensions/loop
 ESLINT=standalone/node_modules/.bin/eslint
 if [ -x "${LOOPDIR}/${ESLINT}" ]; then
   echo 'running eslint; see http://eslint.org/docs/rules/ for error info'
   (cd ${LOOPDIR} && ./${ESLINT} --ext .js --ext .jsm --ext .jsx .)
   if [ $? != 0 ]; then
     exit 1;
   fi
   echo 'eslint run finished.'
 fi
 
 # Build tests coverage.
 MISSINGDEPSMSG="\nMake sure all dependencies are up to date by running
-'npm install' inside the 'browser/components/loop/test/' directory.\n"
+'npm install' inside the 'browser/extensions/loop/test/' directory.\n"
 (
 cd ${LOOPDIR}/test
 if ! npm run-script build-coverage ; then
   echo $MISSINGDEPSMSG && exit 1
 fi
 )
 
 ./mach xpcshell-test ${LOOPDIR}/
rename from browser/themes/windows/loop/menuPanel.png
rename to browser/extensions/loop/skin/linux/menuPanel.png
rename from browser/themes/windows/loop/menuPanel@2x.png
rename to browser/extensions/loop/skin/linux/menuPanel@2x.png
rename from browser/themes/windows/loop/toolbar-inverted.png
rename to browser/extensions/loop/skin/linux/toolbar-inverted.png
rename from browser/themes/windows/loop/toolbar-inverted@2x.png
rename to browser/extensions/loop/skin/linux/toolbar-inverted@2x.png
rename from browser/themes/linux/loop/toolbar.png
rename to browser/extensions/loop/skin/linux/toolbar.png
rename from browser/themes/linux/loop/toolbar@2x.png
rename to browser/extensions/loop/skin/linux/toolbar@2x.png
rename from browser/themes/osx/loop/menuPanel-yosemite.png
rename to browser/extensions/loop/skin/osx/menuPanel-yosemite.png
rename from browser/themes/osx/loop/menuPanel-yosemite@2x.png
rename to browser/extensions/loop/skin/osx/menuPanel-yosemite@2x.png
rename from browser/themes/osx/loop/menuPanel.png
rename to browser/extensions/loop/skin/osx/menuPanel.png
rename from browser/themes/osx/loop/menuPanel@2x.png
rename to browser/extensions/loop/skin/osx/menuPanel@2x.png
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/skin/osx/platform.css
@@ -0,0 +1,47 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Only apply to browser.xul documents */
+@-moz-document url("chrome://browser/content/browser.xul") {
+  /**
+   * XXX Due to bug 1228542, anything in this file that overrides a browser style
+   * must specify !important. Otherwise the style won't get applied correctly
+   * due to the limitations caused by the bug.
+   */
+
+  notification[value="loop-sharing-notification"] {
+    background: #00a9dc !important;
+    padding: 0 !important;
+    border: 0 !important;
+  }
+
+  notification[value="loop-sharing-notification"].paused {
+    background: #ebebeb !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-button {
+    background: #fff !important;
+    border-radius: 0 !important;
+  }
+
+  notification[value="loop-sharing-notification"].paused .notification-button {
+    background: #57bd35 !important;
+  }
+
+  notification[value="loop-sharing-notification"].paused .notification-button:hover {
+    background: #39a017 !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-button:hover,
+  notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
+    background: #ebebeb !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-button-default,
+  notification[value="loop-sharing-notification"].paused .notification-button-default {
+    background: #fff !important;
+  }
+}
rename from browser/themes/osx/loop/toolbar-inverted.png
rename to browser/extensions/loop/skin/osx/toolbar-inverted.png
rename from browser/themes/osx/loop/toolbar-inverted@2x.png
rename to browser/extensions/loop/skin/osx/toolbar-inverted@2x.png
rename from browser/themes/osx/loop/toolbar-yosemite.png
rename to browser/extensions/loop/skin/osx/toolbar-yosemite.png
rename from browser/themes/osx/loop/toolbar-yosemite@2x.png
rename to browser/extensions/loop/skin/osx/toolbar-yosemite@2x.png
rename from browser/themes/osx/loop/toolbar.png
rename to browser/extensions/loop/skin/osx/toolbar.png
rename from browser/themes/osx/loop/toolbar@2x.png
rename to browser/extensions/loop/skin/osx/toolbar@2x.png
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/skin/shared/loop.css
@@ -0,0 +1,286 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Only apply to browser.xul documents */
+@-moz-document url("chrome://browser/content/browser.xul") {
+  /**
+   * XXX Due to bug 1228542, anything in this file that overrides a browser style
+   * must specify !important. Otherwise the style won't get applied correctly
+   * due to the limitations caused by the bug.
+   */
+
+  /*
+     XXX Copied from browser/themes/<platform>/browser.css. Should really be
+     changing the sizes of icons in files to 16px x 16px and no borders.
+   */
+  :-moz-any(toolbar, .widget-overflow-list) #loop-button > .toolbarbutton-icon,
+  :-moz-any(toolbar, .widget-overflow-list) #loop-button > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon {
+    max-width: 18px !important;
+    margin: 0 !important;
+  }
+
+  #loop-button {
+    list-style-image: url(chrome://loop/skin/toolbar.png);
+    -moz-image-region: rect(0, 18px, 18px, 0);
+  }
+
+  toolbar[brighttext] #loop-button {
+    list-style-image: url(chrome://loop/skin/toolbar-inverted.png);
+  }
+
+  #loop-button[state="disabled"],
+  #loop-button[disabled="true"] {
+    -moz-image-region: rect(0, 36px, 18px, 18px);
+  }
+
+  #loop-button:not([disabled="true"])[state="error"] {
+    -moz-image-region: rect(0, 54px, 18px, 36px);
+  }
+
+  #loop-button:not([disabled="true"])[state="action"] {
+    -moz-image-region: rect(0, 72px, 18px, 54px);
+  }
+
+  #loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
+    -moz-image-region: rect(0, 90px, 18px, 72px);
+  }
+
+  #loop-button:not([disabled="true"])[state="active"] {
+    -moz-image-region: rect(0, 108px, 18px, 90px);
+  }
+
+  #loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
+    -moz-image-region: rect(0, 126px, 18px, 108px);
+  }
+
+  @media (min-resolution: 1.1dppx) {
+    #loop-button {
+      list-style-image: url("chrome://loop/skin/toolbar@2x.png");
+      -moz-image-region: rect(0, 36px, 36px, 0);
+    }
+
+    toolbar[brighttext] #loop-button {
+      list-style-image: url("chrome://loop/skin/toolbar-inverted@2x.png");
+    }
+
+    #loop-button[state="disabled"],
+    #loop-button[disabled="true"] {
+      -moz-image-region: rect(0, 72px, 36px, 36px);
+    }
+
+    #loop-button:not([disabled="true"])[state="error"] {
+      -moz-image-region: rect(0, 108px, 36px, 72px);
+    }
+
+    #loop-button:not([disabled="true"])[state="action"] {
+      -moz-image-region: rect(0, 144px, 36px, 108px);
+    }
+
+    #loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
+      -moz-image-region: rect(0, 180px, 36px, 144px);
+    }
+
+    #loop-button:not([disabled="true"])[state="active"] {
+      -moz-image-region: rect(0, 216px, 36px, 180px);
+    }
+
+    #loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
+      -moz-image-region: rect(0, 252px, 36px, 216px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"],
+    toolbarpaletteitem[place="palette"] > #loop-button {
+      list-style-image: url(chrome://loop/skin/menuPanel@2x.png);
+      -moz-image-region: rect(0, 64px, 64px, 0);
+    }
+
+    /* Make sure that the state icons are not shown in the customization palette. */
+    toolbarpaletteitem[place="palette"] > #loop-button {
+      -moz-image-region: rect(0, 64px, 64px, 0) !important;
+    }
+
+    #loop-button[cui-areatype="menu-panel"][state="disabled"],
+    #loop-button[cui-areatype="menu-panel"][disabled="true"] {
+      -moz-image-region: rect(0, 128px, 64px, 64px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] {
+      -moz-image-region: rect(0, 192px, 64px, 128px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] {
+      -moz-image-region: rect(0, 256px, 64px, 192px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
+      -moz-image-region: rect(0, 320px, 64px, 256px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] {
+      -moz-image-region: rect(0, 384px, 64px, 320px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
+      -moz-image-region: rect(0, 448px, 64px, 384px);
+    }
+  }
+
+  @media not all and (min-resolution: 1.1dppx) {
+    #loop-button[cui-areatype="menu-panel"],
+    toolbarpaletteitem[place="palette"] > #loop-button {
+      list-style-image: url(chrome://loop/skin/menuPanel.png);
+      -moz-image-region: rect(0, 32px, 32px, 0);
+    }
+
+    /* Make sure that the state icons are not shown in the customization palette. */
+    toolbarpaletteitem[place="palette"] > #loop-button {
+      -moz-image-region: rect(0, 32px, 32px, 0) !important;
+    }
+
+    #loop-button[cui-areatype="menu-panel"][state="disabled"],
+    #loop-button[cui-areatype="menu-panel"][disabled="true"] {
+      -moz-image-region: rect(0, 64px, 32px, 32px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] {
+      -moz-image-region: rect(0, 96px, 32px, 64px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] {
+      -moz-image-region: rect(0, 128px, 32px, 96px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
+      -moz-image-region: rect(0, 160px, 32px, 128px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] {
+      -moz-image-region: rect(0, 192px, 32px, 160px);
+    }
+
+    #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
+      -moz-image-region: rect(0, 224px, 32px, 192px);
+    }
+  }
+
+  notification[value="loop-sharing-notification"] {
+    -moz-appearance: none !important;
+    height: 40px !important;
+    background-color: #00a9dc !important;
+    box-shadow: 0 40px 1px rgba(0,0,0,.5) inset !important;
+  }
+
+  notification[value="loop-sharing-notification"].paused {
+    background-color: #ebebeb !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-inner {
+    color: #fff !important;
+    padding: 0 !important;
+  }
+
+  notification[value="loop-sharing-notification"].paused .notification-inner {
+    color: #00a9dc !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-button {
+    -moz-appearance: none !important;
+    background-color: #fff !important;
+    border: 0 !important;
+    border-right: solid 1px #ebebeb !important;
+    width: 100px !important;
+    height: 40px !important;
+    margin: 0 !important;
+    list-style-image: url(chrome://loop/content/shared/img/pause-12x12.svg) !important;
+    box-shadow: 0 40px 1px rgba(0,0,0,.5) inset !important;
+    text-shadow: none !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-button:-moz-locale-dir(rtl) {
+    border-right: 0 !important;
+    border-left: solid 1px #ebebeb !important;
+  }
+
+  notification[value="loop-sharing-notification"].paused .notification-button {
+    background-color: #57bd35 !important;
+    color: #fff !important;
+    list-style-image: url(chrome://loop/content/shared/img/play-12x12.svg) !important;
+  }
+
+  notification[value="loop-sharing-notification"].paused .notification-button:hover {
+    background-color: #39a017 !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-button:hover,
+  notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
+    background-color: #ebebeb !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-button-default,
+  notification[value="loop-sharing-notification"].paused .notification-button-default {
+    color: #d92215 !important;
+    background-color: #fff !important;
+    border-right: 0 !important;
+    list-style-image: url(chrome://loop/content/shared/img/stop-12x12.svg) !important;
+  }
+
+  notification[value="loop-sharing-notification"] .notification-button .button-icon {
+    display: block !important;
+    -moz-margin-end: 6px !important;
+  }
+
+  notification[value="loop-sharing-notification"] .button-menubutton-button {
+    min-width: 0 !important;
+  }
+
+  notification[value="loop-sharing-notification"] .messageImage {
+    list-style-image: url(chrome://loop/content/shared/img/icons-16x16.svg#loop-icon-white) !important;
+    margin-inline-start: 14px !important;
+  }
+
+  notification[value="loop-sharing-notification"].paused .messageImage {
+    list-style-image: url(chrome://loop/content/shared/img/icons-16x16.svg#loop-icon-still) !important;
+  }
+
+  notification[value="loop-sharing-notification"] .close-icon {
+    display: none !important;
+  }
+
+  chatbox[src^="about:loopconversation#"] > .chat-titlebar {
+    background-color: #00a9dc !important;
+    border-color: #00a9dc !important;
+  }
+
+  chatbox[src^="about:loopconversation#"] .chat-title {
+    color: white !important;
+  }
+
+  chatbox[src^="about:loopconversation#"] .chat-minimize-button {
+    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#minimize-white") !important;
+  }
+
+  chatbox[src^="about:loopconversation#"] .chat-swap-button {
+    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#expand-white") !important;
+  }
+
+  .chat-loop-hangup {
+    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#exit-white") !important;
+    background-color: #d13f1a !important;
+    border: 1px solid #d13f1a !important;
+    border-top-right-radius: 4px !important;
+    width: 32px !important;
+    height: 26px !important;
+    margin-top: -6px !important;
+    margin-bottom: -5px !important;
+    -moz-margin-start: 6px !important;
+    -moz-margin-end: -5px !important;
+  }
+
+  .chat-toolbarbutton.chat-loop-hangup:-moz-any(:hover,:hover:active) {
+    background-color: #ef6745 !important;
+    border-color: #ef6745 !important;
+  }
+}
rename from browser/themes/windows/loop/menuPanel-aero.png
rename to browser/extensions/loop/skin/windows/menuPanel-aero.png
rename from browser/themes/windows/loop/menuPanel-aero@2x.png
rename to browser/extensions/loop/skin/windows/menuPanel-aero@2x.png
rename from browser/themes/linux/loop/menuPanel.png
rename to browser/extensions/loop/skin/windows/menuPanel.png
rename from browser/themes/linux/loop/menuPanel@2x.png
rename to browser/extensions/loop/skin/windows/menuPanel@2x.png
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/skin/windows/platform.css
@@ -0,0 +1,26 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Only apply to browser.xul documents */
+@-moz-document url("chrome://browser/content/browser.xul") {
+  /**
+   * XXX Due to bug 1228542, anything in this file that overrides a browser style
+   * must specify !important. Otherwise the style won't get applied correctly
+   * due to the limitations caused by the bug.
+   */
+
+  @media (-moz-windows-theme: luna-silver) and (max-resolution: 1dppx) {
+    #loop-button {
+      list-style-image: url(chrome://loop/skin/toolbar-lunaSilver.png)
+    }
+  }
+
+  @media (-moz-windows-theme: luna-silver) and (min-resolution: 1.1dppx) {
+    #loop-button {
+      list-style-image: url(chrome://loop/skin/toolbar-lunaSilver@2x.png)
+    }
+  }
+}
rename from browser/themes/windows/loop/toolbar-XP.png
rename to browser/extensions/loop/skin/windows/toolbar-XP.png
rename from browser/themes/windows/loop/toolbar-XP@2x.png
rename to browser/extensions/loop/skin/windows/toolbar-XP@2x.png
rename from browser/themes/windows/loop/toolbar-aero.png
rename to browser/extensions/loop/skin/windows/toolbar-aero.png
rename from browser/themes/windows/loop/toolbar-aero@2x.png
rename to browser/extensions/loop/skin/windows/toolbar-aero@2x.png
rename from browser/themes/linux/loop/toolbar-inverted.png
rename to browser/extensions/loop/skin/windows/toolbar-inverted.png
rename from browser/themes/linux/loop/toolbar-inverted@2x.png
rename to browser/extensions/loop/skin/windows/toolbar-inverted@2x.png
rename from browser/themes/windows/loop/toolbar-lunaSilver.png
rename to browser/extensions/loop/skin/windows/toolbar-lunaSilver.png
rename from browser/themes/windows/loop/toolbar-lunaSilver@2x.png
rename to browser/extensions/loop/skin/windows/toolbar-lunaSilver@2x.png
rename from browser/themes/windows/loop/toolbar-win8.png
rename to browser/extensions/loop/skin/windows/toolbar-win8.png
rename from browser/themes/windows/loop/toolbar-win8@2x.png
rename to browser/extensions/loop/skin/windows/toolbar-win8@2x.png
rename from browser/themes/windows/loop/toolbar.png
rename to browser/extensions/loop/skin/windows/toolbar.png
rename from browser/themes/windows/loop/toolbar@2x.png
rename to browser/extensions/loop/skin/windows/toolbar@2x.png
rename from browser/components/loop/standalone/.gitignore
rename to browser/extensions/loop/standalone/.gitignore
rename from browser/components/loop/standalone/Makefile
rename to browser/extensions/loop/standalone/Makefile
rename from browser/components/loop/standalone/README.md
rename to browser/extensions/loop/standalone/README.md
rename from browser/components/loop/standalone/content/css/webapp.css
rename to browser/extensions/loop/standalone/content/css/webapp.css
--- a/browser/components/loop/standalone/content/css/webapp.css
+++ b/browser/extensions/loop/standalone/content/css/webapp.css
@@ -82,17 +82,17 @@ html[dir="rtl"] .standalone-overlay-wrap
   left: 0;
   right: auto;
 }
 
 .standalone-overlay-wrapper .icon-help {
   background-size: contain;
   width: 16px;
   height: 16px;
-  background: transparent url("../shared/img/svg/glyph-help-16x16.svg") no-repeat;
+  background: transparent url("../shared/img/glyph-help-16x16.svg") no-repeat;
 }
 
 .standalone-overlay-wrapper > .standalone-moz-logo {
   width: 50px;
   height: 13px;
   position: absolute;
   bottom: 0;
   right: 0;
@@ -266,17 +266,17 @@ html[dir="rtl"] .standalone-overlay-wrap
   justify-content: space-between;
   margin: 3em auto 1em;
   /* This should match the width set in the room-waiting-tile to ensure the edges
      of the content aligns within the tile width. */
   width: 290px;
 }
 
 .room-inner-info-area > .room-waiting-area > a >.room-waiting-help {
-  background: transparent url("../shared/img/svg/glyph-help-16x16.svg") no-repeat;
+  background: transparent url("../shared/img/glyph-help-16x16.svg") no-repeat;
   display: inline-block;
   height: 16px;
   margin-left: 5px;
   vertical-align: middle;
   width: 16px;
 }
 
 .room-inner-info-area > .room-waiting-tile {
rename from browser/components/loop/standalone/content/favicon.ico
rename to browser/extensions/loop/standalone/content/favicon.ico
rename from browser/components/loop/standalone/content/img/gum-chrome.svg
rename to browser/extensions/loop/standalone/content/img/gum-chrome.svg
rename from browser/components/loop/standalone/content/img/gum-firefox.svg
rename to browser/extensions/loop/standalone/content/img/gum-firefox.svg
rename from browser/components/loop/standalone/content/img/gum-opera.svg
rename to browser/extensions/loop/standalone/content/img/gum-opera.svg
rename from browser/components/loop/standalone/content/img/gum-others.svg
rename to browser/extensions/loop/standalone/content/img/gum-others.svg
rename from browser/components/loop/standalone/content/img/hello-logo-text.svg
rename to browser/extensions/loop/standalone/content/img/hello-logo-text.svg
rename from browser/components/loop/standalone/content/img/logo.png
rename to browser/extensions/loop/standalone/content/img/logo.png
rename from browser/components/loop/standalone/content/img/mozilla-logo.svg
rename to browser/extensions/loop/standalone/content/img/mozilla-logo.svg
rename from browser/components/loop/standalone/content/index.html
rename to browser/extensions/loop/standalone/content/index.html
rename from browser/components/loop/standalone/content/js/standaloneAppStore.js
rename to browser/extensions/loop/standalone/content/js/standaloneAppStore.js
rename from browser/components/loop/standalone/content/js/standaloneMetricsStore.js
rename to browser/extensions/loop/standalone/content/js/standaloneMetricsStore.js
rename from browser/components/loop/standalone/content/js/standaloneMozLoop.js
rename to browser/extensions/loop/standalone/content/js/standaloneMozLoop.js
rename from browser/components/loop/standalone/content/js/standaloneRoomViews.js
rename to browser/extensions/loop/standalone/content/js/standaloneRoomViews.js
rename from browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
rename to browser/extensions/loop/standalone/content/js/standaloneRoomViews.jsx
rename from browser/components/loop/standalone/content/js/webapp.js
rename to browser/extensions/loop/standalone/content/js/webapp.js
rename from browser/components/loop/standalone/content/js/webapp.jsx
rename to browser/extensions/loop/standalone/content/js/webapp.jsx
rename from browser/components/loop/standalone/content/l10n/en-US/loop.properties
rename to browser/extensions/loop/standalone/content/l10n/en-US/loop.properties
rename from browser/components/loop/standalone/content/libs/l10n-gaia-02ca67948fe8.js
rename to browser/extensions/loop/standalone/content/libs/l10n-gaia-02ca67948fe8.js
rename from browser/components/loop/standalone/content/libs/l10n-gaia-upstream.txt
rename to browser/extensions/loop/standalone/content/libs/l10n-gaia-upstream.txt
rename from browser/components/loop/standalone/content/robots.txt
rename to browser/extensions/loop/standalone/content/robots.txt
rename from browser/components/loop/standalone/content/webappEntryPoint.js
rename to browser/extensions/loop/standalone/content/webappEntryPoint.js
--- a/browser/components/loop/standalone/content/webappEntryPoint.js
+++ b/browser/extensions/loop/standalone/content/webappEntryPoint.js
@@ -12,42 +12,42 @@
 // To get started, we're using webpack's script loader to load these things in
 // as-is.
 
 /* global require */
 
 // The OpenTok SDK tries to do some heuristic detection of require and
 // assumes a node environment if it's present, which confuses webpack, so
 // we turn that off by forcing require to false in that context.
-require("imports?require=>false!shared/libs/sdk.js");
+require("imports?require=>false!shared/vendor/sdk.js");
 
 // Ultimately, we'll likely want to pull the vendor libraries from npm, as that
 // makes upgrading easier, and it's generally better practice to minify the
 // "source" versions of libraries rather than built artifacts.  We probably do
 // want to minify them ourselves since this allows for better dead-code
 // elimination, but that can be a bit of judgement call.
-require("exports?_!shared/libs/lodash-3.9.3.js");
+require("exports?_!shared/vendor/lodash-3.9.3.js");
 
 // Disable Backbone's AMD auto-detection, as described at:
 //
 // https://github.com/jashkenas/backbone/wiki/Using-Backbone-without-jQuery
 //
-require("expose?Backbone!imports?define=>false!shared/libs/backbone-1.2.1.js");
+require("expose?Backbone!imports?define=>false!shared/vendor/backbone-1.2.1.js");
 
 /* global: __PROD__ */
 if (typeof __PROD__ !== "undefined") {
   // webpack warns if we try to minify some prebuilt libraries, so we
   // pull in the unbuilt version from node_modules
   require("expose?React!react");
   require("expose?React!react/addons");
   require("expose?classNames!classnames");
 } else {
   // our development server setup doesn't yet handle real modules, so for now...
-  require("shared/libs/react-0.13.3.js");
-  require("shared/libs/classnames-2.2.0.js");
+  require("shared/vendor/react-0.13.3.js");
+  require("shared/vendor/classnames-2.2.0.js");
 }
 
 
 // Someday, these will be real modules.  For now, we're chaining loaders
 // to teach webpack how to treat them like modules anyway.
 //
 // We do it in this file rather than globally in webpack.config.js
 // because:
rename from browser/components/loop/standalone/package.json
rename to browser/extensions/loop/standalone/package.json
rename from browser/components/loop/standalone/server.js
rename to browser/extensions/loop/standalone/server.js
--- a/browser/components/loop/standalone/server.js
+++ b/browser/extensions/loop/standalone/server.js
@@ -85,16 +85,20 @@ app.use("/content", express.static(path.
 // These two are based on the above, but handle call urls, that have a /c/ in them.
 app.use("/content/c", express.static(path.join(__dirname,
   standaloneContentDir)));
 app.use("/content/c", express.static(path.join(__dirname, "..", "content")));
 
 // Two lines for the same reason as /content above.
 app.use("/test", express.static(path.join(__dirname, "test")));
 app.use("/test", express.static(path.join(__dirname, "..", "test")));
+// Hacks for desktop to stop errors being raised in the tests for content loading.
+app.use("/test/shared/shared", express.static(path.join(__dirname, "..", "content/shared")));
+app.use("/test/desktop-local/shared", express.static(path.join(__dirname, "..", "content/shared")));
+
 
 // As we don't have hashes on the urls, the best way to serve the index files
 // appears to be to be to closely filter the url and match appropriately.
 function serveIndex(req, res) {
   "use strict";
 
   return res.sendFile(path.join(__dirname, standaloneContentDir, "index.html"));
 }
rename from browser/components/loop/standalone/webpack.config.js
rename to browser/extensions/loop/standalone/webpack.config.js
rename from browser/components/loop/test/.eslintrc
rename to browser/extensions/loop/test/.eslintrc
rename from browser/components/loop/test/coverage/index.html
rename to browser/extensions/loop/test/coverage/index.html
rename from browser/components/loop/test/desktop-local/.eslintrc
rename to browser/extensions/loop/test/desktop-local/.eslintrc
rename from browser/components/loop/test/desktop-local/README.md
rename to browser/extensions/loop/test/desktop-local/README.md
--- a/browser/components/loop/test/desktop-local/README.md
+++ b/browser/extensions/loop/test/desktop-local/README.md
@@ -6,16 +6,16 @@ and the Chai Assertion Library's [BDD in
 [1]: http://visionmedia.github.io/mocha/
 [2]: http://chaijs.com/api/bdd/
 
 Aim your browser at the index.html in this directory on your localhost using
 a file: or HTTP URL to run the tests.  Alternately, from the top-level of your
 Gecko source directory, execute:
 
 ```
-./mach marionette-test browser/components/loop/test/manifest.ini
+./mach marionette-test browser/extensions/loop/test/manifest.ini
 ```
 
 Next steps:
 
 * run using JS http server so the property security context for DOM elements
 is used
 
rename from browser/components/loop/test/desktop-local/conversationAppStore_test.js
rename to browser/extensions/loop/test/desktop-local/conversationAppStore_test.js
rename from browser/components/loop/test/desktop-local/conversation_test.js
rename to browser/extensions/loop/test/desktop-local/conversation_test.js
rename from browser/components/loop/test/desktop-local/feedbackViews_test.js
rename to browser/extensions/loop/test/desktop-local/feedbackViews_test.js
rename from browser/components/loop/test/desktop-local/index.html
rename to browser/extensions/loop/test/desktop-local/index.html
--- a/browser/components/loop/test/desktop-local/index.html
+++ b/browser/extensions/loop/test/desktop-local/index.html
@@ -9,27 +9,27 @@
   <link rel="stylesheet" media="all" href="../shared/vendor/mocha-2.2.5.css">
 </head>
 <body>
   <div id="mocha">
     <p><a href="../">Index</a></p>
   </div>
   <div id="messages"></div>
   <div id="fixtures"></div>
-  <script src="../../content/shared/libs/lodash-3.9.3.js"></script>
+  <script src="../../content/shared/vendor/lodash-3.9.3.js"></script>
   <script src="../shared/loop_mocha_utils.js"></script>
   <script>
     LoopMochaUtils.trapErrors();
   </script>
 
   <!-- libs -->
-  <script src="../../content/libs/l10n.js"></script>
-  <script src="../../content/shared/libs/react-0.13.3.js"></script>
-  <script src="../../content/shared/libs/classnames-2.2.0.js"></script>
-  <script src="../../content/shared/libs/backbone-1.2.1.js"></script>
+  <script src="../../content/panels/vendor/l10n.js"></script>
+  <script src="../../content/shared/vendor/react-0.13.3.js"></script>
+  <script src="../../content/shared/vendor/classnames-2.2.0.js"></script>
+  <script src="../../content/shared/vendor/backbone-1.2.1.js"></script>
 
   <!-- test dependencies -->
   <script src="../shared/vendor/mocha-2.2.5.js"></script>
   <script src="../shared/vendor/chai-3.0.0.js"></script>
   <script src="../shared/vendor/sinon-1.16.1.js"></script>
   <script>
     /*global chai,mocha */
     chai.config.includeStack = true;
@@ -45,22 +45,22 @@
   <script src="../../content/shared/js/validate.js"></script>
   <script src="../../content/shared/js/dispatcher.js"></script>
   <script src="../../content/shared/js/otSdkDriver.js"></script>
   <script src="../../content/shared/js/store.js"></script>
   <script src="../../content/shared/js/activeRoomStore.js"></script>
   <script src="../../content/shared/js/views.js"></script>
   <script src="../../content/shared/js/textChatStore.js"></script>
   <script src="../../content/shared/js/textChatView.js"></script>
-  <script src="../../content/js/conversationAppStore.js"></script>
-  <script src="../../content/js/roomStore.js"></script>
-  <script src="../../content/js/roomViews.js"></script>
-  <script src="../../content/js/feedbackViews.js"></script>
-  <script src="../../content/js/conversation.js"></script>
-  <script src="../../content/js/panel.js"></script>
+  <script src="../../content/panels/js/conversationAppStore.js"></script>
+  <script src="../../content/panels/js/roomStore.js"></script>
+  <script src="../../content/panels/js/roomViews.js"></script>
+  <script src="../../content/panels/js/feedbackViews.js"></script>
+  <script src="../../content/panels/js/conversation.js"></script>
+  <script src="../../content/panels/js/panel.js"></script>
 
   <!-- Test scripts -->
   <script src="conversationAppStore_test.js"></script>
   <script src="conversation_test.js"></script>
   <script src="feedbackViews_test.js"></script>
   <script src="panel_test.js"></script>
   <script src="roomViews_test.js"></script>
   <script src="l10n_test.js"></script>
rename from browser/components/loop/test/desktop-local/l10n_test.js
rename to browser/extensions/loop/test/desktop-local/l10n_test.js
rename from browser/components/loop/test/desktop-local/panel_test.js
rename to browser/extensions/loop/test/desktop-local/panel_test.js
rename from browser/components/loop/test/desktop-local/roomStore_test.js
rename to browser/extensions/loop/test/desktop-local/roomStore_test.js
rename from browser/components/loop/test/desktop-local/roomViews_test.js
rename to browser/extensions/loop/test/desktop-local/roomViews_test.js
rename from browser/components/loop/test/desktop-local/test_desktop_all.py
rename to browser/extensions/loop/test/desktop-local/test_desktop_all.py
rename from browser/components/loop/test/functional/config.py
rename to browser/extensions/loop/test/functional/config.py
rename from browser/components/loop/test/functional/hanging_threads.py
rename to browser/extensions/loop/test/functional/hanging_threads.py
rename from browser/components/loop/test/functional/manifest.ini
rename to browser/extensions/loop/test/functional/manifest.ini
rename from browser/components/loop/test/functional/serversetup.py
rename to browser/extensions/loop/test/functional/serversetup.py
rename from browser/components/loop/test/functional/test_1_browser_call.py
rename to browser/extensions/loop/test/functional/test_1_browser_call.py
rename from browser/components/loop/test/index.html
rename to browser/extensions/loop/test/index.html
rename from browser/components/loop/test/karma/head.js
rename to browser/extensions/loop/test/karma/head.js
rename from browser/components/loop/test/karma/karma.conf.base.js
rename to browser/extensions/loop/test/karma/karma.conf.base.js
rename from browser/components/loop/test/karma/karma.coverage.desktop.js
rename to browser/extensions/loop/test/karma/karma.coverage.desktop.js
--- a/browser/components/loop/test/karma/karma.coverage.desktop.js
+++ b/browser/extensions/loop/test/karma/karma.coverage.desktop.js
@@ -5,52 +5,52 @@
 
 module.exports = function(config) {
   "use strict";
 
   var baseConfig = require("./karma.conf.base.js")(config);
 
   // List of files / patterns to load in the browser.
   baseConfig.files = baseConfig.files.concat([
-    "content/libs/l10n.js",
-    "content/shared/libs/react-0.13.3.js",
-    "content/shared/libs/classnames-2.2.0.js",
-    "content/shared/libs/lodash-3.9.3.js",
-    "content/shared/libs/backbone-1.2.1.js",
+    "content/panels/vendor/l10n.js",
+    "content/shared/vendor/react-0.13.3.js",
+    "content/shared/vendor/classnames-2.2.0.js",
+    "content/shared/vendor/lodash-3.9.3.js",
+    "content/shared/vendor/backbone-1.2.1.js",
     "test/shared/vendor/*.js",
     "test/shared/loop_mocha_utils.js",
     "test/karma/head.js", // Stub out DOM event listener due to races.
     "content/shared/js/loopapi-client.js",
     "content/shared/js/utils.js",
     "content/shared/js/models.js",
     "content/shared/js/mixins.js",
     "content/shared/js/actions.js",
     "content/shared/js/otSdkDriver.js",
     "content/shared/js/validate.js",
     "content/shared/js/dispatcher.js",
     "content/shared/js/store.js",
     "content/shared/js/activeRoomStore.js",
     "content/shared/js/views.js",
     "content/shared/js/textChatStore.js",
     "content/shared/js/textChatView.js",
-    "content/js/feedbackViews.js",
-    "content/js/conversationAppStore.js",
-    "content/js/roomStore.js",
-    "content/js/roomViews.js",
-    "content/js/conversation.js",
+    "content/panels/js/feedbackViews.js",
+    "content/panels/js/conversationAppStore.js",
+    "content/panels/js/roomStore.js",
+    "content/panels/js/roomViews.js",
+    "content/panels/js/conversation.js",
     "test/desktop-local/*.js"
   ]);
 
   // List of files to exclude.
   baseConfig.exclude = baseConfig.exclude.concat([
     "test/desktop-local/panel_test.js"
   ]);
 
   // Preprocess matching files before serving them to the browser.
   // Available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor .
   baseConfig.preprocessors = {
-    "content/js/*.js": ["coverage"]
+    "content/panels/js/*.js": ["coverage"]
   };
 
   baseConfig.coverageReporter.dir = "test/coverage/desktop";
 
   config.set(baseConfig);
 };
rename from browser/components/loop/test/karma/karma.coverage.shared_standalone.js
rename to browser/extensions/loop/test/karma/karma.coverage.shared_standalone.js
--- a/browser/components/loop/test/karma/karma.coverage.shared_standalone.js
+++ b/browser/extensions/loop/test/karma/karma.coverage.shared_standalone.js
@@ -6,21 +6,21 @@
 module.exports = function(config) {
   "use strict";
 
   var baseConfig = require("./karma.conf.base.js")(config);
 
   // List of files / patterns to load in the browser.
   baseConfig.files = baseConfig.files.concat([
     "standalone/content/libs/l10n-gaia-02ca67948fe8.js",
-    "content/shared/libs/lodash-3.9.3.js",
-    "content/shared/libs/backbone-1.2.1.js",
-    "content/shared/libs/react-0.13.3.js",
-    "content/shared/libs/classnames-2.2.0.js",
-    "content/shared/libs/sdk.js",
+    "content/shared/vendor/lodash-3.9.3.js",
+    "content/shared/vendor/backbone-1.2.1.js",
+    "content/shared/vendor/react-0.13.3.js",
+    "content/shared/vendor/classnames-2.2.0.js",
+    "content/shared/vendor/sdk.js",
     "test/shared/vendor/*.js",
     "test/shared/loop_mocha_utils.js",
     "test/karma/head.js", // Add test fixture container
     "content/shared/js/loopapi-client.js",
     "content/shared/js/utils.js",
     "content/shared/js/store.js",
     "content/shared/js/models.js",
     "content/shared/js/mixins.js",
rename from browser/components/loop/test/mochitest/.eslintrc
rename to browser/extensions/loop/test/mochitest/.eslintrc
rename from browser/components/loop/test/mochitest/browser.ini
rename to browser/extensions/loop/test/mochitest/browser.ini
rename from browser/components/loop/test/mochitest/browser_LoopRooms_channel.js
rename to browser/extensions/loop/test/mochitest/browser_LoopRooms_channel.js
--- a/browser/components/loop/test/mochitest/browser_LoopRooms_channel.js
+++ b/browser/extensions/loop/test/mochitest/browser_LoopRooms_channel.js
@@ -7,17 +7,17 @@
  * window.
  */
 "use strict";
 
 var { WebChannel } = Cu.import("resource://gre/modules/WebChannel.jsm", {});
 var { Chat } = Cu.import("resource:///modules/Chat.jsm", {});
 
 const TEST_URI =
-  "example.com/browser/browser/components/loop/test/mochitest/test_loopLinkClicker_channel.html";
+  "example.com/browser/browser/extensions/loop/test/mochitest/test_loopLinkClicker_channel.html";
 const TEST_URI_GOOD = Services.io.newURI("https://" + TEST_URI, null, null);
 const TEST_URI_BAD = Services.io.newURI("http://" + TEST_URI, null, null);
 
 const ROOM_TOKEN = "fake1234";
 const LINKCLICKER_URL_PREFNAME = "loop.linkClicker.url";
 
 var openChatOrig = Chat.open;
 
rename from browser/components/loop/test/mochitest/browser_fxa_login.js
rename to browser/extensions/loop/test/mochitest/browser_fxa_login.js
--- a/browser/components/loop/test/mochitest/browser_fxa_login.js
+++ b/browser/extensions/loop/test/mochitest/browser_fxa_login.js
@@ -3,17 +3,17 @@
 
 /**
  * Test FxA logins with Loop.
  */
 
 "use strict";
 
 const BASE_URL = Services.prefs.getCharPref("loop.server");
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 
 function* checkFxA401() {
   let err = MozLoopService.errors.get("login");
   is(err.code, 401, "Check error code");
   is(err.friendlyMessage, getLoopString("could_not_authenticate"),
      "Check friendlyMessage");
   is(err.friendlyDetails, getLoopString("password_changed_question"),
      "Check friendlyDetails");
rename from browser/components/loop/test/mochitest/browser_loop_fxa_server.js
rename to browser/extensions/loop/test/mochitest/browser_loop_fxa_server.js
--- a/browser/components/loop/test/mochitest/browser_loop_fxa_server.js
+++ b/browser/extensions/loop/test/mochitest/browser_loop_fxa_server.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Test the server mocking FxA integration endpoints on the Loop server.
  */
 
 "use strict";
 
-const BASE_URL = "http://mochi.test:8888/browser/browser/components/loop/test/mochitest/loop_fxa.sjs?";
+const BASE_URL = "http://mochi.test:8888/browser/browser/extensions/loop/test/mochitest/loop_fxa.sjs?";
 
 registerCleanupFunction(function* () {
   yield promiseDeletedOAuthParams(BASE_URL);
 });
 
 add_task(function* required_setup_params() {
   let params = {
     client_id: "my_client_id",
rename from browser/components/loop/test/mochitest/browser_mozLoop_appVersionInfo.js
rename to browser/extensions/loop/test/mochitest/browser_mozLoop_appVersionInfo.js
--- a/browser/components/loop/test/mochitest/browser_mozLoop_appVersionInfo.js
+++ b/browser/extensions/loop/test/mochitest/browser_mozLoop_appVersionInfo.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 var [, gHandlers] = LoopAPI.inspect();
 
 add_task(function* test_mozLoop_appVersionInfo() {
   let appVersionInfo;
   gHandlers.GetAppVersionInfo({}, result => appVersionInfo = result);
 
   Assert.ok(appVersionInfo, "should have appVersionInfo");
 
rename from browser/components/loop/test/mochitest/browser_mozLoop_chat.js
rename to browser/extensions/loop/test/mochitest/browser_mozLoop_chat.js
rename from browser/components/loop/test/mochitest/browser_mozLoop_context.js
rename to browser/extensions/loop/test/mochitest/browser_mozLoop_context.js
--- a/browser/components/loop/test/mochitest/browser_mozLoop_context.js
+++ b/browser/extensions/loop/test/mochitest/browser_mozLoop_context.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * This file contains tests for various context-in-conversations helpers in
  * LoopUI and Loop API.
  */
 "use strict";
 
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 var [, gHandlers] = LoopAPI.inspect();
 
 function promiseGetMetadata() {
   return new Promise(resolve => gHandlers.GetSelectedTabMetadata({}, resolve));
 }
 
 add_task(function* test_mozLoop_getSelectedTabMetadata() {
   let metadata = yield promiseGetMetadata();
rename from browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
rename to browser/extensions/loop/test/mochitest/browser_mozLoop_sharingListeners.js
--- a/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
+++ b/browser/extensions/loop/test/mochitest/browser_mozLoop_sharingListeners.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * This file contains tests for the window.LoopUI active tab trackers.
  */
 "use strict";
 
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 var [, gHandlers] = LoopAPI.inspect();
 
 var handlers = [
   { windowId: null }, { windowId: null }
 ];
 
 function promiseWindowId() {
   return new Promise(resolve => {
rename from browser/components/loop/test/mochitest/browser_mozLoop_socialShare.js
rename to browser/extensions/loop/test/mochitest/browser_mozLoop_socialShare.js
--- a/browser/components/loop/test/mochitest/browser_mozLoop_socialShare.js
+++ b/browser/extensions/loop/test/mochitest/browser_mozLoop_socialShare.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 Cu.import("resource://gre/modules/Promise.jsm");
 const { SocialService } = Cu.import("resource://gre/modules/SocialService.jsm", {});
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 
 var [, gHandlers] = LoopAPI.inspect();
 
 const kShareProvider = {
   name: "provider 1",
   origin: "https://example.com",
   iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
   shareURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar_empty.html"
rename from browser/components/loop/test/mochitest/browser_mozLoop_telemetry.js
rename to browser/extensions/loop/test/mochitest/browser_mozLoop_telemetry.js
--- a/browser/components/loop/test/mochitest/browser_mozLoop_telemetry.js
+++ b/browser/extensions/loop/test/mochitest/browser_mozLoop_telemetry.js
@@ -2,17 +2,17 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /*
  * This file contains tests for the mozLoop telemetry API.
  */
 
 "use strict";
 
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 var [, gHandlers] = LoopAPI.inspect();
 var gConstants;
 gHandlers.GetAllConstants({}, constants => gConstants = constants);
 
 /**
  * Enable local telemetry recording for the duration of the tests.
  */
 add_task(function* test_initialize() {
rename from browser/components/loop/test/mochitest/browser_toolbarbutton.js
rename to browser/extensions/loop/test/mochitest/browser_toolbarbutton.js
--- a/browser/components/loop/test/mochitest/browser_toolbarbutton.js
+++ b/browser/extensions/loop/test/mochitest/browser_toolbarbutton.js
@@ -3,17 +3,17 @@
 
 /**
  * Test the toolbar button states.
  */
 
 "use strict";
 
 Components.utils.import("resource://gre/modules/Promise.jsm", this);
-const { LoopRoomsInternal } = Components.utils.import("resource:///modules/loop/LoopRooms.jsm", {});
+const { LoopRoomsInternal } = Components.utils.import("chrome://loop/content/modules/LoopRooms.jsm", {});
 Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
 
 const fxASampleToken = {
   token_type: "bearer",
   access_token: "1bad3e44b12f77a88fe09f016f6a37c42e40f974bc7a8b432bb0d2f0e37e1752",
   scope: "profile"
 };
 
rename from browser/components/loop/test/mochitest/head.js
rename to browser/extensions/loop/test/mochitest/head.js
--- a/browser/components/loop/test/mochitest/head.js
+++ b/browser/extensions/loop/test/mochitest/head.js
@@ -3,18 +3,18 @@
 
 "use strict";
 
 const HAWK_TOKEN_LENGTH = 64;
 const {
   LOOP_SESSION_TYPE,
   MozLoopServiceInternal,
   MozLoopService
-} = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
-const { LoopRooms } = Cu.import("resource:///modules/loop/LoopRooms.jsm", {});
+} = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
+const { LoopRooms } = Cu.import("chrome://loop/content/modules/LoopRooms.jsm", {});
 
 // Cache this value only once, at the beginning of a
 // test run, so that it doesn't pick up the offline=true
 // if offline mode is requested multiple times in a test run.
 const WAS_OFFLINE = Services.io.offline;
 
 function promisePanelLoaded() {
   return new Promise((resolve, reject) => {
@@ -128,17 +128,17 @@ function promiseOAuthParamsSetup(baseURL
     xhr.setRequestHeader("X-Params", JSON.stringify(params));
     xhr.addEventListener("load", () => resolve(xhr));
     xhr.addEventListener("error", error => reject(error));
     xhr.send();
   });
 }
 
 function* resetFxA() {
-  let global = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
+  let global = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
   global.gHawkClient = null;
   global.gFxAOAuthClientPromise = null;
   global.gFxAOAuthClient = null;
   MozLoopServiceInternal.fxAOAuthProfile = null;
   MozLoopServiceInternal.fxAOAuthTokenData = null;
   const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
   Services.prefs.clearUserPref(fxASessionPref);
   MozLoopService.errors.clear();
@@ -147,17 +147,17 @@ function* resetFxA() {
   yield notified;
 }
 
 function checkFxAOAuthTokenData(aValue) {
   is(MozLoopServiceInternal.fxAOAuthTokenData, aValue, "fxAOAuthTokenData should be " + aValue);
 }
 
 function checkLoggedOutState() {
-  let global = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
+  let global = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
   is(global.gFxAOAuthClientPromise, null, "gFxAOAuthClientPromise should be cleared");
   is(MozLoopService.userProfile, null, "fxAOAuthProfile should be cleared");
   is(global.gFxAOAuthClient, null, "gFxAOAuthClient should be cleared");
   checkFxAOAuthTokenData(null);
   const fxASessionPref = MozLoopServiceInternal.getSessionTokenPrefName(LOOP_SESSION_TYPE.FXA);
   is(Services.prefs.getPrefType(fxASessionPref), Services.prefs.PREF_INVALID,
      "FxA hawk session should be cleared anyways");
 }
rename from browser/components/loop/test/mochitest/loop_fxa.sjs
rename to browser/extensions/loop/test/mochitest/loop_fxa.sjs
rename from browser/components/loop/test/mochitest/test_loopLinkClicker_channel.html
rename to browser/extensions/loop/test/mochitest/test_loopLinkClicker_channel.html
rename from browser/components/loop/test/package.json
rename to browser/extensions/loop/test/package.json
rename from browser/components/loop/test/shared/activeRoomStore_test.js
rename to browser/extensions/loop/test/shared/activeRoomStore_test.js
rename from browser/components/loop/test/shared/crypto_test.js
rename to browser/extensions/loop/test/shared/crypto_test.js
rename from browser/components/loop/test/shared/dispatcher_test.js
rename to browser/extensions/loop/test/shared/dispatcher_test.js
rename from browser/components/loop/test/shared/frontend_tester.py
rename to browser/extensions/loop/test/shared/frontend_tester.py
rename from browser/components/loop/test/shared/index.html
rename to browser/extensions/loop/test/shared/index.html
--- a/browser/components/loop/test/shared/index.html
+++ b/browser/extensions/loop/test/shared/index.html
@@ -9,26 +9,26 @@
   <link rel="stylesheet" media="all" href="vendor/mocha-2.2.5.css">
 </head>
 <body>
   <div id="mocha">
     <p><a href="../">Index</a></p>
   </div>
   <div id="messages"></div>
   <div id="fixtures"></div>
-  <script src="../../content/shared/libs/lodash-3.9.3.js"></script>
+  <script src="../../content/shared/vendor/lodash-3.9.3.js"></script>
   <script src="../shared/loop_mocha_utils.js"></script>
   <script>
     LoopMochaUtils.trapErrors();
   </script>
 
   <!-- libs -->
-  <script src="../../content/shared/libs/react-0.13.3.js"></script>
-  <script src="../../content/shared/libs/classnames-2.2.0.js"></script>
-  <script src="../../content/shared/libs/backbone-1.2.1.js"></script>
+  <script src="../../content/shared/vendor/react-0.13.3.js"></script>
+  <script src="../../content/shared/vendor/classnames-2.2.0.js"></script>
+  <script src="../../content/shared/vendor/backbone-1.2.1.js"></script>
   <script src="../../standalone/content/libs/l10n-gaia-02ca67948fe8.js"></script>
 
   <!-- test dependencies -->
   <script src="vendor/mocha-2.2.5.js"></script>
   <script src="vendor/chai-3.0.0.js"></script>
   <script src="vendor/chai-as-promised-5.1.0.js"></script>
   <script src="vendor/sinon-1.16.1.js"></script>
   <script>
rename from browser/components/loop/test/shared/linkifiedTextView_test.js
rename to browser/extensions/loop/test/shared/linkifiedTextView_test.js
rename from browser/components/loop/test/shared/loop_mocha_utils.js
rename to browser/extensions/loop/test/shared/loop_mocha_utils.js
--- a/browser/components/loop/test/shared/loop_mocha_utils.js
+++ b/browser/extensions/loop/test/shared/loop_mocha_utils.js
@@ -253,17 +253,21 @@ var LoopMochaUtils = (function(global, _
         throw new Error();
       } catch (e) {
         gCaughtIssues.push([args, e.stack]);
       }
       consoleWarn.apply(console, args);
     };
     console.error = function() {
       var args = Array.slice(arguments);
-      gCaughtIssues.push(args);
+      try {
+        throw new Error();
+      } catch (e) {
+        gCaughtIssues.push([args, e.stack]);
+      }
       consoleError.apply(console, args);
     };
   }
 
   /**
    * Adds tests to check no warnings nor errors have occurred since trapErrors
    * was called.
    */
rename from browser/components/loop/test/shared/loopapi-client_test.js
rename to browser/extensions/loop/test/shared/loopapi-client_test.js
rename from browser/components/loop/test/shared/mixins_test.js
rename to browser/extensions/loop/test/shared/mixins_test.js
rename from browser/components/loop/test/shared/models_test.js
rename to browser/extensions/loop/test/shared/models_test.js
rename from browser/components/loop/test/shared/otSdkDriver_test.js
rename to browser/extensions/loop/test/shared/otSdkDriver_test.js
rename from browser/components/loop/test/shared/sdk_mock.js
rename to browser/extensions/loop/test/shared/sdk_mock.js
rename from browser/components/loop/test/shared/store_test.js
rename to browser/extensions/loop/test/shared/store_test.js
rename from browser/components/loop/test/shared/test_shared_all.py
rename to browser/extensions/loop/test/shared/test_shared_all.py
rename from browser/components/loop/test/shared/textChatStore_test.js
rename to browser/extensions/loop/test/shared/textChatStore_test.js
rename from browser/components/loop/test/shared/textChatView_test.js
rename to browser/extensions/loop/test/shared/textChatView_test.js
rename from browser/components/loop/test/shared/utils_test.js
rename to browser/extensions/loop/test/shared/utils_test.js
rename from browser/components/loop/test/shared/validate_test.js
rename to browser/extensions/loop/test/shared/validate_test.js
rename from browser/components/loop/test/shared/vendor/chai-3.0.0.js
rename to browser/extensions/loop/test/shared/vendor/chai-3.0.0.js
rename from browser/components/loop/test/shared/vendor/chai-as-promised-5.1.0.js
rename to browser/extensions/loop/test/shared/vendor/chai-as-promised-5.1.0.js
rename from browser/components/loop/test/shared/vendor/mocha-2.2.5.css
rename to browser/extensions/loop/test/shared/vendor/mocha-2.2.5.css
rename from browser/components/loop/test/shared/vendor/mocha-2.2.5.js
rename to browser/extensions/loop/test/shared/vendor/mocha-2.2.5.js
rename from browser/components/loop/test/shared/vendor/sinon-1.16.1.js
rename to browser/extensions/loop/test/shared/vendor/sinon-1.16.1.js
rename from browser/components/loop/test/shared/views_test.js
rename to browser/extensions/loop/test/shared/views_test.js
--- a/browser/components/loop/test/shared/views_test.js
+++ b/browser/extensions/loop/test/shared/views_test.js
@@ -835,17 +835,17 @@ describe("loop.shared.views", function()
 
     it("should use a default thumbnail for desktop if one is not supplied", function() {
       view = mountTestComponent({
         useDesktopPaths: true,
         url: "http://wonderful.invalid"
       });
 
       expect(view.getDOMNode().querySelector(".context-preview").getAttribute("src"))
-        .eql("loop/shared/img/icons-16x16.svg#globe");
+        .eql("shared/img/icons-16x16.svg#globe");
     });
 
     it("should not display a title if by default", function() {
       view = mountTestComponent({
         url: "http://wonderful.invalid"
       });
 
       expect(view.getDOMNode().querySelector(".context-content > p")).eql(null);
rename from browser/components/loop/test/standalone/index.html
rename to browser/extensions/loop/test/standalone/index.html
--- a/browser/components/loop/test/standalone/index.html
+++ b/browser/extensions/loop/test/standalone/index.html
@@ -9,26 +9,26 @@
   <link rel="stylesheet" media="all" href="../shared/vendor/mocha-2.2.5.css">
 </head>
 <body>
   <div id="mocha">
     <p><a href="../">Index</a></p>
  </div>
   <div id="messages"></div>
   <div id="fixtures"></div>
-  <script src="../../content/shared/libs/lodash-3.9.3.js"></script>
+  <script src="../../content/shared/vendor/lodash-3.9.3.js"></script>
   <script src="../shared/loop_mocha_utils.js"></script>
   <script>
     LoopMochaUtils.trapErrors();
   </script>
 
   <!-- libs -->
-  <script src="../../content/shared/libs/react-0.13.3.js"></script>
-  <script src="../../content/shared/libs/classnames-2.2.0.js"></script>
-  <script src="../../content/shared/libs/backbone-1.2.1.js"></script>
+  <script src="../../content/shared/vendor/react-0.13.3.js"></script>
+  <script src="../../content/shared/vendor/classnames-2.2.0.js"></script>
+  <script src="../../content/shared/vendor/backbone-1.2.1.js"></script>
   <script src="../../standalone/content/libs/l10n-gaia-02ca67948fe8.js"></script>
   <!-- test dependencies -->
   <script src="../shared/vendor/mocha-2.2.5.js"></script>
   <script src="../shared/vendor/chai-3.0.0.js"></script>
   <script src="../shared/vendor/sinon-1.16.1.js"></script>
   <script src="../shared/sdk_mock.js"></script>
   <script>
     chai.config.includeStack = true;
rename from browser/components/loop/test/standalone/standaloneAppStore_test.js
rename to browser/extensions/loop/test/standalone/standaloneAppStore_test.js
rename from browser/components/loop/test/standalone/standaloneMetricsStore_test.js
rename to browser/extensions/loop/test/standalone/standaloneMetricsStore_test.js
rename from browser/components/loop/test/standalone/standaloneMozLoop_test.js
rename to browser/extensions/loop/test/standalone/standaloneMozLoop_test.js
rename from browser/components/loop/test/standalone/standaloneRoomViews_test.js
rename to browser/extensions/loop/test/standalone/standaloneRoomViews_test.js
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/extensions/loop/test/standalone/standaloneRoomViews_test.js
@@ -15,17 +15,17 @@ describe("loop.standaloneRoomViews", fun
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var fixtures = document.querySelector("#fixtures");
 
   var sandbox, dispatcher, activeRoomStore, dispatch;
   var clock, fakeWindow, view;
 
   beforeEach(function() {
-    sandbox = sinon.sandbox.create();
+    sandbox = LoopMochaUtils.createSandbox();
     dispatcher = new loop.Dispatcher();
     dispatch = sandbox.stub(dispatcher, "dispatch");
     activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, {
       mozLoop: {},
       sdkDriver: {}
     });
     var textChatStore = new loop.store.TextChatStore(dispatcher, {
       sdkDriver: {}
@@ -54,16 +54,21 @@ describe("loop.standaloneRoomViews", fun
           return args.terms_of_use_url + " " + args.privacy_notice_url;
         default:
           return key;
       }
     });
 
     // Prevents audio request errors in the test console.
     sandbox.useFakeXMLHttpRequest();
+    sandbox.stub(sharedUtils, "isDesktop").returns(true);
+    LoopMochaUtils.stubLoopRequest({
+      GetDoNotDisturb: sinon.stub().returns(true),
+      GetLoopPref: sinon.stub()
+    });
   });
 
   afterEach(function() {
     loop.shared.mixins.setRootObject(window);
     sandbox.restore();
     clock.restore();
     React.unmountComponentAtNode(fixtures);
     view = null;
rename from browser/components/loop/test/standalone/test_standalone_all.py
rename to browser/extensions/loop/test/standalone/test_standalone_all.py
rename from browser/components/loop/test/standalone/webapp_test.js
rename to browser/extensions/loop/test/standalone/webapp_test.js
rename from browser/components/loop/test/ui-showcase/test_ui-showcase.py
rename to browser/extensions/loop/test/ui-showcase/test_ui-showcase.py
rename from browser/components/loop/test/visual-regression/README.md
rename to browser/extensions/loop/test/visual-regression/README.md
rename from browser/components/loop/test/visual-regression/screenshot
rename to browser/extensions/loop/test/visual-regression/screenshot
rename from browser/components/loop/test/xpcshell/.eslintrc
rename to browser/extensions/loop/test/xpcshell/.eslintrc
--- a/browser/components/loop/test/xpcshell/.eslintrc
+++ b/browser/extensions/loop/test/xpcshell/.eslintrc
@@ -12,25 +12,27 @@
     "do_check_eq": false,
     "do_get_profile": false,
     "do_print": false,
     "do_register_cleanup": false,
     "do_throw": false,
     "do_timeout": false,
     "run_next_test": false,
     // head.js items.
+    "FileUtils": false,
     "MockWebSocketChannel": false,
     "extend": true,
     "getLoopString": false,
     "kServerPushUrl": true,
     "kEndPointUrl": true,
     "loopCrypto": true,
     "loopServer": true,
     "mockPushHandler": true,
     "setupFakeFxAUserProfile": false,
     "setupFakeLoopServer": false,
-    "timerHandlers",
+    "timerHandlers": false,
+    "updateAppInfo": false,
     "waitForCondition": true,
     // Loop specific items
     "MozLoopServiceInternal": true,
     "LoopRoomsInternal": true,
   }
 }
rename from browser/components/loop/test/xpcshell/head.js
rename to browser/extensions/loop/test/xpcshell/head.js
--- a/browser/components/loop/test/xpcshell/head.js
+++ b/browser/extensions/loop/test/xpcshell/head.js
@@ -5,27 +5,64 @@
 
 var { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 // Initialize this before the imports, as some of them need it.
 do_get_profile();
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+
+// Setup the add-ons manager for this test.
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+function registerDirectory(key, dir) {
+  let dirProvider = {
+    getFile: function(prop, persistent) {
+      persistent.value = false;
+      if (prop == key) {
+        return dir.clone();
+      }
+      return null;
+    },
+
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDirectoryServiceProvider,
+                                           Ci.nsISupports])
+  };
+  Services.dirsvc.registerProvider(dirProvider);
+}
+
+// As we're not running in application, we need to setup the features directory
+// manually.
+const distroDir = FileUtils.getDir("GreD", ["browser", "features"], true);
+registerDirectory("XREAppFeat", distroDir);
+
+// Set up application info - xpcshell doesn't have this by default.
+Cu.import("resource://testing-common/AppInfo.jsm");
+updateAppInfo();
+
+// Now trigger the addons-startup, and hence startup the manager itself. This
+// should load the manager correctly.
+var gInternalManager = Cc["@mozilla.org/addons/integration;1"]
+  .getService(Ci.nsIObserver)
+  .QueryInterface(Ci.nsITimerCallback);
+
+gInternalManager.observe(null, "addons-startup", null);
+
 Cu.import("resource://gre/modules/Http.jsm");
 Cu.import("resource://testing-common/httpd.js");
-Cu.import("resource:///modules/loop/MozLoopService.jsm");
+Cu.import("chrome://loop/content/modules/MozLoopService.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource:///modules/loop/LoopRooms.jsm");
+Cu.import("chrome://loop/content/modules/LoopRooms.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
-const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
-const { LoopRoomsInternal, timerHandlers } = Cu.import("resource:///modules/loop/LoopRooms.jsm", {});
+const { MozLoopServiceInternal } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm", {});
+const { LoopRoomsInternal, timerHandlers } = Cu.import("chrome://loop/content/modules/LoopRooms.jsm", {});
 
 XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
-                                  "resource:///modules/loop/MozLoopPushHandler.jsm");
+                                  "chrome://loop/content/modules/MozLoopPushHandler.jsm");
 
 const kMockWebSocketChannelName = "Mock WebSocket Channel";
 const kWebSocketChannelContractID = "@mozilla.org/network/protocol;1?name=wss";
 
 const kServerPushUrl = "ws://localhost";
 const kLoopServerUrl = "http://localhost:3465";
 const kEndPointUrl = "http://example.com/fake";
 const kUAID = "f47ac11b-58ca-4372-9567-0e02b2c3d479";
rename from browser/components/loop/test/xpcshell/test_loopapi_doNotDisturb.js
rename to browser/extensions/loop/test/xpcshell/test_loopapi_doNotDisturb.js
--- a/browser/components/loop/test/xpcshell/test_loopapi_doNotDisturb.js
+++ b/browser/extensions/loop/test/xpcshell/test_loopapi_doNotDisturb.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 var [, gHandlers] = LoopAPI.inspect();
 
 add_task(function* test_mozLoop_doNotDisturb() {
   do_register_cleanup(function() {
     Services.prefs.clearUserPref("loop.do_not_disturb");
   });
 
   // Test doNotDisturb (getter)
rename from browser/components/loop/test/xpcshell/test_loopapi_internal.js
rename to browser/extensions/loop/test/xpcshell/test_loopapi_internal.js
--- a/browser/components/loop/test/xpcshell/test_loopapi_internal.js
+++ b/browser/extensions/loop/test/xpcshell/test_loopapi_internal.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 const [LoopAPIInternal] = LoopAPI.inspect();
 
 add_test(function test_intialize() {
   let [, , pageListeners] = LoopAPI.inspect();
   Assert.equal(pageListeners, null, "Page listeners should not be initialized yet");
 
   LoopAPIInternal.initialize();
   let [, , pageListeners2] = LoopAPI.inspect();
rename from browser/components/loop/test/xpcshell/test_loopapi_prefs.js
rename to browser/extensions/loop/test/xpcshell/test_loopapi_prefs.js
--- a/browser/components/loop/test/xpcshell/test_loopapi_prefs.js
+++ b/browser/extensions/loop/test/xpcshell/test_loopapi_prefs.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
-const { LoopAPI } = Cu.import("resource:///modules/loop/MozLoopAPI.jsm", {});
+const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
 var [, gHandlers] = LoopAPI.inspect();
 
 add_task(function* test_mozLoop_charPref() {
   do_register_cleanup(function() {
     Services.prefs.clearUserPref("loop.test");
   });
 
   // Test setLoopPref
rename from browser/components/loop/test/xpcshell/test_looppush_initialize.js
rename to browser/extensions/loop/test/xpcshell/test_looppush_initialize.js
rename from browser/components/loop/test/xpcshell/test_looprooms.js
rename to browser/extensions/loop/test/xpcshell/test_looprooms.js
--- a/browser/components/loop/test/xpcshell/test_looprooms.js
+++ b/browser/extensions/loop/test/xpcshell/test_looprooms.js
@@ -1,16 +1,16 @@
 /* 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";
 
 Cu.import("resource://services-common/utils.js");
-Cu.import("resource:///modules/loop/LoopRooms.jsm");
+Cu.import("chrome://loop/content/modules/LoopRooms.jsm");
 Cu.import("resource:///modules/Chat.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 timerHandlers.startTimer = callback => callback();
 
 var openChatOrig = Chat.open;
 
 const kKey = "uGIs-kGbYt1hBBwjyW7MLQ";
rename from browser/components/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
rename to browser/extensions/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
--- a/browser/components/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
+++ b/browser/extensions/loop/test/xpcshell/test_looprooms_encryption_in_fxa.js
@@ -2,17 +2,17 @@
  * 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";
 
 timerHandlers.startTimer = callback => callback();
 
 Cu.import("resource://services-common/utils.js");
-const { LOOP_ROOMS_CACHE_FILENAME } = Cu.import("resource:///modules/loop/LoopRoomsCache.jsm", {});
+const { LOOP_ROOMS_CACHE_FILENAME } = Cu.import("chrome://loop/content/modules/LoopRoomsCache.jsm", {});
 
 const kContextEnabledPref = "loop.contextInConverations.enabled";
 
 const kFxAKey = "uGIs-kGbYt1hBBwjyW7MLQ";
 
 // Rooms details as responded by the server.
 const kRoomsResponses = new Map([
   ["_nxD4V4FflQ", {
rename from browser/components/loop/test/xpcshell/test_looprooms_first_notification.js
rename to browser/extensions/loop/test/xpcshell/test_looprooms_first_notification.js
--- a/browser/components/loop/test/xpcshell/test_looprooms_first_notification.js
+++ b/browser/extensions/loop/test/xpcshell/test_looprooms_first_notification.js
@@ -1,15 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://services-common/utils.js");
-Cu.import("resource:///modules/loop/LoopRooms.jsm");
+Cu.import("chrome://loop/content/modules/LoopRooms.jsm");
 Cu.import("resource:///modules/Chat.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 const kGuestKey = "uGIs-kGbYt1hBBwjyW7MLQ";
 const kChannelGuest = MozLoopService.channelIDs.roomsGuest;
 
 const kRoomsResponses = new Map([
   ["_nxD4V4FflQ", {
rename from browser/components/loop/test/xpcshell/test_looprooms_getall.js
rename to browser/extensions/loop/test/xpcshell/test_looprooms_getall.js
--- a/browser/components/loop/test/xpcshell/test_looprooms_getall.js
+++ b/browser/extensions/loop/test/xpcshell/test_looprooms_getall.js
@@ -1,15 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 Cu.import("resource://services-common/utils.js");
-Cu.import("resource:///modules/loop/LoopRooms.jsm");
 Cu.import("resource:///modules/Chat.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 
 timerHandlers.startTimer = callback => callback();
 
 var openChatOrig = Chat.open;
 
 const kKey = "uGIs-kGbYt1hBBwjyW7MLQ";
rename from browser/components/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
rename to browser/extensions/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
--- a/browser/components/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
+++ b/browser/extensions/loop/test/xpcshell/test_looprooms_upgrade_to_encryption.js
@@ -1,18 +1,18 @@
 /* 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";
 
 Cu.import("resource://services-common/utils.js");
 
-const loopCrypto = Cu.import("resource:///modules/loop/crypto.js", {}).LoopCrypto;
-const { LOOP_ROOMS_CACHE_FILENAME } = Cu.import("resource:///modules/loop/LoopRoomsCache.jsm", {});
+const loopCrypto = Cu.import("chrome://loop/content/shared/js/crypto.js", {}).LoopCrypto;
+const { LOOP_ROOMS_CACHE_FILENAME } = Cu.import("chrome://loop/content/modules/LoopRoomsCache.jsm", {});
 
 var gTimerArgs = [];
 
 timerHandlers.startTimer = function(callback, delay) {
   gTimerArgs.push({ callback, delay });
   return gTimerArgs.length;
 };
 
rename from browser/components/loop/test/xpcshell/test_loopservice_dnd.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_dnd.js
rename from browser/components/loop/test/xpcshell/test_loopservice_encryptionkey.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_encryptionkey.js
rename from browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_hawk_errors.js
--- a/browser/components/loop/test/xpcshell/test_loopservice_hawk_errors.js
+++ b/browser/extensions/loop/test/xpcshell/test_loopservice_hawk_errors.js
@@ -5,17 +5,17 @@
  * Unit tests for the error handling for hawkRequest via setError.
  *
  * hawkRequest calls setError itself for 401. Consumers need to report other
  * errors to setError themseleves.
  */
 
 "use strict";
 
-const { INVALID_AUTH_TOKEN } = Cu.import("resource:///modules/loop/MozLoopService.jsm");
+const { INVALID_AUTH_TOKEN } = Cu.import("chrome://loop/content/modules/MozLoopService.jsm");
 
 /**
  * An HTTP request for /NNN responds with a request with a status of NNN.
  */
 function errorRequestHandler(request, response) {
   let responseCode = request.path.substring(1);
   response.setStatusLine(null, responseCode, "Error");
   if (responseCode == 401) {
rename from browser/components/loop/test/xpcshell/test_loopservice_hawk_request.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_hawk_request.js
rename from browser/components/loop/test/xpcshell/test_loopservice_initialize.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_initialize.js
rename from browser/components/loop/test/xpcshell/test_loopservice_locales.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_locales.js
rename from browser/components/loop/test/xpcshell/test_loopservice_loop_prefs.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_loop_prefs.js
rename from browser/components/loop/test/xpcshell/test_loopservice_registration.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_registration.js
rename from browser/components/loop/test/xpcshell/test_loopservice_registration_retry.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_registration_retry.js
rename from browser/components/loop/test/xpcshell/test_loopservice_restart.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_restart.js
rename from browser/components/loop/test/xpcshell/test_loopservice_token_invalid.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_token_invalid.js
rename from browser/components/loop/test/xpcshell/test_loopservice_token_save.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_token_save.js
rename from browser/components/loop/test/xpcshell/test_loopservice_token_send.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_token_send.js
rename from browser/components/loop/test/xpcshell/test_loopservice_token_validation.js
rename to browser/extensions/loop/test/xpcshell/test_loopservice_token_validation.js
rename from browser/components/loop/test/xpcshell/xpcshell.ini
rename to browser/extensions/loop/test/xpcshell/xpcshell.ini
rename from browser/components/loop/ui/README.md
rename to browser/extensions/loop/ui/README.md
--- a/browser/components/loop/ui/README.md
+++ b/browser/extensions/loop/ui/README.md
@@ -1,10 +1,10 @@
 Loop UI Components Showcase
 ===========================
 
 This app file showcases all Loop's view components.
 
 If you want to modify the app, launch the following command:
 
-    browser/components/loop/build-jsx --watch
+    browser/extensions/loop/build-jsx --watch
 
 And start editing the `ui-showcase.jsx` file.
rename from browser/components/loop/ui/fake-l10n.js
rename to browser/extensions/loop/ui/fake-l10n.js
rename from browser/components/loop/ui/fake-mozLoop.js
rename to browser/extensions/loop/ui/fake-mozLoop.js
rename from browser/components/loop/ui/index.html
rename to browser/extensions/loop/ui/index.html
--- a/browser/components/loop/ui/index.html
+++ b/browser/extensions/loop/ui/index.html
@@ -5,55 +5,55 @@
 <html id="outer-html">
   <head>
     <meta charset="utf-8">
     <title>Loop UI Components Showcase</title>
     <link rel="stylesheet" type="text/css" href="../content/shared/css/reset.css">
     <link rel="stylesheet" type="text/css" href="../content/shared/css/common.css">
     <link rel="stylesheet" type="text/css" href="../content/shared/css/conversation.css">
     <link class="fx-embedded-panel" rel="stylesheet" type="text/css"
-          href="../content/css/panel.css">
+          href="../content/panels/css/panel.css">
     <link class="standalone" rel="stylesheet" type="text/css"
           href="../content/css/webapp.css">
     <link rel="stylesheet" type="text/css" href="ui-showcase.css">
  </head>
   <body>
     <script>
       var uncaughtError;
       window.addEventListener("error", function(error) {
         uncaughtError = error;
       });
     </script>
 
     <div id="main"></div>
     <div id="results"></div>
-    <script src="../content/shared/libs/react-0.13.3.js"></script>
-    <script src="../content/shared/libs/classnames-2.2.0.js"></script>
-    <script src="../content/shared/libs/lodash-3.9.3.js"></script>
-    <script src="../content/shared/libs/backbone-1.2.1.js"></script>
+    <script src="../content/shared/vendor/react-0.13.3.js"></script>
+    <script src="../content/shared/vendor/classnames-2.2.0.js"></script>
+    <script src="../content/shared/vendor/lodash-3.9.3.js"></script>
+    <script src="../content/shared/vendor/backbone-1.2.1.js"></script>
     <script src="../test/shared/loop_mocha_utils.js"></script>
     <script src="../content/shared/js/loopapi-client.js"></script>
     <script src="fake-mozLoop.js"></script>
     <script src="fake-l10n.js"></script>
     <script src="../content/shared/js/actions.js"></script>
     <script src="../content/shared/js/utils.js"></script>
     <script src="../content/shared/js/models.js"></script>
     <script src="../content/shared/js/mixins.js"></script>
     <script src="../content/shared/js/validate.js"></script>
     <script src="../content/shared/js/dispatcher.js"></script>
     <script src="../content/shared/js/store.js"></script>
     <script src="../content/shared/js/activeRoomStore.js"></script>
     <script src="../content/shared/js/views.js"></script>
     <script src="../content/shared/js/textChatStore.js"></script>
-    <script src="../content/js/feedbackViews.js"></script>
+    <script src="../content/panels/js/feedbackViews.js"></script>
     <script src="../content/shared/js/textChatView.js"></script>
     <script src="../content/shared/js/urlRegExps.js"></script>
     <script src="../content/shared/js/linkifiedTextView.js"></script>
-    <script src="../content/js/roomStore.js"></script>
-    <script src="../content/js/roomViews.js"></script>
+    <script src="../content/panels/js/roomStore.js"></script>
+    <script src="../content/panels/js/roomViews.js"></script>
     <script src="../standalone/content/js/webapp.js"></script>
     <script src="../standalone/content/js/standaloneRoomViews.js"></script>
-    <script src="../content/js/panel.js"></script>
-    <script src="../content/js/conversation.js"></script>
+    <script src="../content/panels/js/panel.js"></script>
+    <script src="../content/panels/js/conversation.js"></script>
     <script src="react-frame-component.js"></script>
     <script src="ui-showcase.js"></script>
   </body>
 </html>
rename from browser/components/loop/ui/react-frame-component.js
rename to browser/extensions/loop/ui/react-frame-component.js
rename from browser/components/loop/ui/sample-img/video-screen-local.png
rename to browser/extensions/loop/ui/sample-img/video-screen-local.png
rename from browser/components/loop/ui/sample-img/video-screen-remote.png
rename to browser/extensions/loop/ui/sample-img/video-screen-remote.png
rename from browser/components/loop/ui/sample-img/video-screen-terminal.png
rename to browser/extensions/loop/ui/sample-img/video-screen-terminal.png
rename from browser/components/loop/ui/ui-showcase.css
rename to browser/extensions/loop/ui/ui-showcase.css
rename from browser/components/loop/ui/ui-showcase.js
rename to browser/extensions/loop/ui/ui-showcase.js
rename from browser/components/loop/ui/ui-showcase.jsx
rename to browser/extensions/loop/ui/ui-showcase.jsx
--- a/browser/extensions/moz.build
+++ b/browser/extensions/moz.build
@@ -1,10 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DIRS += [
+    'loop',
     'pdfjs',
     'shumway',
 ]
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -671,16 +671,17 @@
 @RESPATH@/chrome/toolkit.manifest
 @RESPATH@/chrome/recording.manifest
 @RESPATH@/chrome/recording/*
 #ifdef MOZ_GTK
 @RESPATH@/browser/chrome/icons/default/default16.png
 @RESPATH@/browser/chrome/icons/default/default32.png
 @RESPATH@/browser/chrome/icons/default/default48.png
 #endif
+@RESPATH@/browser/features/*
 
 ; [Webide Files]
 @RESPATH@/browser/chrome/webide@JAREXT@
 @RESPATH@/browser/chrome/webide.manifest
 @RESPATH@/browser/@PREF_DIR@/webide-prefs.js
 
 ; DevTools
 @RESPATH@/browser/chrome/devtools@JAREXT@
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -42,22 +42,16 @@ browser.jar:
   skin/classic/browser/searchbar.css
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/slowStartup-16.png
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-inverted.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/webRTC-indicator.css
-  skin/classic/browser/loop/menuPanel.png             (loop/menuPanel.png)
-  skin/classic/browser/loop/menuPanel@2x.png          (loop/menuPanel@2x.png)
-  skin/classic/browser/loop/toolbar.png               (loop/toolbar.png)
-  skin/classic/browser/loop/toolbar@2x.png            (loop/toolbar@2x.png)
-  skin/classic/browser/loop/toolbar-inverted.png      (loop/toolbar-inverted.png)
-  skin/classic/browser/loop/toolbar-inverted@2x.png   (loop/toolbar-inverted@2x.png)
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
 * skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
   skin/classic/browser/downloads/allDownloadsViewOverlay.css   (downloads/allDownloadsViewOverlay.css)
   skin/classic/browser/downloads/buttons.png          (downloads/buttons.png)
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -3206,50 +3206,16 @@ menulist.translate-infobar-element > .me
     list-style-image: url("chrome://global/skin/icons/glyph-dropdown@2x.png");
   }
 
   menulist.translate-infobar-element > .menulist-dropmarker > .dropmarker-icon {
     width: 8px;
   }
 }
 
-/* Loop/ Hello browser styles */
-notification[value="loop-sharing-notification"] {
-  background: #00a9dc;
-  padding: 0;
-  border: 0;
-}
-
-notification[value="loop-sharing-notification"].paused {
-  background: #ebebeb;
-}
-
-notification[value="loop-sharing-notification"] .notification-button {
-  background: #fff;
-  border-radius: 0;
-}
-
-notification[value="loop-sharing-notification"].paused .notification-button {
-  background: #57bd35;
-}
-
-notification[value="loop-sharing-notification"].paused .notification-button:hover {
-  background: #39a017;
-}
-
-notification[value="loop-sharing-notification"] .notification-button:hover,
-notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
-  background: #ebebeb;
-}
-
-notification[value="loop-sharing-notification"] .notification-button-default,
-notification[value="loop-sharing-notification"].paused .notification-button-default {
-  background: #fff;
-}
-
 .popup-notification-body[popupid="addon-progress"],
 .popup-notification-body[popupid="addon-install-confirmation"] {
   width: 28em;
   max-width: 28em;
 }
 
 .addon-install-confirmation-name {
   font-weight: bold;
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -68,26 +68,16 @@ browser.jar:
   skin/classic/browser/urlbar-popup-blocked@2x.png
   skin/classic/browser/webRTC-sharingDevice-menubar.png
   skin/classic/browser/webRTC-sharingDevice-menubar@2x.png
   skin/classic/browser/webRTC-sharingMicrophone-menubar.png
   skin/classic/browser/webRTC-sharingMicrophone-menubar@2x.png
   skin/classic/browser/webRTC-sharingScreen-menubar.png
   skin/classic/browser/webRTC-sharingScreen-menubar@2x.png
   skin/classic/browser/webRTC-indicator.css
-  skin/classic/browser/loop/menuPanel.png             (loop/menuPanel.png)
-  skin/classic/browser/loop/menuPanel@2x.png          (loop/menuPanel@2x.png)
-  skin/classic/browser/loop/toolbar.png               (loop/toolbar.png)
-  skin/classic/browser/loop/toolbar@2x.png            (loop/toolbar@2x.png)
-  skin/classic/browser/loop/toolbar-inverted.png      (loop/toolbar-inverted.png)
-  skin/classic/browser/loop/toolbar-inverted@2x.png   (loop/toolbar-inverted@2x.png)
-  skin/classic/browser/yosemite/loop/menuPanel.png          (loop/menuPanel-yosemite.png)
-  skin/classic/browser/yosemite/loop/menuPanel@2x.png       (loop/menuPanel-yosemite@2x.png)
-  skin/classic/browser/yosemite/loop/toolbar.png            (loop/toolbar-yosemite.png)
-  skin/classic/browser/yosemite/loop/toolbar@2x.png         (loop/toolbar-yosemite@2x.png)
 * skin/classic/browser/controlcenter/panel.css        (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
 * skin/classic/browser/customizableui/panelUIOverlay.css    (customizableui/panelUIOverlay.css)
@@ -275,20 +265,16 @@ browser.jar:
 % override chrome://browser/skin/toolbarbutton-dropmarker.png              chrome://browser/skin/lion/toolbarbutton-dropmarker.png                 os=Darwin osversion>=10.7
 % override chrome://browser/skin/tabbrowser/alltabs-box-bkgnd-icon.png     chrome://browser/skin/lion/tabbrowser/alltabs-box-bkgnd-icon.png        os=Darwin osversion>=10.7
 % override chrome://browser/skin/tabview/tabview.png                       chrome://browser/skin/lion/tabview/tabview.png                          os=Darwin osversion>=10.7
 % override chrome://browser/skin/places/toolbar.png                        chrome://browser/skin/lion/places/toolbar.png                           os=Darwin osversion>=10.7
 % override chrome://browser/skin/Toolbar.png                               chrome://browser/skin/yosemite/Toolbar.png                              os=Darwin osversion>=10.10
 % override chrome://browser/skin/Toolbar@2x.png                            chrome://browser/skin/yosemite/Toolbar@2x.png                           os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel.png                             chrome://browser/skin/yosemite/menuPanel.png                            os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel@2x.png                          chrome://browser/skin/yosemite/menuPanel@2x.png                         os=Darwin osversion>=10.10
-% override chrome://browser/skin/loop/menuPanel.png                        chrome://browser/skin/yosemite/loop/menuPanel.png                       os=Darwin osversion>=10.10
-% override chrome://browser/skin/loop/menuPanel@2x.png                     chrome://browser/skin/yosemite/loop/menuPanel@2x.png                    os=Darwin osversion>=10.10
-% override chrome://browser/skin/loop/toolbar.png                          chrome://browser/skin/yosemite/loop/toolbar.png                         os=Darwin osversion>=10.10
-% override chrome://browser/skin/loop/toolbar@2x.png                       chrome://browser/skin/yosemite/loop/toolbar@2x.png                      os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-customize.png                   chrome://browser/skin/yosemite/menuPanel-customize.png                  os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-customize@2x.png                chrome://browser/skin/yosemite/menuPanel-customize@2x.png               os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-exit.png                        chrome://browser/skin/yosemite/menuPanel-exit.png                       os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-exit@2x.png                     chrome://browser/skin/yosemite/menuPanel-exit@2x.png                    os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-help.png                        chrome://browser/skin/yosemite/menuPanel-help.png                       os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-help@2x.png                     chrome://browser/skin/yosemite/menuPanel-help@2x.png                    os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-small.png                       chrome://browser/skin/yosemite/menuPanel-small.png                      os=Darwin osversion>=10.10
 % override chrome://browser/skin/menuPanel-small@2x.png                    chrome://browser/skin/yosemite/menuPanel-small@2x.png                   os=Darwin osversion>=10.10
--- a/browser/themes/shared/browser.inc
+++ b/browser/themes/shared/browser.inc
@@ -1,13 +1,13 @@
 %filter substitution
 
 % Note that zoom-reset-button is a bit different since it doesn't use an image and thus has the image with display: none.
 %define nestedButtons #zoom-out-button, #zoom-reset-button, #zoom-in-button, #cut-button, #copy-button, #paste-button
-%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #fullscreen-button, #sync-button, #feed-button, #tabview-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button, #sidebar-button, @nestedButtons@, #e10s-button, #panic-button, #web-apps-button, #webide-button, #loop-button, #pocket-button
+%define primaryToolbarButtons #back-button, #forward-button, #home-button, #print-button, #downloads-button, #bookmarks-menu-button, #new-tab-button, #new-window-button, #fullscreen-button, #sync-button, #feed-button, #tabview-button, #social-share-button, #open-file-button, #find-button, #developer-button, #preferences-button, #privatebrowsing-button, #save-page-button, #add-ons-button, #history-panelmenu, #nav-bar-overflow-button, #PanelUI-menu-button, #characterencoding-button, #email-link-button, #sidebar-button, @nestedButtons@, #e10s-button, #panic-button, #web-apps-button, #webide-button, #pocket-button
 
 %ifdef XP_MACOSX
 % Prior to 10.7 there wasn't a native fullscreen button so we use #restore-button to exit fullscreen
 % and want it to behave like other toolbar buttons.
 %define primaryToolbarButtons @primaryToolbarButtons@, #restore-button
 %endif
 
 %define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true])
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -186,52 +186,16 @@
   #pocket-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
     -moz-image-region: rect(32px, 992px, 64px, 960px);
   }
 
   toolbaritem[sdkstylewidget="true"] > toolbarbutton {
     -moz-image-region: rect(0, 832px, 32px, 800px);
   }
 
-  #loop-button[cui-areatype="menu-panel"],
-  toolbarpaletteitem[place="palette"] > #loop-button {
-    list-style-image: url(chrome://browser/skin/loop/menuPanel.png);
-    -moz-image-region: rect(0, 32px, 32px, 0);
-  }
-
-  /* Make sure that the state icons are not shown in the customization palette. */
-  toolbarpaletteitem[place="palette"] > #loop-button {
-    -moz-image-region: rect(0, 32px, 32px, 0) !important;
-  }
-
-  #loop-button[cui-areatype="menu-panel"][state="disabled"],
-  #loop-button[cui-areatype="menu-panel"][disabled="true"] {
-    -moz-image-region: rect(0, 64px, 32px, 32px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] {
-    -moz-image-region: rect(0, 96px, 32px, 64px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] {
-    -moz-image-region: rect(0, 128px, 32px, 96px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
-    -moz-image-region: rect(0, 160px, 32px, 128px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] {
-    -moz-image-region: rect(0, 192px, 32px, 160px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
-    -moz-image-region: rect(0, 224px, 32px, 192px);
-  }
-
   /* Wide panel control icons */
 
   #edit-controls@inAnyPanel@ > toolbarbutton,
   #zoom-controls@inAnyPanel@ > toolbarbutton,
   toolbarpaletteitem[place="palette"] > #edit-controls > toolbarbutton,
   toolbarpaletteitem[place="palette"] > #zoom-controls > toolbarbutton {
     list-style-image: var(--menupanel-small-list-style-image);
   }
@@ -370,52 +334,16 @@
   toolbarpaletteitem[place="palette"] > #pocket-button {
     -moz-image-region: rect(0px, 1984px, 64px, 1920px);
   }
 
   #pocket-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
     -moz-image-region: rect(64px, 1984px, 128px, 1920px);
   }
 
-  #loop-button[cui-areatype="menu-panel"],
-  toolbarpaletteitem[place="palette"] > #loop-button {
-    list-style-image: url(chrome://browser/skin/loop/menuPanel@2x.png);
-    -moz-image-region: rect(0, 64px, 64px, 0);
-  }
-
-  /* Make sure that the state icons are not shown in the customization palette. */
-  toolbarpaletteitem[place="palette"] > #loop-button {
-    -moz-image-region: rect(0, 64px, 64px, 0) !important;
-  }
-
-  #loop-button[cui-areatype="menu-panel"][state="disabled"],
-  #loop-button[cui-areatype="menu-panel"][disabled="true"] {
-    -moz-image-region: rect(0, 128px, 64px, 64px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] {
-    -moz-image-region: rect(0, 192px, 64px, 128px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] {
-    -moz-image-region: rect(0, 256px, 64px, 192px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
-    -moz-image-region: rect(0, 320px, 64px, 256px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] {
-    -moz-image-region: rect(0, 384px, 64px, 320px);
-  }
-
-  #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
-    -moz-image-region: rect(0, 448px, 64px, 384px);
-  }
-
   #new-tab-button[cui-areatype="menu-panel"],
   toolbarpaletteitem[place="palette"] > #new-tab-button {
     -moz-image-region: rect(0px, 1088px, 64px, 1024px);
   }
 
   #privatebrowsing-button[cui-areatype="menu-panel"],
   toolbarpaletteitem[place="palette"] > #privatebrowsing-button {
     -moz-image-region: rect(0px, 1152px, 64px, 1088px);
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -440,88 +440,8 @@
     list-style-image: url(chrome://browser/skin/social/services-64@2x.png);
   }
 
   #servicesInstall-notification-icon {
     list-style-image: url(chrome://browser/skin/social/services-16@2x.png);
   }
 %endif
 }
-
-/* Loop notification */
-notification[value="loop-sharing-notification"] {
-  -moz-appearance: none;
-  height: 40px;
-  background-color: #00a9dc;
-  box-shadow: 0 40px 1px rgba(0,0,0,.5) inset;
-}
-
-notification[value="loop-sharing-notification"].paused {
-  background-color: #ebebeb;
-}
-
-notification[value="loop-sharing-notification"] .notification-inner {
-  color: #fff;
-  padding: 0;
-}
-
-notification[value="loop-sharing-notification"].paused .notification-inner {
-  color: #00a9dc;
-}
-
-notification[value="loop-sharing-notification"] .notification-button {
-  -moz-appearance: none;
-  background-color: #fff;
-  border: 0;
-  border-right: solid 1px #ebebeb;
-  width: 100px;
-  height: 40px;
-  margin: 0;
-  list-style-image: url(chrome://browser/content/loop/shared/img/pause-12x12.svg);
-  box-shadow: 0 40px 1px rgba(0,0,0,.5) inset;
-  text-shadow: none;
-}
-
-notification[value="loop-sharing-notification"] .notification-button:-moz-locale-dir(rtl) {
-  border-right: 0;
-  border-left: solid 1px #ebebeb;
-}
-
-notification[value="loop-sharing-notification"].paused .notification-button {
-  background-color: #57bd35;
-  color: #fff;
-  list-style-image: url(chrome://browser/content/loop/shared/img/play-12x12.svg);
-}
-
-notification[value="loop-sharing-notification"].paused .notification-button:hover {
-  background-color: #39a017;
-}
-
-notification[value="loop-sharing-notification"] .notification-button:hover,
-notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
-  background-color: #ebebeb;
-}
-
-notification[value="loop-sharing-notification"] .notification-button-default,
-notification[value="loop-sharing-notification"].paused .notification-button-default {
-  color: #d92215;
-  background-color: #fff;
-  border-right: 0;
-  list-style-image: url(chrome://browser/content/loop/shared/img/stop-12x12.svg);
-}
-
-notification[value="loop-sharing-notification"] .notification-button .button-icon {
-  display: block;
-  -moz-margin-end: 6px;
-}
-
-notification[value="loop-sharing-notification"] .button-menubutton-button {
-  min-width: 0;
-}
-
-notification[value="loop-sharing-notification"] .messageImage {
-  list-style-image: url(chrome://browser/content/loop/shared/img/icons-16x16.svg#loop-icon-white);
-  margin-inline-start: 14px;
-}
-
-notification[value="loop-sharing-notification"].paused .messageImage {
-  list-style-image: url(chrome://browser/content/loop/shared/img/icons-16x16.svg#loop-icon-still);
-}
\ No newline at end of file
--- a/browser/themes/shared/social/chat.inc.css
+++ b/browser/themes/shared/social/chat.inc.css
@@ -100,52 +100,22 @@
 .chat-swap-button:hover:active {
   list-style-image: url("chrome://browser/skin/social/chat-icons.svg#expand-active");
 }
 
 chatbar > chatbox > .chat-titlebar > .chat-swap-button {
   transform: none;
 }
 
-chatbox[src^="about:loopconversation#"] .chat-minimize-button {
-  list-style-image: url("chrome://browser/skin/social/chat-icons.svg#minimize-white");
-}
-
-chatbox[src^="about:loopconversation#"] .chat-swap-button {
-  list-style-image: url("chrome://browser/skin/social/chat-icons.svg#expand-white");
-}
-
-.chat-loop-hangup {
-  list-style-image: url("chrome://browser/skin/social/chat-icons.svg#exit-white");
-  background-color: #d13f1a;
-  border: 1px solid #d13f1a;
-  border-top-right-radius: 4px;
-  width: 32px;
-  height: 26px;
-  margin-top: -6px;
-  margin-bottom: -5px;
-  -moz-margin-start: 6px;
-  -moz-margin-end: -5px;
-}
-
-.chat-toolbarbutton.chat-loop-hangup:-moz-any(:hover,:hover:active) {
-  background-color: #ef6745;
-  border-color: #ef6745;
-}
-
 .chat-title {
   color: #666;
   text-shadow: none;
   cursor: inherit;
 }
 
-chatbox[src^="about:loopconversation#"] .chat-title {
-  color: white;
-}
-
 .chat-titlebar {
   height: 26px;
   min-height: 26px;
   width: 100%;
   margin: 0;
   padding: 5px 4px;
   border: 1px solid #ebebeb;
   border-bottom: 0;
@@ -154,21 +124,16 @@ chatbox[src^="about:loopconversation#"] 
   cursor: pointer;
   background-color: #ebebeb;
 }
 
 .chat-titlebar[selected] {
   background-color: #f0f0f0;
 }
 
-chatbox[src^="about:loopconversation#"] > .chat-titlebar {
-  background-color: #00a9dc;
-  border-color: #00a9dc;
-}
-
 .chat-titlebar > .notification-anchor-icon {
   margin-left: 2px;
   margin-right: 2px;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
--- a/browser/themes/shared/toolbarbuttons.inc.css
+++ b/browser/themes/shared/toolbarbuttons.inc.css
@@ -179,50 +179,16 @@ toolbar[brighttext] #sync-button[status=
 #panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 #web-apps-button[cui-areatype="toolbar"] {
   -moz-image-region: rect(0, 720px, 18px, 702px);
 }
 
-#loop-button {
-  list-style-image: url(chrome://browser/skin/loop/toolbar.png);
-  -moz-image-region: rect(0, 18px, 18px, 0);
-}
-
-toolbar[brighttext] #loop-button {
-  list-style-image: url(chrome://browser/skin/loop/toolbar-inverted.png);
-}
-
-#loop-button[state="disabled"],
-#loop-button[disabled="true"] {
-  -moz-image-region: rect(0, 36px, 18px, 18px);
-}
-
-#loop-button:not([disabled="true"])[state="error"] {
-  -moz-image-region: rect(0, 54px, 18px, 36px);
-}
-
-#loop-button:not([disabled="true"])[state="action"] {
-  -moz-image-region: rect(0, 72px, 18px, 54px);
-}
-
-#loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
-  -moz-image-region: rect(0, 90px, 18px, 72px);
-}
-
-#loop-button:not([disabled="true"])[state="active"] {
-  -moz-image-region: rect(0, 108px, 18px, 90px);
-}
-
-#loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
-  -moz-image-region: rect(0, 126px, 18px, 108px);
-}
-
 #webide-button[cui-areatype="toolbar"] {
   -moz-image-region: rect(0, 738px, 18px, 720px);
 }
 
 #pocket-button[cui-areatype="toolbar"] {
   -moz-image-region: rect(0, 774px, 18px, 756px);
 }
 
@@ -424,44 +390,10 @@ toolbar[brighttext] #loop-button {
 
   #pocket-button[cui-areatype="toolbar"][open] {
 %ifdef XP_MACOSX
     -moz-image-region: rect(72px, 1548px, 108px, 1512px);
 %else
     -moz-image-region: rect(36px, 1548px, 72px, 1512px);
 %endif
   }
-
-  #loop-button {
-    list-style-image: url("chrome://browser/skin/loop/toolbar@2x.png");
-    -moz-image-region: rect(0, 36px, 36px, 0);
-  }
-
-  toolbar[brighttext] #loop-button {
-    list-style-image: url("chrome://browser/skin/loop/toolbar-inverted@2x.png");
-  }
-
-  #loop-button[state="disabled"],
-  #loop-button[disabled="true"] {
-    -moz-image-region: rect(0, 72px, 36px, 36px);
-  }
-
-  #loop-button:not([disabled="true"])[state="error"] {
-    -moz-image-region: rect(0, 108px, 36px, 72px);
-  }
-
-  #loop-button:not([disabled="true"])[state="action"] {
-    -moz-image-region: rect(0, 144px, 36px, 108px);
-  }
-
-  #loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) {
-    -moz-image-region: rect(0, 180px, 36px, 144px);
-  }
-
-  #loop-button:not([disabled="true"])[state="active"] {
-    -moz-image-region: rect(0, 216px, 36px, 180px);
-  }
-
-  #loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
-    -moz-image-region: rect(0, 252px, 36px, 216px);
-  }
 }
 %endif
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -647,26 +647,16 @@ menuitem.bookmark-item {
 %include ../shared/toolbarbuttons.inc.css
 %include ../shared/menupanel.inc.css
 
 @media (-moz-windows-theme: luna-silver) and (max-resolution: 1dppx) {
   :-moz-any(@primaryToolbarButtons@),
   #bookmarks-menu-button.toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
     list-style-image: url("chrome://browser/skin/Toolbar-lunaSilver.png");
   }
-
-  #loop-button {
-    list-style-image: url(chrome://browser/skin/loop/toolbar-lunaSilver.png)
-  }
-}
-
-@media (-moz-windows-theme: luna-silver) and (min-resolution: 1.1dppx) {
-  #loop-button {
-    list-style-image: url(chrome://browser/skin/loop/toolbar-lunaSilver@2x.png)
-  }
 }
 
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-icon,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker,
 #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 #main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled=true] > .toolbarbutton-icon {
   opacity: .4;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -78,32 +78,16 @@ browser.jar:
   skin/classic/browser/toolbarbutton-dropdown-arrow-XPVista7.png
   skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/urlbar-history-dropmarker.png
   skin/classic/browser/urlbar-history-dropmarker@2x.png
   skin/classic/browser/urlbar-history-dropmarker-XPVista7.png
   skin/classic/browser/urlbar-history-dropmarker-XPVista7@2x.png
   skin/classic/browser/webRTC-indicator.css
-  skin/classic/browser/loop/menuPanel.png                      (loop/menuPanel.png)
-  skin/classic/browser/loop/menuPanel@2x.png                   (loop/menuPanel@2x.png)
-  skin/classic/browser/loop/menuPanel-aero.png                 (loop/menuPanel-aero.png)
-  skin/classic/browser/loop/menuPanel-aero@2x.png              (loop/menuPanel-aero@2x.png)
-  skin/classic/browser/loop/toolbar.png                        (loop/toolbar.png)
-  skin/classic/browser/loop/toolbar@2x.png                     (loop/toolbar@2x.png)
-  skin/classic/browser/loop/toolbar-aero.png                   (loop/toolbar-aero.png)
-  skin/classic/browser/loop/toolbar-aero@2x.png                (loop/toolbar-aero@2x.png)
-  skin/classic/browser/loop/toolbar-inverted.png               (loop/toolbar-inverted.png)
-  skin/classic/browser/loop/toolbar-inverted@2x.png            (loop/toolbar-inverted@2x.png)
-  skin/classic/browser/loop/toolbar-lunaSilver.png             (loop/toolbar-lunaSilver.png)
-  skin/classic/browser/loop/toolbar-lunaSilver@2x.png          (loop/toolbar-lunaSilver@2x.png)
-  skin/classic/browser/loop/toolbar-win8.png                   (loop/toolbar-win8.png)
-  skin/classic/browser/loop/toolbar-win8@2x.png                (loop/toolbar-win8@2x.png)
-  skin/classic/browser/loop/toolbar-XP.png                     (loop/toolbar-XP.png)
-  skin/classic/browser/loop/toolbar-XP@2x.png                  (loop/toolbar-XP@2x.png)
 * skin/classic/browser/controlcenter/panel.css                 (controlcenter/panel.css)
   skin/classic/browser/customizableui/background-noise-toolbar.png  (customizableui/background-noise-toolbar.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle.png  (customizableui/customize-titleBar-toggle.png)
   skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png  (customizableui/customize-titleBar-toggle@2x.png)
   skin/classic/browser/customizableui/customizeMode-gridTexture.png  (customizableui/customizeMode-gridTexture.png)
   skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png  (customizableui/customizeMode-separatorHorizontal.png)
   skin/classic/browser/customizableui/customizeMode-separatorVertical.png  (customizableui/customizeMode-separatorVertical.png)
   skin/classic/browser/customizableui/menu-arrow.svg           (customizableui/menu-arrow.svg)
@@ -320,36 +304,21 @@ browser.jar:
 % override chrome://browser/skin/menuPanel-small.png                  chrome://browser/skin/menuPanel-small-aero.png                    os=WINNT osversion=6
 % override chrome://browser/skin/menuPanel-small.png                  chrome://browser/skin/menuPanel-small-aero.png                    os=WINNT osversion=6.1
 % override chrome://browser/skin/menuPanel-small@2x.png               chrome://browser/skin/menuPanel-small-aero@2x.png                 os=WINNT osversion=6
 % override chrome://browser/skin/menuPanel-small@2x.png               chrome://browser/skin/menuPanel-small-aero@2x.png                 os=WINNT osversion=6.1
 % override chrome://browser/skin/Toolbar@2x.png                       chrome://browser/skin/Toolbar-aero@2x.png                         os=WINNT osversion=6
 % override chrome://browser/skin/Toolbar@2x.png                       chrome://browser/skin/Toolbar-aero@2x.png                         os=WINNT osversion=6.1
 % override chrome://browser/skin/Toolbar@2x.png                       chrome://browser/skin/Toolbar-win8@2x.png                         os=WINNT osversion=6.2
 % override chrome://browser/skin/Toolbar@2x.png                       chrome://browser/skin/Toolbar-win8@2x.png                         os=WINNT osversion=6.3
-% override chrome://browser/skin/loop/menuPanel.png                   chrome://browser/skin/loop/menuPanel-aero.png                     os=WINNT osversion=6
-% override chrome://browser/skin/loop/menuPanel.png                   chrome://browser/skin/loop/menuPanel-aero.png                     os=WINNT osversion=6.1
-% override chrome://browser/skin/loop/menuPanel@2x.png                chrome://browser/skin/loop/menuPanel-aero@2x.png                  os=WINNT osversion=6
-% override chrome://browser/skin/loop/menuPanel@2x.png                chrome://browser/skin/loop/menuPanel-aero@2x.png                  os=WINNT osversion=6.1
-
 % override chrome://browser/skin/Toolbar.png                          chrome://browser/skin/Toolbar-XP.png                              os=WINNT osversion<6
 % override chrome://browser/skin/Toolbar.png                          chrome://browser/skin/Toolbar-aero.png                            os=WINNT osversion=6
 % override chrome://browser/skin/Toolbar.png                          chrome://browser/skin/Toolbar-aero.png                            os=WINNT osversion=6.1
 % override chrome://browser/skin/Toolbar.png                          chrome://browser/skin/Toolbar-win8.png                            os=WINNT osversion=6.2
 % override chrome://browser/skin/Toolbar.png                          chrome://browser/skin/Toolbar-win8.png                            os=WINNT osversion=6.3
-% override chrome://browser/skin/loop/toolbar.png                     chrome://browser/skin/loop/toolbar-XP.png                         os=WINNT osversion<6
-% override chrome://browser/skin/loop/toolbar.png                     chrome://browser/skin/loop/toolbar-aero.png                       os=WINNT osversion=6
-% override chrome://browser/skin/loop/toolbar.png                     chrome://browser/skin/loop/toolbar-aero.png                       os=WINNT osversion=6.1
-% override chrome://browser/skin/loop/toolbar.png                     chrome://browser/skin/loop/toolbar-win8.png                       os=WINNT osversion=6.2
-% override chrome://browser/skin/loop/toolbar.png                     chrome://browser/skin/loop/toolbar-win8.png                       os=WINNT osversion=6.3
-% override chrome://browser/skin/loop/toolbar@2x.png                  chrome://browser/skin/loop/toolbar-XP@2x.png                      os=WINNT osversion<6
-% override chrome://browser/skin/loop/toolbar@2x.png                  chrome://browser/skin/loop/toolbar-aero@2x.png                    os=WINNT osversion=6
-% override chrome://browser/skin/loop/toolbar@2x.png                  chrome://browser/skin/loop/toolbar-aero@2x.png                    os=WINNT osversion=6.1
-% override chrome://browser/skin/loop/toolbar@2x.png                  chrome://browser/skin/loop/toolbar-win8@2x.png                    os=WINNT osversion=6.2
-% override chrome://browser/skin/loop/toolbar@2x.png                  chrome://browser/skin/loop/toolbar-win8@2x.png                    os=WINNT osversion=6.3
 % override chrome://browser/skin/preferences/checkbox.png             chrome://browser/skin/preferences/checkbox-aero.png               os=WINNT osversion=6
 % override chrome://browser/skin/preferences/checkbox.png             chrome://browser/skin/preferences/checkbox-aero.png               os=WINNT osversion=6.1
 % override chrome://browser/skin/preferences/checkbox.png             chrome://browser/skin/preferences/checkbox-xp.png                 os=WINNT osversion<6
 
 % override chrome://browser/skin/tabbrowser/tab-background-start.png     chrome://browser/skin/tabbrowser/tab-background-start-preWin10.png     os=WINNT osversion<=6.3
 % override chrome://browser/skin/tabbrowser/tab-background-start@2x.png  chrome://browser/skin/tabbrowser/tab-background-start-preWin10@2x.png  os=WINNT osversion<=6.3
 % override chrome://browser/skin/tabbrowser/tab-background-middle.png    chrome://browser/skin/tabbrowser/tab-background-middle-preWin10.png    os=WINNT osversion<=6.3
 % override chrome://browser/skin/tabbrowser/tab-background-middle@2x.png chrome://browser/skin/tabbrowser/tab-background-middle-preWin10@2x.png os=WINNT osversion<=6.3
--- a/configure.in
+++ b/configure.in
@@ -3756,17 +3756,16 @@ MOZ_ANDROID_HISTORY=
 MOZ_WEBSMS_BACKEND=
 MOZ_ANDROID_BEAM=
 MOZ_LOCALE_SWITCHER=
 MOZ_ANDROID_READING_LIST_SERVICE=
 MOZ_ANDROID_SEARCH_ACTIVITY=
 MOZ_ANDROID_DOWNLOADS_INTEGRATION=
 MOZ_ANDROID_GCM=
 MOZ_ANDROID_MLS_STUMBLER=
-MOZ_ANDROID_SHARE_OVERLAY=
 MOZ_EXCLUDE_HYPHENATION_DICTIONARIES=
 MOZ_INSTALL_TRACKING=
 MOZ_SWITCHBOARD=
 ACCESSIBILITY=1
 MOZ_TIME_MANAGER=
 MOZ_SIMPLEPUSH=
 MOZ_PAY=
 MOZ_AUDIO_CHANNEL_MANAGER=
@@ -4858,23 +4857,16 @@ fi
 
 dnl ========================================================
 dnl = Include Mozilla Location Service Stumbler on Android
 dnl ========================================================
 if test -n "$MOZ_ANDROID_MLS_STUMBLER"; then
     AC_DEFINE(MOZ_ANDROID_MLS_STUMBLER)
 fi
 
-dnl ========================================================
-dnl = Include share overlay on Android
-dnl ========================================================
-if test -n "$MOZ_ANDROID_SHARE_OVERLAY"; then
-    AC_DEFINE(MOZ_ANDROID_SHARE_OVERLAY)
-fi
-
 dnl = Include Tab Queue on Android
 dnl = Temporary build flag to allow development in Nightly
 dnl ========================================================
 if test -n "$MOZ_ANDROID_TAB_QUEUE"; then
     AC_DEFINE(MOZ_ANDROID_TAB_QUEUE)
 fi
 
 dnl =========================================================
@@ -8588,17 +8580,16 @@ AC_SUBST(MOZ_ANDROID_HISTORY)
 AC_SUBST(MOZ_WEBSMS_BACKEND)
 AC_SUBST(MOZ_ANDROID_BEAM)
 AC_SUBST(MOZ_LOCALE_SWITCHER)
 AC_SUBST(MOZ_DISABLE_GECKOVIEW)
 AC_SUBST(MOZ_ANDROID_GCM)
 AC_SUBST(MOZ_ANDROID_GECKOLIBS_AAR)
 AC_SUBST(MOZ_ANDROID_READING_LIST_SERVICE)
 AC_SUBST(MOZ_ANDROID_SEARCH_ACTIVITY)
-AC_SUBST(MOZ_ANDROID_SHARE_OVERLAY)
 AC_SUBST(MOZ_ANDROID_TAB_QUEUE)
 AC_SUBST(MOZ_ANDROID_MLS_STUMBLER)
 AC_SUBST(MOZ_ANDROID_DOWNLOADS_INTEGRATION)
 AC_SUBST(MOZ_ANDROID_APPLICATION_CLASS)
 AC_SUBST(MOZ_ANDROID_BROWSER_INTENT_CLASS)
 AC_SUBST(MOZ_ANDROID_SEARCH_INTENT_CLASS)
 AC_SUBST(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
 AC_SUBST(MOZ_INSTALL_TRACKING)
deleted file mode 100644
--- a/devtools/.eslintignore
+++ /dev/null
@@ -1,22 +0,0 @@
-# Ignore d3
-client/shared/d3.js
-client/webaudioeditor/lib/dagre-d3.js
-
-# Ignore codemirror
-client/sourceeditor/codemirror/*.js
-client/sourceeditor/codemirror/**/*.js
-
-# Ignore jquery test libs
-client/markupview/test/lib_*
-
-# Ignore pre-processed files
-client/framework/toolbox-process-window.js
-client/performance/system.js
-client/webide/webide-prefs.js
-
-# Ignore various libs
-shared/jsbeautify/*
-shared/acorn/*
-shared/tern/*
-shared/pretty-fast/*
-shared/sourcemap/*
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -105,17 +105,20 @@ public:
        mFrame->SetSelectionRange(properties.mStart,
                                  properties.mEnd,
                                  properties.mDirection);
       if (!mTextEditorState->mSelectionRestoreEagerInit) {
         mTextEditorState->HideSelectionIfBlurred();
       }
       mTextEditorState->mSelectionRestoreEagerInit = false;
     }
-    mTextEditorState->FinishedRestoringSelection();
+
+    if (mTextEditorState) {
+      mTextEditorState->FinishedRestoringSelection();
+    }
     return NS_OK;
   }
 
   // Let the text editor tell us we're no longer relevant - avoids use of nsWeakFrame
   void Revoke() {
     mFrame = nullptr;
     mTextEditorState = nullptr;
   }
deleted file mode 100644
--- a/mobile/android/.eslintignore
+++ /dev/null
@@ -1,23 +0,0 @@
-chrome/content
-tests/
-
-# Uses `#filter substitution`
-app/mobile.js
-chrome/content/healthreport-prefs.js
-
-# Uses `#expand`
-chrome/content/about.js
-
-# Not much JS to lint and non-standard at that
-installer/
-locales/
-
-# Pretty sure we're disabling this one anyway
-modules/ContactService.jsm
-
-# es7 proposed: array comprehensions
-#   https://github.com/eslint/espree/issues/125
-modules/WebappManager.jsm
-
-# Non-standard `(catch ex if ...)`
-components/Snippets.js
rename from mobile/android/base/build.gradle
rename to mobile/android/app/base/build.gradle
--- a/mobile/android/base/build.gradle
+++ b/mobile/android/app/base/build.gradle
@@ -19,20 +19,20 @@ android {
     }
 
     lintOptions {
         abortOnError false
     }
 
     sourceSets {
         main {
-            manifest.srcFile 'AndroidManifest.xml'
+            manifest.srcFile "${topsrcdir}/mobile/android/base/AndroidManifest.xml"
+
             java {
                 srcDir "${topobjdir}/gradle/base/src"
-                exclude 'org/mozilla/gecko/resources/**'
 
                 srcDir "${topsrcdir}/mobile/android/search/java"
                 srcDir "${topsrcdir}/mobile/android/javaaddons/java"
 
                 if (mozconfig.substs.MOZ_ANDROID_MLS_STUMBLER) {
                     srcDir "${topsrcdir}/mobile/android/stumbler/java"
                 }
 
@@ -59,19 +59,19 @@ android {
                 }
 
                 srcDir "${project.buildDir}/generated/source/preprocessed_code" // See syncPreprocessedCode.
             }
 
             res {
                 srcDir "${topsrcdir}/${mozconfig.substs.MOZ_BRANDING_DIRECTORY}/res"
                 srcDir "${project.buildDir}/generated/source/preprocessed_resources" // See syncPreprocessedResources.
-                srcDir 'resources'
+                srcDir "${topsrcdir}/mobile/android/base/resources"
                 if (mozconfig.substs.MOZ_CRASHREPORTER) {
-                    srcDir 'crashreporter/res'
+                    srcDir "${topsrcdir}/mobile/android/base/crashreporter/res"
                 }
             }
         }
 
         test {
             java {
                 srcDir "${topsrcdir}/mobile/android/tests/background/junit4/src"
             }
--- a/mobile/android/b2gdroid/confvars.sh
+++ b/mobile/android/b2gdroid/confvars.sh
@@ -86,19 +86,16 @@ if test "$RELEASE_BUILD"; then
 if test "$MOZ_NATIVE_DEVICES"; then
   MOZ_INSTALL_TRACKING=1
 fi
 fi
 
 # Mark as WebGL conformant
 MOZ_WEBGL_CONFORMANT=1
 
-# Enable the share handler.
-MOZ_ANDROID_SHARE_OVERLAY=1
-
 # Enable Tab Queue
 if test "$NIGHTLY_BUILD"; then
   MOZ_ANDROID_TAB_QUEUE=1
 fi
 
 # Use the low-memory GC tuning.
 export JS_GC_SMALL_CHUNK_SIZE=1
 
--- a/mobile/android/base/AccountsHelper.java
+++ b/mobile/android/base/AccountsHelper.java
@@ -17,17 +17,17 @@ import android.util.Log;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.Engaged;
 import org.mozilla.gecko.fxa.login.State;
-import org.mozilla.gecko.restrictions.Restriction;
+import org.mozilla.gecko.restrictions.Restrictable;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.util.EventCallback;
 import org.mozilla.gecko.util.NativeEventListener;
 import org.mozilla.gecko.util.NativeJSObject;
 
 import java.io.IOException;
@@ -78,17 +78,17 @@ public class AccountsHelper implements N
                 "Accounts:DeleteFirefoxAccount",
                 "Accounts:Exist",
                 "Accounts:ProfileUpdated",
                 "Accounts:ShowSyncPreferences");
     }
 
     @Override
     public void handleMessage(String event, NativeJSObject message, final EventCallback callback) {
-        if (!RestrictedProfiles.isAllowed(mContext, Restriction.DISALLOW_MODIFY_ACCOUNTS)) {
+        if (!Restrictions.isAllowed(mContext, Restrictable.MODIFY_ACCOUNTS)) {
             // We register for messages in all contexts; we drop, with a log and an error to JavaScript,
             // when the profile is restricted.  It's better to return errors than silently ignore messages.
             Log.e(LOGTAG, "Profile is not allowed to modify accounts!  Ignoring event: " + event);
             if (callback != null) {
                 callback.sendError("Profile is not allowed to modify accounts!");
             }
             return;
         }
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -418,17 +418,16 @@
                   android:theme="@style/Gecko.Preferences"
                   android:configChanges="orientation|screenSize|locale|layoutDirection"
                   android:excludeFromRecents="true"/>
 
         <provider android:name="org.mozilla.gecko.db.BrowserProvider"
                   android:authorities="@ANDROID_PACKAGE_NAME@.db.browser"
                   android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>
 
-#ifdef MOZ_ANDROID_SHARE_OVERLAY
         <!-- Share overlay activity
 
              Setting launchMode="singleTop" ensures onNewIntent is called when the Activity is
              reused. Ideally we create a new instance but Android L breaks this (bug 1137928). -->
         <activity android:name="org.mozilla.gecko.overlays.ui.ShareDialog"
                   android:label="@string/overlay_share_label"
                   android:theme="@style/OverlayActivity"
                   android:configChanges="keyboard|keyboardHidden|mcc|mnc|locale|layoutDirection"
@@ -440,17 +439,17 @@
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:mimeType="text/plain" />
             </intent-filter>
 
         </activity>
 
         <!-- Service to handle requests from overlays. -->
         <service android:name="org.mozilla.gecko.overlays.service.OverlayActionService" />
-#endif
+
         <!--
           Ensure that passwords provider runs in its own process. (Bug 718760.)
           Process name is per-application to avoid loading CPs from multiple
           Fennec versions into the same process. (Bug 749727.)
           Process name is a mangled version to avoid a Talos bug. (Bug 750548.)
           -->
         <provider android:name="org.mozilla.gecko.db.PasswordsProvider"
                   android:label="@string/sync_configure_engines_title_passwords"
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -44,17 +44,17 @@ import org.mozilla.gecko.menu.GeckoMenu;
 import org.mozilla.gecko.menu.GeckoMenuItem;
 import org.mozilla.gecko.mozglue.ContextUtils;
 import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
 import org.mozilla.gecko.overlays.ui.ShareDialog;
 import org.mozilla.gecko.preferences.ClearOnShutdownPref;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.prompts.Prompt;
 import org.mozilla.gecko.prompts.PromptListItem;
-import org.mozilla.gecko.restrictions.Restriction;
+import org.mozilla.gecko.restrictions.Restrictable;
 import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
 import org.mozilla.gecko.tabqueue.TabQueueHelper;
 import org.mozilla.gecko.tabqueue.TabQueuePrompt;
 import org.mozilla.gecko.tabs.TabHistoryController;
 import org.mozilla.gecko.tabs.TabHistoryController.OnShowTabHistory;
 import org.mozilla.gecko.tabs.TabHistoryFragment;
 import org.mozilla.gecko.tabs.TabHistoryPage;
 import org.mozilla.gecko.tabs.TabsPanel;
@@ -1788,17 +1788,17 @@ public class BrowserApp extends GeckoApp
                                 getSupportFragmentManager().beginTransaction().disallowAddToBackStack().add(mpm, tag).commit();
                             }
                         } catch (Exception ex) {
                             Log.e(LOGTAG, "Error initializing media manager", ex);
                         }
                     }
                 }
 
-                if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED && RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_LOCATION_SERVICE)) {
+                if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED && Restrictions.isAllowed(this, Restrictable.LOCATION_SERVICE)) {
                     // Start (this acts as ping if started already) the stumbler lib; if the stumbler has queued data it will upload it.
                     // Stumbler operates on its own thread, and startup impact is further minimized by delaying work (such as upload) a few seconds.
                     // Avoid any potential startup CPU/thread contention by delaying the pref broadcast.
                     final long oneSecondInMillis = 1000;
                     ThreadUtils.getBackgroundHandler().postDelayed(new Runnable() {
                         @Override
                         public void run() {
                              GeckoPreferences.broadcastStumblerPref(BrowserApp.this);
@@ -2492,17 +2492,17 @@ public class BrowserApp extends GeckoApp
                     final Tab currentTab = Tabs.getInstance().getSelectedTab();
                     if (currentTab != null) {
                         currentTab.setMostRecentHomePanel(panelId);
                     }
                 }
             });
 
             // Don't show the banner in guest mode.
-            if (!RestrictedProfiles.isUserRestricted()) {
+            if (!Restrictions.isUserRestricted()) {
                 final ViewStub homeBannerStub = (ViewStub) findViewById(R.id.home_banner_stub);
                 final HomeBanner homeBanner = (HomeBanner) homeBannerStub.inflate();
                 mHomePager.setBanner(homeBanner);
 
                 // Remove the banner from the view hierarchy if it is dismissed.
                 homeBanner.setOnDismissListener(new HomeBanner.OnDismissListener() {
                     @Override
                     public void onDismiss() {
@@ -3079,21 +3079,21 @@ public class BrowserApp extends GeckoApp
         if (AboutPages.isAboutReader(url)) {
             String urlFromReader = ReaderModeUtils.getUrlFromAboutReader(url);
             if (urlFromReader != null) {
                 url = urlFromReader;
             }
         }
 
         // Disable share menuitem for about:, chrome:, file:, and resource: URIs
-        final boolean shareVisible = RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_SHARE);
+        final boolean shareVisible = Restrictions.isAllowed(this, Restrictable.SHARE);
         share.setVisible(shareVisible);
         final boolean shareEnabled = StringUtils.isShareableUrl(url) && shareVisible;
         share.setEnabled(shareEnabled);
-        MenuUtils.safeSetEnabled(aMenu, R.id.downloads, RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_DOWNLOADS));
+        MenuUtils.safeSetEnabled(aMenu, R.id.downloads, Restrictions.isAllowed(this, Restrictable.DOWNLOAD));
 
         // NOTE: Use MenuUtils.safeSetEnabled because some actions might
         // be on the BrowserToolbar context menu.
         if (Versions.feature11Plus) {
             MenuUtils.safeSetEnabled(aMenu, R.id.page, !isAboutHome(tab));
         }
         MenuUtils.safeSetEnabled(aMenu, R.id.subscribe, tab.hasFeeds());
         MenuUtils.safeSetEnabled(aMenu, R.id.add_search_engine, tab.hasOpenSearch());
@@ -3146,17 +3146,17 @@ public class BrowserApp extends GeckoApp
 
                             shareIntent.putExtra("share_screenshot_uri", Uri.parse(outFile.getPath()));
                         }
                     }
                 }
             }
         }
 
-        final boolean privateTabVisible = RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_PRIVATE_BROWSING);
+        final boolean privateTabVisible = Restrictions.isAllowed(this, Restrictable.PRIVATE_BROWSING);
         MenuUtils.safeSetVisible(aMenu, R.id.new_private_tab, privateTabVisible);
 
         // Disable PDF generation (save and print) for about:home and xul pages.
         boolean allowPDF = (!(isAboutHome(tab) ||
                                tab.getContentType().equals("application/vnd.mozilla.xul+xml") ||
                                tab.getContentType().startsWith("video/")));
         saveAsPDF.setEnabled(allowPDF);
         print.setEnabled(allowPDF);
@@ -3168,21 +3168,21 @@ public class BrowserApp extends GeckoApp
         charEncoding.setVisible(GeckoPreferences.getCharEncodingState());
 
         if (mProfile.inGuestMode()) {
             exitGuestMode.setVisible(true);
         } else {
             enterGuestMode.setVisible(true);
         }
 
-        if (!RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_GUEST_BROWSING)) {
+        if (!Restrictions.isAllowed(this, Restrictable.GUEST_BROWSING)) {
             MenuUtils.safeSetVisible(aMenu, R.id.new_guest_session, false);
         }
 
-        if (!RestrictedProfiles.isAllowed(this, Restriction.DISALLOW_INSTALL_EXTENSION)) {
+        if (!Restrictions.isAllowed(this, Restrictable.INSTALL_EXTENSION)) {
             MenuUtils.safeSetVisible(aMenu, R.id.addons, false);
         }
 
         return true;
     }
 
     private int resolveBookmarkIconID(final boolean isBookmark) {
         if (isBookmark) {
@@ -3791,14 +3791,14 @@ public class BrowserApp extends GeckoApp
     }
 
     @Override
     protected StartupAction getStartupAction(final String passedURL) {
         final boolean inGuestMode = GeckoProfile.get(this).inGuestMode();
         if (inGuestMode) {
             return StartupAction.GUEST;
         }
-        if (RestrictedProfiles.isRestrictedProfile(this)) {
+        if (Restrictions.isRestrictedProfile(this)) {
             return StartupAction.RESTRICTED;
         }
         return (passedURL == null ? StartupAction.NORMAL : StartupAction.URL);
     }
 }
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -2076,17 +2076,17 @@ public abstract class GeckoApp
                     rec.setCurrentSession(currentSession);
                     rec.processDelayed();
                 } else {
                     Log.w(LOGTAG, "Can't record session: rec is null.");
                 }
             }
         });
 
-        RestrictedProfiles.update(this);
+        Restrictions.update(this);
     }
 
     @Override
     public void onWindowFocusChanged(boolean hasFocus) {
         super.onWindowFocusChanged(hasFocus);
 
         if (!mWindowFocusInitialized && hasFocus) {
             mWindowFocusInitialized = true;
rename from mobile/android/base/RestrictedProfiles.java
rename to mobile/android/base/Restrictions.java
--- a/mobile/android/base/RestrictedProfiles.java
+++ b/mobile/android/base/Restrictions.java
@@ -5,29 +5,28 @@