Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 02 Dec 2015 15:22:37 +0100
changeset 309407 633bfeaa021b75241fba50ab3544c46b9a84c00c
parent 309406 01480efcc2813129fe123ffba854ec0048353af8 (current diff)
parent 309309 f6ac392322b3b06d7112a6db1bf6f8ab1853fdcf (diff)
child 309408 c6bacc29209b5a8afb28b296b1647ab461478ba6
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)
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 mozilla-central to mozilla-inbound
devtools/client/debugger/views/sources-view.js
devtools/client/shared/redux/reducers.js
mobile/android/base/background/BackgroundService.java
mobile/android/base/background/ReadingListConstants.java
mobile/android/base/background/bagheera/BagheeraClient.java
mobile/android/base/background/bagheera/BagheeraRequestDelegate.java
mobile/android/base/background/bagheera/BoundedByteArrayEntity.java
mobile/android/base/background/bagheera/DeflateHelper.java
mobile/android/base/background/common/DateUtils.java
mobile/android/base/background/common/EditorBranch.java
mobile/android/base/background/common/GlobalConstants.java
mobile/android/base/background/common/PrefsBranch.java
mobile/android/base/background/common/log/Logger.java
mobile/android/base/background/common/log/writers/AndroidLevelCachingLogWriter.java
mobile/android/base/background/common/log/writers/AndroidLogWriter.java
mobile/android/base/background/common/log/writers/LevelFilteringLogWriter.java
mobile/android/base/background/common/log/writers/LogWriter.java
mobile/android/base/background/common/log/writers/PrintLogWriter.java
mobile/android/base/background/common/log/writers/SimpleTagLogWriter.java
mobile/android/base/background/common/log/writers/StringLogWriter.java
mobile/android/base/background/common/log/writers/TagLogWriter.java
mobile/android/base/background/common/log/writers/ThreadLocalTagLogWriter.java
mobile/android/base/background/common/telemetry/TelemetryWrapper.java
mobile/android/base/background/datareporting/TelemetryRecorder.java
mobile/android/base/background/db/CursorDumper.java
mobile/android/base/background/db/Tab.java
mobile/android/base/background/fxa/FxAccount10AuthDelegate.java
mobile/android/base/background/fxa/FxAccount10CreateDelegate.java
mobile/android/base/background/fxa/FxAccount20CreateDelegate.java
mobile/android/base/background/fxa/FxAccount20LoginDelegate.java
mobile/android/base/background/fxa/FxAccountAgeLockoutHelper.java
mobile/android/base/background/fxa/FxAccountClient.java
mobile/android/base/background/fxa/FxAccountClient10.java
mobile/android/base/background/fxa/FxAccountClient20.java
mobile/android/base/background/fxa/FxAccountClientException.java
mobile/android/base/background/fxa/FxAccountRemoteError.java
mobile/android/base/background/fxa/FxAccountUtils.java
mobile/android/base/background/fxa/PasswordStretcher.java
mobile/android/base/background/fxa/QuickPasswordStretcher.java
mobile/android/base/background/fxa/SkewHandler.java
mobile/android/base/background/fxa/oauth/FxAccountAbstractClient.java
mobile/android/base/background/fxa/oauth/FxAccountAbstractClientException.java
mobile/android/base/background/fxa/oauth/FxAccountOAuthClient10.java
mobile/android/base/background/fxa/oauth/FxAccountOAuthRemoteError.java
mobile/android/base/background/fxa/profile/FxAccountProfileClient10.java
mobile/android/base/background/healthreport/AndroidConfigurationProvider.java
mobile/android/base/background/healthreport/Environment.java
mobile/android/base/background/healthreport/EnvironmentBuilder.java
mobile/android/base/background/healthreport/EnvironmentV1.java
mobile/android/base/background/healthreport/EnvironmentV2.java
mobile/android/base/background/healthreport/HealthReportBroadcastReceiver.java
mobile/android/base/background/healthreport/HealthReportBroadcastService.java
mobile/android/base/background/healthreport/HealthReportConstants.java
mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java
mobile/android/base/background/healthreport/HealthReportDatabases.java
mobile/android/base/background/healthreport/HealthReportExportedBroadcastReceiver.java
mobile/android/base/background/healthreport/HealthReportGenerator.java
mobile/android/base/background/healthreport/HealthReportProvider.java
mobile/android/base/background/healthreport/HealthReportStorage.java
mobile/android/base/background/healthreport/HealthReportUtils.java
mobile/android/base/background/healthreport/ProfileInformationCache.java
mobile/android/base/background/healthreport/prune/HealthReportPruneService.java
mobile/android/base/background/healthreport/prune/PrunePolicy.java
mobile/android/base/background/healthreport/prune/PrunePolicyDatabaseStorage.java
mobile/android/base/background/healthreport/prune/PrunePolicyStorage.java
mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java
mobile/android/base/background/healthreport/upload/HealthReportUploadService.java
mobile/android/base/background/healthreport/upload/ObsoleteDocumentTracker.java
mobile/android/base/background/healthreport/upload/SubmissionClient.java
mobile/android/base/background/healthreport/upload/SubmissionPolicy.java
mobile/android/base/background/nativecode/NativeCrypto.java
mobile/android/base/background/preferences/PreferenceFragment.java
mobile/android/base/background/preferences/PreferenceManagerCompat.java
mobile/android/base/browserid/ASNUtils.java
mobile/android/base/browserid/BrowserIDKeyPair.java
mobile/android/base/browserid/DSACryptoImplementation.java
mobile/android/base/browserid/JSONWebTokenUtils.java
mobile/android/base/browserid/MockMyIDTokenFactory.java
mobile/android/base/browserid/RSACryptoImplementation.java
mobile/android/base/browserid/SigningPrivateKey.java
mobile/android/base/browserid/VerifyingPublicKey.java
mobile/android/base/browserid/verifier/AbstractBrowserIDRemoteVerifierClient.java
mobile/android/base/browserid/verifier/BrowserIDRemoteVerifierClient10.java
mobile/android/base/browserid/verifier/BrowserIDRemoteVerifierClient20.java
mobile/android/base/browserid/verifier/BrowserIDVerifierClient.java
mobile/android/base/browserid/verifier/BrowserIDVerifierDelegate.java
mobile/android/base/browserid/verifier/BrowserIDVerifierException.java
mobile/android/base/fxa/AccountLoader.java
mobile/android/base/fxa/FirefoxAccounts.java
mobile/android/base/fxa/FxAccountConstants.java
mobile/android/base/fxa/SyncStatusListener.java
mobile/android/base/fxa/activities/CustomColorPreference.java
mobile/android/base/fxa/activities/FxAccountAbstractActivity.java
mobile/android/base/fxa/activities/FxAccountAbstractSetupActivity.java
mobile/android/base/fxa/activities/FxAccountAbstractUpdateCredentialsActivity.java
mobile/android/base/fxa/activities/FxAccountConfirmAccountActivity.java
mobile/android/base/fxa/activities/FxAccountConfirmAccountActivityWeb.java
mobile/android/base/fxa/activities/FxAccountCreateAccountActivity.java
mobile/android/base/fxa/activities/FxAccountCreateAccountNotAllowedActivity.java
mobile/android/base/fxa/activities/FxAccountFinishMigratingActivity.java
mobile/android/base/fxa/activities/FxAccountFinishMigratingActivityWeb.java
mobile/android/base/fxa/activities/FxAccountGetStartedActivity.java
mobile/android/base/fxa/activities/FxAccountGetStartedActivityWeb.java
mobile/android/base/fxa/activities/FxAccountMigrationFinishedActivity.java
mobile/android/base/fxa/activities/FxAccountSignInActivity.java
mobile/android/base/fxa/activities/FxAccountStatusActivity.java
mobile/android/base/fxa/activities/FxAccountStatusFragment.java
mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivity.java
mobile/android/base/fxa/activities/FxAccountUpdateCredentialsActivityWeb.java
mobile/android/base/fxa/activities/FxAccountVerifiedAccountActivity.java
mobile/android/base/fxa/activities/FxAccountWebFlowActivity.java
mobile/android/base/fxa/activities/PicassoPreferenceIconTarget.java
mobile/android/base/fxa/authenticator/AccountPickler.java
mobile/android/base/fxa/authenticator/AndroidFxAccount.java
mobile/android/base/fxa/authenticator/FxADefaultLoginStateMachineDelegate.java
mobile/android/base/fxa/authenticator/FxAccountAuthenticator.java
mobile/android/base/fxa/authenticator/FxAccountAuthenticatorService.java
mobile/android/base/fxa/authenticator/FxAccountLoginDelegate.java
mobile/android/base/fxa/authenticator/FxAccountLoginException.java
mobile/android/base/fxa/login/BaseRequestDelegate.java
mobile/android/base/fxa/login/Cohabiting.java
mobile/android/base/fxa/login/Doghouse.java
mobile/android/base/fxa/login/Engaged.java
mobile/android/base/fxa/login/FxAccountLoginStateMachine.java
mobile/android/base/fxa/login/FxAccountLoginTransition.java
mobile/android/base/fxa/login/Married.java
mobile/android/base/fxa/login/MigratedFromSync11.java
mobile/android/base/fxa/login/Separated.java
mobile/android/base/fxa/login/State.java
mobile/android/base/fxa/login/StateFactory.java
mobile/android/base/fxa/login/TokensAndKeysState.java
mobile/android/base/fxa/receivers/FxAccountDeletedReceiver.java
mobile/android/base/fxa/receivers/FxAccountDeletedService.java
mobile/android/base/fxa/receivers/FxAccountUpgradeReceiver.java
mobile/android/base/fxa/sync/FxAccountGlobalSession.java
mobile/android/base/fxa/sync/FxAccountNotificationManager.java
mobile/android/base/fxa/sync/FxAccountProfileService.java
mobile/android/base/fxa/sync/FxAccountSchedulePolicy.java
mobile/android/base/fxa/sync/FxAccountSyncAdapter.java
mobile/android/base/fxa/sync/FxAccountSyncDelegate.java
mobile/android/base/fxa/sync/FxAccountSyncService.java
mobile/android/base/fxa/sync/FxAccountSyncStatusHelper.java
mobile/android/base/fxa/sync/SchedulePolicy.java
mobile/android/base/fxa/tasks/FxAccountCodeResender.java
mobile/android/base/fxa/tasks/FxAccountCreateAccountTask.java
mobile/android/base/fxa/tasks/FxAccountSetupTask.java
mobile/android/base/fxa/tasks/FxAccountSignInTask.java
mobile/android/base/fxa/tasks/FxAccountUnlockCodeResender.java
mobile/android/base/reading/ClientMetadata.java
mobile/android/base/reading/ClientReadingListRecord.java
mobile/android/base/reading/FetchSpec.java
mobile/android/base/reading/LocalReadingListStorage.java
mobile/android/base/reading/ReadingListBackoffObserver.java
mobile/android/base/reading/ReadingListChangeAccumulator.java
mobile/android/base/reading/ReadingListClient.java
mobile/android/base/reading/ReadingListClientContentValuesFactory.java
mobile/android/base/reading/ReadingListClientRecordFactory.java
mobile/android/base/reading/ReadingListDeleteDelegate.java
mobile/android/base/reading/ReadingListInvalidAuthenticationException.java
mobile/android/base/reading/ReadingListRecord.java
mobile/android/base/reading/ReadingListRecordDelegate.java
mobile/android/base/reading/ReadingListRecordResponse.java
mobile/android/base/reading/ReadingListRecordUploadDelegate.java
mobile/android/base/reading/ReadingListResponse.java
mobile/android/base/reading/ReadingListStorage.java
mobile/android/base/reading/ReadingListStorageResponse.java
mobile/android/base/reading/ReadingListSyncAdapter.java
mobile/android/base/reading/ReadingListSyncService.java
mobile/android/base/reading/ReadingListSynchronizer.java
mobile/android/base/reading/ReadingListSynchronizerDelegate.java
mobile/android/base/reading/ReadingListWipeDelegate.java
mobile/android/base/reading/ServerReadingListRecord.java
mobile/android/base/resources/drawable-hdpi/fxaccount_checkbox.png
mobile/android/base/resources/drawable-hdpi/fxaccount_ddarrow_inactive.png
mobile/android/base/resources/drawable-hdpi/fxaccount_intro.png
mobile/android/base/resources/drawable-hdpi/fxaccount_mail.png
mobile/android/base/resources/drawable-hdpi/fxaccount_sync_error.png
mobile/android/base/resources/drawable-hdpi/sync_avatar_default.png
mobile/android/base/resources/drawable-hdpi/sync_desktop.png
mobile/android/base/resources/drawable-hdpi/sync_desktop_inactive.png
mobile/android/base/resources/drawable-hdpi/sync_mobile.png
mobile/android/base/resources/drawable-hdpi/sync_mobile_inactive.png
mobile/android/base/resources/drawable-hdpi/sync_promo.png
mobile/android/base/resources/drawable-v12/fxaccount_password_active.xml
mobile/android/base/resources/drawable-v12/fxaccount_password_button_hide_active.xml
mobile/android/base/resources/drawable-v12/fxaccount_password_button_show_active.xml
mobile/android/base/resources/drawable-v12/fxaccount_password_inactive.xml
mobile/android/base/resources/drawable-xhdpi/fxaccount_intro.png
mobile/android/base/resources/drawable-xhdpi/fxaccount_sync_error.png
mobile/android/base/resources/drawable-xhdpi/sync_desktop.png
mobile/android/base/resources/drawable-xhdpi/sync_desktop_inactive.png
mobile/android/base/resources/drawable-xhdpi/sync_mobile.png
mobile/android/base/resources/drawable-xhdpi/sync_mobile_inactive.png
mobile/android/base/resources/drawable-xhdpi/sync_promo.png
mobile/android/base/resources/drawable-xxhdpi/fxaccount_sync_error.png
mobile/android/base/resources/drawable-xxhdpi/sync_avatar_default.png
mobile/android/base/resources/drawable-xxhdpi/sync_desktop.png
mobile/android/base/resources/drawable-xxhdpi/sync_desktop_inactive.png
mobile/android/base/resources/drawable-xxhdpi/sync_mobile.png
mobile/android/base/resources/drawable-xxhdpi/sync_mobile_inactive.png
mobile/android/base/resources/drawable/fxaccount_button_background.xml
mobile/android/base/resources/drawable/fxaccount_button_background_disabled.xml
mobile/android/base/resources/drawable/fxaccount_button_background_enabled.xml
mobile/android/base/resources/drawable/fxaccount_button_background_pressed.xml
mobile/android/base/resources/drawable/fxaccount_button_color.xml
mobile/android/base/resources/drawable/fxaccount_checkbox_textcolor.xml
mobile/android/base/resources/drawable/fxaccount_linkitem_textcolor.xml
mobile/android/base/resources/drawable/fxaccount_password_active.xml
mobile/android/base/resources/drawable/fxaccount_password_background.xml
mobile/android/base/resources/drawable/fxaccount_password_button_hide_active.xml
mobile/android/base/resources/drawable/fxaccount_password_button_hide_background.xml
mobile/android/base/resources/drawable/fxaccount_password_button_show_active.xml
mobile/android/base/resources/drawable/fxaccount_password_button_show_background.xml
mobile/android/base/resources/drawable/fxaccount_password_inactive.xml
mobile/android/base/resources/drawable/fxaccount_textfield_active.xml
mobile/android/base/resources/drawable/fxaccount_textfield_background.xml
mobile/android/base/resources/drawable/fxaccount_textfield_inactive.xml
mobile/android/base/resources/drawable/fxaccount_textview_error_background.xml
mobile/android/base/resources/drawable/sync_pin_background.xml
mobile/android/base/resources/layout/fxaccount_account_verified.xml
mobile/android/base/resources/layout/fxaccount_confirm_account.xml
mobile/android/base/resources/layout/fxaccount_create_account.xml
mobile/android/base/resources/layout/fxaccount_create_account_not_allowed.xml
mobile/android/base/resources/layout/fxaccount_custom_server_view.xml
mobile/android/base/resources/layout/fxaccount_email_password_view.xml
mobile/android/base/resources/layout/fxaccount_finish_migrating.xml
mobile/android/base/resources/layout/fxaccount_get_started.xml
mobile/android/base/resources/layout/fxaccount_migration_finished.xml
mobile/android/base/resources/layout/fxaccount_preference_list_fragment.xml
mobile/android/base/resources/layout/fxaccount_sign_in.xml
mobile/android/base/resources/layout/fxaccount_status_error_preference.xml
mobile/android/base/resources/layout/fxaccount_update_credentials.xml
mobile/android/base/resources/layout/sync_account.xml
mobile/android/base/resources/layout/sync_list_item.xml
mobile/android/base/resources/layout/sync_redirect_to_setup.xml
mobile/android/base/resources/layout/sync_send_tab.xml
mobile/android/base/resources/layout/sync_setup.xml
mobile/android/base/resources/layout/sync_setup_failure.xml
mobile/android/base/resources/layout/sync_setup_jpake_waiting.xml
mobile/android/base/resources/layout/sync_setup_nointernet.xml
mobile/android/base/resources/layout/sync_setup_pair.xml
mobile/android/base/resources/layout/sync_setup_success.xml
mobile/android/base/resources/layout/sync_setup_webview.xml
mobile/android/base/resources/menu/fxaccount_status_menu.xml
mobile/android/base/resources/values-large-v11/fxaccount_styles.xml
mobile/android/base/resources/values-large-v11/sync_styles.xml
mobile/android/base/resources/values-v11/fxaccount_styles.xml
mobile/android/base/resources/values-v11/sync_styles.xml
mobile/android/base/resources/values/fxaccount_colors.xml
mobile/android/base/resources/values/fxaccount_dimens.xml
mobile/android/base/resources/values/fxaccount_styles.xml
mobile/android/base/resources/values/sync_styles.xml
mobile/android/base/resources/xml/fxaccount_authenticator.xml
mobile/android/base/resources/xml/fxaccount_options.xml
mobile/android/base/resources/xml/fxaccount_status_prefscreen.xml
mobile/android/base/resources/xml/fxaccount_syncadapter.xml
mobile/android/base/resources/xml/sync_authenticator.xml
mobile/android/base/resources/xml/sync_options.xml
mobile/android/base/resources/xml/sync_syncadapter.xml
mobile/android/base/sync/AlreadySyncingException.java
mobile/android/base/sync/BackoffHandler.java
mobile/android/base/sync/BadRequiredFieldJSONException.java
mobile/android/base/sync/CollectionKeys.java
mobile/android/base/sync/CommandProcessor.java
mobile/android/base/sync/CommandRunner.java
mobile/android/base/sync/CredentialException.java
mobile/android/base/sync/CryptoRecord.java
mobile/android/base/sync/DelayedWorkTracker.java
mobile/android/base/sync/EngineSettings.java
mobile/android/base/sync/ExtendedJSONObject.java
mobile/android/base/sync/GlobalSession.java
mobile/android/base/sync/HTTPFailureException.java
mobile/android/base/sync/InfoCollections.java
mobile/android/base/sync/InfoCounts.java
mobile/android/base/sync/JSONRecordFetcher.java
mobile/android/base/sync/KeyBundleProvider.java
mobile/android/base/sync/MetaGlobal.java
mobile/android/base/sync/MetaGlobalException.java
mobile/android/base/sync/MetaGlobalMissingEnginesException.java
mobile/android/base/sync/MetaGlobalNotSetException.java
mobile/android/base/sync/MigrationSentinelSyncStage.java
mobile/android/base/sync/NoCollectionKeysSetException.java
mobile/android/base/sync/NodeAuthenticationException.java
mobile/android/base/sync/NonArrayJSONException.java
mobile/android/base/sync/NonObjectJSONException.java
mobile/android/base/sync/NullClusterURLException.java
mobile/android/base/sync/PersistedMetaGlobal.java
mobile/android/base/sync/PrefsBackoffHandler.java
mobile/android/base/sync/README.txt
mobile/android/base/sync/Server11PreviousPostFailedException.java
mobile/android/base/sync/Server11RecordPostFailedException.java
mobile/android/base/sync/SharedPreferencesClientsDataDelegate.java
mobile/android/base/sync/SharedPreferencesNodeAssignmentCallback.java
mobile/android/base/sync/Sync11Configuration.java
mobile/android/base/sync/SyncConfiguration.java
mobile/android/base/sync/SyncConfigurationException.java
mobile/android/base/sync/SyncConstants.java
mobile/android/base/sync/SyncException.java
mobile/android/base/sync/SynchronizerConfiguration.java
mobile/android/base/sync/ThreadPool.java
mobile/android/base/sync/UnexpectedJSONException.java
mobile/android/base/sync/UnknownSynchronizerConfigurationVersionException.java
mobile/android/base/sync/Utils.java
mobile/android/base/sync/config/AccountPickler.java
mobile/android/base/sync/config/ClientRecordTerminator.java
mobile/android/base/sync/config/ConfigurationMigrator.java
mobile/android/base/sync/config/activities/SelectEnginesActivity.java
mobile/android/base/sync/crypto/CryptoException.java
mobile/android/base/sync/crypto/CryptoInfo.java
mobile/android/base/sync/crypto/HKDF.java
mobile/android/base/sync/crypto/HMACVerificationException.java
mobile/android/base/sync/crypto/KeyBundle.java
mobile/android/base/sync/crypto/MissingCryptoInputException.java
mobile/android/base/sync/crypto/NoKeyBundleException.java
mobile/android/base/sync/crypto/PBKDF2.java
mobile/android/base/sync/crypto/PersistedCrypto5Keys.java
mobile/android/base/sync/delegates/BaseGlobalSessionCallback.java
mobile/android/base/sync/delegates/ClientsDataDelegate.java
mobile/android/base/sync/delegates/FreshStartDelegate.java
mobile/android/base/sync/delegates/GlobalSessionCallback.java
mobile/android/base/sync/delegates/JSONRecordFetchDelegate.java
mobile/android/base/sync/delegates/KeyUploadDelegate.java
mobile/android/base/sync/delegates/MetaGlobalDelegate.java
mobile/android/base/sync/delegates/NodeAssignmentCallback.java
mobile/android/base/sync/delegates/WipeServerDelegate.java
mobile/android/base/sync/jpake/BigIntegerHelper.java
mobile/android/base/sync/jpake/Gx3OrGx4IsZeroOrOneException.java
mobile/android/base/sync/jpake/IncorrectZkpException.java
mobile/android/base/sync/jpake/JPakeClient.java
mobile/android/base/sync/jpake/JPakeCrypto.java
mobile/android/base/sync/jpake/JPakeJson.java
mobile/android/base/sync/jpake/JPakeNoActivePairingException.java
mobile/android/base/sync/jpake/JPakeNumGenerator.java
mobile/android/base/sync/jpake/JPakeNumGeneratorRandom.java
mobile/android/base/sync/jpake/JPakeParty.java
mobile/android/base/sync/jpake/Zkp.java
mobile/android/base/sync/jpake/stage/CompleteStage.java
mobile/android/base/sync/jpake/stage/ComputeFinalStage.java
mobile/android/base/sync/jpake/stage/ComputeKeyVerificationStage.java
mobile/android/base/sync/jpake/stage/ComputeStepOneStage.java
mobile/android/base/sync/jpake/stage/ComputeStepTwoStage.java
mobile/android/base/sync/jpake/stage/DecryptDataStage.java
mobile/android/base/sync/jpake/stage/DeleteChannel.java
mobile/android/base/sync/jpake/stage/GetChannelStage.java
mobile/android/base/sync/jpake/stage/GetRequestStage.java
mobile/android/base/sync/jpake/stage/JPakeStage.java
mobile/android/base/sync/jpake/stage/PutRequestStage.java
mobile/android/base/sync/jpake/stage/VerifyPairingStage.java
mobile/android/base/sync/middleware/Crypto5MiddlewareRepository.java
mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java
mobile/android/base/sync/middleware/MiddlewareRepository.java
mobile/android/base/sync/middleware/MiddlewareRepositorySession.java
mobile/android/base/sync/net/AbstractBearerTokenAuthHeaderProvider.java
mobile/android/base/sync/net/AuthHeaderProvider.java
mobile/android/base/sync/net/BaseResource.java
mobile/android/base/sync/net/BaseResourceDelegate.java
mobile/android/base/sync/net/BasicAuthHeaderProvider.java
mobile/android/base/sync/net/BearerAuthHeaderProvider.java
mobile/android/base/sync/net/BrowserIDAuthHeaderProvider.java
mobile/android/base/sync/net/ConnectionMonitorThread.java
mobile/android/base/sync/net/HMACAuthHeaderProvider.java
mobile/android/base/sync/net/HandleProgressException.java
mobile/android/base/sync/net/HawkAuthHeaderProvider.java
mobile/android/base/sync/net/HttpResponseObserver.java
mobile/android/base/sync/net/MozResponse.java
mobile/android/base/sync/net/Resource.java
mobile/android/base/sync/net/ResourceDelegate.java
mobile/android/base/sync/net/SRPConstants.java
mobile/android/base/sync/net/SyncResponse.java
mobile/android/base/sync/net/SyncStorageCollectionRequest.java
mobile/android/base/sync/net/SyncStorageCollectionRequestDelegate.java
mobile/android/base/sync/net/SyncStorageRecordRequest.java
mobile/android/base/sync/net/SyncStorageRequest.java
mobile/android/base/sync/net/SyncStorageRequestDelegate.java
mobile/android/base/sync/net/SyncStorageRequestIncrementalDelegate.java
mobile/android/base/sync/net/SyncStorageResponse.java
mobile/android/base/sync/net/TLSSocketFactory.java
mobile/android/base/sync/net/WBOCollectionRequestDelegate.java
mobile/android/base/sync/net/WBORequestDelegate.java
mobile/android/base/sync/receivers/SyncAccountDeletedReceiver.java
mobile/android/base/sync/receivers/SyncAccountDeletedService.java
mobile/android/base/sync/receivers/UpgradeReceiver.java
mobile/android/base/sync/repositories/BookmarkNeedsReparentingException.java
mobile/android/base/sync/repositories/BookmarksRepository.java
mobile/android/base/sync/repositories/ConstrainedServer11Repository.java
mobile/android/base/sync/repositories/FetchFailedException.java
mobile/android/base/sync/repositories/HashSetStoreTracker.java
mobile/android/base/sync/repositories/HistoryRepository.java
mobile/android/base/sync/repositories/IdentityRecordFactory.java
mobile/android/base/sync/repositories/InactiveSessionException.java
mobile/android/base/sync/repositories/InvalidBookmarkTypeException.java
mobile/android/base/sync/repositories/InvalidRequestException.java
mobile/android/base/sync/repositories/InvalidSessionTransitionException.java
mobile/android/base/sync/repositories/MultipleRecordsForGuidException.java
mobile/android/base/sync/repositories/NoContentProviderException.java
mobile/android/base/sync/repositories/NoGuidForIdException.java
mobile/android/base/sync/repositories/NoStoreDelegateException.java
mobile/android/base/sync/repositories/NullCursorException.java
mobile/android/base/sync/repositories/ParentNotFoundException.java
mobile/android/base/sync/repositories/ProfileDatabaseException.java
mobile/android/base/sync/repositories/RecordFactory.java
mobile/android/base/sync/repositories/RecordFilter.java
mobile/android/base/sync/repositories/Repository.java
mobile/android/base/sync/repositories/RepositorySession.java
mobile/android/base/sync/repositories/RepositorySessionBundle.java
mobile/android/base/sync/repositories/Server11Repository.java
mobile/android/base/sync/repositories/Server11RepositorySession.java
mobile/android/base/sync/repositories/StoreFailedException.java
mobile/android/base/sync/repositories/StoreTracker.java
mobile/android/base/sync/repositories/StoreTrackingRepositorySession.java
mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java
mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepository.java
mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java
mobile/android/base/sync/repositories/android/AndroidBrowserHistoryDataAccessor.java
mobile/android/base/sync/repositories/android/AndroidBrowserHistoryDataExtender.java
mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepository.java
mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java
mobile/android/base/sync/repositories/android/AndroidBrowserRepository.java
mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java
mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java
mobile/android/base/sync/repositories/android/BookmarksDeletionManager.java
mobile/android/base/sync/repositories/android/BookmarksInsertionManager.java
mobile/android/base/sync/repositories/android/BrowserContractHelpers.java
mobile/android/base/sync/repositories/android/CachedSQLiteOpenHelper.java
mobile/android/base/sync/repositories/android/ClientsDatabase.java
mobile/android/base/sync/repositories/android/ClientsDatabaseAccessor.java
mobile/android/base/sync/repositories/android/FennecTabsRepository.java
mobile/android/base/sync/repositories/android/FormHistoryRepositorySession.java
mobile/android/base/sync/repositories/android/PasswordsRepositorySession.java
mobile/android/base/sync/repositories/android/RepoUtils.java
mobile/android/base/sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java
mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java
mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java
mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java
mobile/android/base/sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java
mobile/android/base/sync/repositories/delegates/RepositorySessionBeginDelegate.java
mobile/android/base/sync/repositories/delegates/RepositorySessionCleanDelegate.java
mobile/android/base/sync/repositories/delegates/RepositorySessionCreationDelegate.java
mobile/android/base/sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java
mobile/android/base/sync/repositories/delegates/RepositorySessionFinishDelegate.java
mobile/android/base/sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java
mobile/android/base/sync/repositories/delegates/RepositorySessionStoreDelegate.java
mobile/android/base/sync/repositories/delegates/RepositorySessionWipeDelegate.java
mobile/android/base/sync/repositories/domain/BookmarkRecord.java
mobile/android/base/sync/repositories/domain/BookmarkRecordFactory.java
mobile/android/base/sync/repositories/domain/ClientRecord.java
mobile/android/base/sync/repositories/domain/ClientRecordFactory.java
mobile/android/base/sync/repositories/domain/FormHistoryRecord.java
mobile/android/base/sync/repositories/domain/HistoryRecord.java
mobile/android/base/sync/repositories/domain/HistoryRecordFactory.java
mobile/android/base/sync/repositories/domain/PasswordRecord.java
mobile/android/base/sync/repositories/domain/PasswordRecordFactory.java
mobile/android/base/sync/repositories/domain/Record.java
mobile/android/base/sync/repositories/domain/RecordParseException.java
mobile/android/base/sync/repositories/domain/TabsRecord.java
mobile/android/base/sync/repositories/domain/TabsRecordFactory.java
mobile/android/base/sync/repositories/domain/VersionConstants.java
mobile/android/base/sync/setup/Constants.java
mobile/android/base/sync/setup/InvalidSyncKeyException.java
mobile/android/base/sync/setup/SyncAccounts.java
mobile/android/base/sync/setup/SyncAuthenticatorService.java
mobile/android/base/sync/setup/activities/AccountActivity.java
mobile/android/base/sync/setup/activities/ActivityUtils.java
mobile/android/base/sync/setup/activities/RedirectToSetupActivity.java
mobile/android/base/sync/setup/activities/SendTabData.java
mobile/android/base/sync/setup/activities/SetupFailureActivity.java
mobile/android/base/sync/setup/activities/SetupSuccessActivity.java
mobile/android/base/sync/setup/activities/SetupSyncActivity.java
mobile/android/base/sync/setup/activities/SyncActivity.java
mobile/android/base/sync/setup/activities/WebURLFinder.java
mobile/android/base/sync/setup/activities/WebViewActivity.java
mobile/android/base/sync/setup/auth/AccountAuthenticator.java
mobile/android/base/sync/setup/auth/AuthenticateAccountStage.java
mobile/android/base/sync/setup/auth/AuthenticationResult.java
mobile/android/base/sync/setup/auth/AuthenticatorStage.java
mobile/android/base/sync/setup/auth/EnsureUserExistenceStage.java
mobile/android/base/sync/setup/auth/FetchUserNodeStage.java
mobile/android/base/sync/stage/AbstractNonRepositorySyncStage.java
mobile/android/base/sync/stage/AbstractSessionManagingSyncStage.java
mobile/android/base/sync/stage/AndroidBrowserBookmarksServerSyncStage.java
mobile/android/base/sync/stage/AndroidBrowserHistoryServerSyncStage.java
mobile/android/base/sync/stage/CheckPreconditionsStage.java
mobile/android/base/sync/stage/CompletedStage.java
mobile/android/base/sync/stage/EnsureClusterURLStage.java
mobile/android/base/sync/stage/EnsureCrypto5KeysStage.java
mobile/android/base/sync/stage/FennecTabsServerSyncStage.java
mobile/android/base/sync/stage/FetchInfoCollectionsStage.java
mobile/android/base/sync/stage/FetchMetaGlobalStage.java
mobile/android/base/sync/stage/FormHistoryServerSyncStage.java
mobile/android/base/sync/stage/GlobalSyncStage.java
mobile/android/base/sync/stage/NoSuchStageException.java
mobile/android/base/sync/stage/NoSyncIDException.java
mobile/android/base/sync/stage/PasswordsServerSyncStage.java
mobile/android/base/sync/stage/SafeConstrainedServer11Repository.java
mobile/android/base/sync/stage/ServerSyncStage.java
mobile/android/base/sync/stage/SyncClientsEngineStage.java
mobile/android/base/sync/stage/UploadMetaGlobalStage.java
mobile/android/base/sync/syncadapter/SyncAdapter.java
mobile/android/base/sync/syncadapter/SyncService.java
mobile/android/base/sync/synchronizer/ConcurrentRecordConsumer.java
mobile/android/base/sync/synchronizer/RecordConsumer.java
mobile/android/base/sync/synchronizer/RecordsChannel.java
mobile/android/base/sync/synchronizer/RecordsChannelDelegate.java
mobile/android/base/sync/synchronizer/RecordsConsumerDelegate.java
mobile/android/base/sync/synchronizer/SerialRecordConsumer.java
mobile/android/base/sync/synchronizer/ServerLocalSynchronizer.java
mobile/android/base/sync/synchronizer/ServerLocalSynchronizerSession.java
mobile/android/base/sync/synchronizer/SessionNotBegunException.java
mobile/android/base/sync/synchronizer/Synchronizer.java
mobile/android/base/sync/synchronizer/SynchronizerDelegate.java
mobile/android/base/sync/synchronizer/SynchronizerSession.java
mobile/android/base/sync/synchronizer/SynchronizerSessionDelegate.java
mobile/android/base/sync/synchronizer/UnbundleError.java
mobile/android/base/sync/synchronizer/UnexpectedSessionException.java
mobile/android/base/sync/telemetry/TelemetryContract.java
mobile/android/base/tokenserver/TokenServerClient.java
mobile/android/base/tokenserver/TokenServerClientDelegate.java
mobile/android/base/tokenserver/TokenServerException.java
mobile/android/base/tokenserver/TokenServerToken.java
mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testSelectionCarets.java
mobile/android/tests/browser/robocop/testSelectionCarets.html
mobile/android/tests/browser/robocop/testSelectionCarets.js
--- a/.eslintignore
+++ b/.eslintignore
@@ -158,8 +158,11 @@ mobile/android/locales/
 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
+
+# Bug 1178739: Ignore this file as a quick fix for "Illegal yield expression"
+mobile/android/modules/HomeProvider.jsm
--- a/addon-sdk/source/test/addons/places/lib/test-places-bookmarks.js
+++ b/addon-sdk/source/test/addons/places/lib/test-places-bookmarks.js
@@ -935,14 +935,14 @@ exports.testCheckSaveOrder = function (a
         'correct ordering of bookmark results');
   }).then(done).catch(assert.fail);
 };
 
 before(exports, (name, assert, done) => resetPlaces(done));
 after(exports, (name, assert, done) => resetPlaces(done));
 
 function saveP () {
-  return promisedEmitter(save.apply(null, Array.slice(arguments)));
+  return promisedEmitter(save.apply(null, Array.prototype.slice.call(arguments)));
 }
 
 function searchP () {
-  return promisedEmitter(search.apply(null, Array.slice(arguments)));
+  return promisedEmitter(search.apply(null, Array.prototype.slice.call(arguments)));
 }
--- a/addon-sdk/source/test/addons/places/lib/test-places-events.js
+++ b/addon-sdk/source/test/addons/places/lib/test-places-events.js
@@ -311,17 +311,17 @@ exports['test history-delete-visits'] = 
 // while constructing the bookmark item requires subsequent calls to that bookmark item.
 // If we destroy the underlying bookmark immediately, these calls will fail.
 //
 // The places SDK abstraction around this alleviates it, but these are low level events.
 after(exports, (name, assert, done) => setTimeout(() => resetPlaces(done), 1));
 before(exports, (name, assert, done) => resetPlaces(done));
 
 function saveP () {
-  return promisedEmitter(save.apply(null, Array.slice(arguments)));
+  return promisedEmitter(save.apply(null, Array.prototype.slice.call(arguments)));
 }
 
 function makeCompleted (done, countTo) {
   let count = 0;
   countTo = countTo || 2;
   return function () {
     if (++count === countTo) done();
   };
--- a/addon-sdk/source/test/addons/places/lib/test-places-history.js
+++ b/addon-sdk/source/test/addons/places/lib/test-places-history.js
@@ -232,13 +232,13 @@ function toBeWithin (range) {
   range = range || 2000;
   var current = new Date() * 1000; // convert to microseconds
   return compared => {
     return compared - current < range;
   };
 }
 
 function searchP () {
-  return promisedEmitter(search.apply(null, Array.slice(arguments)));
+  return promisedEmitter(search.apply(null, Array.prototype.slice.call(arguments)));
 }
 
 before(exports, (name, assert, done) => resetPlaces(done));
 after(exports, (name, assert, done) => resetPlaces(done));
--- a/b2g/config/aries/config.json
+++ b/b2g/config/aries/config.json
@@ -33,17 +33,16 @@
         ["{workdir}/gecko/tools/profiler/merge-profiles.py", "gecko/tools/profiler/"],
         ["{workdir}/scripts/profile-symbolicate.py", "scripts/"],
         ["{workdir}/gecko/tools/rb/fix_stack_using_bpsyms.py", "gecko/tools/rb/"]
     ],
     "env": {
         "VARIANT": "user",
         "MOZILLA_OFFICIAL": "1",
         "MOZ_TELEMETRY_REPORTING": "1",
-        "B2G_UPDATE_CHANNEL": "nightly",
         "GAIA_KEYBOARD_LAYOUTS": "en,pt-BR,es,de,fr,pl,zh-Hans-Pinyin,zh-Hant-Zhuyin,en-Dvorak"
     },
     "b2g_manifest": "aries.xml",
     "b2g_manifest_intree": true,
     "additional_source_tarballs": ["backup-aries.tar.xz"],
     "gecko_l10n_root": "https://hg.mozilla.org/l10n-central",
     "gaia": {
         "l10n": {
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gecko and Gaia -->
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <!-- Gonk-specific things and forks -->
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
   <project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5de6856fad82857028f9f059f50680a9bea5b75c"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5de6856fad82857028f9f059f50680a9bea5b75c"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5de6856fad82857028f9f059f50680a9bea5b75c"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gecko and Gaia -->
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <!-- Gonk-specific things and forks -->
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
   <project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "7847a3c1b28e039631509978518b36fd3c5f9585", 
+        "git_revision": "719d89803fd1809bb1132e564a6d0c255963d4b7", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "0e366e8ecec7ab9426eb8781206119b4f564db84", 
+    "revision": "3835ad6d95f33df085851a787e17cb30613c9a6c", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4-kk/sources.xml
+++ b/b2g/config/nexus-4-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="5de6856fad82857028f9f059f50680a9bea5b75c"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="7847a3c1b28e039631509978518b36fd3c5f9585"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="719d89803fd1809bb1132e564a6d0c255963d4b7"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
copy from browser/base/content/aboutDialog.js
copy to browser/base/content/aboutDialog-appUpdater.js
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog-appUpdater.js
@@ -1,78 +1,14 @@
-# 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/.
-
-// Services = object with smart getters for common XPCOM services
-Components.utils.import("resource://gre/modules/Services.jsm");
-
-const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
-
-function init(aEvent)
-{
-  if (aEvent.target != document)
-    return;
-
-  try {
-    var distroId = Services.prefs.getCharPref("distribution.id");
-    if (distroId) {
-      var distroVersion = Services.prefs.getCharPref("distribution.version");
-
-      var distroIdField = document.getElementById("distributionId");
-      distroIdField.value = distroId + " - " + distroVersion;
-      distroIdField.style.display = "block";
+/* 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/. */
 
-      try {
-        // This is in its own try catch due to bug 895473 and bug 900925.
-        var distroAbout = Services.prefs.getComplexValue("distribution.about",
-          Components.interfaces.nsISupportsString);
-        var distroField = document.getElementById("distribution");
-        distroField.value = distroAbout;
-        distroField.style.display = "block";
-      }
-      catch (ex) {
-        // Pref is unset
-        Components.utils.reportError(ex);
-      }
-    }
-  }
-  catch (e) {
-    // Pref is unset
-  }
+// Note: this file is included in aboutDialog.xul if MOZ_UPDATER is defined.
 
-  // Include the build ID and display warning if this is an "a#" (nightly or aurora) build
-  let version = Services.appinfo.version;
-  if (/a\d+$/.test(version)) {
-    let buildID = Services.appinfo.appBuildID;
-    let buildDate = buildID.slice(0,4) + "-" + buildID.slice(4,6) + "-" + buildID.slice(6,8);
-    document.getElementById("version").textContent += " (" + buildDate + ")";
-    document.getElementById("experimental").hidden = false;
-    document.getElementById("communityDesc").hidden = true;
-  }
-
-#ifdef MOZ_UPDATER
-  gAppUpdater = new appUpdater();
-
-  let defaults = Services.prefs.getDefaultBranch("");
-  let channelLabel = document.getElementById("currentChannel");
-  let currentChannelText = document.getElementById("currentChannelText");
-  channelLabel.value = UpdateUtils.UpdateChannel;
-  if (/^release($|\-)/.test(channelLabel.value))
-      currentChannelText.hidden = true;
-#endif
-
-#ifdef XP_MACOSX
-  // it may not be sized at this point, and we need its width to calculate its position
-  window.sizeToContent();
-  window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
-#endif
-}
-
-#ifdef MOZ_UPDATER
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
                                   "resource://gre/modules/UpdateUtils.jsm");
 
 var gAppUpdater;
@@ -614,9 +550,8 @@ appUpdater.prototype =
   QueryInterface: function(aIID) {
     if (!aIID.equals(Components.interfaces.nsIProgressEventSink) &&
         !aIID.equals(Components.interfaces.nsIRequestObserver) &&
         !aIID.equals(Components.interfaces.nsISupports))
       throw Components.results.NS_ERROR_NO_INTERFACE;
     return this;
   }
 };
-#endif
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -1,14 +1,15 @@
-# 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/.
+/* 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/. */
 
 // Services = object with smart getters for common XPCOM services
 Components.utils.import("resource://gre/modules/Services.jsm");
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
 
 const PREF_EM_HOTFIX_ID = "extensions.hotfix.id";
 
 function init(aEvent)
 {
   if (aEvent.target != document)
     return;
 
@@ -44,579 +45,25 @@ function init(aEvent)
   if (/a\d+$/.test(version)) {
     let buildID = Services.appinfo.appBuildID;
     let buildDate = buildID.slice(0,4) + "-" + buildID.slice(4,6) + "-" + buildID.slice(6,8);
     document.getElementById("version").textContent += " (" + buildDate + ")";
     document.getElementById("experimental").hidden = false;
     document.getElementById("communityDesc").hidden = true;
   }
 
-#ifdef MOZ_UPDATER
-  gAppUpdater = new appUpdater();
-
-  let defaults = Services.prefs.getDefaultBranch("");
-  let channelLabel = document.getElementById("currentChannel");
-  let currentChannelText = document.getElementById("currentChannelText");
-  channelLabel.value = UpdateUtils.UpdateChannel;
-  if (/^release($|\-)/.test(channelLabel.value))
-      currentChannelText.hidden = true;
-#endif
-
-#ifdef XP_MACOSX
-  // it may not be sized at this point, and we need its width to calculate its position
-  window.sizeToContent();
-  window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
-#endif
-}
-
-#ifdef MOZ_UPDATER
-Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
-Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
-Components.utils.import("resource://gre/modules/AddonManager.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils",
-                                  "resource://gre/modules/UpdateUtils.jsm");
-
-var gAppUpdater;
-
-function onUnload(aEvent) {
-  if (gAppUpdater.isChecking)
-    gAppUpdater.checker.stopChecking(Components.interfaces.nsIUpdateChecker.CURRENT_CHECK);
-  // Safe to call even when there isn't a download in progress.
-  gAppUpdater.removeDownloadListener();
-  gAppUpdater = null;
-}
-
-
-function appUpdater()
-{
-  this.updateDeck = document.getElementById("updateDeck");
-
-  // Hide the update deck when there is already an update window open to avoid
-  // syncing issues between them.
-  if (Services.wm.getMostRecentWindow("Update:Wizard")) {
-    this.updateDeck.hidden = true;
-    return;
-  }
+  if (AppConstants.MOZ_UPDATER) {
+    gAppUpdater = new appUpdater();
 
-  XPCOMUtils.defineLazyServiceGetter(this, "aus",
-                                     "@mozilla.org/updates/update-service;1",
-                                     "nsIApplicationUpdateService");
-  XPCOMUtils.defineLazyServiceGetter(this, "checker",
-                                     "@mozilla.org/updates/update-checker;1",
-                                     "nsIUpdateChecker");
-  XPCOMUtils.defineLazyServiceGetter(this, "um",
-                                     "@mozilla.org/updates/update-manager;1",
-                                     "nsIUpdateManager");
-
-  this.bundle = Services.strings.
-                createBundle("chrome://browser/locale/browser.properties");
-
-  let manualURL = Services.urlFormatter.formatURLPref("app.update.url.manual");
-  let manualLink = document.getElementById("manualLink");
-  manualLink.value = manualURL;
-  manualLink.href = manualURL;
-  document.getElementById("failedLink").href = manualURL;
-
-  if (this.updateDisabledAndLocked) {
-    this.selectPanel("adminDisabled");
-    return;
-  }
-
-  if (this.isPending || this.isApplied) {
-    this.selectPanel("apply");
-    return;
-  }
-
-  if (this.aus.isOtherInstanceHandlingUpdates) {
-    this.selectPanel("otherInstanceHandlingUpdates");
-    return;
-  }
-
-  if (this.isDownloading) {
-    this.startDownload();
-    // selectPanel("downloading") is called from setupDownloadingUI().
-    return;
-  }
-
-  // Honor the "Never check for updates" option by not only disabling background
-  // update checks, but also in the About dialog, by presenting a
-  // "Check for updates" button.
-  // If updates are found, the user is then asked if he wants to "Update to <version>".
-  if (!this.updateEnabled) {
-    this.selectPanel("checkForUpdates");
-    return;
+    let defaults = Services.prefs.getDefaultBranch("");
+    let channelLabel = document.getElementById("currentChannel");
+    let currentChannelText = document.getElementById("currentChannelText");
+    channelLabel.value = UpdateUtils.UpdateChannel;
+    if (/^release($|\-)/.test(channelLabel.value))
+        currentChannelText.hidden = true;
   }
 
-  // That leaves the options
-  // "Check for updates, but let me choose whether to install them", and
-  // "Automatically install updates".
-  // In both cases, we check for updates without asking.
-  // In the "let me choose" case, we ask before downloading though, in onCheckComplete.
-  this.checkForUpdates();
+  if (AppConstants.platform == "macosx") {
+    // it may not be sized at this point, and we need its width to calculate its position
+    window.sizeToContent();
+    window.moveTo((screen.availWidth / 2) - (window.outerWidth / 2), screen.availHeight / 5);
+  }
 }
-
-appUpdater.prototype =
-{
-  // true when there is an update check in progress.
-  isChecking: false,
-
-  // true when there is an update already staged / ready to be applied.
-  get isPending() {
-    if (this.update) {
-      return this.update.state == "pending" ||
-             this.update.state == "pending-service";
-    }
-    return this.um.activeUpdate &&
-           (this.um.activeUpdate.state == "pending" ||
-            this.um.activeUpdate.state == "pending-service");
-  },
-
-  // true when there is an update already installed in the background.
-  get isApplied() {
-    if (this.update)
-      return this.update.state == "applied" ||
-             this.update.state == "applied-service";
-    return this.um.activeUpdate &&
-           (this.um.activeUpdate.state == "applied" ||
-            this.um.activeUpdate.state == "applied-service");
-  },
-
-  // true when there is an update download in progress.
-  get isDownloading() {
-    if (this.update)
-      return this.update.state == "downloading";
-    return this.um.activeUpdate &&
-           this.um.activeUpdate.state == "downloading";
-  },
-
-  // true when updating is disabled by an administrator.
-  get updateDisabledAndLocked() {
-    return !this.updateEnabled &&
-           Services.prefs.prefIsLocked("app.update.enabled");
-  },
-
-  // true when updating is enabled.
-  get updateEnabled() {
-    try {
-      return Services.prefs.getBoolPref("app.update.enabled");
-    }
-    catch (e) { }
-    return true; // Firefox default is true
-  },
-
-  // true when updating in background is enabled.
-  get backgroundUpdateEnabled() {
-    return this.updateEnabled &&
-           gAppUpdater.aus.canStageUpdates;
-  },
-
-  // true when updating is automatic.
-  get updateAuto() {
-    try {
-      return Services.prefs.getBoolPref("app.update.auto");
-    }
-    catch (e) { }
-    return true; // Firefox default is true
-  },
-
-  /**
-   * Sets the panel of the updateDeck.
-   *
-   * @param  aChildID
-   *         The id of the deck's child to select, e.g. "apply".
-   */
-  selectPanel: function(aChildID) {
-    let panel = document.getElementById(aChildID);
-
-    let button = panel.querySelector("button");
-    if (button) {
-      if (aChildID == "downloadAndInstall") {
-        let updateVersion = gAppUpdater.update.displayVersion;
-        button.label = this.bundle.formatStringFromName("update.downloadAndInstallButton.label", [updateVersion], 1);
-        button.accessKey = this.bundle.GetStringFromName("update.downloadAndInstallButton.accesskey");
-      }
-      this.updateDeck.selectedPanel = panel;
-      if (!document.commandDispatcher.focusedElement || // don't steal the focus
-          document.commandDispatcher.focusedElement.localName == "button") // except from the other buttons
-        button.focus();
-
-    } else {
-      this.updateDeck.selectedPanel = panel;
-    }
-  },
-
-  /**
-   * Check for updates
-   */
-  checkForUpdates: function() {
-    this.selectPanel("checkingForUpdates");
-    this.isChecking = true;
-    this.checker.checkForUpdates(this.updateCheckListener, true);
-    // after checking, onCheckComplete() is called
-  },
-
-  /**
-   * Check for addon compat, or start the download right away
-   */
-  doUpdate: function() {
-    // skip the compatibility check if the update doesn't provide appVersion,
-    // or the appVersion is unchanged, e.g. nightly update
-    if (!this.update.appVersion ||
-        Services.vc.compare(gAppUpdater.update.appVersion,
-                            Services.appinfo.version) == 0) {
-      this.startDownload();
-    } else {
-      this.checkAddonCompatibility();
-    }
-  },
-
-  /**
-   * Handles oncommand for the "Restart to Update" button
-   * which is presented after the download has been downloaded.
-   */
-  buttonRestartAfterDownload: function() {
-    if (!this.isPending && !this.isApplied)
-      return;
-
-      // Notify all windows that an application quit has been requested.
-      let cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"].
-                       createInstance(Components.interfaces.nsISupportsPRBool);
-      Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
-
-      // Something aborted the quit process.
-      if (cancelQuit.data)
-        return;
-
-      let appStartup = Components.classes["@mozilla.org/toolkit/app-startup;1"].
-                       getService(Components.interfaces.nsIAppStartup);
-
-      // If already in safe mode restart in safe mode (bug 327119)
-      if (Services.appinfo.inSafeMode) {
-        appStartup.restartInSafeMode(Components.interfaces.nsIAppStartup.eAttemptQuit);
-        return;
-      }
-
-      appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit |
-                      Components.interfaces.nsIAppStartup.eRestart);
-    },
-
-  /**
-   * Handles oncommand for the "Apply Update…" button
-   * which is presented if we need to show the billboard or license.
-   */
-  buttonApplyBillboard: function() {
-    const URI_UPDATE_PROMPT_DIALOG = "chrome://mozapps/content/update/updates.xul";
-    var ary = null;
-    ary = Components.classes["@mozilla.org/supports-array;1"].
-          createInstance(Components.interfaces.nsISupportsArray);
-    ary.AppendElement(this.update);
-    var openFeatures = "chrome,centerscreen,dialog=no,resizable=no,titlebar,toolbar=no";
-    Services.ww.openWindow(null, URI_UPDATE_PROMPT_DIALOG, "", openFeatures, ary);
-    window.close(); // close the "About" window; updates.xul takes over.
-  },
-
-  /**
-   * Implements nsIUpdateCheckListener. The methods implemented by
-   * nsIUpdateCheckListener are in a different scope from nsIIncrementalDownload
-   * to make it clear which are used by each interface.
-   */
-  updateCheckListener: {
-    /**
-     * See nsIUpdateService.idl
-     */
-    onCheckComplete: function(aRequest, aUpdates, aUpdateCount) {
-      gAppUpdater.isChecking = false;
-      gAppUpdater.update = gAppUpdater.aus.
-                           selectUpdate(aUpdates, aUpdates.length);
-      if (!gAppUpdater.update) {
-        gAppUpdater.selectPanel("noUpdatesFound");
-        return;
-      }
-
-      if (gAppUpdater.update.unsupported) {
-        if (gAppUpdater.update.detailsURL) {
-          let unsupportedLink = document.getElementById("unsupportedLink");
-          unsupportedLink.href = gAppUpdater.update.detailsURL;
-        }
-        gAppUpdater.selectPanel("unsupportedSystem");
-        return;
-      }
-
-      if (!gAppUpdater.aus.canApplyUpdates) {
-        gAppUpdater.selectPanel("manualUpdate");
-        return;
-      }
-
-      // Firefox no longer displays a license for updates and the licenseURL
-      // check is just in case a distibution does.
-      if (gAppUpdater.update.billboardURL || gAppUpdater.update.licenseURL) {
-        gAppUpdater.selectPanel("applyBillboard");
-        return;
-      }
-
-      if (gAppUpdater.updateAuto) // automatically download and install
-        gAppUpdater.doUpdate();
-      else // ask
-        gAppUpdater.selectPanel("downloadAndInstall");
-    },
-
-    /**
-     * See nsIUpdateService.idl
-     */
-    onError: function(aRequest, aUpdate) {
-      // Errors in the update check are treated as no updates found. If the
-      // update check fails repeatedly without a success the user will be
-      // notified with the normal app update user interface so this is safe.
-      gAppUpdater.isChecking = false;
-      gAppUpdater.selectPanel("noUpdatesFound");
-    },
-
-    /**
-     * See nsISupports.idl
-     */
-    QueryInterface: function(aIID) {
-      if (!aIID.equals(Components.interfaces.nsIUpdateCheckListener) &&
-          !aIID.equals(Components.interfaces.nsISupports))
-        throw Components.results.NS_ERROR_NO_INTERFACE;
-      return this;
-    }
-  },
-
-  /**
-   * Checks the compatibility of add-ons for the application update.
-   */
-  checkAddonCompatibility: function() {
-    try {
-      var hotfixID = Services.prefs.getCharPref(PREF_EM_HOTFIX_ID);
-    }
-    catch (e) { }
-
-    var self = this;
-    AddonManager.getAllAddons(function(aAddons) {
-      self.addons = [];
-      self.addonsCheckedCount = 0;
-      aAddons.forEach(function(aAddon) {
-        // Protect against code that overrides the add-ons manager and doesn't
-        // implement the isCompatibleWith or the findUpdates method.
-        if (!("isCompatibleWith" in aAddon) || !("findUpdates" in aAddon)) {
-          let errMsg = "Add-on doesn't implement either the isCompatibleWith " +
-                       "or the findUpdates method!";
-          if (aAddon.id)
-            errMsg += " Add-on ID: " + aAddon.id;
-          Components.utils.reportError(errMsg);
-          return;
-        }
-
-        // If an add-on isn't appDisabled and isn't userDisabled then it is
-        // either active now or the user expects it to be active after the
-        // restart. If that is the case and the add-on is not installed by the
-        // application and is not compatible with the new application version
-        // then the user should be warned that the add-on will become
-        // incompatible. If an addon's type equals plugin it is skipped since
-        // checking plugins compatibility information isn't supported and
-        // getting the scope property of a plugin breaks in some environments
-        // (see bug 566787). The hotfix add-on is also ignored as it shouldn't
-        // block the user from upgrading.
-        try {
-          if (aAddon.type != "plugin" && aAddon.id != hotfixID &&
-              !aAddon.appDisabled && !aAddon.userDisabled &&
-              aAddon.scope != AddonManager.SCOPE_APPLICATION &&
-              aAddon.isCompatible &&
-              !aAddon.isCompatibleWith(self.update.appVersion,
-                                       self.update.platformVersion))
-            self.addons.push(aAddon);
-        }
-        catch (e) {
-          Components.utils.reportError(e);
-        }
-      });
-      self.addonsTotalCount = self.addons.length;
-      if (self.addonsTotalCount == 0) {
-        self.startDownload();
-        return;
-      }
-
-      self.checkAddonsForUpdates();
-    });
-  },
-
-  /**
-   * Checks if there are updates for add-ons that are incompatible with the
-   * application update.
-   */
-  checkAddonsForUpdates: function() {
-    this.addons.forEach(function(aAddon) {
-      aAddon.findUpdates(this, AddonManager.UPDATE_WHEN_NEW_APP_DETECTED,
-                         this.update.appVersion,
-                         this.update.platformVersion);
-    }, this);
-  },
-
-  /**
-   * See XPIProvider.jsm
-   */
-  onCompatibilityUpdateAvailable: function(aAddon) {
-    for (var i = 0; i < this.addons.length; ++i) {
-      if (this.addons[i].id == aAddon.id) {
-        this.addons.splice(i, 1);
-        break;
-      }
-    }
-  },
-
-  /**
-   * See XPIProvider.jsm
-   */
-  onUpdateAvailable: function(aAddon, aInstall) {
-    if (!Services.blocklist.isAddonBlocklisted(aAddon,
-                                               this.update.appVersion,
-                                               this.update.platformVersion)) {
-      // Compatibility or new version updates mean the same thing here.
-      this.onCompatibilityUpdateAvailable(aAddon);
-    }
-  },
-
-  /**
-   * See XPIProvider.jsm
-   */
-  onUpdateFinished: function(aAddon) {
-    ++this.addonsCheckedCount;
-
-    if (this.addonsCheckedCount < this.addonsTotalCount)
-      return;
-
-    if (this.addons.length == 0) {
-      // Compatibility updates or new version updates were found for all add-ons
-      this.startDownload();
-      return;
-    }
-
-    this.selectPanel("applyBillboard");
-  },
-
-  /**
-   * Starts the download of an update mar.
-   */
-  startDownload: function() {
-    if (!this.update)
-      this.update = this.um.activeUpdate;
-    this.update.QueryInterface(Components.interfaces.nsIWritablePropertyBag);
-    this.update.setProperty("foregroundDownload", "true");
-
-    this.aus.pauseDownload();
-    let state = this.aus.downloadUpdate(this.update, false);
-    if (state == "failed") {
-      this.selectPanel("downloadFailed");
-      return;
-    }
-
-    this.setupDownloadingUI();
-  },
-
-  /**
-   * Switches to the UI responsible for tracking the download.
-   */
-  setupDownloadingUI: function() {
-    this.downloadStatus = document.getElementById("downloadStatus");
-    this.downloadStatus.value =
-      DownloadUtils.getTransferTotal(0, this.update.selectedPatch.size);
-    this.selectPanel("downloading");
-    this.aus.addDownloadListener(this);
-  },
-
-  removeDownloadListener: function() {
-    if (this.aus) {
-      this.aus.removeDownloadListener(this);
-    }
-  },
-
-  /**
-   * See nsIRequestObserver.idl
-   */
-  onStartRequest: function(aRequest, aContext) {
-  },
-
-  /**
-   * See nsIRequestObserver.idl
-   */
-  onStopRequest: function(aRequest, aContext, aStatusCode) {
-    switch (aStatusCode) {
-    case Components.results.NS_ERROR_UNEXPECTED:
-      if (this.update.selectedPatch.state == "download-failed" &&
-          (this.update.isCompleteUpdate || this.update.patchCount != 2)) {
-        // Verification error of complete patch, informational text is held in
-        // the update object.
-        this.removeDownloadListener();
-        this.selectPanel("downloadFailed");
-        break;
-      }
-      // Verification failed for a partial patch, complete patch is now
-      // downloading so return early and do NOT remove the download listener!
-      break;
-    case Components.results.NS_BINDING_ABORTED:
-      // Do not remove UI listener since the user may resume downloading again.
-      break;
-    case Components.results.NS_OK:
-      this.removeDownloadListener();
-      if (this.backgroundUpdateEnabled) {
-        this.selectPanel("applying");
-        let update = this.um.activeUpdate;
-        let self = this;
-        Services.obs.addObserver(function (aSubject, aTopic, aData) {
-          // Update the UI when the background updater is finished
-          let status = aData;
-          if (status == "applied" || status == "applied-service" ||
-              status == "pending" || status == "pending-service") {
-            // If the update is successfully applied, or if the updater has
-            // fallen back to non-staged updates, show the "Restart to Update"
-            // button.
-            self.selectPanel("apply");
-          } else if (status == "failed") {
-            // Background update has failed, let's show the UI responsible for
-            // prompting the user to update manually.
-            self.selectPanel("downloadFailed");
-          } else if (status == "downloading") {
-            // We've fallen back to downloading the full update because the
-            // partial update failed to get staged in the background.
-            // Therefore we need to keep our observer.
-            self.setupDownloadingUI();
-            return;
-          }
-          Services.obs.removeObserver(arguments.callee, "update-staged");
-        }, "update-staged", false);
-      } else {
-        this.selectPanel("apply");
-      }
-      break;
-    default:
-      this.removeDownloadListener();
-      this.selectPanel("downloadFailed");
-      break;
-    }
-  },
-
-  /**
-   * See nsIProgressEventSink.idl
-   */
-  onStatus: function(aRequest, aContext, aStatus, aStatusArg) {
-  },
-
-  /**
-   * See nsIProgressEventSink.idl
-   */
-  onProgress: function(aRequest, aContext, aProgress, aProgressMax) {
-    this.downloadStatus.value =
-      DownloadUtils.getTransferTotal(aProgress, aProgressMax);
-  },
-
-  /**
-   * See nsISupports.idl
-   */
-  QueryInterface: function(aIID) {
-    if (!aIID.equals(Components.interfaces.nsIProgressEventSink) &&
-        !aIID.equals(Components.interfaces.nsIRequestObserver) &&
-        !aIID.equals(Components.interfaces.nsISupports))
-      throw Components.results.NS_ERROR_NO_INTERFACE;
-    return this;
-  }
-};
-#endif
--- a/browser/base/content/aboutDialog.xul
+++ b/browser/base/content/aboutDialog.xul
@@ -32,17 +32,19 @@
 #else
         title="&aboutDialog.title;"
 #endif
         role="dialog"
         aria-describedby="version distribution distributionId communityDesc contributeDesc trademark"
         >
 
   <script type="application/javascript" src="chrome://browser/content/aboutDialog.js"/>
-
+#ifdef MOZ_UPDATER
+  <script type="application/javascript" src="chrome://browser/content/aboutDialog-appUpdater.js"/>
+#endif
   <vbox id="aboutDialogContainer">
     <hbox id="clientBox">
       <vbox id="leftBox" flex="1"/>
       <vbox id="rightBox" flex="1">
 #expand <label id="version">__MOZ_APP_VERSION_DISPLAY__</label>
         <label id="distribution" class="text-blurb"/>
         <label id="distributionId" class="text-blurb"/>
 
--- a/browser/base/content/browser-customization.js
+++ b/browser/base/content/browser-customization.js
@@ -58,19 +58,18 @@ var CustomizationHandler = {
   _customizationEnding: function(aDetails) {
     // Update global UI elements that may have been added or removed
     if (aDetails.changed) {
       gURLBar = document.getElementById("urlbar");
 
       gHomeButton.updateTooltip();
       XULBrowserWindow.init();
 
-#ifndef XP_MACOSX
-      updateEditUIVisibility();
-#endif
+      if (AppConstants.platform != "macosx")
+        updateEditUIVisibility();
 
       // Hacky: update the PopupNotifications' object's reference to the iconBox,
       // if it already exists, since it may have changed if the URL bar was
       // added/removed.
       if (!window.__lookupGetter__("PopupNotifications")) {
         PopupNotifications.iconBox =
           document.getElementById("notification-popup-box");
       }
--- a/browser/base/content/browser-devedition.js
+++ b/browser/base/content/browser-devedition.js
@@ -115,16 +115,15 @@ var DevEdition = {
     Services.obs.removeObserver(this, "lightweight-theme-styling-update", false);
     if (this.styleSheet) {
       this.styleSheet.removeEventListener("load", this);
     }
     this.styleSheet = null;
   }
 };
 
-#ifndef RELEASE_BUILD
 // If the DevEdition theme is going to be applied in gBrowserInit.onLoad,
 // then preload it now.  This prevents a flash of unstyled content where the
 // normal theme is applied while the DevEdition stylesheet is loading.
-if (this != Services.appShell.hiddenDOMWindow && DevEdition.isThemeCurrentlyApplied) {
+if (!AppConstants.RELEASE_BUILD &&
+    this != Services.appShell.hiddenDOMWindow && DevEdition.isThemeCurrentlyApplied) {
   DevEdition.createStyleSheet();
 }
-#endif
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -44,21 +44,21 @@ var FullScreen = {
     // fullscreen menuitem, and menubars.
     let fullscreenCommand = document.getElementById("View:FullScreen");
     if (enterFS) {
       fullscreenCommand.setAttribute("checked", enterFS);
     } else {
       fullscreenCommand.removeAttribute("checked");
     }
 
-#ifdef XP_MACOSX
-    // Make sure the menu items are adjusted.
-    document.getElementById("enterFullScreenItem").hidden = enterFS;
-    document.getElementById("exitFullScreenItem").hidden = !enterFS;
-#endif
+    if (AppConstants.platform == "macosx") {
+      // Make sure the menu items are adjusted.
+      document.getElementById("enterFullScreenItem").hidden = enterFS;
+      document.getElementById("exitFullScreenItem").hidden = !enterFS;
+    }
 
     if (!this._fullScrToggler) {
       this._fullScrToggler = document.getElementById("fullscr-toggler");
       this._fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
       this._fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
     }
 
     if (enterFS) {
@@ -628,15 +628,11 @@ var FullScreen = {
     fullscreenctls.hidden = !aEnterFS;
   }
 };
 XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() {
   // We'll only use OS X Lion full screen if we're
   // * on OS X
   // * on Lion or higher (Darwin 11+)
   // * have fullscreenbutton="true"
-#ifdef XP_MACOSX
-  return parseFloat(Services.sysinfo.getProperty("version")) >= 11 &&
+  return AppConstants.isPlatformAndVersionAtLeast("macosx", 11) &&
          document.documentElement.getAttribute("fullscreenbutton") == "true";
-#else
-  return false;
-#endif
 });
--- a/browser/base/content/browser-gestureSupport.js
+++ b/browser/base/content/browser-gestureSupport.js
@@ -76,21 +76,20 @@ var gGestureSupport = {
         this._doEnd(aEvent);
         break;
       case "MozSwipeGesture":
         aEvent.preventDefault();
         this.onSwipe(aEvent);
         break;
       case "MozMagnifyGestureStart":
         aEvent.preventDefault();
-#ifdef XP_WIN
-        this._setupGesture(aEvent, "pinch", def(25, 0), "out", "in");
-#else
-        this._setupGesture(aEvent, "pinch", def(150, 1), "out", "in");
-#endif
+        let pinchPref = AppConstants.platform == "win"
+                        ? def(25, 0)
+                        : def(150, 1);
+        this._setupGesture(aEvent, "pinch", pinchPref, "out", "in");
         break;
       case "MozRotateGestureStart":
         aEvent.preventDefault();
         this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
         break;
       case "MozMagnifyGestureUpdate":
       case "MozRotateGestureUpdate":
         aEvent.preventDefault();
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -779,21 +779,23 @@ var BookmarksEventHandler = {
    * If the click came through a menu, close the menu.
    * @param aEvent
    *        DOMEvent for the click
    * @param aView
    *        The places view which aEvent should be associated with.
    */
   onClick: function BEH_onClick(aEvent, aView) {
     // Only handle middle-click or left-click with modifiers.
-#ifdef XP_MACOSX
-    var modifKey = aEvent.metaKey || aEvent.shiftKey;
-#else
-    var modifKey = aEvent.ctrlKey || aEvent.shiftKey;
-#endif
+    let modifKey;
+    if (AppConstants.platform == "macosx") {
+      modifKey = aEvent.metaKey || aEvent.shiftKey;
+    } else {
+      modifKey = aEvent.ctrlKey || aEvent.shiftKey;
+    }
+
     if (aEvent.button == 2 || (aEvent.button == 0 && !modifKey))
       return;
 
     var target = aEvent.originalTarget;
     // If this event bubbled up from a menu or menuitem, close the menus.
     // Do this before opening tabs, to avoid hiding the open tabs confirm-dialog.
     if (target.localName == "menu" || target.localName == "menuitem") {
       for (node = target.parentNode; node; node = node.parentNode) {
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -80,24 +80,16 @@ var gPluginHandler = {
         }
         break;
       default:
         Cu.reportError("gPluginHandler did not expect to handle message " + msg.name);
         break;
     }
   },
 
-#ifdef MOZ_CRASHREPORTER
-  get CrashSubmit() {
-    delete this.CrashSubmit;
-    Cu.import("resource://gre/modules/CrashSubmit.jsm", this);
-    return this.CrashSubmit;
-  },
-#endif
-
   // Callback for user clicking on a disabled plugin
   managePlugins: function () {
     BrowserOpenAddonsMgr("addons://list/plugin");
   },
 
   // Callback for user clicking on the link in a click-to-play plugin
   // (where the plugin has an update)
   openPluginUpdatePage: function () {
--- a/browser/base/content/browser-safebrowsing.js
+++ b/browser/base/content/browser-safebrowsing.js
@@ -1,13 +1,15 @@
 /* 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/. */
 
-#ifdef MOZ_SAFE_BROWSING
+// Note: this file is not shipped (through jar.mn)
+// if MOZ_SAFE_BROWSING is not defined.
+
 var gSafeBrowsing = {
 
   setReportPhishingMenu: function() {
     // A phishing page will have a specific about:blocked content documentURI
     var uri = gBrowser.currentURI;
     var isPhishingPage = uri && uri.spec.startsWith("about:blocked?e=phishingBlocked");
 
     // Show/hide the appropriate menu item.
@@ -34,9 +36,8 @@ var gSafeBrowsing = {
    * Used to report a phishing page or a false positive
    * @param name String One of "Phish", "Error", "Malware" or "MalwareError"
    * @return String the report phishing URL.
    */
   getReportURL: function(name) {
     return SafeBrowsing.getReportURL(name, gBrowser.currentURI);
   }
 }
-#endif
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -1,20 +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/. */
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-#ifdef MOZ_SERVICES_CLOUDSYNC
-XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
-                                  "resource://gre/modules/CloudSync.jsm");
-#else
-var CloudSync = null;
-#endif
+if (AppConstants.MOZ_SERVICES_CLOUDSYNC) {
+  XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
+                                    "resource://gre/modules/CloudSync.jsm");
+}
 
 XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
                                   "resource://gre/modules/FxAccounts.jsm");
 
 // gSyncUI handles updating the tools menu and displaying notifications.
 var gSyncUI = {
   _obs: ["weave:service:sync:start",
          "weave:service:sync:finish",
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-tabsintitlebar-stub.js
@@ -0,0 +1,17 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+// This file is used as a stub object for platforms which
+// don't have CAN_DRAW_IN_TITLEBAR defined.
+
+var TabsInTitlebar = {
+  init: function () {},
+  uninit: function () {},
+  allowedBy: function (condition, allow) {},
+  updateAppearance: function updateAppearance(aForce) {},
+  get enabled() {
+    return document.documentElement.getAttribute("tabsintitlebar") == "true";
+  },
+};
new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-tabsintitlebar.js
@@ -0,0 +1,302 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
+
+// Note: the file browser-tabsintitlebar-stub.js is used instead of
+// this one on platforms which don't have CAN_DRAW_IN_TITLEBAR defined.
+
+var TabsInTitlebar = {
+  init: function () {
+    this._readPref();
+    Services.prefs.addObserver(this._prefName, this, false);
+
+    // We need to update the appearance of the titlebar when the menu changes
+    // from the active to the inactive state. We can't, however, rely on
+    // DOMMenuBarInactive, because the menu fires this event and then removes
+    // the inactive attribute after an event-loop spin.
+    //
+    // Because updating the appearance involves sampling the heights and margins
+    // of various elements, it's important that the layout be more or less
+    // settled before updating the titlebar. So instead of listening to
+    // DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
+    // watch the "invalid" attribute directly.
+    let menu = document.getElementById("toolbar-menubar");
+    this._menuObserver = new MutationObserver(this._onMenuMutate);
+    this._menuObserver.observe(menu, {attributes: true});
+
+    this.onAreaReset = function(aArea) {
+      if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
+        this._update(true);
+    };
+    this.onWidgetAdded = this.onWidgetRemoved = function(aWidgetId, aArea) {
+      if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
+        this._update(true);
+    };
+    CustomizableUI.addListener(this);
+
+    this._initialized = true;
+  },
+
+  allowedBy: function (condition, allow) {
+    if (allow) {
+      if (condition in this._disallowed) {
+        delete this._disallowed[condition];
+        this._update(true);
+      }
+    } else {
+      if (!(condition in this._disallowed)) {
+        this._disallowed[condition] = null;
+        this._update(true);
+      }
+    }
+  },
+
+  updateAppearance: function updateAppearance(aForce) {
+    this._update(aForce);
+  },
+
+  get enabled() {
+    return document.documentElement.getAttribute("tabsintitlebar") == "true";
+  },
+
+  observe: function (subject, topic, data) {
+    if (topic == "nsPref:changed")
+      this._readPref();
+  },
+
+  _onMenuMutate: function (aMutations) {
+    for (let mutation of aMutations) {
+      if (mutation.attributeName == "inactive" ||
+          mutation.attributeName == "autohide") {
+        TabsInTitlebar._update(true);
+        return;
+      }
+    }
+  },
+
+  _initialized: false,
+  _disallowed: {},
+  _prefName: "browser.tabs.drawInTitlebar",
+  _lastSizeMode: null,
+
+  _readPref: function () {
+    this.allowedBy("pref",
+                   Services.prefs.getBoolPref(this._prefName));
+  },
+
+  _update: function (aForce=false) {
+    let $ = id => document.getElementById(id);
+    let rect = ele => ele.getBoundingClientRect();
+    let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
+
+    if (!this._initialized || window.fullScreen)
+      return;
+
+    let allowed = true;
+
+    if (!aForce) {
+      // _update is called on resize events, because the window is not ready
+      // after sizemode events. However, we only care about the event when the
+      // sizemode is different from the last time we updated the appearance of
+      // the tabs in the titlebar.
+      let sizemode = document.documentElement.getAttribute("sizemode");
+      if (this._lastSizeMode == sizemode) {
+        return;
+      }
+      let oldSizeMode = this._lastSizeMode;
+      this._lastSizeMode = sizemode;
+      // Don't update right now if we are leaving fullscreen, since the UI is
+      // still changing in the consequent "fullscreen" event. Code there will
+      // call this function again when everything is ready.
+      // See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
+      if (oldSizeMode == "fullscreen") {
+        return;
+      }
+    }
+
+    for (let something in this._disallowed) {
+      allowed = false;
+      break;
+    }
+
+    let titlebar = $("titlebar");
+    let titlebarContent = $("titlebar-content");
+    let menubar = $("toolbar-menubar");
+
+    if (allowed) {
+      // We set the tabsintitlebar attribute first so that our CSS for
+      // tabsintitlebar manifests before we do our measurements.
+      document.documentElement.setAttribute("tabsintitlebar", "true");
+      updateTitlebarDisplay();
+
+      // Try to avoid reflows in this code by calculating dimensions first and
+      // then later set the properties affecting layout together in a batch.
+
+      // Get the full height of the tabs toolbar:
+      let tabsToolbar = $("TabsToolbar");
+      let tabsStyles = window.getComputedStyle(tabsToolbar);
+      let fullTabsHeight = rect(tabsToolbar).height + verticalMargins(tabsStyles);
+      // Buttons first:
+      let captionButtonsBoxWidth = rect($("titlebar-buttonbox-container")).width;
+
+      let secondaryButtonsWidth, menuHeight, fullMenuHeight, menuStyles;
+      if (AppConstants.platform == "macosx") {
+        secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
+        // No need to look up the menubar stuff on OS X:
+        menuHeight = 0;
+        fullMenuHeight = 0;
+      } else {
+        // Otherwise, get the height and margins separately for the menubar
+        menuHeight = rect(menubar).height;
+        menuStyles = window.getComputedStyle(menubar);
+        fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
+      }
+
+      // And get the height of what's in the titlebar:
+      let titlebarContentHeight = rect(titlebarContent).height;
+
+      // Begin setting CSS properties which will cause a reflow
+
+      // If the menubar is around (menuHeight is non-zero), try to adjust
+      // its full height (i.e. including margins) to match the titlebar,
+      // by changing the menubar's bottom padding
+      if (menuHeight) {
+        // Calculate the difference between the titlebar's height and that of the menubar
+        let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
+        let paddingBottom;
+        // The titlebar is bigger:
+        if (menuTitlebarDelta > 0) {
+          fullMenuHeight += menuTitlebarDelta;
+          // If there is already padding on the menubar, we need to add that
+          // to the difference so the total padding is correct:
+          if ((paddingBottom = menuStyles.paddingBottom)) {
+            menuTitlebarDelta += parseFloat(paddingBottom);
+          }
+          menubar.style.paddingBottom = menuTitlebarDelta + "px";
+        // The menubar is bigger, but has bottom padding we can remove:
+        } else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
+          let existingPadding = parseFloat(paddingBottom);
+          // menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
+          let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
+          menubar.style.paddingBottom = desiredPadding + "px";
+          // We've changed the menu height now:
+          fullMenuHeight += desiredPadding - existingPadding;
+        }
+      }
+
+      // Next, we calculate how much we need to stretch the titlebar down to
+      // go all the way to the bottom of the tab strip, if necessary.
+      let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;
+
+      if (tabAndMenuHeight > titlebarContentHeight) {
+        // We need to increase the titlebar content's outer height (ie including margins)
+        // to match the tab and menu height:
+        let extraMargin = tabAndMenuHeight - titlebarContentHeight;
+        if (AppConstants.platform != "macosx") {
+          titlebarContent.style.marginBottom = extraMargin + "px";
+        }
+
+        titlebarContentHeight += extraMargin;
+      } else {
+        titlebarContent.style.removeProperty("margin-bottom");
+      }
+
+      // Then we bring up the titlebar by the same amount, but we add any negative margin:
+      titlebar.style.marginBottom = "-" + titlebarContentHeight + "px";
+
+
+      // Finally, size the placeholders:
+      if (AppConstants.platform == "macosx") {
+        this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
+      }
+      this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
+
+      if (!this._draghandles) {
+        this._draghandles = {};
+        let tmp = {};
+        Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
+
+        let mouseDownCheck = function () {
+          return !this._dragBindingAlive && TabsInTitlebar.enabled;
+        };
+
+        this._draghandles.tabsToolbar = new tmp.WindowDraggingElement(tabsToolbar);
+        this._draghandles.tabsToolbar.mouseDownCheck = mouseDownCheck;
+
+        this._draghandles.navToolbox = new tmp.WindowDraggingElement(gNavToolbox);
+        this._draghandles.navToolbox.mouseDownCheck = mouseDownCheck;
+      }
+    } else {
+      document.documentElement.removeAttribute("tabsintitlebar");
+      updateTitlebarDisplay();
+
+      if (AppConstants.platform == "macosx") {
+        let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
+        this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
+      }
+
+      // Reset the margins and padding that might have been modified:
+      titlebarContent.style.marginTop = "";
+      titlebarContent.style.marginBottom = "";
+      titlebar.style.marginBottom = "";
+      menubar.style.paddingBottom = "";
+    }
+
+    ToolbarIconColor.inferFromText();
+    if (CustomizationHandler.isCustomizing()) {
+      gCustomizeMode.updateLWTStyling();
+    }
+  },
+
+  _sizePlaceholder: function (type, width) {
+    Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
+                  function (node) { node.width = width; });
+  },
+
+  uninit: function () {
+    this._initialized = false;
+    Services.prefs.removeObserver(this._prefName, this);
+    this._menuObserver.disconnect();
+    CustomizableUI.removeListener(this);
+  }
+};
+
+function updateTitlebarDisplay() {
+  if (AppConstants.platform == "macosx") {
+    // OS X and the other platforms differ enough to necessitate this kind of
+    // special-casing. Like the other platforms where we CAN_DRAW_IN_TITLEBAR,
+    // we draw in the OS X titlebar when putting the tabs up there. However, OS X
+    // also draws in the titlebar when a lightweight theme is applied, regardless
+    // of whether or not the tabs are drawn in the titlebar.
+    if (TabsInTitlebar.enabled) {
+      document.documentElement.setAttribute("chromemargin-nonlwtheme", "0,-1,-1,-1");
+      document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
+      document.documentElement.removeAttribute("drawtitle");
+    } else {
+      // We set chromemargin-nonlwtheme to "" instead of removing it as a way of
+      // making sure that LightweightThemeConsumer doesn't take it upon itself to
+      // detect this value again if and when we do a lwtheme state change.
+      document.documentElement.setAttribute("chromemargin-nonlwtheme", "");
+      let isCustomizing = document.documentElement.hasAttribute("customizing");
+      let hasLWTheme = document.documentElement.hasAttribute("lwtheme");
+      let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
+      if ((!hasLWTheme || isCustomizing) && !isPrivate) {
+        document.documentElement.removeAttribute("chromemargin");
+      }
+      document.documentElement.setAttribute("drawtitle", "true");
+    }
+  } else { // not OS X
+    if (TabsInTitlebar.enabled)
+      document.documentElement.setAttribute("chromemargin", "0,2,2,2");
+    else
+      document.documentElement.removeAttribute("chromemargin");
+  }
+}
+
+function onTitlebarMaxClick() {
+  if (window.windowState == window.STATE_MAXIMIZED)
+    window.restore();
+  else
+    window.maximize();
+}
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -99,19 +99,19 @@ var gMultiProcessBrowser =
   window.QueryInterface(Ci.nsIInterfaceRequestor)
         .getInterface(Ci.nsIWebNavigation)
         .QueryInterface(Ci.nsILoadContext)
         .useRemoteTabs;
 var gAppInfo = Cc["@mozilla.org/xre/app-info;1"]
                   .getService(Ci.nsIXULAppInfo)
                   .QueryInterface(Ci.nsIXULRuntime);
 
-#ifndef XP_MACOSX
-var gEditUIVisible = true;
-#endif
+if (AppConstants.platform != "macosx") {
+  var gEditUIVisible = true;
+}
 
 [
   ["gBrowser",            "content"],
   ["gNavToolbox",         "navigator-toolbox"],
   ["gURLBar",             "urlbar"],
   ["gNavigatorBundle",    "bundle_browser"]
 ].forEach(function (elementGlobal) {
   var [name, id] = elementGlobal;
@@ -197,20 +197,20 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource:///modules/Social.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
   "resource://gre/modules/PageThumbs.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "ProcessHangMonitor",
   "resource:///modules/ProcessHangMonitor.jsm");
 
-#ifdef MOZ_SAFE_BROWSING
-XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
-  "resource://gre/modules/SafeBrowsing.jsm");
-#endif
+if (AppConstants.MOZ_SAFE_BROWSING) {
+  XPCOMUtils.defineLazyModuleGetter(this, "SafeBrowsing",
+    "resource://gre/modules/SafeBrowsing.jsm");
+}
 
 XPCOMUtils.defineLazyModuleGetter(this, "gCustomizationTabPreloader",
   "resource:///modules/CustomizationTabPreloader.jsm", "CustomizationTabPreloader");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Translation",
@@ -225,20 +225,21 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
   "resource://gre/modules/FxAccounts.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "gWebRTCUI",
   "resource:///modules/webrtcUI.jsm", "webrtcUI");
 
 XPCOMUtils.defineLazyModuleGetter(this, "TabCrashHandler",
   "resource:///modules/ContentCrashHandlers.jsm");
-#ifdef MOZ_CRASHREPORTER
-XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
-  "resource:///modules/ContentCrashHandlers.jsm");
-#endif
+
+if (AppConstants.MOZ_CRASHREPORTER) {
+  XPCOMUtils.defineLazyModuleGetter(this, "PluginCrashReporter",
+    "resource:///modules/ContentCrashHandlers.jsm");
+}
 
 XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
   "resource:///modules/FormValidationHandler.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
   "resource:///modules/UITour.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "CastingApps",
@@ -261,17 +262,19 @@ var gInitialPages = [
   "about:newtab",
   "about:home",
   "about:privatebrowsing",
   "about:welcomeback",
   "about:sessionrestore"
 ];
 
 XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
-#ifdef XP_WIN
+  if (AppConstants.platform != "win")
+    return null;
+
   // Bug 666808 - AeroPeek support for e10s
   if (gMultiProcessBrowser)
     return null;
 
   const WINTASKBAR_CONTRACTID = "@mozilla.org/windows-taskbar;1";
   if (WINTASKBAR_CONTRACTID in Cc &&
       Cc[WINTASKBAR_CONTRACTID].getService(Ci.nsIWinTaskbar).available) {
     let AeroPeek = Cu.import("resource:///modules/WindowsPreviewPerTab.jsm", {}).AeroPeek;
@@ -279,25 +282,24 @@ XPCOMUtils.defineLazyGetter(this, "Win7F
       onOpenWindow: function () {
         AeroPeek.onOpenWindow(window);
       },
       onCloseWindow: function () {
         AeroPeek.onCloseWindow(window);
       }
     };
   }
-#endif
   return null;
 });
 
-#ifdef MOZ_CRASHREPORTER
-XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
-                                   "@mozilla.org/xre/app-info;1",
-                                   "nsICrashReporter");
-#endif
+if (AppConstants.MOZ_CRASHREPORTER) {
+  XPCOMUtils.defineLazyServiceGetter(this, "gCrashReporter",
+                                     "@mozilla.org/xre/app-info;1",
+                                     "nsICrashReporter");
+}
 
 XPCOMUtils.defineLazyGetter(this, "PageMenuParent", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuParent();
 });
 
 function* browserWindows() {
@@ -514,23 +516,24 @@ var gPopupBlockerObserver = {
     // Only show the notification again if we've not already shown it. Since
     // notifications are per-browser, we don't need to worry about re-adding
     // it.
     if (!gBrowser.selectedBrowser.blockedPopups.reported) {
       if (gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
         var brandBundle = document.getElementById("bundle_brand");
         var brandShortName = brandBundle.getString("brandShortName");
         var popupCount = gBrowser.selectedBrowser.blockedPopups.length;
-#ifdef XP_WIN
-        var popupButtonText = gNavigatorBundle.getString("popupWarningButton");
-        var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButton.accesskey");
-#else
-        var popupButtonText = gNavigatorBundle.getString("popupWarningButtonUnix");
-        var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButtonUnix.accesskey");
-#endif
+
+        var stringKey = AppConstants.platform == "win"
+                        ? "popupWarningButton"
+                        : "popupWarningButtonUnix";
+
+        var popupButtonText = gNavigatorBundle.getString(stringKey);
+        var popupButtonAccesskey = gNavigatorBundle.getString(stringKey + ".accesskey");
+
         var messageBase = gNavigatorBundle.getString("popupWarning.message");
         var message = PluralForm.get(popupCount, messageBase)
                                 .replace("#1", brandShortName)
                                 .replace("#2", popupCount);
 
         var notificationBox = gBrowser.getNotificationBox();
         var notification = notificationBox.getNotificationWithValue("popup-blocked");
         if (notification) {
@@ -1015,22 +1018,16 @@ var gBrowserInit = {
         else if (screen.availWidth >= 2048) {
           defaultWidth = (screen.availWidth / 2) - 20;
           defaultHeight = screen.availHeight - 10;
         }
         else {
           defaultWidth = screen.availWidth * .75;
           defaultHeight = screen.availHeight * .75;
         }
-
-#if MOZ_WIDGET_GTK == 2
-        // On X, we're not currently able to account for the size of the window
-        // border.  Use 28px as a guess (titlebar + bottom window border)
-        defaultHeight -= 28;
-#endif
       }
       document.documentElement.setAttribute("width", defaultWidth);
       document.documentElement.setAttribute("height", defaultHeight);
     }
 
     if (!window.toolbar.visible) {
       // adjust browser UI for popups
       if (gURLBar) {
@@ -1041,17 +1038,16 @@ var gBrowserInit = {
     }
 
     // Misc. inits.
     TabletModeUpdater.init();
     CombinedStopReload.init();
     gPrivateBrowsingUI.init();
     TabsInTitlebar.init();
 
-#ifdef XP_WIN
     if (window.matchMedia("(-moz-os-version: windows-win8)").matches &&
         window.matchMedia("(-moz-windows-default-theme)").matches) {
       let windowFrameColor = Cu.import("resource:///modules/Windows8WindowFrameColor.jsm", {})
                                .Windows8WindowFrameColor.get();
 
       // Formula from W3C's WCAG 2.0 spec's color ratio and relative luminance,
       // section 1.3.4, http://www.w3.org/TR/WCAG20/ .
       windowFrameColor = windowFrameColor.map((color) => {
@@ -1064,17 +1060,16 @@ var gBrowserInit = {
                                 windowFrameColor[1] * 0.7152 +
                                 windowFrameColor[2] * 0.0722;
       let foregroundLuminance = 0; // Default to black for foreground text.
       let contrastRatio = (backgroundLuminance + 0.05) / (foregroundLuminance + 0.05);
       if (contrastRatio < 3) {
         document.documentElement.setAttribute("darkwindowframe", "true");
       }
     }
-#endif
 
     ToolbarIconColor.init();
 
     // Wait until chrome is painted before executing code not critical to making the window visible
     this._boundDelayedStartup = this._delayedStartup.bind(this);
     window.addEventListener("MozAfterPaint", this._boundDelayedStartup);
 
     this._loadHandled = true;
@@ -1193,37 +1188,38 @@ var gBrowserInit = {
       }
       // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
       // Such callers expect that window.arguments[0] is handled as a single URI.
       else {
         loadOneOrMoreURIs(uriToLoad);
       }
     }
 
-#ifdef MOZ_SAFE_BROWSING
-    // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
-    setTimeout(function() { SafeBrowsing.init(); }, 2000);
-#endif
+    if (AppConstants.MOZ_SAFE_BROWSING) {
+      // Bug 778855 - Perf regression if we do this here. To be addressed in bug 779008.
+      setTimeout(function() { SafeBrowsing.init(); }, 2000);
+    }
 
     Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-origin-blocked", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-confirmation", false);
     Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
     window.messageManager.addMessageListener("Browser:URIFixup", gKeywordURIFixup);
 
     BrowserOffline.init();
     OfflineApps.init();
     IndexedDBPromptHelper.init();
-#ifdef E10S_TESTING_ONLY
-    gRemoteTabsUI.init();
-#endif
+
+    if (AppConstants.E10S_TESTING_ONLY)
+      gRemoteTabsUI.init();
+
     // Initialize the full zoom setting.
     // We do this before the session restore service gets initialized so we can
     // apply full zoom settings to tabs restored by the session restore service.
     FullZoom.init();
     PanelUI.init();
     LightweightThemeListener.init();
 
     Services.telemetry.getHistogramById("E10S_WINDOW").add(gMultiProcessBrowser);
@@ -1304,37 +1300,36 @@ var gBrowserInit = {
       }
     }, 3000);
 
     // The object handling the downloads indicator is also initialized here in the
     // delayed startup function, but the actual indicator element is not loaded
     // unless there are downloads to be displayed.
     DownloadsButton.initializeIndicator();
 
-#ifndef XP_MACOSX
-    updateEditUIVisibility();
-    let placesContext = document.getElementById("placesContext");
-    placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
-    placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
-#endif
+    if (AppConstants.platform != "macosx") {
+      updateEditUIVisibility();
+      let placesContext = document.getElementById("placesContext");
+      placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
+      placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
+    }
 
     LightWeightThemeWebInstaller.init();
 
     if (Win7Features)
       Win7Features.onOpenWindow();
 
     FullScreen.init();
 
     // initialize the sync UI
     gSyncUI.init();
     gFxAccounts.init();
 
-#ifdef MOZ_DATA_REPORTING
-    gDataNotificationInfoBar.init();
-#endif
+    if (AppConstants.MOZ_DATA_REPORTING)
+      gDataNotificationInfoBar.init();
 
     gBrowserThumbnails.init();
 
     // Add Devtools menuitems and listeners
     gDevToolsBrowser.registerBrowserWindow(window);
 
     gMenuButtonBadgeManager.init();
 
@@ -1545,22 +1540,23 @@ var gBrowserInit = {
     window.QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIWebNavigation)
           .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
           .QueryInterface(Ci.nsIInterfaceRequestor)
           .getInterface(Ci.nsIXULWindow)
           .XULBrowserWindow = null;
     window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = null;
   },
-
-#ifdef XP_MACOSX
+};
+
+if (AppConstants.platform == "macosx") {
   // nonBrowserWindowStartup(), nonBrowserWindowDelayedStartup(), and
   // nonBrowserWindowShutdown() are used for non-browser windows in
   // macBrowserOverlay
-  nonBrowserWindowStartup: function() {
+  gBrowserInit.nonBrowserWindowStartup = function() {
     // Disable inappropriate commands / submenus
     var disabledItems = ['Browser:SavePage',
                          'Browser:SendLink', 'cmd_pageSetup', 'cmd_print', 'cmd_find', 'cmd_findAgain',
                          'viewToolbarsMenu', 'viewSidebarMenuMenu', 'Browser:Reload',
                          'viewFullZoomMenu', 'pageStyleMenu', 'charsetMenu', 'View:PageSource', 'View:FullScreen',
                          'viewHistorySidebar', 'Browser:AddBookmarkAs', 'Browser:BookmarkAllTabs',
                          'View:PageInfo'];
     var element;
@@ -1603,57 +1599,57 @@ var gBrowserInit = {
       }
     }
 
     if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
       document.getElementById("macDockMenuNewWindow").hidden = true;
     }
 
     this._delayedStartupTimeoutId = setTimeout(this.nonBrowserWindowDelayedStartup.bind(this), 0);
-  },
-
-  nonBrowserWindowDelayedStartup: function() {
+  };
+
+  gBrowserInit.nonBrowserWindowDelayedStartup = function() {
     this._delayedStartupTimeoutId = null;
 
     // initialise the offline listener
     BrowserOffline.init();
 
     // initialize the private browsing UI
     gPrivateBrowsingUI.init();
 
     // initialize the sync UI
     gSyncUI.init();
 
-#ifdef E10S_TESTING_ONLY
-    gRemoteTabsUI.init();
-#endif
-  },
-
-  nonBrowserWindowShutdown: function() {
+    if (AppConstants.E10S_TESTING_ONLY) {
+      gRemoteTabsUI.init();
+    }
+  };
+
+  gBrowserInit.nonBrowserWindowShutdown = function() {
     // If nonBrowserWindowDelayedStartup hasn't run yet, we have no work to do -
     // just cancel the pending timeout and return;
     if (this._delayedStartupTimeoutId) {
       clearTimeout(this._delayedStartupTimeoutId);
       return;
     }
 
     BrowserOffline.uninit();
-  },
-#endif
+  };
 }
 
 
 /* Legacy global init functions */
 var BrowserStartup        = gBrowserInit.onLoad.bind(gBrowserInit);
 var BrowserShutdown       = gBrowserInit.onUnload.bind(gBrowserInit);
-#ifdef XP_MACOSX
-var nonBrowserWindowStartup        = gBrowserInit.nonBrowserWindowStartup.bind(gBrowserInit);
-var nonBrowserWindowDelayedStartup = gBrowserInit.nonBrowserWindowDelayedStartup.bind(gBrowserInit);
-var nonBrowserWindowShutdown       = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit);
-#endif
+
+if (AppConstants.platform == "macosx") {
+  var nonBrowserWindowStartup        = gBrowserInit.nonBrowserWindowStartup.bind(gBrowserInit);
+  var nonBrowserWindowDelayedStartup = gBrowserInit.nonBrowserWindowDelayedStartup.bind(gBrowserInit);
+  var nonBrowserWindowShutdown       = gBrowserInit.nonBrowserWindowShutdown.bind(gBrowserInit);
+}
 
 function HandleAppCommandEvent(evt) {
   switch (evt.command) {
   case "Back":
     BrowserBack();
     break;
   case "Forward":
     BrowserForward();
@@ -1786,22 +1782,21 @@ function BrowserHandleShiftBackspace()
 }
 
 function BrowserStop() {
   const stopFlags = nsIWebNavigation.STOP_ALL;
   gBrowser.webNavigation.stop(stopFlags);
 }
 
 function BrowserReloadOrDuplicate(aEvent) {
-  var backgroundTabModifier = aEvent.button == 1 ||
-#ifdef XP_MACOSX
-    aEvent.metaKey;
-#else
-    aEvent.ctrlKey;
-#endif
+  let metaKeyPressed = AppConstants.platform == "macosx"
+                       ? aEvent.metaKey
+                       : aEvent.ctrlKey;
+  var backgroundTabModifier = aEvent.button == 1 || metaKeyPressed;
+
   if (aEvent.shiftKey && !backgroundTabModifier) {
     BrowserReloadSkipCache();
     return;
   }
 
   let where = whereToOpenLink(aEvent, false, true);
   if (where == "current")
     BrowserReload();
@@ -1854,24 +1849,23 @@ function BrowserGoHome(aEvent) {
   case "window":
     OpenBrowserWindow();
     break;
   }
 }
 
 function loadOneOrMoreURIs(aURIString)
 {
-#ifdef XP_MACOSX
   // we're not a browser window, pass the URI string to a new browser window
   if (window.location.href != getBrowserURL())
   {
     window.openDialog(getBrowserURL(), "_blank", "all,dialog=no", aURIString);
     return;
   }
-#endif
+
   // This function throws for certain malformed URIs, so use exception handling
   // so that we don't disrupt startup
   try {
     gBrowser.loadTabs(aURIString.split("|"), false, true);
   }
   catch (e) {
   }
 }
@@ -1887,31 +1881,29 @@ function focusAndSelectUrlBar() {
   }
   return false;
 }
 
 function openLocation() {
   if (focusAndSelectUrlBar())
     return;
 
-#ifdef XP_MACOSX
   if (window.location.href != getBrowserURL()) {
     var win = getTopWin();
     if (win) {
       // If there's an open browser window, it should handle this command
       win.focus()
       win.openLocation();
     }
     else {
       // If there are no open browser windows, open a new one
       window.openDialog("chrome://browser/content/", "_blank",
                         "chrome,all,dialog=no", BROWSER_NEW_TAB_URL);
     }
   }
-#endif
 }
 
 function BrowserOpenTab()
 {
   openUILinkIn(BROWSER_NEW_TAB_URL, "tab");
 }
 
 /* Called from the openLocation dialog. This allows that dialog to instruct
@@ -1999,23 +1991,21 @@ function BrowserOpenFileWindow()
                      nsIFilePicker.filterHTML);
     fp.displayDirectory = gLastOpenDirectory.path;
     fp.open(fpCallback);
   } catch (ex) {
   }
 }
 
 function BrowserCloseTabOrWindow() {
-#ifdef XP_MACOSX
   // If we're not a browser window, just close the window
   if (window.location.href != getBrowserURL()) {
     closeWindow(true);
     return;
   }
-#endif
 
   // If the current tab is the last one, this will close the window.
   gBrowser.removeCurrentTab({animate: true});
 }
 
 function BrowserTryToCloseWindow()
 {
   if (WindowIsClosing())
@@ -3463,17 +3453,16 @@ const BrowserSearch = {
   },
 
   /**
    * Gives focus to the search bar, if it is present on the toolbar, or loads
    * the default engine's search form otherwise. For Mac, opens a new window
    * or focuses an existing window, if necessary.
    */
   webSearch: function BrowserSearch_webSearch() {
-#ifdef XP_MACOSX
     if (window.location.href != getBrowserURL()) {
       var win = getTopWin();
       if (win) {
         // If there's an open browser window, it should handle this command
         win.focus();
         win.BrowserSearch.webSearch();
       } else {
         // If there are no open browser windows, open a new one
@@ -3484,17 +3473,17 @@ const BrowserSearch = {
           }
         }
         win = window.openDialog(getBrowserURL(), "_blank",
                                 "chrome,all,dialog=no", "about:blank");
         Services.obs.addObserver(observer, "browser-delayed-startup-finished", false);
       }
       return;
     }
-#endif
+
     let openSearchPageIfFieldIsNotActive = function(aSearchBar) {
       if (!aSearchBar || document.activeElement != aSearchBar.textbox.inputField) {
         let url = gBrowser.currentURI.spec.toLowerCase();
         let mm = gBrowser.selectedBrowser.messageManager;
         if (url === "about:home" ||
             (url === "about:newtab" && NewTabUtils.allPages.enabled)) {
           ContentSearch.focusInput(mm);
         } else {
@@ -3640,36 +3629,37 @@ const BrowserSearch = {
    * @param selection [optional]
    *        ({index: The selected index, kind: "key" or "mouse"}) If
    *        the search was a suggested search, this indicates where the
    *        item was in the suggestion list and how the user selected it.
    */
   recordSearchInHealthReport: function (engine, source, selection) {
     BrowserUITelemetry.countSearchEvent(source, null, selection);
     this.recordSearchInTelemetry(engine, source);
-#ifdef MOZ_SERVICES_HEALTHREPORT
-    let reporter = Cc["@mozilla.org/datareporting/service;1"]
+
+    let reporter = AppConstants.MOZ_SERVICES_HEALTHREPORT
+                   ? Cc["@mozilla.org/datareporting/service;1"]
                      .getService()
                      .wrappedJSObject
-                     .healthReporter;
+                     .healthReporter
+                   : null;
 
     // This can happen if the FHR component of the data reporting service is
     // disabled. This is controlled by a pref that most will never use.
     if (!reporter) {
       return;
     }
 
     reporter.onInit().then(function record() {
       try {
         reporter.getProvider("org.mozilla.searches").recordSearch(engine, source);
       } catch (ex) {
         Cu.reportError(ex);
       }
     });
-#endif
   },
 
   _getSearchEngineId: function (engine) {
     if (!engine) {
       return "other";
     }
 
     if (engine.identifier) {
@@ -3962,17 +3952,19 @@ function BrowserCustomizeToolbar() {
  *
  * This doesn't work on Mac, since Mac menus flash when users press their
  * keyboard shortcuts, so edit UI is essentially always visible on the Mac,
  * and we need to always update the edit commands.  Thus on Mac this function
  * is a no op.
  */
 function updateEditUIVisibility()
 {
-#ifndef XP_MACOSX
+  if (AppConstants.platform == "macosx")
+    return;
+
   let editMenuPopupState = document.getElementById("menu_EditPopup").state;
   let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
   let placesContextMenuPopupState = document.getElementById("placesContext").state;
 
   // The UI is visible if the Edit menu is opening or open, if the context menu
   // is open, or if the toolbar has been customized to include the Cut, Copy,
   // or Paste toolbar buttons.
   gEditUIVisible = editMenuPopupState == "showing" ||
@@ -3996,17 +3988,16 @@ function updateEditUIVisibility()
     goSetCommandEnabled("cmd_redo", true);
     goSetCommandEnabled("cmd_cut", true);
     goSetCommandEnabled("cmd_copy", true);
     goSetCommandEnabled("cmd_paste", true);
     goSetCommandEnabled("cmd_selectAll", true);
     goSetCommandEnabled("cmd_delete", true);
     goSetCommandEnabled("cmd_switchTextDirection", true);
   }
-#endif
 }
 
 /**
  * Opens a new tab with the userContextId specified as an attribute of
  * sourceEvent. This attribute is propagated to the top level originAttributes
  * living on the tab's docShell.
  *
  * @param event
@@ -4451,31 +4442,29 @@ var XULBrowserWindow = {
 
     // See bug 358202, when tabs are switched during a drag operation,
     // timers don't fire on windows (bug 203573)
     if (aRequest)
       setTimeout(function () { XULBrowserWindow.asyncUpdateUI(); }, 0);
     else
       this.asyncUpdateUI();
 
-#ifdef MOZ_CRASHREPORTER
-    if (aLocationURI) {
+    if (AppConstants.MOZ_CRASHREPORTER && aLocationURI) {
       let uri = aLocationURI.clone();
       try {
         // If the current URI contains a username/password, remove it.
         uri.userPass = "";
       } catch (ex) { /* Ignore failures on about: URIs. */ }
 
       try {
         gCrashReporter.annotateCrashReport("URL", uri.spec);
       } catch (ex if ex.result == Components.results.NS_ERROR_NOT_INITIALIZED) {
         // Don't make noise when the crash reporter is built but not enabled.
       }
     }
-#endif
   },
 
   asyncUpdateUI: function () {
     FeedHandler.updateFeeds();
     BrowserSearch.updateOpenSearchBadge();
   },
 
   // Left here for add-on compatibility, see bug 752434
@@ -5067,19 +5056,19 @@ function onViewToolbarCommand(aEvent) {
   var isVisible = aEvent.originalTarget.getAttribute("checked") == "true";
   CustomizableUI.setToolbarVisibility(toolbarId, isVisible);
 }
 
 function setToolbarVisibility(toolbar, isVisible, persist=true) {
   let hidingAttribute;
   if (toolbar.getAttribute("type") == "menubar") {
     hidingAttribute = "autohide";
-#ifdef MOZ_WIDGET_GTK
-    Services.prefs.setBoolPref("ui.key.menuAccessKeyFocuses", !isVisible);
-#endif
+    if (AppConstants.platform == "linux") {
+      Services.prefs.setBoolPref("ui.key.menuAccessKeyFocuses", !isVisible);
+    }
   } else {
     hidingAttribute = "collapsed";
   }
 
   toolbar.setAttribute(hidingAttribute, !isVisible);
   if (persist) {
     document.persist(toolbar.id, hidingAttribute);
   }
@@ -5095,279 +5084,16 @@ function setToolbarVisibility(toolbar, i
 
   PlacesToolbarHelper.init();
   BookmarkingUI.onToolbarVisibilityChange();
   gBrowser.updateWindowResizers();
   if (isVisible)
     ToolbarIconColor.inferFromText();
 }
 
-var TabsInTitlebar = {
-  init: function () {
-#ifdef CAN_DRAW_IN_TITLEBAR
-    this._readPref();
-    Services.prefs.addObserver(this._prefName, this, false);
-
-    // We need to update the appearance of the titlebar when the menu changes
-    // from the active to the inactive state. We can't, however, rely on
-    // DOMMenuBarInactive, because the menu fires this event and then removes
-    // the inactive attribute after an event-loop spin.
-    //
-    // Because updating the appearance involves sampling the heights and margins
-    // of various elements, it's important that the layout be more or less
-    // settled before updating the titlebar. So instead of listening to
-    // DOMMenuBarActive and DOMMenuBarInactive, we use a MutationObserver to
-    // watch the "invalid" attribute directly.
-    let menu = document.getElementById("toolbar-menubar");
-    this._menuObserver = new MutationObserver(this._onMenuMutate);
-    this._menuObserver.observe(menu, {attributes: true});
-
-    this.onAreaReset = function(aArea) {
-      if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
-        this._update(true);
-    };
-    this.onWidgetAdded = this.onWidgetRemoved = function(aWidgetId, aArea) {
-      if (aArea == CustomizableUI.AREA_TABSTRIP || aArea == CustomizableUI.AREA_MENUBAR)
-        this._update(true);
-    };
-    CustomizableUI.addListener(this);
-
-    this._initialized = true;
-#endif
-  },
-
-  allowedBy: function (condition, allow) {
-#ifdef CAN_DRAW_IN_TITLEBAR
-    if (allow) {
-      if (condition in this._disallowed) {
-        delete this._disallowed[condition];
-        this._update(true);
-      }
-    } else {
-      if (!(condition in this._disallowed)) {
-        this._disallowed[condition] = null;
-        this._update(true);
-      }
-    }
-#endif
-  },
-
-  updateAppearance: function updateAppearance(aForce) {
-#ifdef CAN_DRAW_IN_TITLEBAR
-    this._update(aForce);
-#endif
-  },
-
-  get enabled() {
-    return document.documentElement.getAttribute("tabsintitlebar") == "true";
-  },
-
-#ifdef CAN_DRAW_IN_TITLEBAR
-  observe: function (subject, topic, data) {
-    if (topic == "nsPref:changed")
-      this._readPref();
-  },
-
-  _onMenuMutate: function (aMutations) {
-    for (let mutation of aMutations) {
-      if (mutation.attributeName == "inactive" ||
-          mutation.attributeName == "autohide") {
-        TabsInTitlebar._update(true);
-        return;
-      }
-    }
-  },
-
-  _initialized: false,
-  _disallowed: {},
-  _prefName: "browser.tabs.drawInTitlebar",
-  _lastSizeMode: null,
-
-  _readPref: function () {
-    this.allowedBy("pref",
-                   Services.prefs.getBoolPref(this._prefName));
-  },
-
-  _update: function (aForce=false) {
-    let $ = id => document.getElementById(id);
-    let rect = ele => ele.getBoundingClientRect();
-    let verticalMargins = cstyle => parseFloat(cstyle.marginBottom) + parseFloat(cstyle.marginTop);
-
-    if (!this._initialized || window.fullScreen)
-      return;
-
-    let allowed = true;
-
-    if (!aForce) {
-      // _update is called on resize events, because the window is not ready
-      // after sizemode events. However, we only care about the event when the
-      // sizemode is different from the last time we updated the appearance of
-      // the tabs in the titlebar.
-      let sizemode = document.documentElement.getAttribute("sizemode");
-      if (this._lastSizeMode == sizemode) {
-        return;
-      }
-      let oldSizeMode = this._lastSizeMode;
-      this._lastSizeMode = sizemode;
-      // Don't update right now if we are leaving fullscreen, since the UI is
-      // still changing in the consequent "fullscreen" event. Code there will
-      // call this function again when everything is ready.
-      // See browser-fullScreen.js: FullScreen.toggle and bug 1173768.
-      if (oldSizeMode == "fullscreen") {
-        return;
-      }
-    }
-
-    for (let something in this._disallowed) {
-      allowed = false;
-      break;
-    }
-
-    let titlebar = $("titlebar");
-    let titlebarContent = $("titlebar-content");
-    let menubar = $("toolbar-menubar");
-
-    if (allowed) {
-      // We set the tabsintitlebar attribute first so that our CSS for
-      // tabsintitlebar manifests before we do our measurements.
-      document.documentElement.setAttribute("tabsintitlebar", "true");
-      updateTitlebarDisplay();
-
-      // Try to avoid reflows in this code by calculating dimensions first and
-      // then later set the properties affecting layout together in a batch.
-
-      // Get the full height of the tabs toolbar:
-      let tabsToolbar = $("TabsToolbar");
-      let tabsStyles = window.getComputedStyle(tabsToolbar);
-      let fullTabsHeight = rect(tabsToolbar).height + verticalMargins(tabsStyles);
-      // Buttons first:
-      let captionButtonsBoxWidth = rect($("titlebar-buttonbox-container")).width;
-
-#ifdef XP_MACOSX
-      let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
-      // No need to look up the menubar stuff on OS X:
-      let menuHeight = 0;
-      let fullMenuHeight = 0;
-#else
-      // Otherwise, get the height and margins separately for the menubar
-      let menuHeight = rect(menubar).height;
-      let menuStyles = window.getComputedStyle(menubar);
-      let fullMenuHeight = verticalMargins(menuStyles) + menuHeight;
-#endif
-
-      // And get the height of what's in the titlebar:
-      let titlebarContentHeight = rect(titlebarContent).height;
-
-      // Begin setting CSS properties which will cause a reflow
-
-      // If the menubar is around (menuHeight is non-zero), try to adjust
-      // its full height (i.e. including margins) to match the titlebar,
-      // by changing the menubar's bottom padding
-      if (menuHeight) {
-        // Calculate the difference between the titlebar's height and that of the menubar
-        let menuTitlebarDelta = titlebarContentHeight - fullMenuHeight;
-        let paddingBottom;
-        // The titlebar is bigger:
-        if (menuTitlebarDelta > 0) {
-          fullMenuHeight += menuTitlebarDelta;
-          // If there is already padding on the menubar, we need to add that
-          // to the difference so the total padding is correct:
-          if ((paddingBottom = menuStyles.paddingBottom)) {
-            menuTitlebarDelta += parseFloat(paddingBottom);
-          }
-          menubar.style.paddingBottom = menuTitlebarDelta + "px";
-        // The menubar is bigger, but has bottom padding we can remove:
-        } else if (menuTitlebarDelta < 0 && (paddingBottom = menuStyles.paddingBottom)) {
-          let existingPadding = parseFloat(paddingBottom);
-          // menuTitlebarDelta is negative; work out what's left, but don't set negative padding:
-          let desiredPadding = Math.max(0, existingPadding + menuTitlebarDelta);
-          menubar.style.paddingBottom = desiredPadding + "px";
-          // We've changed the menu height now:
-          fullMenuHeight += desiredPadding - existingPadding;
-        }
-      }
-
-      // Next, we calculate how much we need to stretch the titlebar down to
-      // go all the way to the bottom of the tab strip, if necessary.
-      let tabAndMenuHeight = fullTabsHeight + fullMenuHeight;
-
-      if (tabAndMenuHeight > titlebarContentHeight) {
-        // We need to increase the titlebar content's outer height (ie including margins)
-        // to match the tab and menu height:
-        let extraMargin = tabAndMenuHeight - titlebarContentHeight;
-#ifndef XP_MACOSX
-        titlebarContent.style.marginBottom = extraMargin + "px";
-#endif
-        titlebarContentHeight += extraMargin;
-      } else {
-        titlebarContent.style.removeProperty("margin-bottom");
-      }
-
-      // Then we bring up the titlebar by the same amount, but we add any negative margin:
-      titlebar.style.marginBottom = "-" + titlebarContentHeight + "px";
-
-
-      // Finally, size the placeholders:
-#ifdef XP_MACOSX
-      this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
-#endif
-      this._sizePlaceholder("caption-buttons", captionButtonsBoxWidth);
-
-      if (!this._draghandles) {
-        this._draghandles = {};
-        let tmp = {};
-        Components.utils.import("resource://gre/modules/WindowDraggingUtils.jsm", tmp);
-
-        let mouseDownCheck = function () {
-          return !this._dragBindingAlive && TabsInTitlebar.enabled;
-        };
-
-        this._draghandles.tabsToolbar = new tmp.WindowDraggingElement(tabsToolbar);
-        this._draghandles.tabsToolbar.mouseDownCheck = mouseDownCheck;
-
-        this._draghandles.navToolbox = new tmp.WindowDraggingElement(gNavToolbox);
-        this._draghandles.navToolbox.mouseDownCheck = mouseDownCheck;
-      }
-    } else {
-      document.documentElement.removeAttribute("tabsintitlebar");
-      updateTitlebarDisplay();
-
-#ifdef XP_MACOSX
-      let secondaryButtonsWidth = rect($("titlebar-secondary-buttonbox")).width;
-      this._sizePlaceholder("fullscreen-button", secondaryButtonsWidth);
-#endif
-      // Reset the margins and padding that might have been modified:
-      titlebarContent.style.marginTop = "";
-      titlebarContent.style.marginBottom = "";
-      titlebar.style.marginBottom = "";
-      menubar.style.paddingBottom = "";
-    }
-
-    ToolbarIconColor.inferFromText();
-    if (CustomizationHandler.isCustomizing()) {
-      gCustomizeMode.updateLWTStyling();
-    }
-  },
-
-  _sizePlaceholder: function (type, width) {
-    Array.forEach(document.querySelectorAll(".titlebar-placeholder[type='"+ type +"']"),
-                  function (node) { node.width = width; });
-  },
-#endif
-
-  uninit: function () {
-#ifdef CAN_DRAW_IN_TITLEBAR
-    this._initialized = false;
-    Services.prefs.removeObserver(this._prefName, this);
-    this._menuObserver.disconnect();
-    CustomizableUI.removeListener(this);
-#endif
-  }
-};
-
 var TabletModeUpdater = {
   init() {
     if (AppConstants.isPlatformAndVersionAtLeast("win", "10")) {
       this.update(WindowsUIUtils.inTabletMode);
       Services.obs.addObserver(this, "tablet-mode-change", false);
     }
   },
 
@@ -5409,62 +5135,16 @@ var gTabletModePageCounter = {
   },
 
   finish() {
     Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD").add("tablet", this._tabletCount);
     Services.telemetry.getKeyedHistogramById("FX_TABLETMODE_PAGE_LOAD").add("desktop", this._desktopCount);
   },
 };
 
-#ifdef CAN_DRAW_IN_TITLEBAR
-function updateTitlebarDisplay() {
-
-#ifdef XP_MACOSX
-  // OS X and the other platforms differ enough to necessitate this kind of
-  // special-casing. Like the other platforms where we CAN_DRAW_IN_TITLEBAR,
-  // we draw in the OS X titlebar when putting the tabs up there. However, OS X
-  // also draws in the titlebar when a lightweight theme is applied, regardless
-  // of whether or not the tabs are drawn in the titlebar.
-  if (TabsInTitlebar.enabled) {
-    document.documentElement.setAttribute("chromemargin-nonlwtheme", "0,-1,-1,-1");
-    document.documentElement.setAttribute("chromemargin", "0,-1,-1,-1");
-    document.documentElement.removeAttribute("drawtitle");
-  } else {
-    // We set chromemargin-nonlwtheme to "" instead of removing it as a way of
-    // making sure that LightweightThemeConsumer doesn't take it upon itself to
-    // detect this value again if and when we do a lwtheme state change.
-    document.documentElement.setAttribute("chromemargin-nonlwtheme", "");
-    let isCustomizing = document.documentElement.hasAttribute("customizing");
-    let hasLWTheme = document.documentElement.hasAttribute("lwtheme");
-    let isPrivate = PrivateBrowsingUtils.isWindowPrivate(window);
-    if ((!hasLWTheme || isCustomizing) && !isPrivate) {
-      document.documentElement.removeAttribute("chromemargin");
-    }
-    document.documentElement.setAttribute("drawtitle", "true");
-  }
-
-#else
-
-  if (TabsInTitlebar.enabled)
-    document.documentElement.setAttribute("chromemargin", "0,2,2,2");
-  else
-    document.documentElement.removeAttribute("chromemargin");
-#endif
-}
-#endif
-
-#ifdef CAN_DRAW_IN_TITLEBAR
-function onTitlebarMaxClick() {
-  if (window.windowState == window.STATE_MAXIMIZED)
-    window.restore();
-  else
-    window.maximize();
-}
-#endif
-
 function displaySecurityInfo()
 {
   BrowserPageInfo(null, "securityTab");
 }
 
 
 var gHomeButton = {
   prefDomain: "browser.startup.homepage",
@@ -5507,36 +5187,36 @@ var gHomeButton = {
     }
 
     return url;
   },
 };
 
 const nodeToTooltipMap = {
   "bookmarks-menu-button": "bookmarksMenuButton.tooltip",
-#ifdef XP_MACOSX
-  "print-button": "printButton.tooltip",
-#endif
   "new-window-button": "newWindowButton.tooltip",
   "new-tab-button": "newTabButton.tooltip",
   "tabs-newtab-button": "newTabButton.tooltip",
   "fullscreen-button": "fullscreenButton.tooltip",
   "downloads-button": "downloads.tooltip",
 };
 const nodeToShortcutMap = {
   "bookmarks-menu-button": "manBookmarkKb",
-#ifdef XP_MACOSX
-  "print-button": "printKb",
-#endif
   "new-window-button": "key_newNavigator",
   "new-tab-button": "key_newNavigatorTab",
   "tabs-newtab-button": "key_newNavigatorTab",
   "fullscreen-button": "key_fullScreen",
   "downloads-button": "key_openDownloads"
 };
+
+if (AppConstants.platform == "macosx") {
+  nodeToTooltipMap["print-button"] = "printButton.tooltip";
+  nodeToShortcutMap["print-button"] = "printKb";
+}
+
 const gDynamicTooltipCache = new Map();
 function UpdateDynamicShortcutTooltipText(aTooltip) {
   let nodeId = aTooltip.triggerNode.id || aTooltip.triggerNode.getAttribute("anonid");
   if (!gDynamicTooltipCache.has(nodeId) && nodeId in nodeToTooltipMap) {
     let strId = nodeToTooltipMap[nodeId];
     let args = [];
     if (nodeId in nodeToShortcutMap) {
       let shortcutId = nodeToShortcutMap[nodeId];
@@ -6600,24 +6280,21 @@ function warnAboutClosingWindow() {
                         createInstance(Ci.nsISupportsPRBool);
   os.notifyObservers(closingCanceled,
                      "browser-lastwindow-close-requested", null);
   if (closingCanceled.data)
     return false;
 
   os.notifyObservers(null, "browser-lastwindow-close-granted", null);
 
-#ifdef XP_MACOSX
   // OS X doesn't quit the application when the last window is closed, but keeps
   // the session alive. Hence don't prompt users to save tabs, but warn about
   // closing multiple tabs.
-  return isPBWindow || gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL);
-#else
-  return true;
-#endif
+  return AppConstants.platform != "macosx"
+         || (isPBWindow || gBrowser.warnAboutClosingTabs(gBrowser.closingTabsEnum.ALL));
 }
 
 var MailIntegration = {
   sendLinkForBrowser: function (aBrowser) {
     this.sendMessage(aBrowser.currentURI.spec, aBrowser.contentTitle);
   },
 
   sendMessage: function (aBody, aSubject) {
@@ -7627,23 +7304,22 @@ var gPrivateBrowsingUI = {
 var gRemoteTabsUI = {
   init: function() {
     if (window.location.href != getBrowserURL() &&
         // Also check hidden window for the Mac no-window case
         window.location.href != "chrome://browser/content/hiddenWindow.xul") {
       return;
     }
 
-#ifdef XP_MACOSX
-    if (Services.prefs.getBoolPref("layers.acceleration.disabled")) {
+    if (AppConstants.platform == "macosx" &&
+        Services.prefs.getBoolPref("layers.acceleration.disabled")) {
       // On OS X, "Disable Hardware Acceleration" also disables OMTC and forces
       // a fallback to Basic Layers. This is incompatible with e10s.
       return;
     }
-#endif
 
     let newNonRemoteWindow = document.getElementById("menu_newNonRemoteWindow");
     let autostart = Services.appinfo.browserTabsRemoteAutostart;
     newNonRemoteWindow.hidden = !autostart;
   }
 };
 
 /**
@@ -7800,21 +7476,21 @@ var TabContextMenu = {
     this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
                       aPopupMenu.triggerNode : gBrowser.selectedTab;
     let disabled = gBrowser.tabs.length == 1;
 
     var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
     for (let menuItem of menuItems)
       menuItem.disabled = disabled;
 
-#ifdef E10S_TESTING_ONLY
-    menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-remote");
-    for (let menuItem of menuItems)
-      menuItem.hidden = !gMultiProcessBrowser;
-#endif
+    if (AppConstants.E10S_TESTING_ONLY) {
+      menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-remote");
+      for (let menuItem of menuItems)
+        menuItem.hidden = !gMultiProcessBrowser;
+    }
 
     disabled = gBrowser.visibleTabs.length == 1;
     menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple-visible");
     for (let menuItem of menuItems)
       menuItem.disabled = disabled;
 
     // Session store
     document.getElementById("context_undoCloseTab").disabled =
@@ -7981,22 +7657,18 @@ Object.defineProperty(this, "Eyedropper"
     let devtools = Cu.import("resource://devtools/shared/Loader.jsm", {}).devtools;
     return devtools.require("devtools/client/eyedropper/eyedropper").Eyedropper;
   },
   configurable: true,
   enumerable: true
 });
 
 XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
-#ifdef XP_WIN
   // Only show resizers on Windows 2000 and XP
-  return parseFloat(Services.sysinfo.getProperty("version")) < 6;
-#else
-  return false;
-#endif
+  return AppConstants.isPlatformAndVersionAtMost("win", "5.9");
 });
 
 var MousePosTracker = {
   _listeners: new Set(),
   _x: 0,
   _y: 0,
   get _windowUtils() {
     delete this._windowUtils;
@@ -8109,19 +7781,18 @@ var ToolbarIconColor = {
 
     function parseRGB(aColorString) {
       let rgb = aColorString.match(/^rgba?\((\d+), (\d+), (\d+)/);
       rgb.shift();
       return rgb.map(x => parseInt(x));
     }
 
     let toolbarSelector = "#navigator-toolbox > toolbar:not([collapsed=true]):not(#addon-bar)";
-#ifdef XP_MACOSX
-    toolbarSelector += ":not([type=menubar])";
-#endif
+    if (AppConstants.platform == "macosx")
+      toolbarSelector += ":not([type=menubar])";
 
     // The getComputedStyle calls and setting the brighttext are separated in
     // two loops to avoid flushing layout and making it dirty repeatedly.
 
     let luminances = new Map;
     for (let toolbar of document.querySelectorAll(toolbarSelector)) {
       let [r, g, b] = parseRGB(getComputedStyle(toolbar).color);
       let luminance = 0.2125 * r + 0.7154 * g + 0.0721 * b;
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -19,20 +19,23 @@
 <script type="application/javascript" src="chrome://browser/content/browser-devedition.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-eme.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-feeds.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-fullScreen.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-fullZoom.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-gestureSupport.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/>
+#ifdef MOZ_SAFE_BROWSING
 <script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/>
+#endif
 <script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-syncui.js"/>
+<script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-tabview.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-thumbnails.js"/>
 <script type="application/javascript" src="chrome://browser/content/browser-trackingprotection.js"/>
 
 #ifdef MOZ_DATA_REPORTING
 <script type="application/javascript" src="chrome://browser/content/browser-data-submission-info-bar.js"/>
 #endif
 
--- a/browser/base/content/sync/aboutSyncTabs.js
+++ b/browser/base/content/sync/aboutSyncTabs.js
@@ -2,29 +2,28 @@
  * 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/. */
 
 var Cu = Components.utils;
 
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-sync/main.js");
 Cu.import("resource:///modules/PlacesUIUtils.jsm");
+Cu.import("resource://gre/modules/AppConstants.jsm");
 Cu.import("resource://gre/modules/PlacesUtils.jsm", this);
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
 
-#ifdef MOZ_SERVICES_CLOUDSYNC
-XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
-                                  "resource://gre/modules/CloudSync.jsm");
-#else
-var CloudSync = null;
-#endif
+if (AppConstants.MOZ_SERVICES_CLOUDSYNC) {
+  XPCOMUtils.defineLazyModuleGetter(this, "CloudSync",
+                                    "resource://gre/modules/CloudSync.jsm");
+}
 
 var RemoteTabViewer = {
   _tabsList: null,
 
   init: function () {
     Services.obs.addObserver(this, "weave:service:login:finish", false);
     Services.obs.addObserver(this, "weave:engine:sync:finish", false);
 
--- a/browser/base/content/test/general/browser_windowopen_reflows.js
+++ b/browser/base/content/test/general/browser_windowopen_reflows.js
@@ -24,19 +24,19 @@ const EXPECTED_REFLOWS = [
   // (https://bugzilla.mozilla.org/show_bug.cgi?id=892154 will fix this)
   "ssi_getWindowDimension@resource:///modules/sessionstore/SessionStore.jsm",
 ];
 
 if (Services.appinfo.OS == "WINNT" || Services.appinfo.OS == "Darwin") {
   // TabsInTitlebar._update causes a reflow on OS X and Windows trying to do calculations
   // since layout info is already dirty. This doesn't seem to happen before
   // MozAfterPaint on Linux.
-  EXPECTED_REFLOWS.push("TabsInTitlebar._update/rect@chrome://browser/content/browser.js|" +
-                          "TabsInTitlebar._update@chrome://browser/content/browser.js|" +
-                          "updateAppearance@chrome://browser/content/browser.js|" +
+  EXPECTED_REFLOWS.push("TabsInTitlebar._update/rect@chrome://browser/content/browser-tabsintitlebar.js|" +
+                          "TabsInTitlebar._update@chrome://browser/content/browser-tabsintitlebar.js|" +
+                          "updateAppearance@chrome://browser/content/browser-tabsintitlebar.js|" +
                           "handleEvent@chrome://browser/content/tabbrowser.xml|");
 }
 
 if (Services.appinfo.OS == "Darwin") {
   // _onOverflow causes a reflow getting widths.
   EXPECTED_REFLOWS.push("OverflowableToolbar.prototype._onOverflow@resource:///modules/CustomizableUI.jsm|" +
                         "OverflowableToolbar.prototype.init@resource:///modules/CustomizableUI.jsm|" +
                         "OverflowableToolbar.prototype.observe@resource:///modules/CustomizableUI.jsm|" +
--- a/browser/base/content/utilityOverlay.js
+++ b/browser/base/content/utilityOverlay.js
@@ -1,14 +1,15 @@
-# -*- indent-tabs-mode: nil; js-indent-level: 4 -*-
-# 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/.
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+ * 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/. */
 
 // Services = object with smart getters for common XPCOM services
+Components.utils.import("resource://gre/modules/AppConstants.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Components.utils.import("resource:///modules/RecentWindow.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "aboutNewTabService",
                                    "@mozilla.org/browser/aboutnewtab-service;1",
                                    "nsIAboutNewTabService");
@@ -131,21 +132,18 @@ function whereToOpenLink( e, ignoreButto
   var alt  =  e.altKey && !ignoreAlt;
 
   // ignoreButton allows "middle-click paste" to use function without always opening in a new window.
   var middle = !ignoreButton && e.button == 1;
   var middleUsesTabs = getBoolPref("browser.tabs.opentabfor.middleclick", true);
 
   // Don't do anything special with right-mouse clicks.  They're probably clicks on context menu items.
 
-#ifdef XP_MACOSX
-  if (meta || (middle && middleUsesTabs))
-#else
-  if (ctrl || (middle && middleUsesTabs))
-#endif
+  var metaKey = AppConstants.platform == "macosx" ? meta : ctrl;
+  if (metaKey || (middle && middleUsesTabs))
     return shift ? "tabshifted" : "tab";
 
   if (alt && getBoolPref("browser.altClickSave", false))
     return "save";
 
   if (shift || (middle && !middleUsesTabs))
     return "window";
 
@@ -508,23 +506,25 @@ function openAboutDialog() {
     let win = enumerator.getNext();
     if (win.closed) {
       continue;
     }
     win.focus();
     return;
   }
 
-#ifdef XP_WIN
-  var features = "chrome,centerscreen,dependent";
-#elifdef XP_MACOSX
-  var features = "chrome,resizable=no,minimizable=no";
-#else
-  var features = "chrome,centerscreen,dependent,dialog=no";
-#endif
+  var features = "chrome,";
+  if (AppConstants.platform == "win") {
+    features += "centerscreen,dependent";
+  } else if (AppConstants.platform == "macosx") {
+    features += "resizable=no,minimizable=no";
+  } else {
+    features += "centerscreen,dependent,dialog=no";
+  }
+
   window.openDialog("chrome://browser/content/aboutDialog.xul", "", features);
 }
 
 function openPreferences(paneID, extraArgs)
 {
   function switchToAdvancedSubPane(doc) {
     if (extraArgs && extraArgs["advancedTab"]) {
       let advancedPaneTabs = doc.getElementById("advancedPrefs");
@@ -598,26 +598,24 @@ function openAdvancedPreferences(tabID)
  * Opens the troubleshooting information (about:support) page for this version
  * of the application.
  */
 function openTroubleshootingPage()
 {
   openUILinkIn("about:support", "tab");
 }
 
-#ifdef MOZ_SERVICES_HEALTHREPORT
 /**
  * Opens the troubleshooting information (about:support) page for this version
  * of the application.
  */
 function openHealthReport()
 {
   openUILinkIn("about:healthreport", "tab");
 }
-#endif
 
 /**
  * Opens the feedback page for this version of the application.
  */
 function openFeedbackPage()
 {
   var url = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
                       .getService(Components.interfaces.nsIURLFormatter)
@@ -630,17 +628,17 @@ function openTourPage()
   let scope = {}
   Components.utils.import("resource:///modules/UITour.jsm", scope);
   openUILinkIn(scope.UITour.url, "tab");
 }
 
 function buildHelpMenu()
 {
   // Enable/disable the "Report Web Forgery" menu item.
-  if (typeof gSafeBrowsing != "undefined")
+  if (typeof gSafeBrowsing != "undefined" && AppConstants.MOZ_SAFE_BROWSING)
     gSafeBrowsing.setReportPhishingMenu();
 }
 
 function isElementVisible(aElement)
 {
   if (!aElement)
     return false;
 
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -9,18 +9,19 @@ browser.jar:
 %  overlay chrome://mozapps/content/update/updates.xul chrome://browser/content/softwareUpdateOverlay.xul
 #endif
 #ifdef XP_WIN
 %  overlay chrome://browser/content/browser.xul chrome://browser/content/win6BrowserOverlay.xul os=WINNT osversion>=6
 #endif
 %  overlay chrome://global/content/viewSource.xul chrome://browser/content/viewSourceOverlay.xul
 %  overlay chrome://global/content/viewPartialSource.xul chrome://browser/content/viewSourceOverlay.xul
 
+        content/browser/aboutDialog-appUpdater.js     (content/aboutDialog-appUpdater.js)
 *       content/browser/aboutDialog.xul               (content/aboutDialog.xul)
-*       content/browser/aboutDialog.js                (content/aboutDialog.js)
+        content/browser/aboutDialog.js                (content/aboutDialog.js)
         content/browser/aboutDialog.css               (content/aboutDialog.css)
         content/browser/aboutRobots.xhtml             (content/aboutRobots.xhtml)
 *       content/browser/abouthome/aboutHome.xhtml     (content/abouthome/aboutHome.xhtml)
         content/browser/abouthome/aboutHome.js        (content/abouthome/aboutHome.js)
 *       content/browser/abouthome/aboutHome.css       (content/abouthome/aboutHome.css)
         content/browser/abouthome/snippet1.png        (content/abouthome/snippet1.png)
         content/browser/abouthome/snippet2.png        (content/abouthome/snippet2.png)
         content/browser/abouthome/downloads.png       (content/abouthome/downloads.png)
@@ -70,36 +71,43 @@ browser.jar:
         content/browser/aboutSocialError.xhtml        (content/aboutSocialError.xhtml)
         content/browser/aboutProviderDirectory.xhtml  (content/aboutProviderDirectory.xhtml)
         content/browser/aboutTabCrashed.css           (content/aboutTabCrashed.css)
         content/browser/aboutTabCrashed.js            (content/aboutTabCrashed.js)
         content/browser/aboutTabCrashed.xhtml         (content/aboutTabCrashed.xhtml)
 *       content/browser/aboutTabGroupsMigration.xhtml (content/aboutTabGroupsMigration.xhtml)
         content/browser/aboutTabGroupsMigration.js    (content/aboutTabGroupsMigration.js)
 *       content/browser/browser.css                   (content/browser.css)
-*       content/browser/browser.js                    (content/browser.js)
+        content/browser/browser.js                    (content/browser.js)
 *       content/browser/browser.xul                   (content/browser.xul)
         content/browser/browser-addons.js             (content/browser-addons.js)
         content/browser/browser-ctrlTab.js            (content/browser-ctrlTab.js)
-*       content/browser/browser-customization.js      (content/browser-customization.js)
+        content/browser/browser-customization.js      (content/browser-customization.js)
         content/browser/browser-data-submission-info-bar.js (content/browser-data-submission-info-bar.js)
-*       content/browser/browser-devedition.js         (content/browser-devedition.js)
+        content/browser/browser-devedition.js         (content/browser-devedition.js)
         content/browser/browser-eme.js                (content/browser-eme.js)
         content/browser/browser-feeds.js              (content/browser-feeds.js)
-*       content/browser/browser-fullScreen.js         (content/browser-fullScreen.js)
+        content/browser/browser-fullScreen.js         (content/browser-fullScreen.js)
         content/browser/browser-fullZoom.js           (content/browser-fullZoom.js)
         content/browser/browser-fxaccounts.js         (content/browser-fxaccounts.js)
-*       content/browser/browser-gestureSupport.js     (content/browser-gestureSupport.js)
-*       content/browser/browser-places.js             (content/browser-places.js)
-*       content/browser/browser-plugins.js            (content/browser-plugins.js)
-*       content/browser/browser-safebrowsing.js       (content/browser-safebrowsing.js)
+        content/browser/browser-gestureSupport.js     (content/browser-gestureSupport.js)
+        content/browser/browser-places.js             (content/browser-places.js)
+        content/browser/browser-plugins.js            (content/browser-plugins.js)
+#ifdef MOZ_SAFE_BROWSING
+        content/browser/browser-safebrowsing.js       (content/browser-safebrowsing.js)
+#endif
         content/browser/browser-sidebar.js            (content/browser-sidebar.js)
         content/browser/browser-social.js             (content/browser-social.js)
-*       content/browser/browser-syncui.js             (content/browser-syncui.js)
+        content/browser/browser-syncui.js             (content/browser-syncui.js)
 *       content/browser/browser-tabPreviews.xml       (content/browser-tabPreviews.xml)
+#ifdef CAN_DRAW_IN_TITLEBAR
+        content/browser/browser-tabsintitlebar.js       (content/browser-tabsintitlebar.js)
+#else
+        content/browser/browser-tabsintitlebar.js       (content/browser-tabsintitlebar-stub.js)
+#endif
         content/browser/browser-thumbnails.js         (content/browser-thumbnails.js)
         content/browser/browser-trackingprotection.js (content/browser-trackingprotection.js)
 *       content/browser/chatWindow.xul                (content/chatWindow.xul)
         content/browser/tab-content.js                (content/tab-content.js)
         content/browser/content.js                    (content/content.js)
         content/browser/social-content.js             (content/social-content.js)
         content/browser/defaultthemes/1.footer.jpg    (content/defaultthemes/1.footer.jpg)
         content/browser/defaultthemes/1.header.jpg    (content/defaultthemes/1.header.jpg)
@@ -137,17 +145,17 @@ browser.jar:
         content/browser/pageinfo/pageInfo.js          (content/pageinfo/pageInfo.js)
         content/browser/pageinfo/pageInfo.css         (content/pageinfo/pageInfo.css)
         content/browser/pageinfo/pageInfo.xml         (content/pageinfo/pageInfo.xml)
         content/browser/pageinfo/feeds.js             (content/pageinfo/feeds.js)
         content/browser/pageinfo/feeds.xml            (content/pageinfo/feeds.xml)
         content/browser/pageinfo/permissions.js       (content/pageinfo/permissions.js)
         content/browser/pageinfo/security.js          (content/pageinfo/security.js)
         content/browser/sync/aboutSyncTabs.xul        (content/sync/aboutSyncTabs.xul)
-*       content/browser/sync/aboutSyncTabs.js         (content/sync/aboutSyncTabs.js)
+        content/browser/sync/aboutSyncTabs.js         (content/sync/aboutSyncTabs.js)
         content/browser/sync/aboutSyncTabs.css        (content/sync/aboutSyncTabs.css)
         content/browser/sync/aboutSyncTabs-bindings.xml  (content/sync/aboutSyncTabs-bindings.xml)
         content/browser/sync/setup.xul                (content/sync/setup.xul)
         content/browser/sync/addDevice.js             (content/sync/addDevice.js)
         content/browser/sync/addDevice.xul            (content/sync/addDevice.xul)
         content/browser/sync/setup.js                 (content/sync/setup.js)
         content/browser/sync/genericChange.xul        (content/sync/genericChange.xul)
         content/browser/sync/genericChange.js         (content/sync/genericChange.js)
@@ -163,17 +171,17 @@ browser.jar:
 *       content/browser/sanitize.xul                  (content/sanitize.xul)
 *       content/browser/sanitizeDialog.js             (content/sanitizeDialog.js)
         content/browser/sanitizeDialog.css            (content/sanitizeDialog.css)
         content/browser/contentSearchUI.js            (content/contentSearchUI.js)
         content/browser/contentSearchUI.css           (content/contentSearchUI.css)
         content/browser/tabbrowser.css                (content/tabbrowser.css)
         content/browser/tabbrowser.xml                (content/tabbrowser.xml)
         content/browser/urlbarBindings.xml            (content/urlbarBindings.xml)
-*       content/browser/utilityOverlay.js             (content/utilityOverlay.js)
+        content/browser/utilityOverlay.js             (content/utilityOverlay.js)
         content/browser/web-panels.js                 (content/web-panels.js)
 *       content/browser/web-panels.xul                (content/web-panels.xul)
 *       content/browser/baseMenuOverlay.xul           (content/baseMenuOverlay.xul)
 *       content/browser/nsContextMenu.js              (content/nsContextMenu.js)
 # XXX: We should exclude this one as well (bug 71895)
 *       content/browser/hiddenWindow.xul              (content/hiddenWindow.xul)
 #ifdef XP_MACOSX
 *       content/browser/macBrowserOverlay.xul         (content/macBrowserOverlay.xul)
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -628,17 +628,17 @@ PlacesViewBase.prototype = {
   batching: function() { },
 
   nodeInserted:
   function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = this._getDOMNodeForPlacesNode(aParentPlacesNode);
     if (!parentElt._built)
       return;
 
-    let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+    let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
                 aIndex + 1;
     this._insertNewItemToPopup(aPlacesNode, parentElt,
                                parentElt.childNodes[index]);
     this._setEmptyPopupStatus(parentElt, false);
   },
 
   nodeMoved:
   function PBV_nodeMoved(aPlacesNode,
@@ -658,17 +658,17 @@ PlacesViewBase.prototype = {
     // we need to do in that case.
     if (elt == this._rootElt)
       return;
 
     let parentElt = this._getDOMNodeForPlacesNode(aNewParentPlacesNode);
     if (parentElt._built) {
       // Move the node.
       parentElt.removeChild(elt);
-      let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+      let index = Array.prototype.indexOf.call(parentElt.childNodes, parentElt._startMarker) +
                   aNewIndex + 1;
       parentElt.insertBefore(elt, parentElt.childNodes[index]);
     }
   },
 
   containerStateChanged:
   function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
     if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
@@ -1406,17 +1406,17 @@ PlacesToolbar.prototype = {
     if (!PlacesUtils.nodeIsFolder(this._resultNode))
       return null;
 
     let dropPoint = { ip: null, beforeIndex: null, folderElt: null };
     let elt = aEvent.target;
     if (elt._placesNode && elt != this._rootElt &&
         elt.localName != "menupopup") {
       let eltRect = elt.getBoundingClientRect();
-      let eltIndex = Array.indexOf(this._rootElt.childNodes, elt);
+      let eltIndex = Array.prototype.indexOf.call(this._rootElt.childNodes, elt);
       if (PlacesUtils.nodeIsFolder(elt._placesNode) &&
           !PlacesUIUtils.isContentsReadOnly(elt._placesNode)) {
         // This is a folder.
         // If we are in the middle of it, drop inside it.
         // Otherwise, drop before it, with regards to RTL mode.
         let threshold = eltRect.width * 0.25;
         if (this.isRTL ? (aEvent.clientX > eltRect.right - threshold)
                        : (aEvent.clientX < eltRect.left + threshold)) {
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -274,17 +274,17 @@ PlacesTreeView.prototype = {
   _buildVisibleSection:
   function PTV__buildVisibleSection(aContainer, aFirstChildRow, aToOpen)
   {
     // There's nothing to do if the container is closed.
     if (!aContainer.containerOpen)
       return 0;
 
     // Inserting the new elements into the rows array in one shot (by
-    // Array.concat) is faster than resizing the array (by splice) on each loop
+    // Array.prototype.concat) is faster than resizing the array (by splice) on each loop
     // iteration.
     let cc = aContainer.childCount;
     let newElements = new Array(cc);
     this._rows = this._rows.splice(0, aFirstChildRow)
                      .concat(newElements, this._rows);
 
     if (this._isPlainContainer(aContainer))
       return cc;
--- a/browser/components/places/tests/chrome/test_editBookmarkOverlay_tags_liveUpdate.xul
+++ b/browser/components/places/tests/chrome/test_editBookmarkOverlay_tags_liveUpdate.xul
@@ -37,17 +37,17 @@
     function checkTagsSelector(aAvailableTags, aCheckedTags) {
       is(PlacesUtils.tagging.allTags.length, aAvailableTags.length,
          "tagging service is in sync.");
       let tagsSelector = document.getElementById("editBMPanel_tagsSelector");
       let children = tagsSelector.childNodes;
       is(children.length, aAvailableTags.length,
           "Found expected number of tags in the tags selector");
 
-      Array.forEach(children, function (aChild) {
+      Array.prototype.forEach.call(children, function (aChild) {
         let tag = aChild.getAttribute("label");
         ok(true, "Found tag '" + tag + "' in the selector");
         ok(aAvailableTags.includes(tag), "Found expected tag");
         let checked = aChild.getAttribute("checked") == "true";
         is(checked, aCheckedTags.includes(tag),
            "Tag is correctly marked");
       });
     }
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/content/actions/breakpoints.js
@@ -0,0 +1,169 @@
+/* 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 constants = require('../constants');
+const promise = require('promise');
+const { asPaused, rdpInvoke } = require('../utils');
+const { PROMISE } = require('devtools/client/shared/redux/middleware/promise');
+const {
+  getSource, getBreakpoint, getBreakpoints, makeLocationId
+} = require('../queries');
+
+// Because breakpoints are just simple data structures, we still need
+// a way to lookup the actual client instance to talk to the server.
+// We keep an internal database of clients based off of actor ID.
+const BREAKPOINT_CLIENT_STORE = new Map();
+
+function setBreakpointClient(actor, client) {
+  BREAKPOINT_CLIENT_STORE.set(actor, client);
+}
+
+function getBreakpointClient(actor) {
+  return BREAKPOINT_CLIENT_STORE.get(actor);
+}
+
+function enableBreakpoint(location) {
+  // Enabling is exactly the same as adding. It will use the existing
+  // breakpoint that still stored.
+  return addBreakpoint(location);
+}
+
+function _breakpointExists(state, location) {
+  const currentBp = getBreakpoint(state, location);
+  return currentBp && !currentBp.disabled;
+}
+
+function _getOrCreateBreakpoint(state, location, condition) {
+  return getBreakpoint(state, location) || { location, condition };
+}
+
+function addBreakpoint(location, condition) {
+  return (dispatch, getState) => {
+    if (_breakpointExists(getState(), location)) {
+      return;
+    }
+
+    const bp = _getOrCreateBreakpoint(getState(), location, condition);
+
+    return dispatch({
+      type: constants.ADD_BREAKPOINT,
+      breakpoint: bp,
+      condition: condition,
+      [PROMISE]: Task.spawn(function*() {
+        const sourceClient = gThreadClient.source(
+          getSource(getState(), bp.location.actor)
+        );
+        const [response, bpClient] = yield rdpInvoke(sourceClient, sourceClient.setBreakpoint, {
+          line: bp.location.line,
+          column: bp.location.column,
+          condition: bp.condition
+        });
+        const { isPending, actualLocation } = response;
+
+        // Save the client instance
+        setBreakpointClient(bpClient.actor, bpClient);
+
+        return {
+          text: DebuggerView.editor.getText(bp.location.line - 1).trim(),
+
+          // If the breakpoint response has an "actualLocation" attached, then
+          // the original requested placement for the breakpoint wasn't
+          // accepted.
+          actualLocation: isPending ? null : actualLocation,
+          actor: bpClient.actor
+        };
+      })
+    });
+  }
+}
+
+function disableBreakpoint(location) {
+  return _removeOrDisableBreakpoint(location, true);
+}
+
+function removeBreakpoint(location) {
+  return _removeOrDisableBreakpoint(location);
+}
+
+function _removeOrDisableBreakpoint(location, isDisabled) {
+  return (dispatch, getState) => {
+    let bp = getBreakpoint(getState(), location);
+    if (!bp) {
+      throw new Error('attempt to remove breakpoint that does not exist');
+    }
+    if (bp.loading) {
+      // TODO(jwl): make this wait until the breakpoint is saved if it
+      // is still loading
+      throw new Error('attempt to remove unsaved breakpoint');
+    }
+
+    const bpClient = getBreakpointClient(bp.actor);
+
+    return dispatch({
+      type: constants.REMOVE_BREAKPOINT,
+      breakpoint: bp,
+      disabled: isDisabled,
+      [PROMISE]: rdpInvoke(bpClient, bpClient.remove)
+    });
+  }
+}
+
+function removeAllBreakpoints() {
+  return (dispatch, getState) => {
+    const breakpoints = getBreakpoints(getState());
+    const activeBreakpoints = breakpoints.filter(bp => !bp.disabled);
+    activeBreakpoints.forEach(bp => removeBreakpoint(bp.location));
+  }
+}
+
+/**
+ * Update the condition of a breakpoint.
+ *
+ * @param object aLocation
+ *        @see DebuggerController.Breakpoints.addBreakpoint
+ * @param string aClients
+ *        The condition to set on the breakpoint
+ * @return object
+ *         A promise that will be resolved with the breakpoint client
+ */
+function setBreakpointCondition(location, condition) {
+  return (dispatch, getState) => {
+    const bp = getBreakpoint(getState(), location);
+    if (!bp) {
+      throw new Error("Breakpoint does not exist at the specified location");
+    }
+    if (bp.loading){
+      // TODO(jwl): when this function is called, make sure the action
+      // creator waits for the breakpoint to exist
+      throw new Error("breakpoint must be saved");
+    }
+
+    const bpClient = getBreakpointClient(bp.actor);
+
+    return dispatch({
+      type: constants.SET_BREAKPOINT_CONDITION,
+      breakpoint: bp,
+      condition: condition,
+      [PROMISE]: Task.spawn(function*() {
+        const newClient = yield bpClient.setCondition(gThreadClient, condition);
+
+        // Remove the old instance and save the new one
+        setBreakpointClient(bpClient.actor, null);
+        setBreakpointClient(newClient.actor, newClient);
+
+        return { actor: newClient.actor };
+      })
+    });
+  };
+}
+
+module.exports = {
+  enableBreakpoint,
+  addBreakpoint,
+  disableBreakpoint,
+  removeBreakpoint,
+  removeAllBreakpoints,
+  setBreakpointCondition
+}
--- a/devtools/client/debugger/content/actions/moz.build
+++ b/devtools/client/debugger/content/actions/moz.build
@@ -1,8 +1,10 @@
 # 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/.
 
 DevToolsModules(
+    'breakpoints.js',
     'event-listeners.js',
+    'sources.js'
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/content/actions/sources.js
@@ -0,0 +1,283 @@
+/* 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 constants = require('../constants');
+const promise = require('promise');
+const { rdpInvoke } = require('../utils');
+const { dumpn } = require("devtools/shared/DevToolsUtils");
+const { PROMISE, HISTOGRAM_ID } = require('devtools/client/shared/redux/middleware/promise');
+const { getSource, getSourceText } = require('../queries');
+
+const NEW_SOURCE_IGNORED_URLS = ["debugger eval code", "XStringBundle"];
+const FETCH_SOURCE_RESPONSE_DELAY = 200; // ms
+
+function getSourceClient(source) {
+  return gThreadClient.source(source);
+}
+
+/**
+ * Handler for the debugger client's unsolicited newSource notification.
+ */
+function newSource(source) {
+  return dispatch => {
+    // Ignore bogus scripts, e.g. generated from 'clientEvaluate' packets.
+    if (NEW_SOURCE_IGNORED_URLS.indexOf(source.url) != -1) {
+      return;
+    }
+
+    // Signal that a new source has been added.
+    window.emit(EVENTS.NEW_SOURCE);
+
+    return dispatch({
+      type: constants.ADD_SOURCE,
+      source: source
+    });
+  };
+}
+
+function selectSource(source, opts) {
+  return (dispatch, getState) => {
+    if (!gThreadClient) {
+      // No connection, do nothing. This happens when the debugger is
+      // shut down too fast and it tries to display a default source.
+      return;
+    }
+
+    source = getSource(getState(), source.actor);
+
+    // Make sure to start a request to load the source text.
+    dispatch(loadSourceText(source));
+
+    dispatch({
+      type: constants.SELECT_SOURCE,
+      source: source,
+      opts: opts
+    });
+  };
+}
+
+function loadSources() {
+  return {
+    type: constants.LOAD_SOURCES,
+    [PROMISE]: Task.spawn(function*() {
+      const response = yield rdpInvoke(gThreadClient, gThreadClient.getSources);
+
+      // Top-level breakpoints may pause the entire loading process
+      // because scripts are executed as they are loaded, so the
+      // engine may pause in the middle of loading all the sources.
+      // This is relatively harmless, as individual `newSource`
+      // notifications are fired for each script and they will be
+      // added to the UI through that.
+      if (!response.sources) {
+        dumpn(
+          "Error getting sources, probably because a top-level " +
+          "breakpoint was hit while executing them"
+        );
+        return;
+      }
+
+      // Ignore bogus scripts, e.g. generated from 'clientEvaluate' packets.
+      return response.sources.filter(source => {
+        return NEW_SOURCE_IGNORED_URLS.indexOf(source.url) === -1;
+      });
+    })
+  }
+}
+
+/**
+ * Set the black boxed status of the given source.
+ *
+ * @param Object aSource
+ *        The source form.
+ * @param bool aBlackBoxFlag
+ *        True to black box the source, false to un-black box it.
+ * @returns Promise
+ *          A promize that resolves to [aSource, isBlackBoxed] or rejects to
+ *          [aSource, error].
+ */
+function blackbox(source, shouldBlackBox) {
+  const client = getSourceClient(source);
+
+  return {
+    type: constants.BLACKBOX,
+    source: source,
+    [PROMISE]: Task.spawn(function*() {
+      yield rdpInvoke(client,
+                      shouldBlackBox ? client.blackBox : client.unblackBox);
+      return {
+        isBlackBoxed: shouldBlackBox
+      }
+    })
+  };
+}
+
+/**
+ * Toggle the pretty printing of a source's text. All subsequent calls to
+ * |getText| will return the pretty-toggled text. Nothing will happen for
+ * non-javascript files.
+ *
+ * @param Object aSource
+ *        The source form from the RDP.
+ * @returns Promise
+ *          A promise that resolves to [aSource, prettyText] or rejects to
+ *          [aSource, error].
+ */
+function togglePrettyPrint(source) {
+  return (dispatch, getState) => {
+    const sourceClient = getSourceClient(source);
+    const wantPretty = !source.isPrettyPrinted;
+
+    return dispatch({
+      type: constants.TOGGLE_PRETTY_PRINT,
+      source: source,
+      [PROMISE]: Task.spawn(function*() {
+        let response;
+
+        // Only attempt to pretty print JavaScript sources.
+        const sourceText = getSourceText(getState(), source.actor);
+        const contentType = sourceText ? sourceText.contentType : null;
+        if (!SourceUtils.isJavaScript(source.url, contentType)) {
+          throw new Error("Can't prettify non-javascript files.");
+        }
+
+        if (wantPretty) {
+          response = yield rdpInvoke(sourceClient,
+                                     sourceClient.prettyPrint,
+                                     Prefs.editorTabSize);
+        }
+        else {
+          response = yield rdpInvoke(sourceClient,
+                                     sourceClient.disablePrettyPrint);
+        }
+
+        // Remove the cached source AST from the Parser, to avoid getting
+        // wrong locations when searching for functions.
+        DebuggerController.Parser.clearSource(source.url);
+
+        return {
+          isPrettyPrinted: wantPretty,
+          text: response.source,
+          contentType: response.contentType
+        };
+      })
+    });
+  };
+}
+
+function loadSourceText(source) {
+  return (dispatch, getState) => {
+    // Fetch the source text only once.
+    let textInfo = getSourceText(getState(), source.actor);
+    if (textInfo) {
+      // It's already loaded or is loading
+      return promise.resolve(textInfo);
+    }
+
+    const sourceClient = getSourceClient(source);
+
+    return dispatch({
+      type: constants.LOAD_SOURCE_TEXT,
+      source: source,
+      [PROMISE]: Task.spawn(function*() {
+        let transportType = gClient.localTransport ? "_LOCAL" : "_REMOTE";
+        let histogramId = "DEVTOOLS_DEBUGGER_DISPLAY_SOURCE" + transportType + "_MS";
+        let histogram = Services.telemetry.getHistogramById(histogramId);
+        let startTime = Date.now();
+
+        const response = yield rdpInvoke(sourceClient, sourceClient.source);
+
+        histogram.add(Date.now() - startTime);
+
+        // Automatically pretty print if enabled and the test is
+        // detected to be "minified"
+        if (Prefs.autoPrettyPrint &&
+            !source.isPrettyPrinted &&
+            SourceUtils.isMinified(source.actor, response.source)) {
+          dispatch(togglePrettyPrint(source));
+        }
+
+        return { text: response.source,
+                 contentType: response.contentType };
+      })
+    });
+  }
+}
+
+/**
+ * Starts fetching all the sources, silently.
+ *
+ * @param array aUrls
+ *        The urls for the sources to fetch. If fetching a source's text
+ *        takes too long, it will be discarded.
+ * @return object
+ *         A promise that is resolved after source texts have been fetched.
+ */
+function getTextForSources(actors) {
+  return (dispatch, getState) => {
+    let deferred = promise.defer();
+    let pending = new Set(actors);
+    let fetched = [];
+
+    // Can't use promise.all, because if one fetch operation is rejected, then
+    // everything is considered rejected, thus no other subsequent source will
+    // be getting fetched. We don't want that. Something like Q's allSettled
+    // would work like a charm here.
+
+    // Try to fetch as many sources as possible.
+    for (let actor of actors) {
+      let source = getSource(getState(), actor);
+      dispatch(loadSourceText(source)).then(({ text, contentType }) => {
+        onFetch([source, text, contentType]);
+      }, err => {
+        onError(source, err);
+      });
+    }
+
+    setTimeout(onTimeout, FETCH_SOURCE_RESPONSE_DELAY);
+
+    /* Called if fetching a source takes too long. */
+    function onTimeout() {
+      pending = new Set();
+      maybeFinish();
+    }
+
+    /* Called if fetching a source finishes successfully. */
+    function onFetch([aSource, aText, aContentType]) {
+      // If fetching the source has previously timed out, discard it this time.
+      if (!pending.has(aSource.actor)) {
+        return;
+      }
+      pending.delete(aSource.actor);
+      fetched.push([aSource.actor, aText, aContentType]);
+      maybeFinish();
+    }
+
+    /* Called if fetching a source failed because of an error. */
+    function onError([aSource, aError]) {
+      pending.delete(aSource.actor);
+      maybeFinish();
+    }
+
+    /* Called every time something interesting happens while fetching sources. */
+    function maybeFinish() {
+      if (pending.size == 0) {
+        // Sort the fetched sources alphabetically by their url.
+        deferred.resolve(fetched.sort(([aFirst], [aSecond]) => aFirst > aSecond));
+      }
+    }
+
+    return deferred.promise;
+  };
+}
+
+module.exports = {
+  newSource,
+  selectSource,
+  loadSources,
+  blackbox,
+  togglePrettyPrint,
+  loadSourceText,
+  getTextForSources
+};
--- a/devtools/client/debugger/content/constants.js
+++ b/devtools/client/debugger/content/constants.js
@@ -1,7 +1,23 @@
 /* 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";
 
 exports.UPDATE_EVENT_BREAKPOINTS = 'UPDATE_EVENT_BREAKPOINTS';
 exports.FETCH_EVENT_LISTENERS = 'FETCH_EVENT_LISTENERS';
+
+exports.TOGGLE_PRETTY_PRINT = 'TOGGLE_PRETTY_PRINT';
+exports.BLACKBOX = 'BLACKBOX';
+
+exports.ADD_BREAKPOINT = 'ADD_BREAKPOINT';
+exports.REMOVE_BREAKPOINT = 'REMOVE_BREAKPOINT';
+exports.ENABLE_BREAKPOINT = 'ENABLE_BREAKPOINT';
+exports.DISABLE_BREAKPOINT = 'DISABLE_BREAKPOINT';
+exports.SET_BREAKPOINT_CONDITION = 'SET_BREAKPOINT_CONDITION'
+
+exports.ADD_SOURCE = 'ADD_SOURCE';
+exports.LOAD_SOURCES = 'LOAD_SOURCES';
+exports.LOAD_SOURCE_TEXT = 'LOAD_SOURCE_TEXT';
+exports.SELECT_SOURCE = 'SELECT_SOURCE';
+exports.UNLOAD = 'UNLOAD';
+exports.RELOAD = 'RELOAD';
--- a/devtools/client/debugger/content/globalActions.js
+++ b/devtools/client/debugger/content/globalActions.js
@@ -1,9 +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";
 
 const constants = require('./constants');
 
-// No global actions right now, but I'm sure there will be soon.
-module.exports = {};
+// Fired when the page is being unloaded, for example when it's being
+// navigated away from.
+function unload() {
+  return {
+    type: constants.UNLOAD
+  }
+}
+
+module.exports = { unload };
--- a/devtools/client/debugger/content/moz.build
+++ b/devtools/client/debugger/content/moz.build
@@ -7,10 +7,11 @@ DIRS += [
     'actions',
     'reducers',
     'views',
 ]
 
 DevToolsModules(
     'constants.js',
     'globalActions.js',
+    'queries.js',
     'utils.js'
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/content/queries.js
@@ -0,0 +1,70 @@
+
+function getSource(state, actor) {
+  return state.sources.sources[actor];
+}
+
+function getSources(state) {
+  return state.sources.sources;
+}
+
+function getSourceCount(state) {
+  return Object.keys(state.sources.sources).length;
+}
+
+function getSourceByURL(state, url) {
+  for(let k in state.sources.sources) {
+    const source = state.sources.sources[k];
+    if (source.url === url) {
+      return source;
+    }
+  }
+}
+
+function getSourceByActor(state, actor) {
+  for(let k in state.sources.sources) {
+    const source = state.sources.sources[k];
+    if (source.actor === actor) {
+      return source;
+    }
+  }
+}
+
+function getSelectedSource(state) {
+  return state.sources.sources[state.sources.selectedSource];
+}
+
+function getSelectedSourceOpts(state) {
+  return state.sources.selectedSourceOpts;
+}
+
+function getSourceText(state, actor) {
+  return state.sources.sourcesText[actor];
+}
+
+function getBreakpoints(state) {
+  return Object.keys(state.breakpoints.breakpoints).map(k => {
+    return state.breakpoints.breakpoints[k];
+  });
+}
+
+function getBreakpoint(state, location) {
+  return state.breakpoints.breakpoints[makeLocationId(location)];
+}
+
+function makeLocationId(location) {
+  return location.actor + ':' + location.line.toString();
+}
+
+module.exports = {
+  getSource,
+  getSources,
+  getSourceCount,
+  getSourceByURL,
+  getSourceByActor,
+  getSelectedSource,
+  getSelectedSourceOpts,
+  getSourceText,
+  getBreakpoint,
+  getBreakpoints,
+  makeLocationId
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/content/reducers/async-requests.js
@@ -0,0 +1,31 @@
+/* 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 constants = require('../constants');
+const initialState = [];
+
+function update(state = initialState, action, emitChange) {
+  const { seqId } = action;
+
+  if (action.type === constants.UNLOAD) {
+    return initialState;
+  }
+  else if (seqId) {
+    let newState;
+    if (action.status === 'start') {
+      newState = [...state, seqId];
+    }
+    else if (action.status === 'error' || action.status === 'done') {
+      newState = state.filter(id => id !== seqId);
+    }
+
+    emitChange('open-requests', newState);
+    return newState;
+  }
+
+  return state;
+}
+
+module.exports = update;
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/content/reducers/breakpoints.js
@@ -0,0 +1,128 @@
+/* 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 constants = require('../constants');
+const Immutable = require('devtools/client/shared/vendor/seamless-immutable');
+const { mergeIn, setIn, deleteIn } = require('../utils');
+const { makeLocationId } = require('../queries');
+
+const initialState = Immutable({
+  breakpoints: {}
+});
+
+function update(state = initialState, action, emitChange) {
+  switch(action.type) {
+  case constants.ADD_BREAKPOINT: {
+    const id = makeLocationId(action.breakpoint.location);
+
+    if (action.status === 'start') {
+      const existingBp = state.breakpoints[id];
+      const bp = existingBp || Immutable(action.breakpoint);
+
+      state = setIn(state, ['breakpoints', id], bp.merge({
+        disabled: false,
+        loading: true,
+        condition: action.condition || bp.condition || undefined
+      }));
+
+      emitChange(existingBp ? "breakpoint-enabled" : "breakpoint-added",
+                 state.breakpoints[id]);
+      return state;
+    }
+    else if (action.status === 'done') {
+      const { actor, text } = action.value;
+      let { actualLocation } = action.value;
+
+      // If the breakpoint moved, update the map
+      if (actualLocation) {
+        // XXX Bug 1227417: The `setBreakpoint` RDP request rdp
+        // request returns an `actualLocation` field that doesn't
+        // conform to the regular { actor, line } location shape, but
+        // it has a `source` field. We should fix that.
+        actualLocation = { actor: actualLocation.source.actor,
+                           line: actualLocation.line };
+
+        state = deleteIn(state, ['breakpoints', id]);
+
+        const movedId = makeLocationId(actualLocation);
+        const currentBp = state.breakpoints[movedId] || Immutable(action.breakpoint);
+        const prevLocation = action.breakpoint.location;
+        const newBp = currentBp.merge({ location: actualLocation });
+        state = setIn(state, ['breakpoints', movedId], newBp);
+
+        emitChange('breakpoint-moved', {
+          breakpoint: newBp,
+          prevLocation: prevLocation
+        });
+      }
+
+      const finalLocation = (
+        actualLocation ? actualLocation : action.breakpoint.location
+      );
+      const finalLocationId = makeLocationId(finalLocation);
+      state = mergeIn(state, ['breakpoints', finalLocationId], {
+        disabled: false,
+        loading: false,
+        actor: actor,
+        text: text
+      });
+      emitChange('breakpoint-updated', state.breakpoints[finalLocationId]);
+      return state;
+    }
+    else if (action.status === 'error') {
+      // Remove the optimistic update
+      emitChange('breakpoint-removed', state.breakpoints[id]);
+      return deleteIn(state, ['breakpoints', id]);
+    }
+      break;
+  }
+
+  case constants.REMOVE_BREAKPOINT: {
+    if (action.status === 'done') {
+      const id = makeLocationId(action.breakpoint.location);
+      const bp = state.breakpoints[id];
+
+      if (action.disabled) {
+        state = mergeIn(state, ['breakpoints', id],
+                        { loading: false, disabled: true });
+        emitChange('breakpoint-disabled', state.breakpoints[id]);
+        return state;
+      }
+
+      state = deleteIn(state, ['breakpoints', id]);
+      emitChange('breakpoint-removed', bp);
+      return state;
+    }
+    break;
+  }
+
+  case constants.SET_BREAKPOINT_CONDITION: {
+    const id = makeLocationId(action.breakpoint.location);
+    const bp = state.breakpoints[id];
+
+    if (action.status === 'start') {
+      return mergeIn(state, ['breakpoints', id], {
+        loading: true,
+        condition: action.condition
+      });
+    }
+    else if (action.status === 'done') {
+      return mergeIn(state, ['breakpoints', id], {
+        loading: false,
+        // Setting a condition creates a new breakpoint client as of
+        // now, so we need to update the actor
+        actor: action.value.actor
+      });
+    }
+    else if (action.status === 'error') {
+      return deleteIn(state, ['breakpoints', id]);
+    }
+    break;
+  }}
+
+  return state;
+}
+
+module.exports = update;
--- a/devtools/client/debugger/content/reducers/event-listeners.js
+++ b/devtools/client/debugger/content/reducers/event-listeners.js
@@ -12,26 +12,26 @@ const initialState = {
   listeners: [],
   fetchingListeners: false,
 };
 
 function update(state = initialState, action, emit) {
   switch(action.type) {
   case constants.UPDATE_EVENT_BREAKPOINTS:
     state.activeEventNames = action.eventNames;
-    emit("@redux:activeEventNames", state.activeEventNames);
+    emit("activeEventNames", state.activeEventNames);
     break;
   case constants.FETCH_EVENT_LISTENERS:
     if (action.status === "begin") {
       state.fetchingListeners = true;
     }
     else if (action.status === "done") {
       state.fetchingListeners = false;
       state.listeners = action.listeners;
-      emit("@redux:listeners", state.listeners);
+      emit("event-listeners", state.listeners);
     }
     break;
   }
 
   return state;
 }
 
 module.exports = update;
--- a/devtools/client/debugger/content/reducers/index.js
+++ b/devtools/client/debugger/content/reducers/index.js
@@ -1,8 +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";
 
 const eventListeners = require('./event-listeners');
+const sources = require('./sources');
+const breakpoints = require('./breakpoints');
+const asyncRequests = require('./async-requests');
 
-exports.eventListeners = eventListeners;
+module.exports = {
+  eventListeners,
+  sources,
+  breakpoints,
+  asyncRequests
+};
--- a/devtools/client/debugger/content/reducers/moz.build
+++ b/devtools/client/debugger/content/reducers/moz.build
@@ -1,9 +1,12 @@
 # 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/.
 
 DevToolsModules(
+    'async-requests.js',
+    'breakpoints.js',
     'event-listeners.js',
-    'index.js'
+    'index.js',
+    'sources.js'
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/content/reducers/sources.js
@@ -0,0 +1,117 @@
+/* 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 constants = require('../constants');
+const Immutable = require('devtools/client/shared/vendor/seamless-immutable');
+const { mergeIn, setIn } = require('../utils');
+
+const initialState = Immutable({
+  sources: {},
+  selectedSource: null,
+  selectedSourceOpts: null,
+  sourcesText: {}
+});
+
+function update(state = initialState, action, emitChange) {
+  switch(action.type) {
+  case constants.ADD_SOURCE:
+    emitChange('source', action.source);
+    return mergeIn(state, ['sources', action.source.actor], action.source);
+
+  case constants.LOAD_SOURCES:
+    if (action.status === 'done') {
+      // We don't need to actually load the sources into the state.
+      // Loading sources actually forces the server to emit several
+      // individual newSources packets which will eventually fire
+      // ADD_SOURCE actions.
+      //
+      // We still emit this event so that the UI can show an "empty
+      // text" label if no sources were loaded.
+      emitChange('sources', state.sources);
+    }
+    break;
+
+  case constants.SELECT_SOURCE:
+    emitChange('source-selected', action.source);
+    return state.merge({
+      selectedSource: action.source.actor,
+      selectedSourceOpts: action.opts
+    });
+
+  case constants.LOAD_SOURCE_TEXT: {
+    const s = _updateText(state, action);
+    emitChange('source-text-loaded', s.sources[action.source.actor]);
+    return s;
+  }
+
+  case constants.BLACKBOX:
+    if (action.status === 'done') {
+      const s = mergeIn(state,
+                        ['sources', action.source.actor, 'isBlackBoxed'],
+                        action.value.isBlackBoxed);
+      emitChange('blackboxed', s.sources[action.source.actor]);
+      return s;
+    }
+    break;
+
+  case constants.TOGGLE_PRETTY_PRINT:
+    let s = state;
+    if (action.status === "error") {
+      s = mergeIn(state, ['sourcesText', action.source.actor], {
+        loading: false
+      });
+
+      // If it errored, just display the source as it way before.
+      emitChange('prettyprinted', s.sources[action.source.actor]);
+    }
+    else {
+      s = _updateText(state, action);
+      // Don't do this yet, the progress bar is still imperatively shown
+      // from the source view. We will fix in the next iteration.
+      // emitChange('source-text-loaded', s.sources[action.source.actor]);
+
+      if (action.status === 'done') {
+        s = mergeIn(s,
+                    ['sources', action.source.actor, 'isPrettyPrinted'],
+                    action.value.isPrettyPrinted);
+        emitChange('prettyprinted', s.sources[action.source.actor]);
+      }
+    }
+    return s;
+
+  case constants.UNLOAD:
+    // Reset the entire state to just the initial state, a blank state
+    // if you will.
+    return initialState;
+  }
+
+  return state;
+}
+
+function _updateText(state, action) {
+  const { source } = action;
+
+  if (action.status === 'start') {
+    // Merge this in, don't set it. That way the previous value is
+    // still stored here, and we can retrieve it if whatever we're
+    // doing fails.
+    return mergeIn(state, ['sourcesText', source.actor], {
+      loading: true
+    });
+  }
+  else if (action.status === 'error') {
+    return setIn(state, ['sourcesText', source.actor], {
+      error: action.error
+    });
+  }
+  else {
+    return setIn(state, ['sourcesText', source.actor], {
+      text: action.value.text,
+      contentType: action.value.contentType
+    });
+  }
+}
+
+module.exports = update;
--- a/devtools/client/debugger/content/utils.js
+++ b/devtools/client/debugger/content/utils.js
@@ -1,26 +1,31 @@
 /* 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 { promiseInvoke } = require("devtools/shared/async-utils");
 const { reportException } = require("devtools/shared/DevToolsUtils");
 
+// RDP utils
+
 function rdpInvoke(client, method, ...args) {
-  return promiseInvoke(client, method, ...args)
+  return (promiseInvoke(client, method, ...args)
     .then((packet) => {
-      let { error, message } = packet;
-      if (error) {
-        throw new Error(error + ": " + message);
+      if (packet.error) {
+        let { error, message } = packet;
+        const err = new Error(error + ": " + message);
+        err.rdpError = error;
+        err.rdpMessage = message;
+        throw err;
       }
 
       return packet;
-    });
+    }));
 }
 
 function asPaused(client, func) {
   if (client.state != "paused") {
     return Task.spawn(function*() {
       yield rdpInvoke(client, client.interrupt);
       let result;
 
@@ -37,9 +42,63 @@ function asPaused(client, func) {
       yield rdpInvoke(client, client.resume);
       return result;
     });
   } else {
     return func();
   }
 }
 
-module.exports = { rdpInvoke, asPaused };
+function handleError(err) {
+  reportException("promise", err.toString());
+}
+
+function onReducerEvents(controller, listeners, thisContext) {
+  Object.keys(listeners).forEach(name => {
+    const listener = listeners[name];
+    controller.onChange(name, payload => {
+      listener.call(thisContext, payload);
+    });
+  });
+}
+
+function _getIn(destObj, path) {
+  return path.reduce(function(acc, name) {
+    return acc[name];
+  }, destObj);
+}
+
+function mergeIn(destObj, path, value) {
+  path = [...path];
+  path.reverse();
+  var obj = path.reduce(function(acc, name) {
+    return { [name]: acc };
+  }, value);
+
+  return destObj.merge(obj, { deep: true });
+}
+
+function setIn(destObj, path, value) {
+  destObj = mergeIn(destObj, path, null);
+  return mergeIn(destObj, path, value);
+}
+
+function updateIn(destObj, path, fn) {
+  return setIn(destObj, path, fn(_getIn(destObj, path)));
+}
+
+function deleteIn(destObj, path) {
+  const objPath = path.slice(0, -1);
+  const propName = path[path.length - 1];
+  const obj = _getIn(destObj, objPath);
+  return setIn(destObj, objPath, obj.without(propName));
+}
+
+module.exports = {
+  rdpInvoke,
+  asPaused,
+  handleError,
+  onReducerEvents,
+  mergeIn,
+  setIn,
+  updateIn,
+  deleteIn
+};
--- a/devtools/client/debugger/content/views/event-listeners-view.js
+++ b/devtools/client/debugger/content/views/event-listeners-view.js
@@ -4,29 +4,26 @@
 "use strict";
 
 const actions = require('../actions/event-listeners');
 const { bindActionCreators } = require('devtools/client/shared/vendor/redux');
 
 /**
  * Functions handling the event listeners UI.
  */
-function EventListenersView(store, DebuggerController) {
+function EventListenersView(controller) {
   dumpn("EventListenersView was instantiated");
 
-  this.actions = bindActionCreators(actions, store.dispatch);
-  this.getState = () => store.getState().eventListeners;
+  this.actions = bindActionCreators(actions, controller.dispatch);
+  this.getState = () => controller.getState().eventListeners;
 
   this._onCheck = this._onCheck.bind(this);
   this._onClick = this._onClick.bind(this);
-  this._onListeners = this._onListeners.bind(this);
 
-  this.Breakpoints = DebuggerController.Breakpoints;
-  this.controller = DebuggerController;
-  this.controller.on("@redux:listeners", this._onListeners);
+  controller.onChange("event-listeners", this.renderListeners.bind(this));
 }
 
 EventListenersView.prototype = Heritage.extend(WidgetMethods, {
   /**
    * Initialization function, called when the debugger is started.
    */
   initialize: function() {
     dumpn("Initializing the EventListenersView");
@@ -47,20 +44,18 @@ EventListenersView.prototype = Heritage.
   },
 
   /**
    * Destruction function, called when the debugger is closed.
    */
   destroy: function() {
     dumpn("Destroying the EventListenersView");
 
-    this.controller.off("@redux:listeners", this._onListeners);
     this.widget.removeEventListener("check", this._onCheck, false);
     this.widget.removeEventListener("click", this._onClick, false);
-    this.controller = this.Breakpoints = null;
   },
 
   renderListeners: function(listeners) {
     listeners.forEach(listener => {
       this.addListener(listener, { staged: true });
     });
 
     // Flushes all the prepared events into the event listeners container.
@@ -281,22 +276,15 @@ EventListenersView.prototype = Heritage.
     // when retrieving the target's item, to ignore the checkbox.
     let eventItem = this.getItemForElement(target, { noSiblings: true });
     if (eventItem) {
       let newState = eventItem.attachment.checkboxState ^= 1;
       this.callMethod("checkItem", eventItem.target, newState);
     }
   },
 
-  /**
-   * Called when listeners change.
-   */
-  _onListeners: function(_, listeners) {
-    this.renderListeners(listeners);
-  },
-
   _eventCheckboxTooltip: "",
   _onSelectorString: "",
   _inSourceString: "",
   _inNativeCodeString: ""
 });
 
 module.exports = EventListenersView;
--- a/devtools/client/debugger/content/views/moz.build
+++ b/devtools/client/debugger/content/views/moz.build
@@ -1,8 +1,9 @@
 # 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/.
 
 DevToolsModules(
-    'event-listeners-view.js'
+    'event-listeners-view.js',
+    'sources-view.js'
 )
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/content/views/sources-view.js
@@ -0,0 +1,1317 @@
+/* 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 utils = require('../utils');
+const {
+  getSelectedSource,
+  getSourceByURL,
+  getBreakpoint,
+  getBreakpoints,
+  makeLocationId
+} = require('../queries');
+const actions = Object.assign(
+  {},
+  require('../actions/sources'),
+  require('../actions/breakpoints')
+);
+const { bindActionCreators } = require('devtools/client/shared/vendor/redux');
+
+const NEW_SOURCE_DISPLAY_DELAY = 200; // ms
+const FUNCTION_SEARCH_POPUP_POSITION = "topcenter bottomleft";
+const BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH = 1000; // chars
+const BREAKPOINT_CONDITIONAL_POPUP_POSITION = "before_start";
+const BREAKPOINT_CONDITIONAL_POPUP_OFFSET_X = 7; // px
+const BREAKPOINT_CONDITIONAL_POPUP_OFFSET_Y = -3; // px
+
+/**
+ * Functions handling the sources UI.
+ */
+function SourcesView(controller, DebuggerView) {
+  dumpn("SourcesView was instantiated");
+
+  utils.onReducerEvents(controller, {
+    "sources": this.renderSources,
+    "source": this.renderSource,
+    "blackboxed": this.renderBlackBoxed,
+    "prettyprinted": this.updateToolbarButtonsState,
+    "source-selected": this.renderSourceSelected,
+    "breakpoint-updated": bp => this.renderBreakpoint(bp),
+    "breakpoint-enabled": bp => this.renderBreakpoint(bp),
+    "breakpoint-disabled": bp => this.renderBreakpoint(bp),
+    "breakpoint-removed": bp => this.renderBreakpoint(bp, true),
+  }, this);
+
+  this.getState = controller.getState;
+  this.actions = bindActionCreators(actions, controller.dispatch);
+  this.DebuggerView = DebuggerView;
+  this.Parser = DebuggerController.Parser;
+
+  this.togglePrettyPrint = this.togglePrettyPrint.bind(this);
+  this.toggleBlackBoxing = this.toggleBlackBoxing.bind(this);
+  this.toggleBreakpoints = this.toggleBreakpoints.bind(this);
+
+  this._onEditorCursorActivity = this._onEditorCursorActivity.bind(this);
+  this._onMouseDown = this._onMouseDown.bind(this);
+  this._onSourceSelect = this._onSourceSelect.bind(this);
+  this._onStopBlackBoxing = this._onStopBlackBoxing.bind(this);
+  this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
+  this._onBreakpointClick = this._onBreakpointClick.bind(this);
+  this._onBreakpointCheckboxClick = this._onBreakpointCheckboxClick.bind(this);
+  this._onConditionalPopupShowing = this._onConditionalPopupShowing.bind(this);
+  this._onConditionalPopupShown = this._onConditionalPopupShown.bind(this);
+  this._onConditionalPopupHiding = this._onConditionalPopupHiding.bind(this);
+  this._onConditionalTextboxKeyPress = this._onConditionalTextboxKeyPress.bind(this);
+  this._onCopyUrlCommand = this._onCopyUrlCommand.bind(this);
+  this._onNewTabCommand = this._onNewTabCommand.bind(this);
+}
+
+SourcesView.prototype = Heritage.extend(WidgetMethods, {
+  /**
+   * Initialization function, called when the debugger is started.
+   */
+  initialize: function() {
+    dumpn("Initializing the SourcesView");
+
+    this.widget = new SideMenuWidget(document.getElementById("sources"), {
+      contextMenu: document.getElementById("debuggerSourcesContextMenu"),
+      showArrows: true
+    });
+
+    this._preferredSourceURL = null;
+    this._unnamedSourceIndex = 0;
+    this.emptyText = L10N.getStr("noSourcesText");
+    this._blackBoxCheckboxTooltip = L10N.getStr("blackBoxCheckboxTooltip");
+
+    this._commandset = document.getElementById("debuggerCommands");
+    this._popupset = document.getElementById("debuggerPopupset");
+    this._cmPopup = document.getElementById("sourceEditorContextMenu");
+    this._cbPanel = document.getElementById("conditional-breakpoint-panel");
+    this._cbTextbox = document.getElementById("conditional-breakpoint-panel-textbox");
+    this._blackBoxButton = document.getElementById("black-box");
+    this._stopBlackBoxButton = document.getElementById("black-boxed-message-button");
+    this._prettyPrintButton = document.getElementById("pretty-print");
+    this._toggleBreakpointsButton = document.getElementById("toggle-breakpoints");
+    this._newTabMenuItem = document.getElementById("debugger-sources-context-newtab");
+    this._copyUrlMenuItem = document.getElementById("debugger-sources-context-copyurl");
+
+    this._noResultsFoundToolTip = new Tooltip(document);
+    this._noResultsFoundToolTip.defaultPosition = FUNCTION_SEARCH_POPUP_POSITION;
+
+    if (Prefs.prettyPrintEnabled) {
+      this._prettyPrintButton.removeAttribute("hidden");
+    }
+
+    this._editorContainer = document.getElementById("editor");
+    this._editorContainer.addEventListener("mousedown", this._onMouseDown, false);
+
+    this.widget.addEventListener("select", this._onSourceSelect, false);
+
+    this._stopBlackBoxButton.addEventListener("click", this._onStopBlackBoxing, false);
+    this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing, false);
+    this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown, false);
+    this._cbPanel.addEventListener("popuphiding", this._onConditionalPopupHiding, false);
+    this._cbTextbox.addEventListener("keypress", this._onConditionalTextboxKeyPress, false);
+    this._copyUrlMenuItem.addEventListener("command", this._onCopyUrlCommand, false);
+    this._newTabMenuItem.addEventListener("command", this._onNewTabCommand, false);
+
+    this.allowFocusOnRightClick = true;
+    this.autoFocusOnSelection = false;
+    this.autoFocusOnFirstItem = false;
+
+    // Sort the contents by the displayed label.
+    this.sortContents((aFirst, aSecond) => {
+      return +(aFirst.attachment.label.toLowerCase() >
+               aSecond.attachment.label.toLowerCase());
+    });
+
+    // Sort known source groups towards the end of the list
+    this.widget.groupSortPredicate = function(a, b) {
+      if ((a in KNOWN_SOURCE_GROUPS) == (b in KNOWN_SOURCE_GROUPS)) {
+        return a.localeCompare(b);
+      }
+      return (a in KNOWN_SOURCE_GROUPS) ? 1 : -1;
+    };
+
+    this._addCommands();
+  },
+
+  /**
+   * Destruction function, called when the debugger is closed.
+   */
+  destroy: function() {
+    dumpn("Destroying the SourcesView");
+
+    this.widget.removeEventListener("select", this._onSourceSelect, false);
+    this._stopBlackBoxButton.removeEventListener("click", this._onStopBlackBoxing, false);
+    this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShowing, false);
+    this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShown, false);
+    this._cbPanel.removeEventListener("popuphiding", this._onConditionalPopupHiding, false);
+    this._cbTextbox.removeEventListener("keypress", this._onConditionalTextboxKeyPress, false);
+    this._copyUrlMenuItem.removeEventListener("command", this._onCopyUrlCommand, false);
+    this._newTabMenuItem.removeEventListener("command", this._onNewTabCommand, false);
+  },
+
+  empty: function() {
+    WidgetMethods.empty.call(this);
+    this._unnamedSourceIndex = 0;
+    this._selectedBreakpoint = null;
+  },
+
+  /**
+   * Add commands that XUL can fire.
+   */
+  _addCommands: function() {
+    XULUtils.addCommands(this._commandset, {
+      addBreakpointCommand: e => this._onCmdAddBreakpoint(e),
+      addConditionalBreakpointCommand: e => this._onCmdAddConditionalBreakpoint(e),
+      blackBoxCommand: () => this.toggleBlackBoxing(),
+      unBlackBoxButton: () => this._onStopBlackBoxing(),
+      prettyPrintCommand: () => this.togglePrettyPrint(),
+      toggleBreakpointsCommand: () =>this.toggleBreakpoints(),
+      togglePromiseDebuggerCommand: () => this.togglePromiseDebugger(),
+      nextSourceCommand: () => this.selectNextItem(),
+      prevSourceCommand: () => this.selectPrevItem()
+    });
+  },
+
+  /**
+   * Sets the preferred location to be selected in this sources container.
+   * @param string aUrl
+   */
+  set preferredSource(aUrl) {
+    this._preferredValue = aUrl;
+
+    // Selects the element with the specified value in this sources container,
+    // if already inserted.
+    if (this.containsValue(aUrl)) {
+      this.selectedValue = aUrl;
+    }
+  },
+
+  sourcesDidUpdate: function() {
+    if (!getSelectedSource(this.getState())) {
+      let url = this._preferredSourceURL;
+      let source = url && getSourceByURL(this.getState(), url);
+      if (source) {
+        this.actions.selectSource(source);
+      }
+      else {
+        setNamedTimeout("new-source", NEW_SOURCE_DISPLAY_DELAY, () => {
+          if (!getSelectedSource(this.getState()) && this.itemCount > 0) {
+            this.actions.selectSource(this.getItemAtIndex(0).attachment.source);
+          }
+        });
+      }
+    }
+  },
+
+  renderSource: function(source) {
+    this.addSource(source, { staged: false });
+    for(let bp of getBreakpoints(this.getState())) {
+      if (bp.location.actor === source.actor) {
+        this.renderBreakpoint(bp);
+      }
+    }
+    this.sourcesDidUpdate();
+  },
+
+  renderSources: function(sources) {
+    if (Object.keys(sources).length === 0) {
+      this.emptyText = L10N.getStr("noSourcesText");
+    }
+  },
+
+  /**
+   * Adds a source to this sources container.
+   *
+   * @param object aSource
+   *        The source object coming from the active thread.
+   * @param object aOptions [optional]
+   *        Additional options for adding the source. Supported options:
+   *        - staged: true to stage the item to be appended later
+   */
+  addSource: function(aSource, aOptions = {}) {
+    if (!aSource.url && !aOptions.force) {
+      // We don't show any unnamed eval scripts yet (see bug 1124106)
+      return;
+    }
+
+    let { label, group, unicodeUrl } = this._parseUrl(aSource);
+
+    let contents = document.createElement("label");
+    contents.className = "plain dbg-source-item";
+    contents.setAttribute("value", label);
+    contents.setAttribute("crop", "start");
+    contents.setAttribute("flex", "1");
+    contents.setAttribute("tooltiptext", unicodeUrl);
+
+    // If the source is blackboxed, apply the appropriate style.
+    if (gThreadClient.source(aSource).isBlackBoxed) {
+      contents.classList.add("black-boxed");
+    }
+
+    // Append a source item to this container.
+    this.push([contents, aSource.actor], {
+      staged: aOptions.staged, /* stage the item to be appended later? */
+      attachment: {
+        label: label,
+        group: group,
+        checkboxState: !aSource.isBlackBoxed,
+        checkboxTooltip: this._blackBoxCheckboxTooltip,
+        source: aSource
+      }
+    });
+  },
+
+  _parseUrl: function(aSource) {
+    let fullUrl = aSource.url;
+    let url, unicodeUrl, label, group;
+
+    if (!fullUrl) {
+      unicodeUrl = 'SCRIPT' + this._unnamedSourceIndex++;
+      label = unicodeUrl;
+      group = L10N.getStr("anonymousSourcesLabel");
+    }
+    else {
+      let url = fullUrl.split(" -> ").pop();
+      label = aSource.addonPath ? aSource.addonPath : SourceUtils.getSourceLabel(url);
+      group = aSource.addonID ? aSource.addonID : SourceUtils.getSourceGroup(url);
+      unicodeUrl = NetworkHelper.convertToUnicode(unescape(fullUrl));
+    }
+
+    return {
+      label: label,
+      group: group,
+      unicodeUrl: unicodeUrl
+    };
+  },
+
+  renderBreakpoint: function(breakpoint, removed) {
+    if (removed) {
+      // Be defensive about the breakpoint not existing.
+      if (this._getBreakpoint(breakpoint)) {
+        this._removeBreakpoint(breakpoint)
+      }
+    }
+    else {
+      if (this._getBreakpoint(breakpoint)) {
+        this._updateBreakpointStatus(breakpoint);
+      }
+      else {
+        this._addBreakpoint(breakpoint);
+      }
+    }
+  },
+
+  /**
+   * Adds a breakpoint to this sources container.
+   *
+   * @param object aBreakpointClient
+   *               See Breakpoints.prototype._showBreakpoint
+   * @param object aOptions [optional]
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _addBreakpoint: function(breakpoint, options = {}) {
+    let disabled = breakpoint.disabled;
+    let location = breakpoint.location;
+
+    // Get the source item to which the breakpoint should be attached.
+    let sourceItem = this.getItemByValue(location.actor);
+    if (!sourceItem) {
+      return;
+    }
+
+    // Create the element node and menu popup for the breakpoint item.
+    let breakpointArgs = Heritage.extend(breakpoint.asMutable(), options);
+    let breakpointView = this._createBreakpointView.call(this, breakpointArgs);
+    let contextMenu = this._createContextMenu.call(this, breakpointArgs);
+
+    // Append a breakpoint child item to the corresponding source item.
+    sourceItem.append(breakpointView.container, {
+      attachment: Heritage.extend(breakpointArgs, {
+        actor: location.actor,
+        line: location.line,
+        view: breakpointView,
+        popup: contextMenu
+      }),
+      attributes: [
+        ["contextmenu", contextMenu.menupopupId]
+      ],
+      // Make sure that when the breakpoint item is removed, the corresponding
+      // menupopup and commandset are also destroyed.
+      finalize: this._onBreakpointRemoved
+    });
+
+    if (typeof breakpoint.condition === "string") {
+      this.highlightBreakpoint(breakpoint.location, {
+        openPopup: true,
+        noEditorUpdate: true
+      });
+    }
+
+    window.emit(EVENTS.BREAKPOINT_SHOWN_IN_PANE);
+  },
+
+  /**
+   * Removes a breakpoint from this sources container.
+   * It does not also remove the breakpoint from the controller. Be careful.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _removeBreakpoint: function(breakpoint) {
+    // When a parent source item is removed, all the child breakpoint items are
+    // also automagically removed.
+    let sourceItem = this.getItemByValue(breakpoint.location.actor);
+    if (!sourceItem) {
+      return;
+    }
+
+    // Clear the breakpoint view.
+    sourceItem.remove(this._getBreakpoint(breakpoint));
+
+    if (this._selectedBreakpoint &&
+       (queries.makeLocationId(this._selectedBreakpoint.location) ===
+        queries.makeLocationId(breakpoint.location))) {
+      this._selectedBreakpoint = null;
+    }
+
+    window.emit(EVENTS.BREAKPOINT_HIDDEN_IN_PANE);
+  },
+
+  _getBreakpoint: function(bp) {
+    return this.getItemForPredicate(item => {
+      return item.attachment.actor === bp.location.actor &&
+        item.attachment.line === bp.location.line;
+    });
+  },
+
+  /**
+   * Updates a breakpoint.
+   *
+   * @param object breakpoint
+   */
+  _updateBreakpointStatus: function(breakpoint) {
+    let location = breakpoint.location;
+    let breakpointItem = this._getBreakpoint(getBreakpoint(this.getState(), location));
+    if (!breakpointItem) {
+      return promise.reject(new Error("No breakpoint found."));
+    }
+
+    // Breakpoint will now be enabled.
+    let attachment = breakpointItem.attachment;
+
+    // Update the corresponding menu items to reflect the enabled state.
+    let prefix = "bp-cMenu-"; // "breakpoints context menu"
+    let identifier = makeLocationId(location);
+    let enableSelfId = prefix + "enableSelf-" + identifier + "-menuitem";
+    let disableSelfId = prefix + "disableSelf-" + identifier + "-menuitem";
+    let enableSelf = document.getElementById(enableSelfId);
+    let disableSelf = document.getElementById(disableSelfId);
+
+    if (breakpoint.disabled) {
+      enableSelf.removeAttribute("hidden");
+      disableSelf.setAttribute("hidden", true);
+      attachment.view.checkbox.removeAttribute("checked");
+    }
+    else {
+      enableSelf.setAttribute("hidden", true);
+      disableSelf.removeAttribute("hidden");
+      attachment.view.checkbox.setAttribute("checked", "true");
+
+      // Update the breakpoint toggle button checked state.
+      this._toggleBreakpointsButton.removeAttribute("checked");
+    }
+
+  },
+
+  /**
+   * Highlights a breakpoint in this sources container.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   * @param object aOptions [optional]
+   *        An object containing some of the following boolean properties:
+   *          - openPopup: tells if the expression popup should be shown.
+   *          - noEditorUpdate: tells if you want to skip editor updates.
+   */
+  highlightBreakpoint: function(aLocation, aOptions = {}) {
+    let breakpoint = getBreakpoint(this.getState(), aLocation);
+    if (!breakpoint) {
+      return;
+    }
+
+    // Breakpoint will now be selected.
+    this._selectBreakpoint(breakpoint);
+
+    // Update the editor location if necessary.
+    if (!aOptions.noEditorUpdate) {
+      this.DebuggerView.setEditorLocation(aLocation.actor, aLocation.line, { noDebug: true });
+    }
+
+    // If the breakpoint requires a new conditional expression, display
+    // the panel to input the corresponding expression.
+    if (aOptions.openPopup) {
+      return this._openConditionalPopup();
+    } else {
+      return this._hideConditionalPopup();
+    }
+  },
+
+  /**
+   * Highlight the breakpoint on the current currently focused line/column
+   * if it exists.
+   */
+  highlightBreakpointAtCursor: function() {
+    let actor = this.selectedValue;
+    let line = this.DebuggerView.editor.getCursor().line + 1;
+
+    let location = { actor: actor, line: line };
+    this.highlightBreakpoint(location, { noEditorUpdate: true });
+  },
+
+  /**
+   * Unhighlights the current breakpoint in this sources container.
+   */
+  unhighlightBreakpoint: function() {
+    this._hideConditionalPopup();
+    this._unselectBreakpoint();
+  },
+
+   /**
+    * Display the message thrown on breakpoint condition
+    */
+  showBreakpointConditionThrownMessage: function(aLocation, aMessage = "") {
+    let breakpointItem = this._getBreakpoint(getBreakpoint(this.getState(), aLocation));
+    if (!breakpointItem) {
+      return;
+    }
+    let attachment = breakpointItem.attachment;
+    attachment.view.container.classList.add("dbg-breakpoint-condition-thrown");
+    attachment.view.message.setAttribute("value", aMessage);
+  },
+
+  /**
+   * Update the checked/unchecked and enabled/disabled states of the buttons in
+   * the sources toolbar based on the currently selected source's state.
+   */
+  updateToolbarButtonsState: function(source) {
+    if (source.isBlackBoxed) {
+      this._blackBoxButton.setAttribute("checked", true);
+      this._prettyPrintButton.setAttribute("checked", true);
+    } else {
+      this._blackBoxButton.removeAttribute("checked");
+      this._prettyPrintButton.removeAttribute("checked");
+    }
+
+    if (source.isPrettyPrinted) {
+      this._prettyPrintButton.setAttribute("checked", true);
+    } else {
+      this._prettyPrintButton.removeAttribute("checked");
+    }
+  },
+
+  /**
+   * Toggle the pretty printing of the selected source.
+   */
+  togglePrettyPrint: function() {
+    if (this._prettyPrintButton.hasAttribute("disabled")) {
+      return;
+    }
+
+    this.DebuggerView.showProgressBar();
+    const source = getSelectedSource(this.getState());
+    const sourceClient = gThreadClient.source(source);
+    const shouldPrettyPrint = !source.isPrettyPrinted;
+
+    // This is only here to give immediate feedback,
+    // `renderPrettyPrinted` will set the final status of the buttons
+    if (shouldPrettyPrint) {
+      this._prettyPrintButton.setAttribute("checked", true);
+    } else {
+      this._prettyPrintButton.removeAttribute("checked");
+    }
+
+    this.actions.togglePrettyPrint(source);
+  },
+
+  /**
+   * Toggle the black boxed state of the selected source.
+   */
+  toggleBlackBoxing: Task.async(function*() {
+    const source = getSelectedSource(this.getState());
+    const shouldBlackBox = !source.isBlackBoxed;
+
+    // Be optimistic that the (un-)black boxing will succeed, so
+    // enable/disable the pretty print button and check/uncheck the
+    // black box button immediately.
+    if (shouldBlackBox) {
+      this._prettyPrintButton.setAttribute("disabled", true);
+      this._blackBoxButton.setAttribute("checked", true);
+    } else {
+      this._prettyPrintButton.removeAttribute("disabled");
+      this._blackBoxButton.removeAttribute("checked");
+    }
+
+    this.actions.blackbox(source, shouldBlackBox);
+  }),
+
+  renderBlackBoxed: function(source) {
+    const sourceItem = this.getItemByValue(source.actor);
+    sourceItem.prebuiltNode.classList.toggle(
+      "black-boxed",
+      source.isBlackBoxed
+    );
+
+    if (getSelectedSource(this.getState()).actor === source.actor) {
+      this.updateToolbarButtonsState(source);
+    }
+  },
+
+  /**
+   * Toggles all breakpoints enabled/disabled.
+   */
+  toggleBreakpoints: function() {
+    let breakpoints = getBreakpoints(this.getState());
+    let hasBreakpoints = breakpoints.length > 0;
+    let hasEnabledBreakpoints = breakpoints.some(bp => !bp.disabled);
+
+    if (hasBreakpoints && hasEnabledBreakpoints) {
+      this._toggleBreakpointsButton.setAttribute("checked", true);
+      this._onDisableAll();
+    } else {
+      this._toggleBreakpointsButton.removeAttribute("checked");
+      this._onEnableAll();
+    }
+  },
+
+  togglePromiseDebugger: function() {
+    if (Prefs.promiseDebuggerEnabled) {
+      let promisePane = this.DebuggerView._promisePane;
+      promisePane.hidden = !promisePane.hidden;
+
+      if (!this.DebuggerView._promiseDebuggerIframe) {
+        this.DebuggerView._initializePromiseDebugger();
+      }
+    }
+  },
+
+  hidePrettyPrinting: function() {
+    this._prettyPrintButton.style.display = 'none';
+
+    if (this._blackBoxButton.style.display === 'none') {
+      let sep = document.querySelector('#sources-toolbar .devtools-separator');
+      sep.style.display = 'none';
+    }
+  },
+
+  hideBlackBoxing: function() {
+    this._blackBoxButton.style.display = 'none';
+
+    if (this._prettyPrintButton.style.display === 'none') {
+      let sep = document.querySelector('#sources-toolbar .devtools-separator');
+      sep.style.display = 'none';
+    }
+  },
+
+  getDisplayURL: function(source) {
+    if (!source.url) {
+      return this.getItemByValue(source.actor).attachment.label;
+    }
+    return NetworkHelper.convertToUnicode(unescape(source.url))
+  },
+
+  /**
+   * Marks a breakpoint as selected in this sources container.
+   *
+   * @param object aItem
+   *        The breakpoint item to select.
+   */
+  _selectBreakpoint: function(bp) {
+    if (this._selectedBreakpoint === bp) {
+      return;
+    }
+    this._unselectBreakpoint();
+    this._selectedBreakpoint = bp;
+
+    const item = this._getBreakpoint(bp);
+    item.target.classList.add("selected");
+
+    // Ensure the currently selected breakpoint is visible.
+    this.widget.ensureElementIsVisible(item.target);
+  },
+
+  /**
+   * Marks the current breakpoint as unselected in this sources container.
+   */
+  _unselectBreakpoint: function() {
+    if (!this._selectedBreakpoint) {
+      return;
+    }
+
+    const item = this._getBreakpoint(this._selectedBreakpoint);
+    item.target.classList.remove("selected");
+
+    this._selectedBreakpoint = null;
+  },
+
+  /**
+   * Opens a conditional breakpoint's expression input popup.
+   */
+  _openConditionalPopup: function() {
+    let breakpointItem = this._getBreakpoint(this._selectedBreakpoint);
+    let attachment = breakpointItem.attachment;
+    // Check if this is an enabled conditional breakpoint, and if so,
+    // retrieve the current conditional epression.
+    let bp = getBreakpoint(this.getState(), attachment);
+    let expr = (bp ? (bp.condition || "") : "");
+
+    // Update the conditional expression textbox. If no expression was
+    // previously set, revert to using an empty string by default.
+    this._cbTextbox.value = expr;
+
+    // Show the conditional expression panel. The popup arrow should be pointing
+    // at the line number node in the breakpoint item view.
+    this._cbPanel.hidden = false;
+    this._cbPanel.openPopup(breakpointItem.attachment.view.lineNumber,
+                            BREAKPOINT_CONDITIONAL_POPUP_POSITION,
+                            BREAKPOINT_CONDITIONAL_POPUP_OFFSET_X,
+                            BREAKPOINT_CONDITIONAL_POPUP_OFFSET_Y);
+  },
+
+  /**
+   * Hides a conditional breakpoint's expression input popup.
+   */
+  _hideConditionalPopup: function() {
+    this._cbPanel.hidden = true;
+
+    // Sometimes this._cbPanel doesn't have hidePopup method which doesn't
+    // break anything but simply outputs an exception to the console.
+    if (this._cbPanel.hidePopup) {
+      this._cbPanel.hidePopup();
+    }
+  },
+
+  /**
+   * Customization function for creating a breakpoint item's UI.
+   *
+   * @param object aOptions
+   *        A couple of options or flags supported by this operation:
+   *          - location: the breakpoint's source location and line number
+   *          - disabled: the breakpoint's disabled state, boolean
+   *          - text: the breakpoint's line text to be displayed
+   *          - message: thrown string when the breakpoint condition throws
+   * @return object
+   *         An object containing the breakpoint container, checkbox,
+   *         line number and line text nodes.
+   */
+  _createBreakpointView: function(aOptions) {
+    let { location, disabled, text, message } = aOptions;
+    let identifier = makeLocationId(location);
+
+    let checkbox = document.createElement("checkbox");
+    if (!disabled) {
+      checkbox.setAttribute("checked", true);
+    }
+    checkbox.className = "dbg-breakpoint-checkbox";
+
+    let lineNumberNode = document.createElement("label");
+    lineNumberNode.className = "plain dbg-breakpoint-line";
+    lineNumberNode.setAttribute("value", location.line);
+
+    let lineTextNode = document.createElement("label");
+    lineTextNode.className = "plain dbg-breakpoint-text";
+    lineTextNode.setAttribute("value", text);
+    lineTextNode.setAttribute("crop", "end");
+    lineTextNode.setAttribute("flex", "1");
+
+    let tooltip = text ? text.substr(0, BREAKPOINT_LINE_TOOLTIP_MAX_LENGTH) : "";
+    lineTextNode.setAttribute("tooltiptext", tooltip);
+
+    let thrownNode = document.createElement("label");
+    thrownNode.className = "plain dbg-breakpoint-condition-thrown-message dbg-breakpoint-text";
+    thrownNode.setAttribute("value", message);
+    thrownNode.setAttribute("crop", "end");
+    thrownNode.setAttribute("flex", "1");
+
+    let bpLineContainer = document.createElement("hbox");
+    bpLineContainer.className = "plain dbg-breakpoint-line-container";
+    bpLineContainer.setAttribute("flex", "1");
+
+    bpLineContainer.appendChild(lineNumberNode);
+    bpLineContainer.appendChild(lineTextNode);
+
+    let bpDetailContainer = document.createElement("vbox");
+    bpDetailContainer.className = "plain dbg-breakpoint-detail-container";
+    bpDetailContainer.setAttribute("flex", "1");
+
+    bpDetailContainer.appendChild(bpLineContainer);
+    bpDetailContainer.appendChild(thrownNode);
+
+    let container = document.createElement("hbox");
+    container.id = "breakpoint-" + identifier;
+    container.className = "dbg-breakpoint side-menu-widget-item-other";
+    container.classList.add("devtools-monospace");
+    container.setAttribute("align", "center");
+    container.setAttribute("flex", "1");
+
+    container.addEventListener("click", this._onBreakpointClick, false);
+    checkbox.addEventListener("click", this._onBreakpointCheckboxClick, false);
+
+    container.appendChild(checkbox);
+    container.appendChild(bpDetailContainer);
+
+    return {
+      container: container,
+      checkbox: checkbox,
+      lineNumber: lineNumberNode,
+      lineText: lineTextNode,
+      message: thrownNode
+    };
+  },
+
+  /**
+   * Creates a context menu for a breakpoint element.
+   *
+   * @param object aOptions
+   *        A couple of options or flags supported by this operation:
+   *          - location: the breakpoint's source location and line number
+   *          - disabled: the breakpoint's disabled state, boolean
+   * @return object
+   *         An object containing the breakpoint commandset and menu popup ids.
+   */
+  _createContextMenu: function(aOptions) {
+    let { location, disabled } = aOptions;
+    let identifier = makeLocationId(location);
+
+    let commandset = document.createElement("commandset");
+    let menupopup = document.createElement("menupopup");
+    commandset.id = "bp-cSet-" + identifier;
+    menupopup.id = "bp-mPop-" + identifier;
+
+    createMenuItem.call(this, "enableSelf", !disabled);
+    createMenuItem.call(this, "disableSelf", disabled);
+    createMenuItem.call(this, "deleteSelf");
+    createMenuSeparator();
+    createMenuItem.call(this, "setConditional");
+    createMenuSeparator();
+    createMenuItem.call(this, "enableOthers");
+    createMenuItem.call(this, "disableOthers");
+    createMenuItem.call(this, "deleteOthers");
+    createMenuSeparator();
+    createMenuItem.call(this, "enableAll");
+    createMenuItem.call(this, "disableAll");
+    createMenuSeparator();
+    createMenuItem.call(this, "deleteAll");
+
+    this._popupset.appendChild(menupopup);
+    this._commandset.appendChild(commandset);
+
+    return {
+      commandsetId: commandset.id,
+      menupopupId: menupopup.id
+    };
+
+    /**
+     * Creates a menu item specified by a name with the appropriate attributes
+     * (label and handler).
+     *
+     * @param string aName
+     *        A global identifier for the menu item.
+     * @param boolean aHiddenFlag
+     *        True if this menuitem should be hidden.
+     */
+    function createMenuItem(aName, aHiddenFlag) {
+      let menuitem = document.createElement("menuitem");
+      let command = document.createElement("command");
+
+      let prefix = "bp-cMenu-"; // "breakpoints context menu"
+      let commandId = prefix + aName + "-" + identifier + "-command";
+      let menuitemId = prefix + aName + "-" + identifier + "-menuitem";
+
+      let label = L10N.getStr("breakpointMenuItem." + aName);
+      let func = "_on" + aName.charAt(0).toUpperCase() + aName.slice(1);
+
+      command.id = commandId;
+      command.setAttribute("label", label);
+      command.addEventListener("command", () => this[func](location), false);
+
+      menuitem.id = menuitemId;
+      menuitem.setAttribute("command", commandId);
+      aHiddenFlag && menuitem.setAttribute("hidden", "true");
+
+      commandset.appendChild(command);
+      menupopup.appendChild(menuitem);
+    }
+
+    /**
+     * Creates a simple menu separator element and appends it to the current
+     * menupopup hierarchy.
+     */
+    function createMenuSeparator() {
+      let menuseparator = document.createElement("menuseparator");
+      menupopup.appendChild(menuseparator);
+    }
+  },
+
+  /**
+   * Copy the source url from the currently selected item.
+   */
+  _onCopyUrlCommand: function() {
+    let selected = this.selectedItem && this.selectedItem.attachment;
+    if (!selected) {
+      return;
+    }
+    clipboardHelper.copyString(selected.source.url);
+  },
+
+  /**
+   * Opens selected item source in a new tab.
+   */
+  _onNewTabCommand: function() {
+    let win = Services.wm.getMostRecentWindow("navigator:browser");
+    let selected = this.selectedItem.attachment;
+    win.openUILinkIn(selected.source.url, "tab", { relatedToCurrent: true });
+  },
+
+  /**
+   * Function called each time a breakpoint item is removed.
+   *
+   * @param object aItem
+   *        The corresponding item.
+   */
+  _onBreakpointRemoved: function(aItem) {
+    dumpn("Finalizing breakpoint item: " + aItem.stringify());
+
+    // Destroy the context menu for the breakpoint.
+    let contextMenu = aItem.attachment.popup;
+    document.getElementById(contextMenu.commandsetId).remove();
+    document.getElementById(contextMenu.menupopupId).remove();
+  },
+
+  _onMouseDown: function(e) {
+    this.hideNoResultsTooltip();
+
+    if (!e.metaKey) {
+      return;
+    }
+
+    let editor = this.DebuggerView.editor;
+    let identifier = this._findIdentifier(e.clientX, e.clientY);
+
+    if (!identifier) {
+        return;
+    }
+
+    let foundDefinitions = this._getFunctionDefinitions(identifier);
+
+    if (!foundDefinitions || !foundDefinitions.definitions) {
+      return;
+    }
+
+    this._showFunctionDefinitionResults(identifier, foundDefinitions.definitions, editor);
+  },
+
+  /**
+   * Searches for function definition of a function in a given source file
+   */
+
+  _findDefinition: function(parsedSource, aName) {
+    let functionDefinitions = parsedSource.getNamedFunctionDefinitions(aName);
+
+    let resultList = [];
+
+    if (!functionDefinitions || !functionDefinitions.length || !functionDefinitions[0].length) {
+      return {
+        definitions: resultList
+      };
+    }
+
+    // functionDefinitions is a list with an object full of metadata,
+    // extract the data and use to construct a more useful, less
+    // cluttered, contextual list
+    for (let i = 0; i < functionDefinitions.length; i++) {
+      let functionDefinition = {
+        source: functionDefinitions[i].sourceUrl,
+        startLine: functionDefinitions[i][0].functionLocation.start.line,
+        startColumn: functionDefinitions[i][0].functionLocation.start.column,
+        name: functionDefinitions[i][0].functionName
+      }
+
+      resultList.push(functionDefinition)
+    }
+
+    return {
+     definitions: resultList
+    };
+  },
+
+  /**
+   * Searches for an identifier underneath the specified position in the
+   * source editor.
+   *
+   * @param number x, y
+   *        The left/top coordinates where to look for an identifier.
+   */
+  _findIdentifier: function(x, y) {
+    let parsedSource = SourceUtils.parseSource(this.DebuggerView, this.Parser);
+    let identifierInfo = SourceUtils.findIdentifier(this.DebuggerView.editor, parsedSource, x, y);
+
+    // Not hovering over an identifier
+    if (!identifierInfo) {
+        return;
+    }
+
+    return identifierInfo;
+  },
+
+  /**
+   * The selection listener for the source editor.
+   */
+  _onEditorCursorActivity: function(e) {
+    let editor = this.DebuggerView.editor;
+    let start = editor.getCursor("start").line + 1;
+    let end = editor.getCursor().line + 1;
+    let source = getSelectedSource(this.getState());
+
+    if (source) {
+      let location = { actor: source.actor, line: start };
+      if (getBreakpoint(this.getState(), location) && start == end) {
+        this.highlightBreakpoint(location, { noEditorUpdate: true });
+      } else {
+        this.unhighlightBreakpoint();
+      }
+    }
+  },
+
+  /*
+   * Uses function definition data to perform actions in different
+   * cases of how many locations were found: zero, one, or multiple definitions
+   */
+  _showFunctionDefinitionResults: function(aHoveredFunction, aDefinitionList, aEditor) {
+    let definitions = aDefinitionList;
+    let hoveredFunction = aHoveredFunction;
+
+    //show a popup saying no results were found
+    if (definitions.length == 0) {
+      this._noResultsFoundToolTip.setTextContent({
+          messages: [L10N.getStr("noMatchingStringsText")]
+      });
+
+      this._markedIdentifier = aEditor.markText(
+        { line: hoveredFunction.location.start.line - 1, ch: hoveredFunction.location.start.column },
+        { line: hoveredFunction.location.end.line - 1, ch: hoveredFunction.location.end.column });
+
+      this._noResultsFoundToolTip.show(this._markedIdentifier.anchor);
+
+    } else if (definitions.length == 1) {
+      this.DebuggerView.setEditorLocation(definitions[0].source, definitions[0].startLine);
+    } else {
+      // TODO: multiple definitions found, do something else
+      this.DebuggerView.setEditorLocation(definitions[0].source, definitions[0].startLine);
+    }
+},
+
+  /**
+   * Hides the tooltip and clear marked text popup.
+   */
+  hideNoResultsTooltip: function() {
+    this._noResultsFoundToolTip.hide();
+    if (this._markedIdentifier) {
+      this._markedIdentifier.clear();
+      this._markedIdentifier = null;
+    }
+  },
+
+  /*
+   * Gets the definition locations from function metadata
+   */
+  _getFunctionDefinitions: function(aIdentifierInfo) {
+    let parsedSource = SourceUtils.parseSource(this.DebuggerView, this.Parser);
+    let definition_info = this._findDefinition(parsedSource, aIdentifierInfo.name);
+
+    //Did not find any definitions for the identifier
+    if (!definition_info) {
+      return;
+    }
+
+    return definition_info;
+  },
+
+  /**
+   * The select listener for the sources container.
+   */
+  _onSourceSelect: function({ detail: sourceItem }) {
+    if (!sourceItem) {
+      return;
+    }
+
+    const { source } = sourceItem.attachment;
+    this.actions.selectSource(source);
+  },
+
+  renderSourceSelected: function(source) {
+    // Set window title. No need to split the url by " -> " here,
+    // because it was already sanitized when the source was added.
+    document.title = L10N.getFormatStr("DebuggerWindowScriptTitle", source.url);
+
+    if (source.url) {
+      this._preferredSourceURL = source.url;
+    }
+    this.updateToolbarButtonsState(source);
+    this._selectItem(this.getItemByValue(source.actor));
+  },
+
+  /**
+   * The click listener for the "stop black boxing" button.
+   */
+  _onStopBlackBoxing: Task.async(function*() {
+    this.actions.blackbox(getSelectedSource(this.getState()), false);
+  }),
+
+  /**
+   * The click listener for a breakpoint container.
+   */
+  _onBreakpointClick: function(e) {
+    let sourceItem = this.getItemForElement(e.target);
+    let breakpointItem = this.getItemForElement.call(sourceItem, e.target);
+    let attachment = breakpointItem.attachment;
+    let bp = getBreakpoint(this.getState(), attachment);
+    if (bp) {
+      this.highlightBreakpoint(bp.location, {
+        openPopup: bp.condition && e.button == 0
+      });
+    } else {
+      this.highlightBreakpoint(bp.location);
+    }
+  },
+
+  /**
+   * The click listener for a breakpoint checkbox.
+   */
+  _onBreakpointCheckboxClick: function(e) {
+    let sourceItem = this.getItemForElement(e.target);
+    let breakpointItem = this.getItemForElement.call(sourceItem, e.target);
+    let bp = getBreakpoint(this.getState(), breakpointItem.attachment);
+
+    if (bp.disabled) {
+      this.actions.enableBreakpoint(bp.location);
+    }
+    else {
+      this.actions.disableBreakpoint(bp.location);
+    }
+
+    // Don't update the editor location (avoid propagating into _onBreakpointClick).
+    e.preventDefault();
+    e.stopPropagation();
+  },
+
+  /**
+   * The popup showing listener for the breakpoints conditional expression panel.
+   */
+  _onConditionalPopupShowing: function() {
+    this._conditionalPopupVisible = true; // Used in tests.
+    window.emit(EVENTS.CONDITIONAL_BREAKPOINT_POPUP_SHOWING);
+  },
+
+  /**
+   * The popup shown listener for the breakpoints conditional expression panel.
+   */
+  _onConditionalPopupShown: function() {
+    this._cbTextbox.focus();
+    this._cbTextbox.select();
+  },
+
+  /**
+   * The popup hiding listener for the breakpoints conditional expression panel.
+   */
+  _onConditionalPopupHiding: function() {
+    this._conditionalPopupVisible = false; // Used in tests.
+
+    // Check if this is an enabled conditional breakpoint, and if so,
+    // save the current conditional expression.
+    let bp = this._selectedBreakpoint;
+    if (bp) {
+      let condition = this._cbTextbox.value;
+      this.actions.setBreakpointCondition(bp.location, condition);
+    }
+  },
+
+  /**
+   * The keypress listener for the breakpoints conditional expression textbox.
+   */
+  _onConditionalTextboxKeyPress: function(e) {
+    if (e.keyCode == e.DOM_VK_RETURN) {
+      this._hideConditionalPopup();
+    }
+  },
+
+  /**
+   * Called when the add breakpoint key sequence was pressed.
+   */
+  _onCmdAddBreakpoint: function(e) {
+    let actor = this.selectedValue;
+    let line = (e && e.sourceEvent.target.tagName == 'menuitem' ?
+                this.DebuggerView.clickedLine + 1 :
+                this.DebuggerView.editor.getCursor().line + 1);
+    let location = { actor, line };
+    let bp = getBreakpoint(this.getState(), location);
+
+    // If a breakpoint already existed, remove it now.
+    if (bp) {
+      this.actions.removeBreakpoint(bp.location);
+    }
+    // No breakpoint existed at the required location, add one now.
+    else {
+      this.actions.addBreakpoint(location);
+    }
+  },
+
+  /**
+   * Called when the add conditional breakpoint key sequence was pressed.
+   */
+  _onCmdAddConditionalBreakpoint: function(e) {
+    let actor = this.selectedValue;
+    let line = (e && e.sourceEvent.target.tagName == 'menuitem' ?
+                this.DebuggerView.clickedLine + 1 :
+                this.DebuggerView.editor.getCursor().line + 1);
+    let location = { actor, line };
+    let bp = getBreakpoint(this.getState(), location);
+
+    // If a breakpoint already existed or wasn't a conditional, morph it now.
+    if (bp) {
+      this.highlightBreakpoint(bp.location, { openPopup: true });
+    }
+    // No breakpoint existed at the required location, add one now.
+    else {
+      this.actions.addBreakpoint(location, "");
+    }
+  },
+
+  getOtherBreakpoints: function(location) {
+    const bps = getBreakpoints(this.getState());
+    if (location) {
+      return bps.filter(bp => {
+        return (bp.location.actor !== location.actor ||
+                bp.location.line !== location.line);
+      });
+    }
+    return bps;
+  },
+
+  /**
+   * Function invoked on the "setConditional" menuitem command.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _onSetConditional: function(aLocation) {
+    // Highlight the breakpoint and show a conditional expression popup.
+    this.highlightBreakpoint(aLocation, { openPopup: true });
+  },
+
+  /**
+   * Function invoked on the "enableSelf" menuitem command.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _onEnableSelf: function(aLocation) {
+    // Enable the breakpoint, in this container and the controller store.
+    this.actions.enableBreakpoint(aLocation);
+  },
+
+  /**
+   * Function invoked on the "disableSelf" menuitem command.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _onDisableSelf: function(aLocation) {
+    const bp = getBreakpoint(this.getState(), aLocation);
+    if (!bp.disabled) {
+      this.actions.disableBreakpoint(aLocation);
+    }
+  },
+
+  /**
+   * Function invoked on the "deleteSelf" menuitem command.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _onDeleteSelf: function(aLocation) {
+    this.actions.removeBreakpoint(aLocation);
+  },
+
+  /**
+   * Function invoked on the "enableOthers" menuitem command.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _onEnableOthers: function(aLocation) {
+    let other = this.getOtherBreakpoints(aLocation);
+    // TODO(jwl): batch these and interrupt the thread for all of them
+    other.forEach(bp => this._onEnableSelf(bp.location));
+  },
+
+  /**
+   * Function invoked on the "disableOthers" menuitem command.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _onDisableOthers: function(aLocation) {
+    let other = this.getOtherBreakpoints(aLocation);
+    other.forEach(bp => this._onDisableSelf(bp.location));
+  },
+
+  /**
+   * Function invoked on the "deleteOthers" menuitem command.
+   *
+   * @param object aLocation
+   *        @see DebuggerController.Breakpoints.addBreakpoint
+   */
+  _onDeleteOthers: function(aLocation) {
+    let other = this.getOtherBreakpoints(aLocation);
+    other.forEach(bp => this._onDeleteSelf(bp.location));
+  },
+
+  /**
+   * Function invoked on the "enableAll" menuitem command.
+   */
+  _onEnableAll: function() {
+    this._onEnableOthers(undefined);
+  },
+
+  /**
+   * Function invoked on the "disableAll" menuitem command.
+   */
+  _onDisableAll: function() {
+    this._onDisableOthers(undefined);
+  },
+
+  /**
+   * Function invoked on the "deleteAll" menuitem command.
+   */
+  _onDeleteAll: function() {
+    this._onDeleteOthers(undefined);
+  },
+
+  _commandset: null,
+  _popupset: null,
+  _cmPopup: null,
+  _cbPanel: null,
+  _cbTextbox: null,
+  _selectedBreakpointItem: null,
+  _conditionalPopupVisible: false,
+  _noResultsFoundToolTip: null,
+  _markedIdentifier: null,
+  _selectedBreakpoint: null,
+  _conditionalPopupVisible: false
+});
+
+module.exports = SourcesView;
--- a/devtools/client/debugger/debugger-commands.js
+++ b/devtools/client/debugger/debugger-commands.js
@@ -532,27 +532,25 @@ exports.items.push({
       }
 
       // Send the black box request to each source we are black boxing. As we
       // get responses, accumulate the results in `blackBoxed`.
 
       const blackBoxed = [];
 
       for (let source of toBlackBox) {
-        activeThread.source(source)[cmd.clientMethod](function({ error }) {
-          if (error) {
-            blackBoxed.push(lookup("ErrorDesc") + " " + source.url);
-          } else {
-            blackBoxed.push(source.url);
-          }
-
+        dbg.blackbox(source, cmd.clientMethod === "blackBox").then(() => {
+          blackBoxed.push(source.url);
+        }, err => {
+          blackBoxed.push(lookup("ErrorDesc") + " " + source.url);
+        }).then(() => {
           if (toBlackBox.length === blackBoxed.length) {
             displayResults();
           }
-        });
+        })
       }
 
       // List the results for the user.
 
       function displayResults() {
         const results = doc.createElement("div");
         results.textContent = lookup("NonEmptyDesc");
 
--- a/devtools/client/debugger/debugger-controller.js
+++ b/devtools/client/debugger/debugger-controller.js
@@ -98,28 +98,57 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://devtools/shared/event-emitter.js");
 Cu.import("resource://devtools/client/shared/widgets/SimpleListWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/BreadcrumbsWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/SideMenuWidget.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesView.jsm");
 Cu.import("resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
 
+/**
+ * Localization convenience methods.
+ */
+var L10N = new ViewHelpers.L10N(DBG_STRINGS_URI);
+
 Cu.import("resource://devtools/client/shared/browser-loader.js");
 const require = BrowserLoader("resource://devtools/client/debugger/", this).require;
 XPCOMUtils.defineConstant(this, "require", require);
 
-const {TargetFactory} = require("devtools/client/framework/target");
-const {Toolbox} = require("devtools/client/framework/toolbox");
-const DevToolsUtils = require("devtools/shared/DevToolsUtils");
-const promise = require("devtools/shared/deprecated-sync-thenables");
-const Editor = require("devtools/client/sourceeditor/editor");
-const DebuggerEditor = require("devtools/client/sourceeditor/debugger");
-const {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
-const FastListWidget = require("devtools/client/shared/widgets/FastListWidget");
+// React
+const React = require("devtools/client/shared/vendor/react");
+const ReactDOM = require("devtools/client/shared/vendor/react-dom");
+const { Provider } = require("devtools/client/shared/vendor/react-redux");
+
+// Used to create the Redux store
+const createStore = require("devtools/client/shared/redux/create-store")({
+  getTargetClient: () => DebuggerController.client,
+  log: false
+});
+const {
+  makeStateBroadcaster,
+  enhanceStoreWithBroadcaster,
+  combineBroadcastingReducers
+} = require("devtools/client/shared/redux/non-react-subscriber");
+const { bindActionCreators } = require('devtools/client/shared/vendor/redux');
+const reducers = require("./content/reducers/index");
+const { onReducerEvents } = require("./content/utils");
+
+const waitUntilService = require("devtools/client/shared/redux/middleware/wait-service");
+var services = {
+  WAIT_UNTIL: waitUntilService.NAME
+};
+
+var {TargetFactory} = require("devtools/client/framework/target");
+var {Toolbox} = require("devtools/client/framework/toolbox");
+var DevToolsUtils = require("devtools/shared/DevToolsUtils");
+var promise = require("devtools/shared/deprecated-sync-thenables");
+var Editor = require("devtools/client/sourceeditor/editor");
+var DebuggerEditor = require("devtools/client/sourceeditor/debugger");
+var {Tooltip} = require("devtools/client/shared/widgets/Tooltip");
+var FastListWidget = require("devtools/client/shared/widgets/FastListWidget");
 
 XPCOMUtils.defineConstant(this, "EVENTS", EVENTS);
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Parser",
   "resource://devtools/shared/Parser.jsm");
@@ -145,148 +174,221 @@ var DebuggerController = {
   /**
    * Initializes the debugger controller.
    */
   initialize: function() {
     dumpn("Initializing the DebuggerController");
 
     this.startupDebugger = this.startupDebugger.bind(this);
     this.shutdownDebugger = this.shutdownDebugger.bind(this);
-    this._onTabNavigated = this._onTabNavigated.bind(this);
+    this._onNavigate = this._onNavigate.bind(this);
+    this._onWillNavigate = this._onWillNavigate.bind(this);
     this._onTabDetached = this._onTabDetached.bind(this);
+
+    const broadcaster = makeStateBroadcaster(() => !!this.activeThread);
+    const reducer = combineBroadcastingReducers(
+      reducers,
+      broadcaster.emitChange
+    );
+    // TODO: Bug 1228867, clean this up and probably abstract it out
+    // better.
+    //
+    // We only want to process async event that are appropriate for
+    // this page. The devtools are open across page reloads, so async
+    // requests from the last page might bleed through if reloading
+    // fast enough. We check to make sure the async action is part of
+    // a current request, and ignore it if not.
+    let store = createStore((state, action) => {
+      if (action.seqId &&
+         (action.status === 'done' || action.status === 'error') &&
+         state && state.asyncRequests.indexOf(action.seqId) === -1) {
+        return state;
+      }
+      return reducer(state, action);
+    });
+    store = enhanceStoreWithBroadcaster(store, broadcaster);
+
+    // This controller right now acts as the store that's globally
+    // available, so just copy the Redux API onto it.
+    Object.keys(store).forEach(name => {
+      this[name] = store[name];
+    });
   },
 
   /**
    * Initializes the view.
    *
    * @return object
    *         A promise that is resolved when the debugger finishes startup.
    */
   startupDebugger: Task.async(function*() {
     if (this._startup) {
       return;
     }
 
-    yield DebuggerView.initialize();
+    DebuggerView.initialize();
     this._startup = true;
   }),
 
   /**
    * Destroys the view and disconnects the debugger client from the server.
    *
    * @return object
    *         A promise that is resolved when the debugger finishes shutdown.
    */
   shutdownDebugger: Task.async(function*() {
     if (this._shutdown) {
       return;
     }
 
-    yield DebuggerView.destroy();
-    this.SourceScripts.disconnect();
+    yield this._settleAllRequests();
+
+    DebuggerView.destroy();
     this.StackFrames.disconnect();
     this.ThreadState.disconnect();
     if (this._target.isTabActor) {
       this.Workers.disconnect();
     }
+
     this.disconnect();
 
     this._shutdown = true;
   }),
 
+  _settleAllRequests: function() {
+    const requests = this.getState().asyncRequests;
+
+    if (requests.length > 0) {
+      const deferred = promise.defer();
+      this.onChange('open-requests', function checkSettled(reqs) {
+        if (reqs.length === 0) {
+          deferred.resolve();
+        }
+
+        this.offChange('open-requests', checkSettled);
+      }.bind(this));
+      return deferred.promise;
+    }
+
+    return promise.resolve();
+  },
+
   /**
    * Initiates remote debugging based on the current target, wiring event
    * handlers as necessary.
    *
    * @return object
    *         A promise that is resolved when the debugger finishes connecting.
    */
   connect: Task.async(function*() {
     if (this._connected) {
       return;
     }
 
     let target = this._target;
     let { client, form: { chromeDebugger, actor } } = target;
     target.on("close", this._onTabDetached);
-    target.on("navigate", this._onTabNavigated);
-    target.on("will-navigate", this._onTabNavigated);
+    target.on("navigate", this._onNavigate);
+    target.on("will-navigate", this._onWillNavigate);
     this.client = client;
 
     if (target.isAddon) {
       yield this._startAddonDebugging(actor);
     } else if (!target.isTabActor) {
       // Some actors like AddonActor or RootActor for chrome debugging
       // do not support attach/detach and can be used directly
       yield this._startChromeDebugging(chromeDebugger);
     } else {
       yield this._startDebuggingTab();
     }