diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-15 03:34:50 +0000 |
commit | def92d1b8e9d373e2f6f27c366d578d97d8960c6 (patch) | |
tree | 2ef34b9ad8bb9a9220e05d60352558b15f513894 /mobile/android/android-components/components/browser/engine-system/src | |
parent | Adding debian version 125.0.3-1. (diff) | |
download | firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.tar.xz firefox-def92d1b8e9d373e2f6f27c366d578d97d8960c6.zip |
Merging upstream version 126.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'mobile/android/android-components/components/browser/engine-system/src')
148 files changed, 31365 insertions, 0 deletions
diff --git a/mobile/android/android-components/components/browser/engine-system/src/androidTest/java/mozilla/components/browser/engine/system/VersionTest.kt b/mobile/android/android-components/components/browser/engine-system/src/androidTest/java/mozilla/components/browser/engine/system/VersionTest.kt new file mode 100644 index 0000000000..dca1cead0c --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/androidTest/java/mozilla/components/browser/engine/system/VersionTest.kt @@ -0,0 +1,28 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertTrue +import org.junit.Test + +class VersionTest { + @Test + fun testParsingOfActualWebViewVersion() { + runBlocking(Dispatchers.Main) { + val context: Context = ApplicationProvider.getApplicationContext() + val engine = SystemEngine(context) + val version = engine.version + + assertTrue(version.major > 60) + + // 60.0.3112 was released 2017-07-31. + assertTrue(version.isAtLeast(60, 0, 3113)) + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/androidTest/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/browser/engine-system/src/androidTest/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..cf1c399ea8 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/androidTest/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,2 @@ +mock-maker-inline +// This allows mocking final classes (classes are final by default in Kotlin) diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/AndroidManifest.xml b/mobile/android/android-components/components/browser/engine-system/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..e16cda1d34 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/AndroidManifest.xml @@ -0,0 +1,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/. --> +<manifest /> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/NestedWebView.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/NestedWebView.kt new file mode 100644 index 0000000000..74b3479b11 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/NestedWebView.kt @@ -0,0 +1,170 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.annotation.SuppressLint +import android.content.Context +import android.view.MotionEvent +import android.view.MotionEvent.ACTION_CANCEL +import android.view.MotionEvent.ACTION_DOWN +import android.view.MotionEvent.ACTION_MOVE +import android.view.MotionEvent.ACTION_UP +import android.view.MotionEvent.obtain +import android.webkit.WebView +import androidx.annotation.VisibleForTesting +import androidx.core.view.NestedScrollingChild +import androidx.core.view.NestedScrollingChildHelper +import androidx.core.view.ViewCompat +import mozilla.components.concept.engine.INPUT_HANDLED +import mozilla.components.concept.engine.INPUT_UNHANDLED +import mozilla.components.concept.engine.InputResultDetail + +/** + * WebView that supports nested scrolls (for using in a CoordinatorLayout). + * + * This code is a simplified version of the NestedScrollView implementation + * which can be found in the support library: + * [android.support.v4.widget.NestedScrollView] + * + * Based on: + * https://github.com/takahirom/webview-in-coordinatorlayout + */ +class NestedWebView(context: Context) : WebView(context), NestedScrollingChild { + + @VisibleForTesting + internal var lastY: Int = 0 + + @VisibleForTesting + internal val scrollOffset = IntArray(2) + + private val scrollConsumed = IntArray(2) + + @VisibleForTesting + internal var nestedOffsetY: Int = 0 + + @VisibleForTesting + internal var childHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this) + + /** + * How user's MotionEvent will be handled. + * + * @see InputResultDetail + */ + internal var inputResultDetail = InputResultDetail.newInstance() + + init { + isNestedScrollingEnabled = true + } + + @SuppressLint("ClickableViewAccessibility") + override fun onTouchEvent(ev: MotionEvent): Boolean { + val event = obtain(ev) + val action = ev.actionMasked + + if (action == ACTION_DOWN) { + nestedOffsetY = 0 + } + + val eventY = event.y.toInt() + event.offsetLocation(0f, nestedOffsetY.toFloat()) + + when (action) { + ACTION_MOVE -> { + var deltaY = lastY - eventY + + if (dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) { + deltaY -= scrollConsumed[1] + event.offsetLocation(0f, (-scrollOffset[1]).toFloat()) + nestedOffsetY += scrollOffset[1] + } + + lastY = eventY - scrollOffset[1] + + if (dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) { + lastY -= scrollOffset[1] + event.offsetLocation(0f, scrollOffset[1].toFloat()) + nestedOffsetY += scrollOffset[1] + } + } + + ACTION_DOWN -> { + lastY = eventY + startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL) + } + + // We don't care about other touch events + ACTION_UP, ACTION_CANCEL -> stopNestedScroll() + } + + // Execute event handler from parent class in all cases + val eventHandled = callSuperOnTouchEvent(event) + updateInputResult(eventHandled) + + // Recycle previously obtained event + event.recycle() + + return eventHandled + } + + @VisibleForTesting + internal fun callSuperOnTouchEvent(event: MotionEvent): Boolean { + return super.onTouchEvent(event) + } + + // NestedScrollingChild + + override fun setNestedScrollingEnabled(enabled: Boolean) { + childHelper.isNestedScrollingEnabled = enabled + } + + override fun isNestedScrollingEnabled(): Boolean { + return childHelper.isNestedScrollingEnabled + } + + override fun startNestedScroll(axes: Int): Boolean { + return childHelper.startNestedScroll(axes) + } + + override fun stopNestedScroll() { + childHelper.stopNestedScroll() + } + + override fun hasNestedScrollingParent(): Boolean { + return childHelper.hasNestedScrollingParent() + } + + override fun dispatchNestedScroll( + dxConsumed: Int, + dyConsumed: Int, + dxUnconsumed: Int, + dyUnconsumed: Int, + offsetInWindow: IntArray?, + ): Boolean { + return childHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, offsetInWindow) + } + + override fun dispatchNestedPreScroll(dx: Int, dy: Int, consumed: IntArray?, offsetInWindow: IntArray?): Boolean { + return childHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow) + } + + override fun dispatchNestedFling(velocityX: Float, velocityY: Float, consumed: Boolean): Boolean { + return childHelper.dispatchNestedFling(velocityX, velocityY, consumed) + } + + override fun dispatchNestedPreFling(velocityX: Float, velocityY: Float): Boolean { + return childHelper.dispatchNestedPreFling(velocityX, velocityY) + } + + @VisibleForTesting + internal fun updateInputResult(eventHandled: Boolean) { + inputResultDetail = inputResultDetail.copy( + if (eventHandled) { + INPUT_HANDLED + } else { + INPUT_UNHANDLED + }, + ) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngine.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngine.kt new file mode 100644 index 0000000000..0117b53014 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngine.kt @@ -0,0 +1,152 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.content.Context +import android.util.AttributeSet +import android.util.JsonReader +import android.webkit.WebSettings +import android.webkit.WebView +import androidx.annotation.VisibleForTesting +import mozilla.components.concept.base.profiler.Profiler +import mozilla.components.concept.engine.DefaultSettings +import mozilla.components.concept.engine.Engine +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy +import mozilla.components.concept.engine.EngineSessionState +import mozilla.components.concept.engine.EngineView +import mozilla.components.concept.engine.Settings +import mozilla.components.concept.engine.history.HistoryTrackingDelegate +import mozilla.components.concept.engine.utils.EngineVersion +import org.json.JSONObject +import java.lang.IllegalStateException + +/** + * WebView-based implementation of the Engine interface. + */ +class SystemEngine( + private val context: Context, + private val defaultSettings: Settings = DefaultSettings(), +) : Engine { + init { + initDefaultUserAgent(context) + } + + /** + * Creates a new WebView-based EngineView implementation. + */ + override fun createView(context: Context, attrs: AttributeSet?): EngineView { + return SystemEngineView(context, attrs) + } + + /** + * Creates a new WebView-based EngineSession implementation. + */ + override fun createSession(private: Boolean, contextId: String?): EngineSession { + if (private) { + // TODO Implement private browsing: https://github.com/mozilla-mobile/android-components/issues/649 + throw UnsupportedOperationException("Private browsing is not supported in ${this::class.java.simpleName}") + } else if (contextId != null) { + throw UnsupportedOperationException( + "Contextual identities are not supported in ${this::class.java.simpleName}", + ) + } + + return SystemEngineSession(context, defaultSettings) + } + + /** + * Opens a speculative connection to the host of [url]. + * + * Note: This implementation is a no-op. + */ + override fun speculativeConnect(url: String) = Unit + + /** + * See [Engine.profiler]. + */ + override val profiler: Profiler? = null + + /** + * See [Engine.name] + */ + override fun name(): String = "System" + + @Suppress("TooGenericExceptionCaught") + override val version: EngineVersion + get() { + val userAgent = WebSettings.getDefaultUserAgent(context) + val version = try { + "Chrome/([^ ]+)".toRegex().find(userAgent)?.groups?.get(1)?.value + ?: throw IllegalStateException("Could not get version from user agent: $userAgent") + } catch (e: IllegalStateException) { + throw IllegalStateException("Could not get version from user agent: $userAgent") + } catch (e: IndexOutOfBoundsException) { + throw IllegalStateException("Could not get version from user agent: $userAgent") + } + + return EngineVersion.parse(version) + ?: throw IllegalStateException("Could not determine engine version: $version") + } + + override fun createSessionState(json: JSONObject): EngineSessionState { + return SystemEngineSessionState.fromJSON(json) + } + + override fun createSessionStateFrom(reader: JsonReader): EngineSessionState { + return SystemEngineSessionState.from(reader) + } + + /** + * See [Engine.settings] + */ + override val settings: Settings = object : Settings() { + private var internalRemoteDebuggingEnabled = false + override var remoteDebuggingEnabled: Boolean + get() = internalRemoteDebuggingEnabled + set(value) { + WebView.setWebContentsDebuggingEnabled(value) + internalRemoteDebuggingEnabled = value + } + + override var userAgentString: String? + get() = defaultSettings.userAgentString + set(value) { + defaultSettings.userAgentString = value + } + + override var trackingProtectionPolicy: TrackingProtectionPolicy? + get() = defaultSettings.trackingProtectionPolicy + set(value) { + defaultSettings.trackingProtectionPolicy = value + } + + override var historyTrackingDelegate: HistoryTrackingDelegate? + get() = defaultSettings.historyTrackingDelegate + set(value) { + defaultSettings.historyTrackingDelegate = value + } + }.apply { + this.remoteDebuggingEnabled = defaultSettings.remoteDebuggingEnabled + this.trackingProtectionPolicy = defaultSettings.trackingProtectionPolicy + if (defaultSettings.userAgentString == null) { + defaultSettings.userAgentString = defaultUserAgent + } + } + + companion object { + // In Robolectric tests we can't call WebSettings.getDefaultUserAgent(context) + // as this would result in a NPE. So, we expose this field to circumvent the call. + @VisibleForTesting + var defaultUserAgent: String? = null + + private fun initDefaultUserAgent(context: Context): String { + if (defaultUserAgent == null) { + defaultUserAgent = WebSettings.getDefaultUserAgent(context) + } + return defaultUserAgent as String + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineSession.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineSession.kt new file mode 100644 index 0000000000..aaf2ec1634 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineSession.kt @@ -0,0 +1,636 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.content.Context +import android.webkit.CookieManager +import android.webkit.WebChromeClient +import android.webkit.WebSettings +import android.webkit.WebSettings.LOAD_NO_CACHE +import android.webkit.WebStorage +import android.webkit.WebView +import android.webkit.WebViewClient +import android.webkit.WebViewDatabase +import androidx.annotation.VisibleForTesting +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import mozilla.components.browser.errorpages.ErrorType +import mozilla.components.concept.engine.Engine.BrowsingData +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.EngineSessionState +import mozilla.components.concept.engine.Settings +import mozilla.components.concept.engine.history.HistoryTrackingDelegate +import mozilla.components.concept.engine.request.RequestInterceptor +import mozilla.components.concept.engine.shopping.ProductAnalysis +import mozilla.components.concept.engine.shopping.ProductAnalysisStatus +import mozilla.components.concept.engine.shopping.ProductRecommendation +import mozilla.components.concept.engine.translate.TranslationOptions +import kotlin.reflect.KProperty + +internal val xRequestHeader = mapOf( + // For every request WebView sends a "X-requested-with" header with the package name of the + // application. We can't really prevent that but we can at least send an empty value. + // Unfortunately the additional headers will not be propagated to subsequent requests + // (e.g. redirects). See issue #696. + "X-Requested-With" to "", +) + +/** + * WebView-based EngineSession implementation. + */ +@Suppress("LargeClass", "TooManyFunctions") +class SystemEngineSession( + context: Context, + private val defaultSettings: Settings? = null, +) : EngineSession() { + private val resources = context.resources + + @Volatile internal lateinit var internalSettings: Settings + + @Volatile internal var historyTrackingDelegate: HistoryTrackingDelegate? = null + + @Volatile internal var trackingProtectionPolicy: TrackingProtectionPolicy? = null + + @Volatile internal var webFontsEnabled = true + + @Volatile internal var currentUrl = "" + + @Volatile internal var useWideViewPort: Boolean? = null // See [toggleDesktopMode] + + @Volatile internal var fullScreenCallback: WebChromeClient.CustomViewCallback? = null + + // This is public for FFTV which needs access to the WebView instance. We can mark it internal once + // https://github.com/mozilla-mobile/android-components/issues/1616 is resolved. + @Volatile var webView: WebView = NestedWebView(context) + set(value) { + field = value + initSettings() + } + + init { + initSettings() + } + + /** + * See [EngineSession.loadUrl]. Note that [LoadUrlFlags] are ignored in this engine + * implementation. + */ + override fun loadUrl( + url: String, + parent: EngineSession?, + flags: LoadUrlFlags, + additionalHeaders: Map<String, String>?, + ) { + notifyObservers { onLoadUrl() } + + val headers = + if (additionalHeaders == null) { + xRequestHeader + } else { + xRequestHeader + additionalHeaders + } + + if (!url.isEmpty()) { + currentUrl = url + webView.loadUrl(url, headers) + } + } + + /** + * See [EngineSession.loadData] + */ + override fun loadData(data: String, mimeType: String, encoding: String) { + webView.loadData(data, mimeType, encoding) + notifyObservers { onLoadData() } + } + + override fun requestPdfToDownload() { + throw UnsupportedOperationException("PDF support is not available in this engine") + } + + override fun requestPrintContent() { + throw UnsupportedOperationException("Print support is not available in this engine") + } + + /** + * See [EngineSession.stopLoading] + */ + override fun stopLoading() { + webView.stopLoading() + } + + /** + * See [EngineSession.reload] + * @param flags currently not supported in `SystemEngineSession`. + */ + override fun reload(flags: LoadUrlFlags) { + webView.reload() + } + + /** + * See [EngineSession.goBack] + */ + override fun goBack(userInteraction: Boolean) { + webView.goBack() + if (webView.canGoBack()) { + notifyObservers { onNavigateBack() } + } + } + + /** + * See [EngineSession.goForward] + */ + override fun goForward(userInteraction: Boolean) { + webView.goForward() + if (webView.canGoForward()) { + notifyObservers { onNavigateForward() } + } + } + + /** + * See [EngineSession.goToHistoryIndex] + */ + override fun goToHistoryIndex(index: Int) { + val historyList = webView.copyBackForwardList() + webView.goBackOrForward(index - historyList.currentIndex) + notifyObservers { onGotoHistoryIndex() } + } + + /** + * See [EngineSession.restoreState] + */ + override fun restoreState(state: EngineSessionState): Boolean { + if (state !is SystemEngineSessionState) { + throw IllegalArgumentException("Can only restore from SystemEngineSessionState") + } + + return state.bundle?.let { webView.restoreState(it) } != null + } + + /** + * See [EngineSession.updateTrackingProtection] + */ + override fun updateTrackingProtection(policy: TrackingProtectionPolicy) { + // Make sure Url matcher is preloaded now that tracking protection is enabled + CoroutineScope(Dispatchers.IO).launch { + SystemEngineView.getOrCreateUrlMatcher(resources, policy) + } + + // TODO check if policy should be applied for this session type + // (regular|private) once we support private browsing in system engine: + // https://github.com/mozilla-mobile/android-components/issues/649 + trackingProtectionPolicy = policy + notifyObservers { onTrackerBlockingEnabledChange(true) } + } + + @VisibleForTesting + internal fun disableTrackingProtection() { + trackingProtectionPolicy = null + notifyObservers { onTrackerBlockingEnabledChange(false) } + } + + /** + * See [EngineSession.close] + */ + override fun close() { + super.close() + // The WebView instance must remain useable for the duration of this session. + // We can only destroy it once we're sure this session will not be used + // again which is why destroy happens here are not part of regular (activity) + // lifecycle event. + webView.destroy() + } + + /** + * See [EngineSession.clearData] + */ + @Suppress("TooGenericExceptionCaught") + override fun clearData(data: BrowsingData, host: String?, onSuccess: () -> Unit, onError: (Throwable) -> Unit) { + webView.apply { + try { + if (data.contains(BrowsingData.DOM_STORAGES)) { + webStorage().deleteAllData() + } + if (data.contains(BrowsingData.IMAGE_CACHE) || data.contains(BrowsingData.NETWORK_CACHE)) { + clearCache(true) + } + if (data.contains(BrowsingData.COOKIES)) { + CookieManager.getInstance().removeAllCookies(null) + } + if (data.contains(BrowsingData.AUTH_SESSIONS)) { + webViewDatabase(context).clearHttpAuthUsernamePassword() + } + if (data.contains(BrowsingData.ALL)) { + clearSslPreferences() + clearFormData() + clearMatches() + clearHistory() + } + onSuccess() + } catch (e: Throwable) { + onError(e) + } + } + } + + /** + * See [EngineSession.findAll] + */ + override fun findAll(text: String) { + notifyObservers { onFind(text) } + webView.findAllAsync(text) + } + + /** + * See [EngineSession.findNext] + */ + override fun findNext(forward: Boolean) { + webView.findNext(forward) + } + + /** + * See [EngineSession.clearFindMatches] + */ + override fun clearFindMatches() { + webView.clearMatches() + } + + /** + * Clears the internal back/forward list. + */ + override fun purgeHistory() { + webView.clearHistory() + } + + /** + * See [EngineSession.settings] + */ + override val settings: Settings + get() = internalSettings + + class WebSetting<T>(private val get: () -> T, private val set: (T) -> Unit) { + operator fun getValue(thisRef: Any?, property: KProperty<*>): T = get() + operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = set(value) + } + + @VisibleForTesting + internal fun initSettings() { + webView.settings.apply { + // Explicitly set global defaults. + + cacheMode = LOAD_NO_CACHE + databaseEnabled = false + + setDeprecatedWebSettings(this) + + // We currently don't implement the callback to support turning this on. + setGeolocationEnabled(false) + + // webViewSettings built-in zoom controls are the only supported ones, + // so they should be turned on but hidden. + builtInZoomControls = true + displayZoomControls = false + + initSettings(webView, this) + } + } + + @Suppress("DEPRECATION") + private fun setDeprecatedWebSettings(webSettings: WebSettings) { + // Since API26 an autofill platform feature is used instead of WebView's form data. This + // has no effect. Form data is supported on pre-26 API versions. + webSettings.saveFormData = false + // Deprecated in API18. + webSettings.savePassword = false + } + + private fun setUseWideViewPort(settings: WebSettings, useWideViewPort: Boolean?) { + this.useWideViewPort = useWideViewPort + useWideViewPort?.let { settings.useWideViewPort = it } + } + + private fun initSettings(webView: WebView, s: WebSettings) { + internalSettings = object : Settings() { + override var javascriptEnabled by WebSetting(s::getJavaScriptEnabled, s::setJavaScriptEnabled) + override var domStorageEnabled by WebSetting(s::getDomStorageEnabled, s::setDomStorageEnabled) + override var allowFileAccess by WebSetting(s::getAllowFileAccess, s::setAllowFileAccess) + override var allowContentAccess by WebSetting(s::getAllowContentAccess, s::setAllowContentAccess) + override var userAgentString by WebSetting(s::getUserAgentString, s::setUserAgentString) + override var displayZoomControls by WebSetting(s::getDisplayZoomControls, s::setDisplayZoomControls) + override var loadWithOverviewMode by WebSetting(s::getLoadWithOverviewMode, s::setLoadWithOverviewMode) + override var useWideViewPort: Boolean? + get() = this@SystemEngineSession.useWideViewPort + set(value) = setUseWideViewPort(s, value) + override var supportMultipleWindows by WebSetting(s::supportMultipleWindows, s::setSupportMultipleWindows) + + @Suppress("DEPRECATION") + // Deprecation will be handled in https://github.com/mozilla-mobile/android-components/issues/8513 + override var allowFileAccessFromFileURLs by WebSetting( + s::getAllowFileAccessFromFileURLs, + s::setAllowFileAccessFromFileURLs, + ) + + @Suppress("DEPRECATION") + // Deprecation will be handled in https://github.com/mozilla-mobile/android-components/issues/8514 + override var allowUniversalAccessFromFileURLs by WebSetting( + s::getAllowUniversalAccessFromFileURLs, + s::setAllowUniversalAccessFromFileURLs, + ) + + override var mediaPlaybackRequiresUserGesture by WebSetting( + s::getMediaPlaybackRequiresUserGesture, + s::setMediaPlaybackRequiresUserGesture, + ) + override var javaScriptCanOpenWindowsAutomatically by WebSetting( + s::getJavaScriptCanOpenWindowsAutomatically, + s::setJavaScriptCanOpenWindowsAutomatically, + ) + + override var verticalScrollBarEnabled + get() = webView.isVerticalScrollBarEnabled + set(value) { webView.isVerticalScrollBarEnabled = value } + + override var horizontalScrollBarEnabled + get() = webView.isHorizontalScrollBarEnabled + set(value) { webView.isHorizontalScrollBarEnabled = value } + + override var webFontsEnabled + get() = this@SystemEngineSession.webFontsEnabled + set(value) { this@SystemEngineSession.webFontsEnabled = value } + + override var trackingProtectionPolicy: TrackingProtectionPolicy? + get() = this@SystemEngineSession.trackingProtectionPolicy + set(value) = value?.let { updateTrackingProtection(it) } ?: disableTrackingProtection() + + override var historyTrackingDelegate: HistoryTrackingDelegate? + get() = this@SystemEngineSession.historyTrackingDelegate + set(value) { this@SystemEngineSession.historyTrackingDelegate = value } + + override var requestInterceptor: RequestInterceptor? = null + }.apply { + defaultSettings?.let { + javascriptEnabled = it.javascriptEnabled + domStorageEnabled = it.domStorageEnabled + webFontsEnabled = it.webFontsEnabled + displayZoomControls = it.displayZoomControls + loadWithOverviewMode = it.loadWithOverviewMode + useWideViewPort = it.useWideViewPort + trackingProtectionPolicy = it.trackingProtectionPolicy + historyTrackingDelegate = it.historyTrackingDelegate + requestInterceptor = it.requestInterceptor + mediaPlaybackRequiresUserGesture = it.mediaPlaybackRequiresUserGesture + javaScriptCanOpenWindowsAutomatically = it.javaScriptCanOpenWindowsAutomatically + allowFileAccess = it.allowFileAccess + allowContentAccess = it.allowContentAccess + allowUniversalAccessFromFileURLs = it.allowUniversalAccessFromFileURLs + allowFileAccessFromFileURLs = it.allowFileAccessFromFileURLs + verticalScrollBarEnabled = it.verticalScrollBarEnabled + horizontalScrollBarEnabled = it.horizontalScrollBarEnabled + userAgentString = it.userAgentString + supportMultipleWindows = it.supportMultipleWindows + } + } + } + + /** + * See [EngineSession.toggleDesktopMode] + * + * Precondition: + * If settings.useWideViewPort = true, then webSettings.useWideViewPort is always on + * If settings.useWideViewPort = false or null, then webSettings.useWideViewPort can be on/off + */ + override fun toggleDesktopMode(enable: Boolean, reload: Boolean) { + val webSettings = webView.settings + webSettings.userAgentString = toggleDesktopUA(webSettings.userAgentString, enable) + webSettings.useWideViewPort = if (settings.useWideViewPort == true) true else enable + + notifyObservers { onDesktopModeChange(enable) } + + if (reload) { + webView.reload() + } + } + + /** + * Checks for if PDF Viewer is used. + */ + override fun checkForPdfViewer( + onResult: (Boolean) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Checking for PDF viewer is not available in this engine") + } + + /** + * /** + * See [EngineSession.requestProductRecommendations] + */ + */ + override fun requestProductRecommendations( + url: String, + onResult: (List<ProductRecommendation>) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Analysis of product reviews for shopping is not available in this engine") + } + + /** + * See [EngineSession.requestProductAnalysis] + */ + override fun requestProductAnalysis( + url: String, + onResult: (ProductAnalysis) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Analysis of product reviews for shopping is not available in this engine") + } + + /** + * See [EngineSession.reanalyzeProduct] + */ + override fun reanalyzeProduct( + url: String, + onResult: (String) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Reanalyzing product reviews for shopping is not available in this engine") + } + + /** + * See [EngineSession.requestAnalysisStatus] + */ + override fun requestAnalysisStatus( + url: String, + onResult: (ProductAnalysisStatus) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Requesting product analysis status is not available in this engine") + } + + /** + * See [EngineSession.sendClickAttributionEvent] + */ + override fun sendClickAttributionEvent( + aid: String, + onResult: (Boolean) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Sending click attribution event is not available in this engine") + } + + /** + * See [EngineSession.sendImpressionAttributionEvent] + */ + override fun sendImpressionAttributionEvent( + aid: String, + onResult: (Boolean) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Sending impression attribution event is not available in this engine") + } + + /** + * See [EngineSession.sendPlacementAttributionEvent] + */ + override fun sendPlacementAttributionEvent( + aid: String, + onResult: (Boolean) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Sending placement attribution event is not available in this engine") + } + + /** + * See [EngineSession.reportBackInStock] + */ + override fun reportBackInStock( + url: String, + onResult: (String) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Reporting back in stock is not available in this engine") + } + + /** + * See [EngineSession.requestTranslate] + */ + override fun requestTranslate( + fromLanguage: String, + toLanguage: String, + options: TranslationOptions?, + ) { + throw UnsupportedOperationException("Translate support is not available in this engine") + } + + /** + * See [EngineSession.requestTranslationRestore] + */ + override fun requestTranslationRestore() { + throw UnsupportedOperationException("Translate restore support is not available in this engine") + } + + /** + * See [EngineSession.getNeverTranslateSiteSetting] + */ + override fun getNeverTranslateSiteSetting( + onResult: (Boolean) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Getting the site's translate setting is not available in this engine.") + } + + /** + * See [EngineSession.setNeverTranslateSiteSetting] + */ + override fun setNeverTranslateSiteSetting( + setting: Boolean, + onResult: () -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Setting the site's translate setting is not available in this engine") + } + + override fun hasCookieBannerRuleForSession( + onResult: (Boolean) -> Unit, + onException: (Throwable) -> Unit, + ) { + throw UnsupportedOperationException("Cookie Banner handling is not available in this engine") + } + + /** + * See [EngineSession.exitFullScreenMode] + */ + override fun exitFullScreenMode() { + fullScreenCallback?.onCustomViewHidden() + } + + internal fun toggleDesktopUA(userAgent: String, requestDesktop: Boolean): String { + return if (requestDesktop) { + userAgent.replace("Mobile", "eliboM").replace("Android", "diordnA") + } else { + userAgent.replace("eliboM", "Mobile").replace("diordnA", "Android") + } + } + + internal fun webStorage(): WebStorage = WebStorage.getInstance() + + internal fun webViewDatabase(context: Context) = WebViewDatabase.getInstance(context) + + /** + * Helper method to notify observers from other classes in this package. This is needed as + * almost everything is implemented by WebView and its listeners. There is no actual concept of + * a session when using WebView. + */ + internal fun internalNotifyObservers(block: Observer.() -> Unit) { + super.notifyObservers(block) + } + + companion object { + /** + * Provides an ErrorType corresponding to the error code provided. + * + * Chromium's mapping (internal error code, to Android WebView error code) is described at: + * https://goo.gl/vspwct (ErrorCodeConversionHelper.java) + */ + internal fun webViewErrorToErrorType(errorCode: Int) = + when (errorCode) { + WebViewClient.ERROR_UNKNOWN -> ErrorType.UNKNOWN + + // This is probably the most commonly shown error. If there's no network, we inevitably + // show this. + WebViewClient.ERROR_HOST_LOOKUP -> ErrorType.ERROR_UNKNOWN_HOST + + WebViewClient.ERROR_CONNECT -> ErrorType.ERROR_CONNECTION_REFUSED + + // It's unclear what this actually means - it's not well documented. Based on looking at + // ErrorCodeConversionHelper this could happen if networking is disabled during load, in which + // case the generic error is good enough: + WebViewClient.ERROR_IO -> ErrorType.ERROR_CONNECTION_REFUSED + + WebViewClient.ERROR_TIMEOUT -> ErrorType.ERROR_NET_TIMEOUT + + WebViewClient.ERROR_REDIRECT_LOOP -> ErrorType.ERROR_REDIRECT_LOOP + + WebViewClient.ERROR_UNSUPPORTED_SCHEME -> ErrorType.ERROR_UNKNOWN_PROTOCOL + + WebViewClient.ERROR_FAILED_SSL_HANDSHAKE -> ErrorType.ERROR_SECURITY_SSL + + WebViewClient.ERROR_BAD_URL -> ErrorType.ERROR_MALFORMED_URI + + // Seems to be an indication of OOM, insufficient resources, or too many queued DNS queries + WebViewClient.ERROR_TOO_MANY_REQUESTS -> ErrorType.UNKNOWN + + WebViewClient.ERROR_FILE_NOT_FOUND -> ErrorType.ERROR_FILE_NOT_FOUND + + // There's no mapping for the following errors yet. At the time this library was + // extracted from Focus we didn't use any of those errors. + // WebViewClient.ERROR_UNSUPPORTED_AUTH_SCHEME + // WebViewClient.ERROR_AUTHENTICATION + // WebViewClient.ERROR_FILE + else -> ErrorType.UNKNOWN + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineSessionState.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineSessionState.kt new file mode 100644 index 0000000000..198fc9a303 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineSessionState.kt @@ -0,0 +1,95 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.os.Bundle +import android.util.JsonReader +import android.util.JsonToken +import android.util.JsonWriter +import mozilla.components.concept.engine.EngineSessionState +import org.json.JSONObject + +class SystemEngineSessionState( + internal val bundle: Bundle?, +) : EngineSessionState { + override fun writeTo(writer: JsonWriter) { + writer.beginObject() + + bundle?.keySet()?.forEach { key -> + when ( + @Suppress("DEPRECATION") + val value = bundle[key] + ) { + is Number -> writer.name(key).value(value) + is String -> writer.name(key).value(value) + is Boolean -> writer.name(key).value(value) + } + } + + writer.endObject() + writer.flush() + } + + companion object { + fun fromJSON(json: JSONObject): SystemEngineSessionState { + return SystemEngineSessionState(json.toBundle()) + } + + /** + * Creates a [SystemEngineSessionState] from the given [JsonReader]. + */ + fun from(reader: JsonReader): SystemEngineSessionState { + return SystemEngineSessionState(reader.toBundle()) + } + } +} + +private fun JsonReader.toBundle(): Bundle { + beginObject() + + val bundle = Bundle() + + while (peek() != JsonToken.END_OBJECT) { + val name = nextName() + + when (peek()) { + JsonToken.NULL -> nextNull() + JsonToken.BOOLEAN -> bundle.putBoolean(name, nextBoolean()) + JsonToken.STRING -> bundle.putString(name, nextString()) + JsonToken.NUMBER -> bundle.putDouble(name, nextDouble()) + JsonToken.BEGIN_OBJECT -> bundle.putBundle(name, toBundle()) + else -> skipValue() + } + } + + endObject() + + return bundle +} + +private fun JSONObject.toBundle(): Bundle { + val bundle = Bundle() + + keys().forEach { key -> + val value = get(key) + bundle.put(key, value) + } + + return bundle +} + +private fun Bundle.put(key: String, value: Any) { + when (value) { + is Int -> putInt(key, value) + is Double -> putDouble(key, value) + is Long -> putLong(key, value) + is Float -> putFloat(key, value) + is Char -> putChar(key, value) + is Short -> putShort(key, value) + is Byte -> putByte(key, value) + is String -> putString(key, value) + is Boolean -> putBoolean(key, value) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineView.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineView.kt new file mode 100644 index 0000000000..d0a5a6af8e --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/SystemEngineView.kt @@ -0,0 +1,835 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.annotation.TargetApi +import android.app.Activity +import android.content.Context +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.Canvas +import android.net.Uri +import android.net.http.SslError +import android.os.Build +import android.os.Build.VERSION.SDK_INT +import android.os.Handler +import android.os.Message +import android.util.AttributeSet +import android.view.PixelCopy +import android.view.View +import android.webkit.CookieManager +import android.webkit.DownloadListener +import android.webkit.HttpAuthHandler +import android.webkit.JsPromptResult +import android.webkit.JsResult +import android.webkit.PermissionRequest +import android.webkit.SslErrorHandler +import android.webkit.ValueCallback +import android.webkit.WebChromeClient +import android.webkit.WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE +import android.webkit.WebResourceError +import android.webkit.WebResourceRequest +import android.webkit.WebResourceResponse +import android.webkit.WebView +import android.webkit.WebView.HitTestResult.EMAIL_TYPE +import android.webkit.WebView.HitTestResult.GEO_TYPE +import android.webkit.WebView.HitTestResult.IMAGE_TYPE +import android.webkit.WebView.HitTestResult.PHONE_TYPE +import android.webkit.WebView.HitTestResult.SRC_ANCHOR_TYPE +import android.webkit.WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE +import android.webkit.WebViewClient +import android.widget.FrameLayout +import androidx.annotation.VisibleForTesting +import androidx.annotation.VisibleForTesting.Companion.PRIVATE +import androidx.core.net.toUri +import kotlinx.coroutines.runBlocking +import mozilla.components.browser.engine.system.matcher.UrlMatcher +import mozilla.components.browser.engine.system.permission.SystemPermissionRequest +import mozilla.components.browser.engine.system.window.SystemWindowRequest +import mozilla.components.browser.errorpages.ErrorType +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy +import mozilla.components.concept.engine.EngineView +import mozilla.components.concept.engine.HitResult +import mozilla.components.concept.engine.InputResultDetail +import mozilla.components.concept.engine.content.blocking.Tracker +import mozilla.components.concept.engine.prompt.PromptRequest +import mozilla.components.concept.engine.request.RequestInterceptor.InterceptionResponse +import mozilla.components.concept.engine.selection.SelectionActionDelegate +import mozilla.components.concept.engine.window.WindowRequest +import mozilla.components.concept.storage.PageVisit +import mozilla.components.concept.storage.VisitType +import mozilla.components.support.ktx.android.view.getRectWithViewLocation +import mozilla.components.support.ktx.kotlin.tryGetHostFromUrl +import mozilla.components.support.utils.DownloadUtils + +/** + * WebView-based implementation of EngineView. + */ +@Suppress("TooManyFunctions") +class SystemEngineView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : FrameLayout(context, attrs, defStyleAttr), EngineView, View.OnLongClickListener { + @VisibleForTesting(otherwise = PRIVATE) + internal var session: SystemEngineSession? = null + + override var selectionActionDelegate: SelectionActionDelegate? = null + + /** + * Render the content of the given session. + */ + override fun render(session: EngineSession) { + removeAllViews() + + this.session = session as SystemEngineSession + (session.webView.parent as? SystemEngineView)?.removeView(session.webView) + addView(initWebView(session.webView)) + } + + override fun release() { + this.session = null + + removeAllViews() + } + + override fun onLongClick(view: View?): Boolean { + val result = session?.webView?.hitTestResult + return result?.let { handleLongClick(result.type, result.extra ?: "") } ?: false + } + + override fun onPause() { + session?.apply { + webView.onPause() + webView.pauseTimers() + } + } + + override fun onResume() { + session?.apply { + webView.onResume() + webView.resumeTimers() + } + } + + override fun onDestroy() { + session?.apply { + // The WebView instance is long-lived, as it's referenced in the + // engine session. We can't destroy it here since the session + // might be used with a different engine view instance later. + + // Further, when this engine view gets destroyed, we need to + // remove/detach the WebView so that engine view's activity context + // can properly be destroyed and gc'ed. The WebView instances are + // created with the context provided to the engine (application + // context) and reference their parent (this engine view). Since + // we're keeping the engine session (and their WebView) instances + // in the SessionManager until closed we'd otherwise prevent + // this engine view and its context from getting gc'ed. + (webView.parent as? SystemEngineView)?.removeView(webView) + } + } + + internal fun initWebView(webView: WebView): WebView { + webView.tag = "mozac_system_engine_webview" + webView.webViewClient = createWebViewClient() + webView.webChromeClient = createWebChromeClient() + webView.setDownloadListener(createDownloadListener()) + webView.setFindListener(createFindListener()) + return webView + } + + @Suppress("ComplexMethod", "NestedBlockDepth") + private fun createWebViewClient() = object : WebViewClient() { + override fun doUpdateVisitedHistory(view: WebView, url: String, isReload: Boolean) { + // TODO private browsing not supported for SystemEngine + // https://github.com/mozilla-mobile/android-components/issues/649 + // Check if the delegate wants this type of url. + val delegate = session?.settings?.historyTrackingDelegate ?: return + + if (!delegate.shouldStoreUri(url)) { + return + } + + val visitType = when (isReload) { + true -> VisitType.RELOAD + false -> VisitType.LINK + } + + runBlocking { + session?.settings?.historyTrackingDelegate?.onVisited(url, PageVisit(visitType)) + } + } + + override fun onPageStarted(view: WebView, url: String?, favicon: Bitmap?) { + url?.let { + session?.currentUrl = url + session?.internalNotifyObservers { + onLoadingStateChange(true) + onLocationChange(it, false) + onNavigationStateChange(view.canGoBack(), view.canGoForward()) + } + } + } + + override fun onPageFinished(view: WebView?, url: String?) { + url?.let { + val cert = view?.certificate + session?.internalNotifyObservers { + onLocationChange(it, false) + onLoadingStateChange(false) + onSecurityChange( + secure = cert != null, + host = cert?.let { Uri.parse(url).host }, + issuer = cert?.issuedBy?.oName, + ) + } + } + } + + @Suppress("ReturnCount", "NestedBlockDepth", "LongMethod") + override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { + if (session?.webFontsEnabled == false && UrlMatcher.isWebFont(request.url)) { + return WebResourceResponse(null, null, null) + } + + session?.trackingProtectionPolicy?.let { + val resourceUri = request.url + val scheme = resourceUri.scheme + val path = resourceUri.path + + if (!request.isForMainFrame && scheme != "http" && scheme != "https") { + // Block any malformed non-http(s) URIs. WebView will already ignore things like market: URLs, + // but not in all cases (malformed market: URIs, such as market:://... will still end up here). + // (Note: data: URIs are automatically handled by WebView, and won't end up here either.) + // file:// URIs are disabled separately by setting WebSettings.setAllowFileAccess() + return WebResourceResponse(null, null, null) + } + + // WebView always requests a favicon, even though it won't be used anywhere. This check + // isn't able to block all favicons (some of them will be loaded using <link rel="shortcut icon"> + // with a custom URL which we can't match or detect), but reduces the amount of unnecessary + // favicon loading that's performed. + if (path != null && path.endsWith("/favicon.ico")) { + return WebResourceResponse(null, null, null) + } + + val (matches, stringCategory) = getOrCreateUrlMatcher(resources, it).matches( + resourceUri, + Uri.parse(session?.currentUrl), + ) + + if (!request.isForMainFrame && matches) { + session?.internalNotifyObservers { + val matchedCategories = stringCategory.toTrackingProtectionCategories() + onTrackerBlocked( + Tracker( + resourceUri.toString(), + matchedCategories, + ), + ) + } + return WebResourceResponse(null, null, null) + } + } + + val isRedirect = if (SDK_INT >= Build.VERSION_CODES.N) { + request.isRedirect + } else { + false + } + + session?.let { session -> + session.settings.requestInterceptor?.let { interceptor -> + interceptor.onLoadRequest( + session, + request.url.toString(), + session.currentUrl, + request.hasGesture(), + session.currentUrl.tryGetHostFromUrl() == request.url.host, + isRedirect, + false, + request.isForMainFrame, + )?.apply { + return when (this) { + is InterceptionResponse.Content -> + WebResourceResponse(mimeType, encoding, data.byteInputStream()) + is InterceptionResponse.Url -> { + view.post { view.loadUrl(url) } + super.shouldInterceptRequest(view, request) + } + is InterceptionResponse.AppIntent -> { + if (request.isForMainFrame) { + session.notifyObservers { + onLaunchIntentRequest(url = url, appIntent = appIntent) + } + } + + super.shouldInterceptRequest(view, request) + } + + is InterceptionResponse.Deny -> super.shouldInterceptRequest(view, request) + } + } + } + } + + if (request.isForMainFrame) { + session?.let { + it.notifyObservers { + onLoadRequest(request.url.toString(), request.hasGesture(), true) + } + } + } + + return super.shouldInterceptRequest(view, request) + } + + override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { + handler.cancel() + session?.let { session -> + session.settings.requestInterceptor?.onErrorRequest( + session, + ErrorType.ERROR_SECURITY_SSL, + error.url, + )?.apply { + view.loadUrl(this.uri) + } + } + } + + @Deprecated("Deprecated in Java") + override fun onReceivedError(view: WebView, errorCode: Int, description: String?, failingUrl: String?) { + session?.let { session -> + val errorType = SystemEngineSession.webViewErrorToErrorType(errorCode) + session.settings.requestInterceptor?.onErrorRequest( + session, + errorType, + failingUrl, + )?.apply { + view.loadUrl(this.uri) + } + } + } + + @TargetApi(Build.VERSION_CODES.M) + override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) { + session?.let { session -> + if (!request.isForMainFrame) { + return + } + val errorType = SystemEngineSession.webViewErrorToErrorType(error.errorCode) + session.settings.requestInterceptor?.onErrorRequest( + session, + errorType, + request.url.toString(), + )?.apply { + view.loadUrl(this.uri) + } + } + } + + override fun onReceivedHttpAuthRequest(view: WebView, handler: HttpAuthHandler, host: String, realm: String) { + val session = session ?: return handler.cancel() + + val formattedUrl = session.currentUrl.toUri().let { uri -> + "${uri.scheme ?: "http"}://${uri.host ?: host}" + } + + // Trim obnoxiously long realms. + val trimmedRealm = if (realm.length > MAX_REALM_LENGTH) { + realm.substring(0, MAX_REALM_LENGTH) + "\u2026" + } else { + realm + } + + val message = if (trimmedRealm.isEmpty()) { + context.getString(R.string.mozac_browser_engine_system_auth_no_realm_message, formattedUrl) + } else { + context.getString(R.string.mozac_browser_engine_system_auth_message, trimmedRealm, formattedUrl) + } + + val credentials = view.getAuthCredentials(host, realm) + val userName = credentials.first + val password = credentials.second + + session.notifyObservers { + onPromptRequest( + PromptRequest.Authentication( + formattedUrl, + "", + message, + userName, + password, + PromptRequest.Authentication.Method.HOST, + PromptRequest.Authentication.Level.NONE, + onConfirm = { user, pass -> handler.proceed(user, pass) }, + onDismiss = { handler.cancel() }, + ), + ) + } + } + } + + @Suppress("ComplexMethod") + private fun createWebChromeClient() = object : WebChromeClient() { + override fun getVisitedHistory(callback: ValueCallback<Array<String>>) { + // TODO private browsing not supported for SystemEngine + // https://github.com/mozilla-mobile/android-components/issues/649 + session?.settings?.historyTrackingDelegate?.let { + runBlocking { + callback.onReceiveValue(it.getVisited().toTypedArray()) + } + } + } + + override fun onProgressChanged(view: WebView?, newProgress: Int) { + session?.internalNotifyObservers { onProgress(newProgress) } + } + + override fun onReceivedTitle(view: WebView, title: String?) { + val titleOrEmpty = title ?: "" + // TODO private browsing not supported for SystemEngine + // https://github.com/mozilla-mobile/android-components/issues/649 + session?.currentUrl?.takeIf { it.isNotEmpty() }?.let { url -> + session?.settings?.historyTrackingDelegate?.let { delegate -> + runBlocking { + delegate.onTitleChanged(url, titleOrEmpty) + } + } + } + session?.internalNotifyObservers { + onTitleChange(titleOrEmpty) + onNavigationStateChange(view.canGoBack(), view.canGoForward()) + } + } + + override fun onShowCustomView(view: View, callback: CustomViewCallback) { + addFullScreenView(view, callback) + session?.internalNotifyObservers { onFullScreenChange(true) } + } + + override fun onHideCustomView() { + removeFullScreenView() + session?.internalNotifyObservers { onFullScreenChange(false) } + } + + override fun onPermissionRequestCanceled(request: PermissionRequest) { + session?.internalNotifyObservers { onCancelContentPermissionRequest(SystemPermissionRequest(request)) } + } + + override fun onPermissionRequest(request: PermissionRequest) { + session?.internalNotifyObservers { onContentPermissionRequest(SystemPermissionRequest(request)) } + } + + override fun onJsAlert(view: WebView, url: String?, message: String?, result: JsResult): Boolean { + val session = session ?: return applyDefaultJsDialogBehavior(result) + + // When an alert is triggered from a iframe, url is equals to about:blank, using currentUrl as a fallback. + val safeUrl = if (url.isNullOrBlank()) { + session.currentUrl + } else { + if (url.contains("about")) session.currentUrl else url + } + + val title = context.getString(R.string.mozac_browser_engine_system_alert_title, safeUrl) + + val onDismiss: () -> Unit = { + result.cancel() + } + + val onConfirm: (Boolean) -> Unit = { _ -> result.confirm() } + + session.notifyObservers { + onPromptRequest( + PromptRequest.Alert( + title, + message ?: "", + false, + onConfirm, + onDismiss, + ), + ) + } + return true + } + + override fun onJsPrompt( + view: WebView?, + url: String?, + message: String?, + defaultValue: String?, + result: JsPromptResult, + ): Boolean { + val session = session ?: return applyDefaultJsDialogBehavior(result) + + val title = context.getString(R.string.mozac_browser_engine_system_alert_title, url ?: session.currentUrl) + + val onDismiss: () -> Unit = { + result.cancel() + } + + val onConfirm: (Boolean, String) -> Unit = { _, valueInput -> + result.confirm(valueInput) + } + + session.notifyObservers { + onPromptRequest( + PromptRequest.TextPrompt( + title, + message ?: "", + defaultValue ?: "", + false, + onConfirm, + onDismiss, + ), + ) + } + return true + } + + override fun onJsConfirm(view: WebView?, url: String?, message: String?, result: JsResult): Boolean { + val session = session ?: return applyDefaultJsDialogBehavior(result) + val title = context.getString(R.string.mozac_browser_engine_system_alert_title, url ?: session.currentUrl) + + val onDismiss: () -> Unit = { + result.cancel() + } + + val onConfirmPositiveButton: (Boolean) -> Unit = { _ -> + result.confirm() + } + + val onConfirmNegativeButton: (Boolean) -> Unit = { _ -> + result.cancel() + } + + session.notifyObservers { + onPromptRequest( + PromptRequest.Confirm( + title, + message ?: "", + false, + "", + "", + "", + onConfirmPositiveButton, + onConfirmNegativeButton, + {}, + onDismiss, + ), + ) + } + return true + } + + override fun onShowFileChooser( + webView: WebView?, + filePathCallback: ValueCallback<Array<Uri>>?, + fileChooserParams: FileChooserParams?, + ): Boolean { + var mimeTypes = fileChooserParams?.acceptTypes ?: arrayOf() + + if (mimeTypes.isNotEmpty() && mimeTypes.first().isNullOrEmpty()) { + mimeTypes = arrayOf() + } + + val isMultipleFilesSelection = fileChooserParams?.mode == MODE_OPEN_MULTIPLE + + val captureMode = if (fileChooserParams?.isCaptureEnabled == true) { + PromptRequest.File.FacingMode.ANY + } else { + PromptRequest.File.FacingMode.NONE + } + + val onSelectMultiple: (Context, Array<Uri>) -> Unit = { _, uris -> + filePathCallback?.onReceiveValue(uris) + } + + val onSelectSingle: (Context, Uri) -> Unit = { _, uri -> + filePathCallback?.onReceiveValue(arrayOf(uri)) + } + + val onDismiss: () -> Unit = { + filePathCallback?.onReceiveValue(null) + } + + session?.notifyObservers { + onPromptRequest( + PromptRequest.File( + mimeTypes, + isMultipleFilesSelection, + captureMode, + onSelectSingle, + onSelectMultiple, + onDismiss, + ), + ) + } + + return true + } + + override fun onCreateWindow( + view: WebView, + isDialog: Boolean, + isUserGesture: Boolean, + resultMsg: Message?, + ): Boolean { + session?.internalNotifyObservers { + val newEngineSession = SystemEngineSession(context, session?.settings) + onWindowRequest( + SystemWindowRequest( + view, + newEngineSession, + NestedWebView(context), + isDialog, + isUserGesture, + resultMsg, + ), + ) + } + return true + } + + override fun onCloseWindow(window: WebView) { + session?.internalNotifyObservers { + onWindowRequest(SystemWindowRequest(window, type = WindowRequest.Type.CLOSE)) + } + } + } + + internal fun createDownloadListener(): DownloadListener { + return DownloadListener { url, userAgent, contentDisposition, mimetype, contentLength -> + session?.internalNotifyObservers { + val fileName = DownloadUtils.guessFileName(contentDisposition, null, url, mimetype) + val cookie = CookieManager.getInstance().getCookie(url) + onExternalResource(url, fileName, contentLength, mimetype, cookie, userAgent) + } + } + } + + internal fun createFindListener(): WebView.FindListener { + return WebView.FindListener { activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Boolean -> + session?.internalNotifyObservers { + onFindResult(activeMatchOrdinal, numberOfMatches, isDoneCounting) + } + } + } + + internal fun handleLongClick(type: Int, extra: String): Boolean { + val result: HitResult? = when (type) { + EMAIL_TYPE -> { + HitResult.EMAIL(extra) + } + GEO_TYPE -> { + HitResult.GEO(extra) + } + PHONE_TYPE -> { + HitResult.PHONE(extra) + } + IMAGE_TYPE -> { + HitResult.IMAGE(extra) + } + SRC_ANCHOR_TYPE -> { + HitResult.UNKNOWN(extra) + } + SRC_IMAGE_ANCHOR_TYPE -> { + // HitTestResult.getExtra() contains only the image URL, and not the link + // URL. Internally, WebView's HitTestData contains both, but they only + // make it available via requestFocusNodeHref... + val message = Message() + message.target = ImageHandler(session) + session?.webView?.requestFocusNodeHref(message) + null + } + else -> null + } + result?.let { + session?.internalNotifyObservers { onLongPress(it) } + return true + } + return false + } + + internal fun addFullScreenView(view: View, callback: WebChromeClient.CustomViewCallback) { + val webView = findViewWithTag<WebView>("mozac_system_engine_webview") + val layoutParams = FrameLayout.LayoutParams( + FrameLayout.LayoutParams.MATCH_PARENT, + FrameLayout.LayoutParams.MATCH_PARENT, + ) + webView?.apply { this.visibility = View.INVISIBLE } + + session?.fullScreenCallback = callback + + view.tag = "mozac_system_engine_fullscreen" + addView(view, layoutParams) + } + + internal fun removeFullScreenView() { + val view = findViewWithTag<View>("mozac_system_engine_fullscreen") + val webView = findViewWithTag<WebView>("mozac_system_engine_webview") + view?.let { + webView?.apply { this.visibility = View.VISIBLE } + removeView(view) + } + } + + // Deprecation will be handled in https://github.com/mozilla-mobile/android-components/issues/8514 + @Suppress("DEPRECATION") + class ImageHandler(val session: SystemEngineSession?) : Handler() { + override fun handleMessage(msg: Message) { + val url = msg.data.getString("url") + val src = msg.data.getString("src") + + if (url == null || src == null) { + throw IllegalStateException("WebView did not supply url or src for image link") + } + + session?.internalNotifyObservers { onLongPress(HitResult.IMAGE_SRC(src, url)) } + } + } + + override fun setVerticalClipping(clippingHeight: Int) { + // no-op + } + + override fun setDynamicToolbarMaxHeight(height: Int) { + // no-op + } + + override fun setActivityContext(context: Context?) { + // no-op + } + + override fun canScrollVerticallyUp() = session?.webView?.canScrollVertically(-1) ?: false + + override fun canScrollVerticallyDown() = session?.webView?.canScrollVertically(1) ?: false + + override fun getInputResultDetail(): InputResultDetail { + return (session?.webView as? NestedWebView)?.inputResultDetail + ?: InputResultDetail.newInstance() + } + + override fun captureThumbnail(onFinish: (Bitmap?) -> Unit) { + val webView = session?.webView + if (webView == null) { + onFinish(null) + return + } + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + createThumbnailUsingDrawingView(webView, onFinish) + } else { + createThumbnailUsingPixelCopy(webView, onFinish) + } + } + + override fun clearSelection() { + // no-op + } + + private fun createThumbnailUsingDrawingView(view: View, onFinish: (Bitmap?) -> Unit) { + val outBitmap = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888) + val canvas = Canvas(outBitmap) + view.draw(canvas) + onFinish(outBitmap) + } + + @TargetApi(Build.VERSION_CODES.O) + private fun createThumbnailUsingPixelCopy(view: View, onFinish: (Bitmap?) -> Unit) { + val out = Bitmap.createBitmap(view.width, view.height, Bitmap.Config.ARGB_8888) + val viewRect = view.getRectWithViewLocation() + val window = (context as Activity).window + + PixelCopy.request( + window, + viewRect, + out, + { copyResult -> + val result = if (copyResult == PixelCopy.SUCCESS) out else null + onFinish(result) + }, + handler, + ) + } + + private fun applyDefaultJsDialogBehavior(result: JsResult?): Boolean { + result?.cancel() + return true + } + + @Suppress("Deprecation") + private fun WebView.getAuthCredentials(host: String, realm: String): Pair<String, String> { + val credentials = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + session?.webViewDatabase(context)?.getHttpAuthUsernamePassword(host, realm) + } else { + this.getHttpAuthUsernamePassword(host, realm) + } + + var credentialsPair = "" to "" + + if (!credentials.isNullOrEmpty() && credentials.size == 2) { + val user = credentials[0] ?: "" + val pass = credentials[1] ?: "" + + credentialsPair = user to pass + } + return credentialsPair + } + + companion object { + + // Maximum number of successive dialogs before we prompt users to disable dialogs. + internal const val MAX_SUCCESSIVE_DIALOG_COUNT: Int = 2 + + // Minimum time required between dialogs in seconds before enabling the stop dialog. + internal const val MAX_SUCCESSIVE_DIALOG_SECONDS_LIMIT: Int = 3 + + // Maximum realm length to be shown in authentication dialog. + internal const val MAX_REALM_LENGTH: Int = 50 + + // Number of milliseconds in 1 second. + internal const val SECOND_MS: Int = 1000 + + @Volatile + internal var URL_MATCHER: UrlMatcher? = null + + private val urlMatcherCategoryMap = mapOf( + UrlMatcher.ADVERTISING to TrackingProtectionPolicy.TrackingCategory.AD, + UrlMatcher.ANALYTICS to TrackingProtectionPolicy.TrackingCategory.ANALYTICS, + UrlMatcher.CONTENT to TrackingProtectionPolicy.TrackingCategory.CONTENT, + UrlMatcher.SOCIAL to TrackingProtectionPolicy.TrackingCategory.SOCIAL, + UrlMatcher.CRYPTOMINING to TrackingProtectionPolicy.TrackingCategory.CRYPTOMINING, + UrlMatcher.FINGERPRINTING to TrackingProtectionPolicy.TrackingCategory.FINGERPRINTING, + ) + + private fun String?.toTrackingProtectionCategories(): List<TrackingProtectionPolicy.TrackingCategory> { + val category = urlMatcherCategoryMap[this] + return if (category != null) { + listOf(category) + } else { + emptyList() + } + } + + @Synchronized + internal fun getOrCreateUrlMatcher(resources: Resources, policy: TrackingProtectionPolicy): UrlMatcher { + val categories = urlMatcherCategoryMap.filterValues { policy.contains(it) }.keys + + URL_MATCHER?.setCategoriesEnabled(categories) ?: run { + URL_MATCHER = UrlMatcher.createMatcher( + resources, + R.raw.domain_blocklist, + R.raw.domain_safelist, + categories, + ) + } + + return URL_MATCHER as UrlMatcher + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/ReversibleString.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/ReversibleString.kt new file mode 100644 index 0000000000..5625fda9f3 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/ReversibleString.kt @@ -0,0 +1,98 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.matcher + +/** + * A String wrapper utility that allows for efficient string reversal. We + * regularly need to reverse strings. The standard way of doing this in Java + * would be to copy the string to reverse (e.g. using StringBuffer.reverse()). + * This seems wasteful when we only read our Strings character by character, + * in which case can just transpose positions as needed. + */ +abstract class ReversibleString private constructor( + protected val string: String, + protected val offsetStart: Int, + protected val offsetEnd: Int, +) { + abstract val isReversed: Boolean + abstract fun charAt(position: Int): Char + abstract fun substring(startIndex: Int): ReversibleString + + init { + if (offsetStart > offsetEnd || offsetStart < 0 || offsetEnd < 0) { + throw StringIndexOutOfBoundsException("Cannot create negative-length String") + } + } + + /** + * Returns the length of this string. + */ + fun length(): Int = offsetEnd - offsetStart + + /** + * Reverses this string. + */ + fun reverse(): ReversibleString = + if (isReversed) { + ForwardString(string, offsetStart, offsetEnd) + } else { + ReverseString(string, offsetStart, offsetEnd) + } + + private class ForwardString( + string: String, + offsetStart: Int, + offsetEnd: Int, + ) : ReversibleString(string, offsetStart, offsetEnd) { + override val isReversed: Boolean = false + + override fun charAt(position: Int): Char { + if (position > length()) { + throw StringIndexOutOfBoundsException() + } + return string[position + offsetStart] + } + + override fun substring(startIndex: Int): ReversibleString { + return ForwardString(string, offsetStart + startIndex, offsetEnd) + } + } + + private class ReverseString( + string: String, + offsetStart: Int, + offsetEnd: Int, + ) : ReversibleString(string, offsetStart, offsetEnd) { + override val isReversed: Boolean = true + + override fun charAt(position: Int): Char { + if (position > length()) { + throw StringIndexOutOfBoundsException() + } + return string[length() - 1 - position + offsetStart] + } + + override fun substring(startIndex: Int): ReversibleString { + return ReverseString(string, offsetStart, offsetEnd - startIndex) + } + } + + companion object { + /** + * Create a [ReversibleString] for the provided [String]. + */ + fun create(string: String): ReversibleString { + return ForwardString(string, 0, string.length) + } + } +} + +fun String.reversible(): ReversibleString { + return ReversibleString.create(this) +} + +fun String.reverse(): ReversibleString { + return this.reversible().reverse() +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/Safelist.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/Safelist.kt new file mode 100644 index 0000000000..d75d9966ec --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/Safelist.kt @@ -0,0 +1,176 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.matcher + +import android.net.Uri +import android.text.TextUtils +import android.util.JsonReader +import java.util.ArrayList + +/** + * Stores safe-listed URIs for individual hosts. + */ +internal class Safelist { + private val rootNode: SafelistTrie = SafelistTrie.createRootNode() + + /** + * Adds the provided safelist for the provided host. + * + * @param host the reversed host URI ("foo.com".reverse()) + * @param safelist a [Trie] representing the safe-listed URIs + */ + fun put(host: ReversibleString, safelist: Trie) { + rootNode.putSafelist(host, safelist) + } + + /** + * Checks if the given resource is safe-listed for the given host. + * + * @param host the host URI as string ("foo.com") + * @param host the resources URI as string ("bar.com") + */ + fun contains(host: String, resource: String): Boolean { + return contains(Uri.parse(host), Uri.parse(resource)) + } + + /** + * Checks if the given resource is safe-listed for the given host. + * + * @param hostUri the host URI + * @param resource the resources URI + */ + fun contains(hostUri: Uri, resource: Uri): Boolean { + return if (TextUtils.isEmpty(hostUri.host) || TextUtils.isEmpty(resource.host) || hostUri.scheme == "data") { + false + } else if (resource.scheme?.isPermittedResourceProtocol() == true && + hostUri.scheme?.isSupportedProtocol() == true + ) { + contains(hostUri.host!!.reverse(), resource.host!!.reverse(), rootNode) + } else { + false + } + } + + private fun contains(site: ReversibleString, resource: ReversibleString, revHostTrie: Trie): Boolean { + val next = revHostTrie.children.get(site.charAt(0).code) as? SafelistTrie ?: return false + + if (next.safelist?.findNode(resource) != null) { + return true + } + + return if (site.length() == 1) false else contains(site.substring(1), resource, next) + } + + /** + * Check if this String is a valid resource protocol. + */ + private fun String.isPermittedResourceProtocol(): Boolean { + return this.startsWith("http") || + this.startsWith("https") || + this.startsWith("file") || + this.startsWith("data") || + this.startsWith("javascript") || + this.startsWith("about") + } + + /** + * Check if this String is a supported protocol. + */ + private fun String.isSupportedProtocol(): Boolean { + return this.isPermittedResourceProtocol() || this.startsWith("error") + } + + companion object { + /** + * Parses json for safe-listed URIs. + * + * @param reader a JsonReader + * @return the safe list. + */ + @Suppress("NestedBlockDepth") + fun fromJson(reader: JsonReader): Safelist { + val safelist = Safelist() + reader.beginObject() + + while (reader.hasNext()) { + reader.skipValue() + reader.beginObject() + + val safelistTrie = Trie.createRootNode() + val propertyList = ArrayList<String>() + while (reader.hasNext()) { + val itemName = reader.nextName() + if (itemName == "properties") { + reader.beginArray() + while (reader.hasNext()) { + propertyList.add(reader.nextString()) + } + reader.endArray() + } else if (itemName == "resources") { + reader.beginArray() + while (reader.hasNext()) { + safelistTrie.put(reader.nextString().reverse()) + } + reader.endArray() + } + } + propertyList.forEach { safelist.put(it.reverse(), safelistTrie) } + reader.endObject() + } + reader.endObject() + return safelist + } + } +} + +/** + * A [Trie] implementation which stores a safe list (another [Trie]). + */ +internal class SafelistTrie private constructor(character: Char, parent: SafelistTrie?) : Trie(character, parent) { + var safelist: Trie? = null + + override fun createNode(character: Char, parent: Trie): Trie { + return SafelistTrie(character, parent as SafelistTrie) + } + + /** + * Adds new nodes (recursively) for all chars in the provided string and stores + * the provide safelist Trie. + * + * @param string the string for which a node should be added. + * @param safelist the safelist to store. + * @return the newly created node or the existing one. + */ + fun putSafelist(string: String, safelist: Trie) { + this.putSafelist(string.reversible(), safelist) + } + + /** + * Adds new nodes (recursively) for all chars in the provided string and stores + * the provide safelist Trie. + * + * @param string the string for which a node should be added. + * @param safelist the safelist to store. + * @return the newly created node or the existing one. + */ + fun putSafelist(string: ReversibleString, safelist: Trie) { + val node = super.put(string) as SafelistTrie + + if (node.safelist != null) { + throw IllegalStateException("Safelist already set for node $string") + } + + node.safelist = safelist + } + + companion object { + /** + * Creates a new root node. + */ + fun createRootNode(): SafelistTrie { + return SafelistTrie(Character.MIN_VALUE, null) + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/Trie.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/Trie.kt new file mode 100644 index 0000000000..2a46ba1381 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/Trie.kt @@ -0,0 +1,114 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.matcher + +import android.util.SparseArray + +/** + * Simple implementation of a Trie, used for indexing URLs. + */ +open class Trie constructor(character: Char, parent: Trie?) { + val children = SparseArray<Trie>() + private var terminator = false + + init { + parent?.children?.put(character.code, this) + } + + /** + * Finds the node corresponding to the provided string. + * + * @param string the string to search. + * @return the corresponding node if found, otherwise null. + */ + fun findNode(string: String): Trie? { + return this.findNode(string.reversible()) + } + + /** + * Finds the node corresponding to the provided string. + * + * @param string the string to search. + * @return the corresponding node if found, otherwise null. + */ + fun findNode(string: ReversibleString): Trie? { + var match: Trie? = null + if (terminator && (string.length() == 0 || string.charAt(0) == '.')) { + // Match found and we're at a domain boundary. This is important, because + // we don't want to return on partial domain matches. If the trie node is bar.com, + // and the search string is foo-bar.com, we shouldn't match, but + // foo.bar.com should match.) + match = this + } else if (string.length() != 0) { + val next = children.get(string.charAt(0).code) + match = next?.findNode(string.substring(1)) + } + return match + } + + /** + * Adds new nodes (recursively) for all chars in the provided string. + * + * @param string the string for which a node should be added. + * @return the newly created node or the existing one. + */ + fun put(string: String): Trie { + return this.put(string.reversible()) + } + + /** + * Adds new nodes (recursively) for all chars in the provided string. + * + * @param string the string for which a node should be added. + * @return the newly created node or the existing one. + */ + fun put(string: ReversibleString): Trie { + if (string.length() == 0) { + terminator = true + return this + } + + val character = string.charAt(0) + val child = put(character) + return child.put(string.substring(1)) + } + + /** + * Adds a new node for the provided character if none exists. + * + * @param character the character for which a node should be added. + * @return the newly created node or the existing one. + */ + fun put(character: Char): Trie { + val existingChild = children.get(character.code) + + if (existingChild != null) { + return existingChild + } + + val newChild = createNode(character, this) + children.put(character.code, newChild) + return newChild + } + + /** + * Creates a new node for the provided character and parent node. + * + * @param character the character this node represents + * @param parent the parent of this node + */ + open fun createNode(character: Char, parent: Trie): Trie { + return Trie(character, parent) + } + + companion object { + /** + * Creates a new root node. + */ + fun createRootNode(): Trie { + return Trie(Character.MIN_VALUE, null) + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/UrlMatcher.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/UrlMatcher.kt new file mode 100644 index 0000000000..b496cca756 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/matcher/UrlMatcher.kt @@ -0,0 +1,323 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.matcher + +import android.content.Context +import android.content.res.Resources +import android.net.Uri +import android.util.JsonReader +import androidx.annotation.RawRes +import java.io.InputStreamReader +import java.io.Reader +import java.nio.charset.StandardCharsets.UTF_8 +import java.util.LinkedList + +/** + * Provides functionality to process categorized URL block/safe lists and match + * URLs against these lists. + */ +class UrlMatcher { + private val categories: MutableMap<String, Trie> + internal val enabledCategories = HashSet<String>() + + private val safelist: Safelist? + private val previouslyMatched = HashSet<String>() + private val previouslyUnmatched = HashSet<String>() + + constructor(patterns: Array<String>) { + categories = HashMap() + safelist = null + + val defaultCategory = Trie.createRootNode() + patterns.forEach { defaultCategory.put(it.reverse()) } + categories[DEFAULT] = defaultCategory + enabledCategories.add(DEFAULT) + } + + internal constructor( + enabledCategories: Set<String>, + supportedCategories: Set<String>, + categoryMap: MutableMap<String, Trie>, + safelist: Safelist? = null, + ) { + this.safelist = safelist + this.categories = categoryMap + + for ((key) in categoryMap) { + if (!supportedCategories.contains(key)) { + throw IllegalArgumentException("$key categoryMap contains undeclared category") + } + } + + enabledCategories.forEach { setCategoryEnabled(it, true) } + } + + /** + * Enables the provided categories. + * + * @param categories set of categories to enable. + */ + fun setCategoriesEnabled(categories: Set<String>) { + if (enabledCategories != categories) { + enabledCategories.removeAll { it != DEFAULT } + categories.forEach { setCategoryEnabled(it, true) } + } + } + + internal fun setCategoryEnabled(category: String, enabled: Boolean) { + if (enabled) { + if (enabledCategories.contains(category)) { + return + } else { + enabledCategories.add(category) + previouslyUnmatched.clear() + } + } else { + if (!enabledCategories.contains(category)) { + return + } else { + enabledCategories.remove(category) + previouslyMatched.clear() + } + } + } + + /** + * Checks if the given page URI is blocklisted for the given resource URI. + * Returns true if the site (page URI) is allowed to access + * the resource URI, otherwise false. + * + * @param resourceURI URI of a resource to be loaded by the page + * @param pageURI URI of the page + * @return a [Pair] of <Boolean, String?> the first indicates, if the URI matches and the second + * indicates the category of the match if available otherwise null. + */ + fun matches(resourceURI: String, pageURI: String): Pair<Boolean, String?> { + return matches(Uri.parse(resourceURI), Uri.parse(pageURI)) + } + + /** + * Checks if the given page URI is blocklisted for the given resource URI. + * Returns true if the site (page URI) is allowed to access + * the resource URI, otherwise false. + * + * @param resourceURI URI of a resource to be loaded by the page + * @param pageURI URI of the page + * @return a [Pair] of <Boolean, String?> the first indicates, if the URI matches and the second + * indicates the category of the match if available otherwise null. + */ + @Suppress("ReturnCount", "ComplexMethod") + fun matches(resourceURI: Uri, pageURI: Uri): Pair<Boolean, String?> { + val resourceURLString = resourceURI.toString() + val resourceHost = resourceURI.host + val pageHost = pageURI.host + val notMatchesFound = false to null + + if (previouslyUnmatched.contains(resourceURLString)) { + return notMatchesFound + } + + if (safelist?.contains(pageURI, resourceURI) == true) { + return notMatchesFound + } + + if (pageHost != null && pageHost == resourceHost) { + return notMatchesFound + } + + if (previouslyMatched.contains(resourceURLString)) { + return true to null + } + + if (resourceHost == null) { + return notMatchesFound + } + + for ((key, value) in categories) { + if (enabledCategories.contains(key) && value.findNode(resourceHost.reverse()) != null) { + previouslyMatched.add(resourceURLString) + return true to key + } + } + + previouslyUnmatched.add(resourceURLString) + return notMatchesFound + } + + companion object { + const val ADVERTISING = "Advertising" + const val ANALYTICS = "Analytics" + const val CONTENT = "Content" + const val SOCIAL = "Social" + const val DEFAULT = "default" + const val CRYPTOMINING = "Cryptomining" + const val FINGERPRINTING = "Fingerprinting" + + private val ignoredCategories = setOf("Legacy Disconnect", "Legacy Content") + private val webfontExtensions = arrayOf(".woff2", ".woff", ".eot", ".ttf", ".otf") + private val supportedCategories = setOf( + ADVERTISING, + ANALYTICS, + SOCIAL, + CONTENT, + CRYPTOMINING, + FINGERPRINTING, + ) + + /** + * Creates a new matcher instance for the provided URL lists. + * + * @deprecated Pass resources directly + * @param blocklistFile resource ID to a JSON file containing the block list + * @param safelistFile resource ID to a JSON file containing the safe list + */ + fun createMatcher( + context: Context, + @RawRes blocklistFile: Int, + @RawRes safelistFile: Int, + enabledCategories: Set<String> = supportedCategories, + ): UrlMatcher = + createMatcher(context.resources, blocklistFile, safelistFile, enabledCategories) + + /** + * Creates a new matcher instance for the provided URL lists. + * + * @param blocklistFile resource ID to a JSON file containing the block list + * @param safelistFile resource ID to a JSON file containing the safe list + */ + fun createMatcher( + resources: Resources, + @RawRes blocklistFile: Int, + @RawRes safelistFile: Int, + enabledCategories: Set<String> = supportedCategories, + ): UrlMatcher { + val blocklistReader = InputStreamReader(resources.openRawResource(blocklistFile), UTF_8) + val safelistReader = InputStreamReader(resources.openRawResource(safelistFile), UTF_8) + return createMatcher(blocklistReader, safelistReader, enabledCategories) + } + + /** + * Creates a new matcher instance for the provided URL lists. + * + * @param block reader containing the block list + * @param safe resource ID to a JSON file containing the safe list + */ + fun createMatcher( + block: Reader, + safe: Reader, + enabledCategories: Set<String> = supportedCategories, + ): UrlMatcher { + val categoryMap = HashMap<String, Trie>() + + JsonReader(block).use { + jsonReader -> + loadCategories(jsonReader, categoryMap) + } + + var safelist: Safelist? + JsonReader(safe).use { jsonReader -> safelist = Safelist.fromJson(jsonReader) } + return UrlMatcher(enabledCategories, supportedCategories, categoryMap, safelist) + } + + /** + * Checks if the given URI points to a Web font. + * @param uri the URI to check. + * + * @return true if the URI is a Web font, otherwise false. + */ + fun isWebFont(uri: Uri): Boolean { + val path = uri.path ?: return false + return webfontExtensions.find { path.endsWith(it) } != null + } + + private fun loadCategories( + reader: JsonReader, + categoryMap: MutableMap<String, Trie>, + override: Boolean = false, + ): Map<String, Trie> { + reader.beginObject() + + while (reader.hasNext()) { + val name = reader.nextName() + if (name == "categories") { + extractCategories(reader, categoryMap, override) + } else { + reader.skipValue() + } + } + + reader.endObject() + return categoryMap + } + + @Suppress("ThrowsCount", "ComplexMethod", "NestedBlockDepth") + private fun extractCategories(reader: JsonReader, categoryMap: MutableMap<String, Trie>, override: Boolean) { + reader.beginObject() + + val socialOverrides = LinkedList<String>() + while (reader.hasNext()) { + val categoryName = reader.nextName() + when { + ignoredCategories.contains(categoryName) -> reader.skipValue() + else -> { + val categoryTrie: Trie? + if (!override) { + if (categoryMap.containsKey(categoryName)) { + throw IllegalStateException("Cannot insert already loaded category") + } + categoryTrie = Trie.createRootNode() + categoryMap[categoryName] = categoryTrie + } else { + categoryTrie = categoryMap[categoryName] + if (categoryTrie == null) { + throw IllegalStateException("Cannot add override items to nonexistent category") + } + } + extractCategory(reader) { url, _ -> categoryTrie.put(url.reverse()) } + } + } + } + + val socialTrie = categoryMap[SOCIAL] + if (socialTrie == null && !override) { + throw IllegalStateException("Expected social list to exist") + } + socialOverrides.forEach { socialTrie?.put(it.reverse()) } + reader.endObject() + } + + private fun extractCategory(reader: JsonReader, callback: (String, String) -> Unit) { + reader.beginArray() + while (reader.hasNext()) { + extractSite(reader, callback) + } + reader.endArray() + } + + private fun extractSite(reader: JsonReader, callback: (String, String) -> Unit) { + reader.beginObject() + val siteOwner = reader.nextName() + + reader.beginObject() + while (reader.hasNext()) { + reader.skipValue() + val nextToken = reader.peek() + if (nextToken.name == "STRING") { + // Sometimes there's a "dnt" entry, with unspecified purpose. + reader.skipValue() + } else { + reader.beginArray() + while (reader.hasNext()) { + callback(reader.nextString(), siteOwner) + } + reader.endArray() + } + } + reader.endObject() + + reader.endObject() + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/permission/SystemPermissionRequest.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/permission/SystemPermissionRequest.kt new file mode 100644 index 0000000000..0579b359e1 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/permission/SystemPermissionRequest.kt @@ -0,0 +1,41 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.permission + +import android.webkit.PermissionRequest.RESOURCE_AUDIO_CAPTURE +import android.webkit.PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID +import android.webkit.PermissionRequest.RESOURCE_VIDEO_CAPTURE +import mozilla.components.concept.engine.permission.Permission +import mozilla.components.concept.engine.permission.PermissionRequest + +/** + * WebView-based implementation of [PermissionRequest]. + * + * @property nativeRequest the underlying WebView permission request. + */ +class SystemPermissionRequest(private val nativeRequest: android.webkit.PermissionRequest) : PermissionRequest { + override val uri: String = nativeRequest.origin.toString() + override val id: String = java.util.UUID.randomUUID().toString() + + override val permissions = nativeRequest.resources.map { resource -> + permissionsMap.getOrElse(resource) { Permission.Generic(resource) } + } + + override fun grant(permissions: List<Permission>) { + nativeRequest.grant(permissions.map { it.id }.toTypedArray()) + } + + override fun reject() { + nativeRequest.deny() + } + + companion object { + val permissionsMap = mapOf( + RESOURCE_AUDIO_CAPTURE to Permission.ContentAudioCapture(RESOURCE_AUDIO_CAPTURE), + RESOURCE_VIDEO_CAPTURE to Permission.ContentVideoCapture(RESOURCE_VIDEO_CAPTURE), + RESOURCE_PROTECTED_MEDIA_ID to Permission.ContentProtectedMediaId(RESOURCE_PROTECTED_MEDIA_ID), + ) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/window/SystemWindowRequest.kt b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/window/SystemWindowRequest.kt new file mode 100644 index 0000000000..38fd33284b --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/java/mozilla/components/browser/engine/system/window/SystemWindowRequest.kt @@ -0,0 +1,52 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.window + +import android.os.Message +import android.webkit.WebView +import mozilla.components.browser.engine.system.SystemEngineSession +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.window.WindowRequest + +/** + * WebView-based implementation of [WindowRequest]. + * + * @property webView the WebView from which the request originated. + * @property newWebView the WebView to use for opening a new window, may be null for close requests. + * @property newEngineSession the new [EngineSession] to handle this request. + * @property openAsDialog whether or not the window should be opened as a dialog, defaults to false. + * @property triggeredByUser whether or not the request was triggered by the user, defaults to false. + * @property resultMsg the message to send to the new WebView, may be null. + */ +class SystemWindowRequest( + private val webView: WebView, + private val newEngineSession: EngineSession? = null, + private val newWebView: WebView? = null, + val openAsDialog: Boolean = false, + val triggeredByUser: Boolean = false, + private val resultMsg: Message? = null, + override val type: WindowRequest.Type = WindowRequest.Type.OPEN, +) : WindowRequest { + + override val url: String = "" + + override fun prepare(): EngineSession { + requireNotNull(newEngineSession) + + newWebView?.let { + (newEngineSession as SystemEngineSession).webView = it + } + return newEngineSession + } + + override fun start() { + val message = resultMsg + val transport = message?.obj as? WebView.WebViewTransport + transport?.let { + it.webView = newWebView + message.sendToTarget() + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/raw/domain_blocklist.json b/mobile/android/android-components/components/browser/engine-system/src/main/res/raw/domain_blocklist.json new file mode 100644 index 0000000000..845571cfa0 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/raw/domain_blocklist.json @@ -0,0 +1,11046 @@ +{ + "license": "Copyright 2010-2019 Disconnect, Inc. / This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. / This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. / You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.", + "categories": { + "Advertising": [ + { + "2leep.com": { + "http://2leep.com/": [ + "2leep.com" + ] + } + }, + { + "33Across": { + "http://33across.com/": [ + "33across.com" + ] + } + }, + { + "365Media": { + "http://365media.com/": [ + "365media.com" + ] + } + }, + { + "4INFO": { + "http://www.4info.com/": [ + "4info.com", + "adhaven.com" + ] + } + }, + { + "4mads": { + "http://4mads.com/": [ + "4mads.com" + ] + } + }, + { + "Abax Interactive": { + "http://abaxinteractive.com/": [ + "abaxinteractive.com" + ] + } + }, + { + "Accelia": { + "http://www.accelia.net/": [ + "accelia.net", + "durasite.net" + ] + } + }, + { + "Accordant Media": { + "http://www.accordantmedia.com/": [ + "accordantmedia.com" + ] + } + }, + { + "Acquisio": { + "http://www.acquisio.com/": [ + "acquisio.com", + "clickequations.net" + ] + } + }, + { + "Actisens": { + "http://www.actisens.com/": [ + "actisens.com", + "gestionpub.com" + ] + } + }, + { + "ActiveConversion": { + "http://www.activeconversion.com/": [ + "activeconversion.com", + "activemeter.com" + ] + } + }, + { + "Act-On": { + "http://www.act-on.com/": [ + "act-on.com", + "actonsoftware.com" + ] + } + }, + { + "Acuity": { + "http://www.acuity.com/": [ + "acuity.com", + "acuityads.com", + "acuityplatform.com" + ] + } + }, + { + "AD2ONE": { + "http://www.ad2onegroup.com/": [ + "ad2onegroup.com" + ] + } + }, + { + "Ad4Game": { + "http://ad4game.com/": [ + "ad4game.com" + ] + } + }, + { + "ad6media": { + "http://www.ad6media.fr/": [ + "ad6media.fr" + ] + } + }, + { + "Adabra": { + "https://www.adabra.com/": [ + "adabra.com" + ] + } + }, + { + "Adality": { + "http://adality.de/": [ + "adality.de", + "adrtx.net" + ] + } + }, + { + "AdaptiveAds": { + "http://www.adaptiveads.com/": [ + "adaptiveads.com" + ] + } + }, + { + "Adaptly": { + "http://adaptly.com/": [ + "adaptly.com" + ] + } + }, + { + "Adara Media": { + "http://www.adaramedia.com/": [ + "adaramedia.com", + "opinmind.com", + "yieldoptimizer.com" + ] + } + }, + { + "Adatus": { + "http://www.adatus.com/": [ + "adatus.com" + ] + } + }, + { + "Adbot": { + "https://adbot.tw/": [ + "adbot.tw" + ] + } + }, + { + "Adbrain": { + "http://www.adbrain.com/": [ + "adbrain.com", + "adbrn.com" + ] + } + }, + { + "adBrite": { + "http://www.adbrite.com/": [ + "adbrite.com" + ] + } + }, + { + "Adbroker.de": { + "http://adbroker.de/": [ + "adbroker.de" + ] + } + }, + { + "Adchemy": { + "http://www.adchemy.com/": [ + "adchemy.com" + ] + } + }, + { + "AdCirrus": { + "http://adcirrus.com/": [ + "adcirrus.com" + ] + } + }, + { + "Ad Decisive": { + "http://www.addecisive.com/": [ + "a2dfp.net", + "addecisive.com" + ] + } + }, + { + "addGloo": { + "http://www.addgloo.com/": [ + "addgloo.com" + ] + } + }, + { + "Addvantage Media": { + "http://www.addvantagemedia.com/": [ + "addvantagemedia.com" + ] + } + }, + { + "Ad Dynamo": { + "http://www.addynamo.com/": [ + "addynamo.com", + "addynamo.net" + ] + } + }, + { + "Adelphic": { + "https://adelphic.com/": [ + "adelphic.com", + "ipredictive.com" + ] + } + }, + { + "AdEngage": { + "http://adengage.com/": [ + "adengage.com" + ] + } + }, + { + "AD Europe": { + "http://www.adeurope.com/": [ + "adeurope.com" + ] + } + }, + { + "AdExtent": { + "http://www.adextent.com/": [ + "adextent.com" + ] + } + }, + { + "AdF.ly": { + "http://adf.ly/": [ + "adf.ly" + ] + } + }, + { + "Adfonic": { + "http://adfonic.com/": [ + "adfonic.com" + ] + } + }, + { + "Adforge": { + "http://adforgeinc.com/": [ + "adforgeinc.com" + ] + } + }, + { + "Adform": { + "http://www.adform.com/": [ + "adform.com", + "adform.net", + "adformdsp.net" + ] + } + }, + { + "AdFox": { + "http://adfox.ru/": [ + "adfox.ru" + ] + } + }, + { + "AdFrontiers": { + "http://www.adfrontiers.com/": [ + "adfrontiers.com" + ] + } + }, + { + "Adfunky": { + "http://www.adfunky.com/": [ + "adfunky.com", + "adfunkyserver.com" + ] + } + }, + { + "Adfusion": { + "http://www.adfusion.com/": [ + "adfusion.com" + ] + } + }, + { + "AdGainerSolutions": { + "http://adgainersolutions.com/adgainer/": [ + "adgainersolutions.com" + ] + } + }, + { + "AdGent Digital": { + "http://www.adgentdigital.com/": [ + "adgentdigital.com", + "shorttailmedia.com" + ] + } + }, + { + "AdGibbon": { + "http://www.adgibbon.com/": [ + "adgibbon.com" + ] + } + }, + { + "Adglare": { + "https://www.adglare.com/": [ + "adglare.com", + "adglare.net" + ] + } + }, + { + "adhood": { + "http://www.adhood.com/": [ + "adhood.com" + ] + } + }, + { + "Adiant": { + "http://www.adiant.com/": [ + "adblade.com", + "adiant.com" + ] + } + }, + { + "AdInsight": { + "http://www.adinsight.com/": [ + "adinsight.com", + "adinsight.eu" + ] + } + }, + { + "AdIQuity": { + "http://adiquity.com/": [ + "adiquity.com" + ] + } + }, + { + "ADITION": { + "http://www.adition.com/": [ + "adition.com" + ] + } + }, + { + "AdJug": { + "http://www.adjug.com/": [ + "adjug.com" + ] + } + }, + { + "AdJuggler": { + "http://www.adjuggler.com/": [ + "adjuggler.com", + "adjuggler.net" + ] + } + }, + { + "Adjust": { + "https://adjust.com": [ + "adjust.com" + ] + } + }, + { + "AdKeeper": { + "http://www.adkeeper.com/": [ + "adkeeper.com", + "akncdn.com" + ] + } + }, + { + "AdKernel": { + "http://adkernel.com": [ + "adkernel.com" + ] + } + }, + { + "Ad Knife": { + "http://static.adknife.com/": [ + "adknife.com" + ] + } + }, + { + "Adknowledge": { + "http://www.adknowledge.com/": [ + "adknowledge.com", + "adparlor.com", + "bidsystem.com", + "cubics.com", + "lookery.com" + ] + } + }, + { + "AdLantis": { + "http://www.adlantis.jp/": [ + "adimg.net", + "adlantis.jp" + ] + } + }, + { + "AdLeave": { + "http://www.adleave.com/": [ + "adleave.com" + ] + } + }, + { + "Adlibrium": { + "http://www.adlibrium.com/": [ + "adlibrium.com" + ] + } + }, + { + "Adlucent": { + "http://adlucent.com": [ + "adlucent.com" + ] + } + }, + { + "Ad Magnet": { + "http://www.admagnet.com/": [ + "admagnet.com", + "admagnet.net" + ] + } + }, + { + "Admarketplace": { + "http://www.admarketplace.com/": [ + "admarketplace.com", + "admarketplace.net", + "ampxchange.com" + ] + } + }, + { + "AdMarvel": { + "http://www.admarvel.com/": [ + "admarvel.com" + ] + } + }, + { + "AdMatrix": { + "http://www.admatrix.jp/": [ + "admatrix.jp" + ] + } + }, + { + "AdMaven": { + "https://ad-maven.com/": [ + "ad-maven.com", + "agreensdistra.info", + "boudja.com", + "rensovetors.info", + "wrethicap.info" + ] + } + }, + { + "AdMaximizer Network": { + "http://admaximizer.com/": [ + "admaximizer.com" + ] + } + }, + { + "AdMedia": { + "http://www.admedia.com/": [ + "admedia.com" + ] + } + }, + { + "Admeta": { + "http://www.admeta.com/": [ + "admeta.com", + "atemda.com" + ] + } + }, + { + "Admicro": { + "http://www.admicro.vn/": [ + "admicro.vn", + "vcmedia.vn" + ] + } + }, + { + "Admixer": { + "https://admixer.co.kr/main": [ + "admixer.co.kr" + ] + } + }, + { + "Admized": { + "http://www.admized.com/": [ + "admized.com" + ] + } + }, + { + "Admobile": { + "http://admobile.com/": [ + "admobile.com" + ] + } + }, + { + "Admotion": { + "http://www.admotion.com/": [ + "admotion.com", + "nspmotion.com" + ] + } + }, + { + "Adnetik": { + "http://adnetik.com/": [ + "adnetik.com", + "wtp101.com" + ] + } + }, + { + "AdNetwork.net": { + "http://www.adnetwork.net/": [ + "adnetwork.net" + ] + } + }, + { + "Adnium": { + "https://adnium.com": [ + "adnium.com" + ] + } + }, + { + "adnologies": { + "http://www.adnologies.com/": [ + "adnologies.com", + "heias.com" + ] + } + }, + { + "Adobe": { + "http://www.adobe.com/": [ + "2o7.net", + "auditude.com", + "demdex.com", + "demdex.net", + "dmtracker.com", + "efrontier.com", + "everestads.net", + "everestjs.net", + "everesttech.net", + "hitbox.com", + "omniture.com", + "omtrdc.net", + "touchclarity.com" + ] + } + }, + { + "AdOcean": { + "http://www.adocean-global.com/": [ + "adocean-global.com", + "adocean.pl" + ] + } + }, + { + "Adometry": { + "http://www.adometry.com/": [ + "adometry.com", + "dmtry.com" + ] + } + }, + { + "Adomik": { + "http://www.adomik.com/": [ + "adomik.com" + ] + } + }, + { + "AdOnion": { + "http://www.adonion.com/": [ + "adonion.com" + ] + } + }, + { + "Adorika": { + "http://www.clickotmedia.com/": [ + "clickotmedia.com" + ] + } + }, + { + "Adotmob": { + "https://adotmob.com/": [ + "adotmob.com" + ] + } + }, + { + "ADP Dealer Services": { + "http://www.adpdealerservices.com/": [ + "admission.net", + "adpdealerservices.com", + "cobalt.com" + ] + } + }, + { + "ad pepper media": { + "http://www.adpepper.us/": [ + "adpepper.com", + "adpepper.us" + ] + } + }, + { + "AdPerfect": { + "http://www.adperfect.com/": [ + "adperfect.com" + ] + } + }, + { + "Adperium": { + "http://www.adperium.com/": [ + "adperium.com" + ] + } + }, + { + "Adpersia": { + "http://www.adpersia.com/": [ + "adpersia.com" + ] + } + }, + { + "adPrecision": { + "http://adprecision.net/": [ + "adprs.net", + "aprecision.net" + ] + } + }, + { + "AdPredictive": { + "http://www.adpredictive.com/": [ + "adpredictive.com" + ] + } + }, + { + "AdReactor": { + "http://www.adreactor.com/": [ + "adreactor.com" + ] + } + }, + { + "AdReady": { + "http://www.adready.com/": [ + "adready.com", + "adreadytractions.com" + ] + } + }, + { + "AdRevolution": { + "http://adrevolution.com/": [ + "adrevolution.com" + ] + } + }, + { + "AdRiver": { + "http://adriver.ru/": [ + "adriver.ru" + ] + } + }, + { + "adrolays": { + "http://adrolays.com/": [ + "adrolays.com", + "adrolays.de" + ] + } + }, + { + "AdRoll": { + "http://www.adroll.com/": [ + "adroll.com" + ] + } + }, + { + "adscale": { + "http://www.adscale.de/": [ + "adscale.de" + ] + } + }, + { + "Adscience": { + "https://www.adscience.nl/": [ + "adscience.nl" + ] + } + }, + { + "AdServerPub": { + "http://www.adserverpub.com/": [ + "adserverpub.com" + ] + } + }, + { + "AdShuffle": { + "http://www.adshuffle.com/": [ + "adshuffle.com" + ] + } + }, + { + "AdSide": { + "http://www.adside.com/": [ + "adside.com", + "doclix.com" + ] + } + }, + { + "AdSpeed": { + "http://www.adspeed.com/": [ + "adspeed.com", + "adspeed.net" + ] + } + }, + { + "Adsperity": { + "https://www.adsperity.com/": [ + "adsperity.com" + ] + } + }, + { + "AdSpirit": { + "http://www.adspirit.de/": [ + "adspirit.com", + "adspirit.de", + "adspirit.net" + ] + } + }, + { + "Adsrevenue.net": { + "http://adsrevenue.net/": [ + "adsrevenue.net" + ] + } + }, + { + "AdStir": { + "https://en.ad-stir.com/": [ + "ad-stir.com" + ] + } + }, + { + "AdsTours": { + "http://www.adstours.com/": [ + "adstours.com", + "clickintext.net" + ] + } + }, + { + "Adsty": { + "http://adsty.com/": [ + "adsty.com", + "adx1.com" + ] + } + }, + { + "Adsupply": { + "http://www.adsupply.com/": [ + "4dsply.com", + "adsupply.com" + ] + } + }, + { + "Adswizz": { + "http://adswizz.com": [ + "adswizz.com" + ] + } + }, + { + "ADTECH": { + "http://www.adtech.com/": [ + "adtech.com", + "adtech.de", + "adtechus.com" + ] + } + }, + { + "Adtegrity.com": { + "http://www.adtegrity.com/": [ + "adtegrity.com", + "adtegrity.net" + ] + } + }, + { + "ADTELLIGENCE": { + "http://www.adtelligence.de/": [ + "adtelligence.de" + ] + } + }, + { + "Adthink": { + "https://adthink.com/": [ + "adthink.com", + "audienceinsights.net" + ] + } + }, + { + "AdTiger": { + "http://www.adtiger.de/": [ + "adtiger.de" + ] + } + }, + { + "AdTruth": { + "http://adtruth.com/": [ + "adtruth.com" + ] + } + }, + { + "Adult AdWorld": { + "http://adultadworld.com/": [ + "adultadworld.com" + ] + } + }, + { + "Adultmoda": { + "http://www.adultmoda.com/": [ + "adultmoda.com" + ] + } + }, + { + "Adventive": { + "http://adventive.com/": [ + "adventive.com" + ] + } + }, + { + "Adverline": { + "http://www.adverline.com/": [ + "adnext.fr", + "adverline.com" + ] + } + }, + { + "Adversal.com": { + "http://www.adversal.com/": [ + "adv-adserver.com", + "adversal.com" + ] + } + }, + { + "Adverticum": { + "http://www.adverticum.com/": [ + "adsmart.com", + "adverticum.com", + "adverticum.net" + ] + } + }, + { + "Advertise.com": { + "http://www.advertise.com/": [ + "advertise.com" + ] + } + }, + { + "AdvertiseSpace": { + "http://www.advertisespace.com/": [ + "advertisespace.com" + ] + } + }, + { + "Advert Stream": { + "http://www.advertstream.com/": [ + "advertstream.com" + ] + } + }, + { + "Advisor Media": { + "http://advisormedia.cz/": [ + "advisormedia.cz" + ] + } + }, + { + "Adworx": { + "http://adworx.at/": [ + "adworx.at", + "adworx.be", + "adworx.nl" + ] + } + }, + { + "AdXpansion": { + "http://www.adxpansion.com/": [ + "adxpansion.com" + ] + } + }, + { + "Adxvalue": { + "http://adxvalue.com/": [ + "adxvalue.com", + "adxvalue.de" + ] + } + }, + { + "adyard": { + "http://adyard.de/": [ + "adyard.de" + ] + } + }, + { + "AdYield": { + "http://www.adyield.com/": [ + "adxyield.com", + "adyield.com" + ] + } + }, + { + "AdYouLike": { + "https://www.adyoulike.com/": [ + "adyoulike.com", + "omnitagjs.com", + "pulpix.com" + ] + } + }, + { + "ADZ": { + "http://www.adzcentral.com/": [ + "adzcentral.com" + ] + } + }, + { + "Adzerk": { + "http://www.adzerk.com/": [ + "adzerk.com", + "adzerk.net" + ] + } + }, + { + "adzly": { + "http://www.adzly.com/": [ + "adzly.com" + ] + } + }, + { + "Aegis Group": { + "http://www.aemedia.com/": [ + "aemedia.com", + "bluestreak.com" + ] + } + }, + { + "AERIFY MEDIA": { + "http://aerifymedia.com/": [ + "aerifymedia.com", + "anonymous-media.com" + ] + } + }, + { + "Affectv": { + "http://affectv.co.uk/": [ + "affectv.co.uk" + ] + } + }, + { + "affilinet": { + "http://www.affili.net/": [ + "affili.net", + "affilinet-inside.de", + "banner-rotation.com", + "successfultogether.co.uk" + ] + } + }, + { + "Affine": { + "http://www.affine.tv/": [ + "affine.tv", + "affinesystems.com" + ] + } + }, + { + "Affinity": { + "http://www.affinity.com/": [ + "affinity.com" + ] + } + }, + { + "AfterDownload": { + "http://www.afterdownload.com/": [ + "afdads.com", + "afterdownload.com" + ] + } + }, + { + "Aim4Media": { + "http://aim4media.com/": [ + "aim4media.com" + ] + } + }, + { + "Airpush": { + "http://www.airpush.com/": [ + "airpush.com" + ] + } + }, + { + "AK": { + "http://www.aggregateknowledge.com/": [ + "aggregateknowledge.com", + "agkn.com" + ] + } + }, + { + "Akamai": { + "http://www.akamai.com/": [ + "imiclk.com" + ] + } + }, + { + "Albacross": { + "https://albacross.com": [ + "albacross.com" + ] + } + }, + { + "AllStarMediaGroup": { + "http://allstarmediagroup.com/": [ + "allstarmediagroup.com" + ] + } + }, + { + "Aloodo": { + "https://aloodo.com/": [ + "aloodo.com" + ] + } + }, + { + "AlterGeo": { + "http://altergeo.ru/": [ + "altergeo.ru" + ] + } + }, + { + "Amazon.com": { + "http://www.amazon.com/": [ + "amazon-adsystem.com", + "amazon.ca", + "amazon.co.jp", + "amazon.co.uk", + "amazon.de", + "amazon.es", + "amazon.fr", + "amazon.it", + "assoc-amazon.com" + ] + } + }, + { + "Ambient Digital": { + "http://ambientdigital.com.vn/": [ + "adnetwork.vn", + "ambientdigital.com.vn" + ] + } + }, + { + "Amobee": { + "http://amobee.com/": [ + "adconion.com", + "amgdgt.com", + "amobee.com", + "euroclick.com", + "smartclip.com", + "turn.com" + ] + } + }, + { + "AndBeyond": { + "http://andbeyond.media/": [ + "andbeyond.media" + ] + } + }, + { + "Answers.com": { + "http://www.answers.com/": [ + "dsply.com" + ] + } + }, + { + "AOL": { + "http://www.aol.com/": [ + "adsonar.com", + "adtechjp.com", + "advertising.com", + "aolcloud.net", + "atwola.com", + "leadback.com", + "tacoda.net", + "vidible.tv" + ] + } + }, + { + "AppCast": { + "https://appcast.io/": [ + "appcast.io" + ] + } + }, + { + "Appenda": { + "http://www.appenda.com/": [ + "appenda.com" + ] + } + }, + { + "AppFlood": { + "http://appflood.com/": [ + "appflood.com" + ] + } + }, + { + "Appier": { + "http://appier.com/": [ + "appier.com" + ] + } + }, + { + "Applifier": { + "http://www.applifier.com/": [ + "applifier.com" + ] + } + }, + { + "Applovin": { + "http://www.applovin.com/": [ + "applovin.com" + ] + } + }, + { + "AppNexus": { + "http://www.appnexus.com/": [ + "adlantic.nl", + "adnxs.com", + "adrdgt.com", + "alenty.com", + "appnexus.com" + ] + } + }, + { + "AppsFlyer": { + "http://appsflyer.com/": [ + "appsflyer.com" + ] + } + }, + { + "appssavvy": { + "http://appssavvy.com/": [ + "appssavvy.com" + ] + } + }, + { + "Arkwrights Homebrew": { + "http://www.arkwrightshomebrew.com/": [ + "arkwrightshomebrew.com", + "ctasnet.com" + ] + } + }, + { + "AT Internet": { + "http://www.atinternet.com/": [ + "hit-parade.com" + ] + } + }, + { + "ATN": { + "http://affiliatetracking.com/": [ + "affiliatetracking.com" + ] + } + }, + { + "Atoomic.com": { + "http://www.atoomic.com/": [ + "atoomic.com" + ] + } + }, + { + "Atrinsic": { + "http://atrinsic.com/": [ + "atrinsic.com" + ] + } + }, + { + "AT&T": { + "http://www.att.com/": [ + "att.com", + "yp.com" + ] + } + }, + { + "Audience2Media": { + "http://www.audience2media.com/": [ + "audience2media.com" + ] + } + }, + { + "Audience Ad Network": { + "http://audienceadnetwork.com/": [ + "audienceadnetwork.com" + ] + } + }, + { + "AudienceScience": { + "http://www.audiencescience.com/": [ + "audiencescience.com", + "revsci.net", + "targetingmarketplace.com", + "wunderloop.net" + ] + } + }, + { + "Augme": { + "http://www.augme.com/": [ + "augme.com", + "hipcricket.com" + ] + } + }, + { + "Augur": { + "http://www.augur.io/": [ + "augur.io" + ] + } + }, + { + "AUTOCENTRE.UA": { + "http://www.autocentre.ua/": [ + "am.ua", + "autocentre.ua" + ] + } + }, + { + "Automattic": { + "http://automattic.com/": [ + "pubmine.com" + ] + } + }, + { + "Avalanchers": { + "http://www.avalanchers.com/": [ + "avalanchers.com" + ] + } + }, + { + "AvantLink": { + "http://www.avantlink.com/": [ + "avantlink.com" + ] + } + }, + { + "Avocet": { + "https://avocet.io/": [ + "avocet.io" + ] + } + }, + { + "Avsads": { + "http://avsads.com/": [ + "avsads.com" + ] + } + }, + { + "AWeber": { + "http://www.aweber.com/": [ + "aweber.com" + ] + } + }, + { + "Awin": { + "http://www.awin.com/": [ + "digitalwindow.com", + "dwin1.com", + "perfiliate.com" + ] + } + }, + { + "Azet": { + "http://mediaimpact.sk/": [ + "azetklik.sk", + "rsz.sk" + ] + } + }, + { + "BackBeat Media": { + "http://www.backbeatmedia.com/": [ + "backbeatmedia.com" + ] + } + }, + { + "Bannerconnect": { + "http://www.bannerconnect.net/": [ + "bannerconnect.net" + ] + } + }, + { + "Barilliance": { + "http://www.barilliance.com/": [ + "barilliance.com" + ] + } + }, + { + "BaronsNetworks": { + "http://baronsoffers.com/": [ + "baronsoffers.com" + ] + } + }, + { + "Batanga Network": { + "http://www.batanganetwork.com/": [ + "batanga.com", + "batanganetwork.com" + ] + } + }, + { + "BeachFront": { + "http://beachfront.com/": [ + "beachfront.com" + ] + } + }, + { + "Beanstock Media": { + "http://www.beanstockmedia.com/": [ + "beanstockmedia.com" + ] + } + }, + { + "beencounter": { + "http://www.beencounter.com/": [ + "beencounter.com" + ] + } + }, + { + "Begun": { + "http://www.begun.ru/": [ + "begun.ru" + ] + } + }, + { + "belboon": { + "http://www.belboon.com/": [ + "adbutler.de", + "belboon.com" + ] + } + }, + { + "Betgenius": { + "http://www.betgenius.com/": [ + "betgenius.com", + "connextra.com" + ] + } + }, + { + "BetweenDigital": { + "http://betweendigital.com": [ + "betweendigital.com" + ] + } + }, + { + "Bidfluence": { + "https://www.bidfluence.com/": [ + "bidfluence.com" + ] + } + }, + { + "Bidr": { + "http://bidr.io": [ + "bidr.io" + ] + } + }, + { + "BidSwitch": { + "https://www.bidswitch.com/": [ + "bidswitch.net", + "mfadsrvr.com" + ] + } + }, + { + "Bidtellect": { + "https://www.bidtellect.com/": [ + "bidtellect.com", + "bttrack.com" + ] + } + }, + { + "BidVertiser": { + "http://www.bidvertiser.com/": [ + "bidvertiser.com" + ] + } + }, + { + "BigClick": { + "http://bigclick.me/": [ + "bgclck.me", + "xcvgdf.party" + ] + } + }, + { + "bigmirnet": { + "http://www.bigmir.net/": [ + "bigmir.net" + ] + } + }, + { + "BinLayer": { + "http://binlayer.com/": [ + "binlayer.com" + ] + } + }, + { + "Bitcoin Plus": { + "http://www.bitcoinplus.com/": [ + "bitcoinplus.com" + ] + } + }, + { + "BitMedia": { + "https://bitmedia.io/": [ + "bitmedia.io" + ] + } + }, + { + "BittAds": { + "http://www.bittads.com/": [ + "bittads.com" + ] + } + }, + { + "Bizo": { + "http://www.bizo.com/": [ + "bizo.com", + "bizographics.com" + ] + } + }, + { + "Black Label Ads": { + "http://www.blacklabelads.com/": [ + "blacklabelads.com" + ] + } + }, + { + "BlogCatalog": { + "http://www.blogcatalog.com/": [ + "blogcatalog.com" + ] + } + }, + { + "BlogFrog": { + "http://theblogfrog.com/": [ + "theblogfrog.com" + ] + } + }, + { + "BlogHer": { + "http://www.blogher.com/": [ + "blogher.com", + "blogherads.com" + ] + } + }, + { + "BlogRollr": { + "http://blogrollr.com/": [ + "blogrollr.com" + ] + } + }, + { + "BLOOM Digital Platforms": { + "http://bloom-hq.com/": [ + "adgear.com", + "adgrx.com", + "bloom-hq.com" + ] + } + }, + { + "BlueKai": { + "http://www.bluekai.com/": [ + "bkrtx.com", + "bluekai.com", + "tracksimple.com" + ] + } + }, + { + "Blu Trumpet": { + "http://www.blutrumpet.com/": [ + "blutrumpet.com" + ] + } + }, + { + "Boo-Box": { + "http://boo-box.com/": [ + "boo-box.com" + ] + } + }, + { + "BoostBox": { + "https://www.boostbox.com.br/": [ + "boostbox.com.br" + ] + } + }, + { + "Bouncex": { + "https://www.bouncex.com/": [ + "bounceexchange.com", + "bouncex.com", + "bouncex.net" + ] + } + }, + { + "Brainient": { + "http://brainient.com/": [ + "brainient.com" + ] + } + }, + { + "Brand Affinity Technologies": { + "http://www.brandaffinity.net/": [ + "brandaffinity.net" + ] + } + }, + { + "Brandcrumb": { + "http://www.brandcrumb.com": [ + "brandcrumb.com" + ] + } + }, + { + "Brand.net": { + "http://www.brand.net/": [ + "brand.net" + ] + } + }, + { + "Brandscreen": { + "http://www.brandscreen.com/": [ + "brandscreen.com", + "rtbidder.net" + ] + } + }, + { + "BreakTime": { + "https://www.breaktime.com.tw/": [ + "breaktime.com.tw" + ] + } + }, + { + "BrightRoll": { + "http://www.brightroll.com/": [ + "brightroll.com", + "btrll.com" + ] + } + }, + { + "BrightTag": { + "http://www.brighttag.com/": [ + "brighttag.com", + "btstatic.com", + "thebrighttag.com" + ] + } + }, + { + "Brilig": { + "http://www.brilig.com/": [ + "brilig.com" + ] + } + }, + { + "BuckSense": { + "http://www.bucksense.com": [ + "bucksense.com" + ] + } + }, + { + "Burstly": { + "http://www.burstly.com/": [ + "burstly.com" + ] + } + }, + { + "Burst Media": { + "http://www.burstmedia.com/": [ + "burstbeacon.com", + "burstdirectads.com", + "burstmedia.com", + "burstnet.com", + "giantrealm.com" + ] + } + }, + { + "BusinessOnline": { + "http://www.businessol.com/": [ + "businessol.com" + ] + } + }, + { + "Button": { + "https://www.usebutton.com": [ + "usebutton.com" + ] + } + }, + { + "BuySellAds": { + "http://buysellads.com/": [ + "beaconads.com", + "buysellads.com" + ] + } + }, + { + "Buysight": { + "http://www.buysight.com/": [ + "buysight.com", + "permuto.com", + "pulsemgr.com" + ] + } + }, + { + "BuzzParadise": { + "http://www.buzzparadise.com/": [ + "buzzparadise.com" + ] + } + }, + { + "BV! MEDIA": { + "http://www.bvmedia.ca/": [ + "bvmedia.ca", + "networldmedia.com", + "networldmedia.net" + ] + } + }, + { + "c1exchange": { + "https://c1exchange.com/": [ + "c1exchange.com" + ] + } + }, + { + "C3 Metrics": { + "http://c3metrics.com/": [ + "attributionmodel.com", + "c3metrics.com", + "c3tag.com" + ] + } + }, + { + "Cadreon": { + "http://www.cadreon.com/": [ + "cadreon.com" + ] + } + }, + { + "CampaignGrid": { + "http://www.campaigngrid.com/": [ + "campaigngrid.com" + ] + } + }, + { + "CAPITALDATA": { + "http://www.capitaldata.fr/": [ + "capitaldata.fr" + ] + } + }, + { + "Carambola": { + "https://www.carambola.com/": [ + "carambo.la" + ] + } + }, + { + "Caraytech": { + "http://www.caraytech.com.ar/": [ + "caraytech.com.ar", + "e-planning.net" + ] + } + }, + { + "Cart.ro": { + "http://www.cart.ro/": [ + "cart.ro", + "statistics.ro" + ] + } + }, + { + "CartsGuru": { + "https://carts.guru/": [ + "carts.guru" + ] + } + }, + { + "Casale Media": { + "http://www.casalemedia.com/": [ + "casalemedia.com", + "medianet.com" + ] + } + }, + { + "CBproADS": { + "http://www.cbproads.com/": [ + "cbproads.com" + ] + } + }, + { + "Cedato": { + "https://www.cedato.com/": [ + "cedato.com" + ] + } + }, + { + "Chango": { + "http://www.chango.com/": [ + "chango.ca", + "chango.com" + ] + } + }, + { + "ChannelAdvisor": { + "http://www.channeladvisor.com/": [ + "channeladvisor.com", + "searchmarketing.com" + ] + } + }, + { + "Channel Intelligence": { + "http://www.channelintelligence.com/": [ + "channelintelligence.com" + ] + } + }, + { + "Chartboost": { + "https://www.chartboost.com/": [ + "chartboost.com" + ] + } + }, + { + "CheckM8": { + "http://www.checkm8.com/": [ + "checkm8.com" + ] + } + }, + { + "Chitika": { + "http://chitika.com/": [ + "chitika.com", + "chitika.net" + ] + } + }, + { + "ChoiceStream": { + "http://www.choicestream.com/": [ + "choicestream.com" + ] + } + }, + { + "ClearLink": { + "https://www.clearlink.com/": [ + "clearlink.com" + ] + } + }, + { + "ClearSaleing": { + "http://www.clearsaleing.com/": [ + "clearsaleing.com", + "csdata1.com", + "csdata2.com", + "csdata3.com" + ] + } + }, + { + "Clearsearch Media": { + "http://www.clearsearchmedia.com/": [ + "clearsearchmedia.com", + "csm-secure.com" + ] + } + }, + { + "ClearSight Interactive": { + "http://www.clearsightinteractive.com/": [ + "clearsightinteractive.com", + "csi-tracking.com" + ] + } + }, + { + "ClickAider": { + "http://clickaider.com/": [ + "clickaider.com" + ] + } + }, + { + "Clickayab": { + "http://www.clickyab.com": [ + "clickyab.com" + ] + } + }, + { + "Clickbooth": { + "http://www.clickbooth.com/": [ + "adtoll.com", + "clickbooth.com" + ] + } + }, + { + "ClickDimensions": { + "http://www.clickdimensions.com/": [ + "clickdimensions.com" + ] + } + }, + { + "ClickDistrict": { + "http://www.clickdistrict.com/": [ + "clickdistrict.com", + "creative-serving.com" + ] + } + }, + { + "ClickFrog": { + "https://clickfrog.ru/": [ + "bashirian.biz", + "buckridge.link", + "clickfrog.ru", + "franecki.net", + "quitzon.net", + "reichelcormier.bid", + "wisokykulas.bid" + ] + } + }, + { + "ClickFuel": { + "http://clickfuel.com/": [ + "conversiondashboard.com" + ] + } + }, + { + "ClickInc": { + "http://www.clickinc.com/": [ + "clickinc.com" + ] + } + }, + { + "Clicksor": { + "http://www.clicksor.com/": [ + "clicksor.com", + "clicksor.net" + ] + } + }, + { + "Clickwinks": { + "http://www.clickwinks.com/": [ + "clickwinks.com" + ] + } + }, + { + "ClicManager": { + "http://www.clicmanager.fr/": [ + "clicmanager.fr" + ] + } + }, + { + "Clixtell": { + "https://www.clixtell.com/": [ + "clixtell.com" + ] + } + }, + { + "Clove Network": { + "http://www.clovenetwork.com/": [ + "clovenetwork.com" + ] + } + }, + { + "Cognitive Match": { + "http://www.cognitivematch.com/": [ + "cmads.com.tw", + "cmadsasia.com", + "cmadseu.com", + "cmmeglobal.com", + "cognitivematch.com" + ] + } + }, + { + "Collective": { + "http://collective.com/": [ + "collective-media.net", + "collective.com", + "oggifinogi.com", + "tumri.com", + "tumri.net", + "yt1187.net" + ] + } + }, + { + "Commission Junction": { + "http://www.cj.com/": [ + "apmebf.com", + "awltovhc.com", + "cj.com", + "ftjcfx.com", + "kcdwa.com", + "qksz.com", + "qksz.net", + "tqlkg.com", + "yceml.net" + ] + } + }, + { + "Communicator Corp": { + "http://www.communicatorcorp.com/": [ + "communicatorcorp.com" + ] + } + }, + { + "Compass Labs": { + "http://compasslabs.com/": [ + "compasslabs.com" + ] + } + }, + { + "Complex Media": { + "http://www.complexmedianetwork.com/": [ + "complex.com", + "complexmedianetwork.com" + ] + } + }, + { + "comScore": { + "http://www.comscore.com/": [ + "adxpose.com", + "proxilinks.com", + "proximic.com", + "proximic.net" + ] + } + }, + { + "Connatix.com": { + "https://connatix.com/": [ + "connatix.com" + ] + } + }, + { + "Connexity": { + "http://www.connexity.com/": [ + "pricegrabber.com" + ] + } + }, + { + "Consilium Media": { + "http://www.consiliummedia.com/": [ + "consiliummedia.com" + ] + } + }, + { + "Consumable": { + "http://consumable.com/": [ + "consumable.com" + ] + } + }, + { + "CONTAXE": { + "http://www.contaxe.com/": [ + "contaxe.com" + ] + } + }, + { + "ContentABC": { + "http://contentabc.com/": [ + "contentabc.com" + ] + } + }, + { + "CONTEXTin": { + "http://www.contextin.com/": [ + "admailtiser.com", + "contextin.com" + ] + } + }, + { + "ContextuAds": { + "http://www.contextuads.com/": [ + "agencytradingdesk.net", + "contextuads.com" + ] + } + }, + { + "CONTEXTWEB": { + "http://www.contextweb.com/": [ + "contextweb.com" + ] + } + }, + { + "ConvergeDirect": { + "http://www.convergedirect.com/": [ + "convergedirect.com", + "convergetrack.com" + ] + } + }, + { + "ConversantMedia": { + "http://conversantmedia.com": [ + "adserver.com", + "conversantmedia.com", + "dotomi.com", + "dtmpub.com", + "emjcd.com", + "fastclick.com", + "fastclick.net", + "greystripe.com", + "lduhtrp.net", + "mediaplex.com", + "valueclick.com", + "valueclick.net", + "valueclickmedia.com" + ] + } + }, + { + "ConversionRuler": { + "http://www.conversionruler.com/": [ + "conversionruler.com" + ] + } + }, + { + "Conversive": { + "http://www.conversive.nl/": [ + "conversive.nl" + ] + } + }, + { + "CoreMotives": { + "http://coremotives.com/": [ + "coremotives.com" + ] + } + }, + { + "Cox Digital Solutions": { + "http://www.coxdigitalsolutions.com/": [ + "adify.com", + "afy11.net", + "coxdigitalsolutions.com" + ] + } + }, + { + "CPMStar": { + "http://www.cpmstar.com/": [ + "cpmstar.com" + ] + } + }, + { + "CPX Interactive": { + "http://www.cpxinteractive.com/": [ + "adreadypixels.com", + "cpxadroit.com", + "cpxinteractive.com" + ] + } + }, + { + "Creafi": { + "http://www.creafi.com/": [ + "creafi.com" + ] + } + }, + { + "Crimtan": { + "http://www.crimtan.com/": [ + "crimtan.com" + ] + } + }, + { + "Crisp Media": { + "http://www.crispmedia.com/": [ + "crispmedia.com" + ] + } + }, + { + "Criteo": { + "http://www.criteo.com/": [ + "criteo.com", + "criteo.net", + "hlserve.com", + "hooklogic.com", + "storetail.io" + ] + } + }, + { + "Cross Pixel": { + "http://crosspixel.net/": [ + "crosspixel.net", + "crosspixelmedia.com", + "crsspxl.com" + ] + } + }, + { + "cXense": { + "http://www.cxense.com/": [ + "cxense.com", + "emediate.biz", + "emediate.com", + "emediate.dk", + "emediate.eu" + ] + } + }, + { + "Cyberplex": { + "http://www.cyberplex.com/": [ + "cyberplex.com" + ] + } + }, + { + "Dada": { + "http://dada.pro/": [ + "dada.pro", + "simply.com" + ] + } + }, + { + "Datalogix": { + "http://www.datalogix.com/": [ + "nexac.com", + "nextaction.net" + ] + } + }, + { + "DataXu": { + "http://www.dataxu.com/": [ + "dataxu.com", + "dataxu.net", + "mexad.com", + "w55c.net" + ] + } + }, + { + "Datonics": { + "http://datonics.com/": [ + "datonics.com", + "pro-market.net" + ] + } + }, + { + "Datran Media": { + "http://www.datranmedia.com/": [ + "datranmedia.com", + "displaymarketplace.com" + ] + } + }, + { + "Datvantage": { + "http://datvantage.com/": [ + "datvantage.com" + ] + } + }, + { + "DC Storm": { + "http://www.dc-storm.com/": [ + "dc-storm.com", + "stormiq.com" + ] + } + }, + { + "Dedicated Media": { + "http://www.dedicatedmedia.com/": [ + "dedicatedmedia.com", + "dedicatednetworks.com" + ] + } + }, + { + "Delivr": { + "http://delivr.com/": [ + "delivr.com", + "percentmobile.com" + ] + } + }, + { + "Delta Projects": { + "http://www.deltaprojects.se/": [ + "adaction.se", + "de17a.com", + "deltaprojects.se" + ] + } + }, + { + "Demand Media": { + "http://www.demandmedia.com/": [ + "demandmedia.com", + "indieclick.com" + ] + } + }, + { + "Deutsche Post DHL": { + "http://www.dp-dhl.com/": [ + "adcloud.com", + "adcloud.net", + "dp-dhl.com" + ] + } + }, + { + "Developer Media": { + "http://developermedia.com/": [ + "developermedia.com", + "lqcdn.com" + ] + } + }, + { + "DG": { + "http://www.dgit.com/": [ + "dgit.com", + "eyeblaster.com", + "eyewonder.com", + "mdadx.com", + "serving-sys.com", + "unicast.com" + ] + } + }, + { + "dianomi": { + "http://www.dianomi.com/": [ + "dianomi.com" + ] + } + }, + { + "Didit": { + "http://www.didit.com/": [ + "did-it.com", + "didit.com" + ] + } + }, + { + "DigitalAdConsortium": { + "https://www.dac.co.jp/": [ + "impact-ad.jp" + ] + } + }, + { + "Digital River": { + "http://www.digitalriver.com/": [ + "digitalriver.com", + "keywordmax.com", + "netflame.cc" + ] + } + }, + { + "Digital Target": { + "http://digitaltarget.ru": [ + "digitaltarget.ru" + ] + } + }, + { + "Digitize": { + "http://www.digitize.ie/": [ + "digitize.ie" + ] + } + }, + { + "DirectAdvert": { + "http://www.directadvert.ru/": [ + "directadvert.ru" + ] + } + }, + { + "Direct Response Group": { + "http://www.directresponsegroup.com/": [ + "directresponsegroup.com", + "ppctracking.net" + ] + } + }, + { + "Directtrack": { + "http://directtrack.com/": [ + "directtrack.com" + ] + } + }, + { + "Disqus": { + "http://disqus.com/": [ + "disqusads.com" + ] + } + }, + { + "DistrictM": { + "https://districtm.net": [ + "districtm.io" + ] + } + }, + { + "dmpxs": { + "http://bob.dmpxs.com": [ + "dmpxs.com" + ] + } + }, + { + "DoublePimp": { + "http://doublepimp.com/": [ + "doublepimp.com" + ] + } + }, + { + "DoublePositive": { + "http://www.doublepositive.com/": [ + "bid-tag.com", + "doublepositive.com" + ] + } + }, + { + "Drawbridge": { + "http://drawbrid.ge/": [ + "adsymptotic.com", + "drawbrid.ge" + ] + } + }, + { + "DS-IQ": { + "http://www.ds-iq.com/": [ + "ds-iq.com" + ] + } + }, + { + "DSNR Group": { + "http://www.dsnrmg.com/": [ + "dsnrgroup.com", + "dsnrmg.com", + "traffiliate.com", + "z5x.com", + "z5x.net" + ] + } + }, + { + "DynAdmic": { + "https://dynadmic.com/": [ + "dynadmic.com", + "dyntrk.com" + ] + } + }, + { + "DynamicOxygen": { + "http://www.dynamicoxygen.com/": [ + "dynamicoxygen.com", + "exitjunction.com" + ] + } + }, + { + "DynamicYield": { + "https://www.dynamicyield.com/": [ + "px-eu.dynamicyield.com", + "px.dynamicyield.com" + ] + } + }, + { + "Earnify": { + "http://earnify.com/": [ + "earnify.com" + ] + } + }, + { + "eBay": { + "http://www.ebay.com/": [ + "ebay.com" + ] + } + }, + { + "Effective Measure": { + "http://www.effectivemeasure.com/": [ + "effectivemeasure.com", + "effectivemeasure.net" + ] + } + }, + { + "ekolay": { + "http://www.ekolay.net/": [ + "e-kolay.net", + "ekolay.net" + ] + } + }, + { + "Eleavers": { + "http://eleavers.com/": [ + "eleavers.com" + ] + } + }, + { + "Emego": { + "http://www.usemax.de/": [ + "usemax.de" + ] + } + }, + { + "Emerse": { + "https://www.emerse.com": [ + "emerse.com" + ] + } + }, + { + "EMX": { + "https://emxdigital.com/": [ + "brealtime.com", + "clearstream.tv", + "emxdgt.com", + "emxdigital.com" + ] + } + }, + { + "Enecto": { + "http://www.enecto.com/": [ + "enecto.com" + ] + } + }, + { + "engage:BDR": { + "http://engagebdr.com/": [ + "bnmla.com", + "engagebdr.com" + ] + } + }, + { + "Engago Technology": { + "http://www.engago.com/": [ + "appmetrx.com", + "engago.com" + ] + } + }, + { + "Engine Network": { + "http://enginenetwork.com/": [ + "enginenetwork.com" + ] + } + }, + { + "Ensighten": { + "http://www.ensighten.com/": [ + "ensighten.com" + ] + } + }, + { + "Entireweb": { + "http://www.entireweb.com/": [ + "entireweb.com" + ] + } + }, + { + "Epic Media Group": { + "http://www.theepicmediagroup.com/": [ + "epicadvertising.com", + "epicmarketplace.com", + "epicmobileads.com", + "theepicmediagroup.com", + "trafficmp.com" + ] + } + }, + { + "Epsilon": { + "http://www.epsilon.com/": [ + "epsilon.com" + ] + } + }, + { + "EQ Ads": { + "http://www.eqads.com/": [ + "eqads.com" + ] + } + }, + { + "EroAdvertising": { + "http://www.ero-advertising.com/": [ + "ero-advertising.com" + ] + } + }, + { + "Etarget": { + "http://etargetnet.com/": [ + "etarget.eu", + "etargetnet.com" + ] + } + }, + { + "Etineria": { + "http://www.etineria.com/": [ + "adwitserver.com", + "etineria.com" + ] + } + }, + { + "eTrigue": { + "http://www.etrigue.com/": [ + "etrigue.com" + ] + } + }, + { + "Evergage": { + "http://www.evergage.com": [ + "mybuys.com", + "veruta.com" + ] + } + }, + { + "Everyday Health": { + "http://www.everydayhealth.com/": [ + "everydayhealth.com", + "waterfrontmedia.com" + ] + } + }, + { + "Evisions Marketing": { + "http://www.evisionsmarketing.com/": [ + "engineseeker.com", + "evisionsmarketing.com" + ] + } + }, + { + "Evolve": { + "http://www.evolvemediacorp.com/": [ + "evolvemediacorp.com", + "evolvemediametrics.com", + "gorillanation.com" + ] + } + }, + { + "eWayDirect": { + "http://www.ewaydirect.com/": [ + "ewaydirect.com", + "ixs1.net" + ] + } + }, + { + "ewebse": { + "http://ewebse.com/": [ + "777seo.com", + "ewebse.com" + ] + } + }, + { + "excitad": { + "http://excitad.com/": [ + "excitad.com" + ] + } + }, + { + "eXelate": { + "http://exelate.com/": [ + "exelate.com", + "exelator.com" + ] + } + }, + { + "ExoClick": { + "http://www.exoclick.com/": [ + "exoclick.com" + ] + } + }, + { + "Exosrv": { + "http://main.exosrv.com/": [ + "exosrv.com" + ] + } + }, + { + "Experian": { + "http://www.experian.com/": [ + "audienceiq.com", + "experian.com" + ] + } + }, + { + "expo-MAX": { + "http://expo-max.com/": [ + "expo-max.com" + ] + } + }, + { + "Exponential Interactive": { + "http://www.exponential.com/": [ + "adotube.com", + "exponential.com", + "fulltango.com", + "tribalfusion.com" + ] + } + }, + { + "Extension Factory": { + "http://www.extensionfactory.com/": [ + "extensionfactory.com" + ] + } + }, + { + "EXTENSIONS.RU": { + "http://extensions.ru/": [ + "extensions.ru" + ] + } + }, + { + "Eyeconomy": { + "http://www.eyeconomy.co.uk/": [ + "eyeconomy.co.uk", + "eyeconomy.com", + "sublimemedia.net" + ] + } + }, + { + "EyeNewton": { + "http://eyenewton.ru/": [ + "eyenewton.ru" + ] + } + }, + { + "eyeReturn Marketing": { + "http://www.eyereturnmarketing.com/": [ + "eyereturn.com", + "eyereturnmarketing.com" + ] + } + }, + { + "Eyeviewdigital": { + "http://www.eyeviewdigital.com/": [ + "eyeviewdigital.com" + ] + } + }, + { + "Facebook": { + "http://www.facebook.com/": [ + "atlassolutions.com" + ] + } + }, + { + "Facilitate Digital": { + "http://www.facilitatedigital.com/": [ + "adsfac.eu", + "adsfac.info", + "adsfac.net", + "adsfac.sg", + "adsfac.us", + "facilitatedigital.com" + ] + } + }, + { + "Fairfax Media": { + "http://www.fxj.com.au/": [ + "fairfax.com.au", + "fxj.com.au" + ] + } + }, + { + "faithadnet": { + "http://www.faithadnet.com/": [ + "faithadnet.com" + ] + } + }, + { + "Fanplayr": { + "https://fanplayr.com/": [ + "fanplayr.com" + ] + } + }, + { + "Fathom": { + "http://www.fathomdelivers.com/": [ + "fathomdelivers.com", + "fathomseo.com" + ] + } + }, + { + "Federated Media": { + "http://www.federatedmedia.net/": [ + "federatedmedia.net", + "fmpub.net", + "lijit.com" + ] + } + }, + { + "FetchBack": { + "http://www.fetchback.com/": [ + "fetchback.com" + ] + } + }, + { + "Fiksu": { + "http://www.fiksu.com/": [ + "fiksu.com" + ] + } + }, + { + "FinancialContent": { + "http://www.financialcontent.com/": [ + "financialcontent.com" + ] + } + }, + { + "Fizz-Buzz Media": { + "http://www.fizzbuzzmedia.com/": [ + "fizzbuzzmedia.com", + "fizzbuzzmedia.net" + ] + } + }, + { + "Flashtalking": { + "http://www.flashtalking.com/": [ + "flashtalking.com" + ] + } + }, + { + "Flite": { + "http://www.flite.com/": [ + "flite.com", + "widgetserver.com" + ] + } + }, + { + "Fluct": { + "https://corp.fluct.jp/": [ + "adingo.jp", + "fluct.jp" + ] + } + }, + { + "Flytxt": { + "http://www.flytxt.com/": [ + "flytxt.com" + ] + } + }, + { + "Forbes": { + "http://www.forbes.com/": [ + "brandsideplatform.com", + "forbes.com" + ] + } + }, + { + "Fox One Stop Media": { + "http://www.foxonestop.com/": [ + "fimserve.com", + "foxnetworks.com", + "foxonestop.com", + "mobsmith.com", + "myads.com", + "othersonline.com" + ] + } + }, + { + "FreakOut": { + "http://fout.jp/": [ + "fout.jp" + ] + } + }, + { + "Freedom Communications": { + "http://www.freedom.com/": [ + "freedom.com" + ] + } + }, + { + "FreeWheel": { + "http://www.freewheel.tv/": [ + "stickyadstv.com" + ] + } + }, + { + "FriendFinder Networks": { + "http://ffn.com/": [ + "adultfriendfinder.com", + "ffn.com", + "pop6.com" + ] + } + }, + { + "Friends2Follow": { + "https://friends2follow.com/": [ + "tracking.friends2follow.com" + ] + } + }, + { + "Frog Sex": { + "http://www.frogsex.com/": [ + "double-check.com", + "frogsex.com" + ] + } + }, + { + "FuelX": { + "https://fuelx.com/": [ + "fuel451.com", + "fuelx.com" + ] + } + }, + { + "Future Ads": { + "https://www.futureads.com/": [ + "futureads.com", + "resultlinks.com" + ] + } + }, + { + "Fyber": { + "https://www.fyber.com/": [ + "fyber.com" + ] + } + }, + { + "Game Advertising Online": { + "http://www.game-advertising-online.com/": [ + "game-advertising-online.com" + ] + } + }, + { + "Games2win": { + "http://www.games2win.com/": [ + "games2win.com", + "inviziads.com" + ] + } + }, + { + "Gamned": { + "http://www.gamned.com/": [ + "gamned.com" + ] + } + }, + { + "Gannett": { + "http://www.gannett.com/": [ + "gannett.com", + "pointroll.com" + ] + } + }, + { + "GB-World": { + "http://www.gb-world.net/": [ + "gb-world.net" + ] + } + }, + { + "Gemius": { + "http://www.gemius.com/": [ + "gemius.com", + "gemius.pl" + ] + } + }, + { + "Genesis Media": { + "http://www.genesismedia.com/": [ + "genesismedia.com", + "genesismediaus.com" + ] + } + }, + { + "GENIEE": { + "https://geniee.co.jp/": [ + "geniee.co.jp", + "gssprt.jp" + ] + } + }, + { + "GENIE GROUP": { + "http://www.geniegroupltd.co.uk/": [ + "geniegroupltd.co.uk" + ] + } + }, + { + "GeoAds": { + "http://www.geoads.com/": [ + "geoads.com" + ] + } + }, + { + "GetGlue": { + "http://getglue.com/": [ + "getglue.com", + "smrtlnks.com" + ] + } + }, + { + "GetIntent": { + "http://getintent.com/": [ + "adhigh.net", + "getintent.com" + ] + } + }, + { + "GISMAds": { + "http://www.gismads.jp/": [ + "gismads.jp" + ] + } + }, + { + "Glam Media": { + "http://www.glammedia.com/": [ + "glam.com", + "glammedia.com" + ] + } + }, + { + "Gleam": { + "https://gleam.io/": [ + "fraudjs.io", + "gleam.io" + ] + } + }, + { + "Globe7": { + "http://www.globe7.com/": [ + "globe7.com" + ] + } + }, + { + "GoDataFeed": { + "http://godatafeed.com/": [ + "godatafeed.com" + ] + } + }, + { + "Goldbach": { + "http://www.goldbachgroup.com/": [ + "goldbach.com", + "goldbachgroup.com" + ] + } + }, + { + "GoldSpot Media": { + "http://www.goldspotmedia.com/": [ + "goldspotmedia.com" + ] + } + }, + { + "Google": { + "http://www.google.com/": [ + "2mdn.net", + "admeld.com", + "admob.com", + "adservice.google.ca", + "adservice.google.com", + "adwords.google.com", + "cc-dt.com", + "destinationurl.com", + "doubleclick.net", + "googleadservices.com", + "googlesyndication.com", + "googletagservices.com", + "invitemedia.com", + "smtad.net", + "teracent.com", + "teracent.net", + "ytsa.net" + ] + } + }, + { + "Grapeshot": { + "http://www.grapeshot.co.uk/": [ + "grapeshot.co.uk" + ] + } + }, + { + "Graphnium": { + "https://www.graphinium.com/": [ + "crm4d.com" + ] + } + }, + { + "Grocery Shopping Network": { + "http://www.groceryshopping.net/": [ + "groceryshopping.net" + ] + } + }, + { + "GroovinAds": { + "http://www.groovinads.com/": [ + "groovinads.com" + ] + } + }, + { + "Gruner + Jahr": { + "http://www.guj.de/": [ + "guj.de", + "ligatus.com" + ] + } + }, + { + "GumGum": { + "http://gumgum.com/": [ + "gumgum.com" + ] + } + }, + { + "Gunggo": { + "http://www.gunggo.com/": [ + "gunggo.com" + ] + } + }, + { + "Hands Mobile": { + "http://www.hands.com.br/": [ + "hands.com.br" + ] + } + }, + { + "Harrenmedia": { + "http://www.harrenmedia.com/": [ + "harrenmedia.com", + "harrenmedianetwork.com" + ] + } + }, + { + "HealthPricer": { + "http://www.healthpricer.com/": [ + "adacado.com", + "healthpricer.com" + ] + } + }, + { + "Hearst": { + "http://www.hearst.com/": [ + "hearst.com", + "ic-live.com", + "iclive.com", + "icrossing.com", + "sptag.com", + "sptag1.com", + "sptag2.com", + "sptag3.com" + ] + } + }, + { + "HilltopAds": { + "https://hilltopads.com/": [ + "hilltopads.com", + "hilltopads.net", + "shoporielder.pro" + ] + } + }, + { + "Hi-media": { + "http://www.hi-media.com/": [ + "comclick.com", + "hi-media.com" + ] + } + }, + { + "Horyzon Media": { + "http://www.horyzon-media.com/": [ + "horyzon-media.com" + ] + } + }, + { + "HotMart": { + "https://www.hotmart.com/en/": [ + "hotmart.com" + ] + } + }, + { + "HOTWords": { + "http://www.hotwords.com/": [ + "hotwords.com", + "hotwords.es" + ] + } + }, + { + "HP": { + "http://www.hp.com/": [ + "hp.com", + "optimost.com" + ] + } + }, + { + "Httpool": { + "http://www.httpool.com/": [ + "httpool.com" + ] + } + }, + { + "HUNT Mobile Ads": { + "http://www.huntmads.com/": [ + "huntmads.com" + ] + } + }, + { + "Hurra.com": { + "http://www.hurra.com/": [ + "hurra.com" + ] + } + }, + { + "IAB": { + "https://iabtechlab.com/": [ + "digitru.st" + ] + } + }, + { + "IAC": { + "http://www.iac.com/": [ + "iac.com", + "iacadvertising.com" + ] + } + }, + { + "iBehavior": { + "http://www.i-behavior.com/": [ + "i-behavior.com", + "ib-ibi.com" + ] + } + }, + { + "IBM": { + "http://www.ibm.com/": [ + "unica.com" + ] + } + }, + { + "ID5": { + "http://id5.io/": [ + "id5-sync.com" + ] + } + }, + { + "IDG": { + "http://www.idg.com/": [ + "idg.com", + "idgtechnetwork.com" + ] + } + }, + { + "iEntry": { + "http://www.ientry.com/": [ + "600z.com", + "ientry.com" + ] + } + }, + { + "IgnitAd": { + "http://www.ignitad.com/": [ + "ignitad.com" + ] + } + }, + { + "IgnitionOne": { + "http://www.ignitionone.com/": [ + "ignitionone.com", + "ignitionone.net", + "searchignite.com" + ] + } + }, + { + "Improve Digital": { + "www.improvedigital.com/": [ + "360yield.com", + "improvedigital.com" + ] + } + }, + { + "Inadco": { + "http://www.inadco.com/": [ + "anadcoads.com", + "inadco.com", + "inadcoads.com" + ] + } + }, + { + "IndexExchange": { + "https://www.indexexchange.com": [ + "indexexchange.com" + ] + } + }, + { + "Infectious Media": { + "http://www.infectiousmedia.com/": [ + "impressiondesk.com", + "infectiousmedia.com" + ] + } + }, + { + "Inflection Point Media": { + "http://www.inflectionpointmedia.com/": [ + "inflectionpointmedia.com" + ] + } + }, + { + "Infogroup": { + "http://www.infogroup.com/": [ + "infogroup.com" + ] + } + }, + { + "Infolinks": { + "http://www.infolinks.com/": [ + "infolinks.com" + ] + } + }, + { + "Infra-Ad": { + "http://www.infra-ad.com/": [ + "infra-ad.com" + ] + } + }, + { + "InMobi": { + "http://www.inmobi.com/": [ + "aerserv.com", + "inmobi.com", + "sproutinc.com" + ] + } + }, + { + "inneractive": { + "http://inner-active.com/": [ + "inner-active.com" + ] + } + }, + { + "Innity": { + "http://innity.com/": [ + "innity.com" + ] + } + }, + { + "InsightExpress": { + "http://www.insightexpress.com/": [ + "insightexpress.com", + "insightexpressai.com" + ] + } + }, + { + "InSkin Media": { + "http://inskinmedia.com/": [ + "inskinmedia.com" + ] + } + }, + { + "Instinctive": { + "https://instinctive.io/": [ + "instinctive.io", + "instinctiveads.com" + ] + } + }, + { + "Integral Ad Science": { + "https://integralads.com/": [ + "adsafemedia.com", + "adsafeprotected.com", + "iasds01.com", + "integralads.com" + ] + } + }, + { + "Intent Media": { + "http://www.intentmedia.com/": [ + "intentmedia.com", + "intentmedia.net" + ] + } + }, + { + "Intergi": { + "http://intergi.com/": [ + "intergi.com" + ] + } + }, + { + "Intermarkets": { + "http://www.intermarkets.net/": [ + "intermarkets.net" + ] + } + }, + { + "Intermundo Media": { + "http://intermundomedia.com/": [ + "intermundomedia.com" + ] + } + }, + { + "Internet Brands": { + "http://www.internetbrands.com/": [ + "ibpxl.com", + "internetbrands.com" + ] + } + }, + { + "Interpolls": { + "http://www.interpolls.com/": [ + "interpolls.com" + ] + } + }, + { + "Inuvo": { + "http://inuvo.com/": [ + "inuvo.com" + ] + } + }, + { + "InvestingChannel": { + "http://investingchannel.com/": [ + "investingchannel.com" + ] + } + }, + { + "IponWeb": { + "https://www.iponweb.com/": [ + "iponweb.com", + "iponweb.net" + ] + } + }, + { + "iPROM": { + "http://www.iprom.si/": [ + "centraliprom.com", + "iprom.net", + "iprom.si", + "mediaiprom.com" + ] + } + }, + { + "iPromote": { + "http://www.ipromote.com/": [ + "ipromote.com" + ] + } + }, + { + "iProspect": { + "http://www.iprospect.com/": [ + "clickmanage.com", + "iprospect.com" + ] + } + }, + { + "ISI Technologies": { + "http://digbro.com/": [ + "adversalservers.com", + "digbro.com" + ] + } + }, + { + "ismatlab.com": { + "http://ismatlab.com": [ + "ismatlab.com" + ] + } + }, + { + "I.UA": { + "http://www.i.ua/": [ + "i.ua" + ] + } + }, + { + "Jaroop": { + "http://www.jaroop.com/": [ + "jaroop.com" + ] + } + }, + { + "JasperLabs": { + "http://www.jasperlabs.com/": [ + "jasperlabs.com" + ] + } + }, + { + "Jemm": { + "http://jemmgroup.com/": [ + "jemmgroup.com" + ] + } + }, + { + "Jink": { + "http://www.jink.de/": [ + "jink.de", + "jinkads.com" + ] + } + }, + { + "Jirbo": { + "http://jirbo.com/": [ + "adcolony.com", + "jirbo.com" + ] + } + }, + { + "Jivox": { + "http://www.jivox.com/": [ + "jivox.com" + ] + } + }, + { + "JobThread": { + "http://www.jobthread.com/": [ + "jobthread.com" + ] + } + }, + { + "JuicyAds": { + "http://www.juicyads.com/": [ + "juicyads.com" + ] + } + }, + { + "Jumptap": { + "http://www.jumptap.com/": [ + "jumptap.com" + ] + } + }, + { + "justuno": { + "https://www.justuno.com/": [ + "justuno.com" + ] + } + }, + { + "Kargo": { + "https://kargo.com/": [ + "kargo.com" + ] + } + }, + { + "Kenshoo": { + "http://www.kenshoo.com/": [ + "kenshoo.com", + "xg4ken.com" + ] + } + }, + { + "Keyade": { + "http://www.keyade.com/": [ + "keyade.com" + ] + } + }, + { + "Keywee": { + "https://keywee.co": [ + "keywee.co" + ] + } + }, + { + "KissMyAds": { + "http://kissmyads.com/": [ + "kissmyads.com" + ] + } + }, + { + "Kitara Media": { + "http://www.kitaramedia.com/": [ + "103092804.com", + "kitaramedia.com" + ] + } + }, + { + "KIT digital": { + "http://kitd.com/": [ + "keewurd.com", + "kitd.com", + "peerset.com" + ] + } + }, + { + "Kokteyl": { + "http://www.kokteyl.com/": [ + "admost.com", + "kokteyl.com" + ] + } + }, + { + "Komli": { + "http://www.komli.com/": [ + "komli.com" + ] + } + }, + { + "Kontera": { + "http://www.kontera.com/": [ + "kontera.com" + ] + } + }, + { + "Korrelate": { + "http://korrelate.com/": [ + "adsummos.com", + "adsummos.net", + "korrelate.com" + ] + } + }, + { + "Krux": { + "http://www.krux.com/": [ + "krux.com", + "kruxdigital.com", + "krxd.net" + ] + } + }, + { + "Lakana": { + "http://www.lakana.com/": [ + "ibsys.com", + "lakana.com" + ] + } + }, + { + "Layer-Ad.org": { + "http://layer-ad.org/": [ + "layer-ad.org" + ] + } + }, + { + "Layer Ads": { + "http://layer-ads.net/": [ + "layer-ads.net" + ] + } + }, + { + "LeadBolt": { + "http://www.leadbolt.com/": [ + "leadbolt.com" + ] + } + }, + { + "LeadFormix": { + "http://www.leadformix.com/": [ + "leadforce1.com", + "leadformix.com" + ] + } + }, + { + "LeanPlum": { + "https://www.leanplum.com/": [ + "leanplum.com" + ] + } + }, + { + "Legolas Media": { + "http://www.legolas-media.com/": [ + "legolas-media.com" + ] + } + }, + { + "Levexis": { + "http://www.levexis.com/": [ + "levexis.com" + ] + } + }, + { + "Lexos Media": { + "http://www.lexosmedia.com/": [ + "adbull.com", + "lexosmedia.com" + ] + } + }, + { + "LifeStreet": { + "http://lifestreetmedia.com/": [ + "lfstmedia.com", + "lifestreetmedia.com" + ] + } + }, + { + "LinkConnector": { + "http://www.linkconnector.com/": [ + "linkconnector.com" + ] + } + }, + { + "LinkShare": { + "http://www.linkshare.com/": [ + "linkshare.com", + "linksynergy.com" + ] + } + }, + { + "Linkz": { + "http://www.linkz.net/": [ + "linkz.net" + ] + } + }, + { + "Listrak": { + "http://www.listrak.com/": [ + "listrak.com", + "listrakbi.com" + ] + } + }, + { + "LiveIntent": { + "http://www.liveintent.com/": [ + "liadm.com", + "liveintent.com" + ] + } + }, + { + "LiveInternet": { + "http://www.liveinternet.ru": [ + "liveinternet.ru", + "yadro.ru" + ] + } + }, + { + "LiveRamp": { + "https://liveramp.com/": [ + "liveramp.com", + "tvpixel.com" + ] + } + }, + { + "LKQD": { + "http://lkqd.com": [ + "lkqd.com", + "lkqd.net" + ] + } + }, + { + "Local Yokel Media": { + "http://www.localyokelmedia.com/": [ + "localyokelmedia.com" + ] + } + }, + { + "Localytics": { + "https://www.localytics.com/": [ + "localytics.com" + ] + } + }, + { + "LockerDome": { + "https://lockerdome.com/": [ + "lockerdome.com" + ] + } + }, + { + "Longboard Media": { + "http://longboardmedia.com/": [ + "longboardmedia.com" + ] + } + }, + { + "Loomia": { + "http://www.loomia.com/": [ + "loomia.com" + ] + } + }, + { + "LoopFuse": { + "https://www.loopfuse.net/": [ + "lfov.net", + "loopfuse.net" + ] + } + }, + { + "LoopMe": { + "https://loopme.com/": [ + "loopme.com" + ] + } + }, + { + "LotLinx": { + "https://www.lotlinx.com": [ + "lotlinx.com" + ] + } + }, + { + "Lower My Bills": { + "http://lowermybills.com": [ + "lowermybills.com" + ] + } + }, + { + "lptracker": { + "https://lptracker.io/": [ + "lptracker.io" + ] + } + }, + { + "LucidMedia": { + "http://www.lucidmedia.com/": [ + "lucidmedia.com" + ] + } + }, + { + "m6d": { + "http://m6d.com/": [ + "m6d.com", + "media6degrees.com" + ] + } + }, + { + "Madhouse": { + "http://www.madhouse.cn/": [ + "madhouse.cn" + ] + } + }, + { + "Madison Logic": { + "http://www.madisonlogic.com/": [ + "dinclinx.com", + "madisonlogic.com" + ] + } + }, + { + "madvertise": { + "http://madvertise.com/": [ + "madvertise.com" + ] + } + }, + { + "Magnetic": { + "http://www.magnetic.com/": [ + "domdex.com", + "domdex.net", + "magnetic.com", + "qjex.net" + ] + } + }, + { + "Magnify360": { + "http://www.magnify360.com/": [ + "dialogmgr.com", + "magnify360.com" + ] + } + }, + { + "MailChimp": { + "http://mailchimp.com/": [ + "campaign-archive1.com", + "list-manage.com", + "mailchimp.com" + ] + } + }, + { + "Manifest": { + "http://www.manifest.ru/": [ + "bannerbank.ru", + "manifest.ru" + ] + } + }, + { + "Marchex": { + "http://www.marchex.com/": [ + "industrybrains.com", + "marchex.com" + ] + } + }, + { + "Marimedia": { + "http://www.marimedia.net/": [ + "marimedia.net" + ] + } + }, + { + "MarketGid": { + "http://www.marketgid.com/": [ + "dt00.net", + "dt07.net", + "marketgid.com" + ] + } + }, + { + "Marketo": { + "http://www.marketo.com/": [ + "marketo.com", + "marketo.net" + ] + } + }, + { + "Martini Media": { + "http://martinimedianetwork.com/": [ + "martiniadnetwork.com", + "martinimedianetwork.com" + ] + } + }, + { + "mashero": { + "http://www.mashero.com/": [ + "mashero.com" + ] + } + }, + { + "Match.com": { + "http://www.match.com/": [ + "chemistry.com", + "match.com", + "meetic-partners.com" + ] + } + }, + { + "Matomy": { + "http://www.matomy.com/": [ + "adnetinteractive.com", + "adsmarket.com", + "matomy.com", + "matomymarket.com", + "matomymedia.com", + "mediawhiz.com", + "optimatic.com", + "xtendmedia.com" + ] + } + }, + { + "MaxBounty": { + "http://www.maxbounty.com/": [ + "maxbounty.com", + "mb01.com" + ] + } + }, + { + "MaxPoint": { + "http://maxpointinteractive.com/": [ + "maxpointinteractive.com", + "maxusglobal.com", + "mxptint.net" + ] + } + }, + { + "MdotM": { + "http://mdotm.com/": [ + "mdotm.com" + ] + } + }, + { + "MediaBrix": { + "http://www.mediabrix.com/": [ + "mediabrix.com" + ] + } + }, + { + "MediaCom": { + "http://www.mediacom.com/": [ + "mediacom.com" + ] + } + }, + { + "mediaFORGE": { + "http://www.mediaforge.com/": [ + "mediaforge.com" + ] + } + }, + { + "Medialets": { + "http://www.medialets.com/": [ + "medialets.com" + ] + } + }, + { + "MediaMath": { + "http://www.mediamath.com/": [ + "adroitinteractive.com", + "designbloxlive.com", + "mathtag.com", + "mediamath.com" + ] + } + }, + { + "media.net": { + "http://www.media.net/": [ + "media.net" + ] + } + }, + { + "Mediaocean": { + "http://www.mediaocean.com/": [ + "adbuyer.com", + "mediaocean.com" + ] + } + }, + { + "MediaShakers": { + "http://www.mediashakers.com/": [ + "media-servers.net", + "mediashakers.com" + ] + } + }, + { + "MediaTrust": { + "http://www.mediatrust.com/": [ + "mediatrust.com" + ] + } + }, + { + "Medicx Media Solutions": { + "http://www.medicxmedia.com/": [ + "medicxmedia.com" + ] + } + }, + { + "MegaIndex": { + "http://www.megaindex.ru/": [ + "megaindex.ru" + ] + } + }, + { + "Mercent": { + "http://www.mercent.com/": [ + "mercent.com" + ] + } + }, + { + "MerchantAdvantage": { + "http://www.merchantadvantage.com/": [ + "merchantadvantage.com" + ] + } + }, + { + "Merchenta": { + "http://www.merchenta.com/": [ + "merchenta.com" + ] + } + }, + { + "Merkle": { + "https://www.merkleinc.com/": [ + "rimmkaufman.com", + "rkdms.com" + ] + } + }, + { + "Meta Network": { + "http://www.metanetwork.com/": [ + "metanetwork.com" + ] + } + }, + { + "Meteor": { + "http://www.meteorsolutions.com/": [ + "meteorsolutions.com" + ] + } + }, + { + "MetrixLab": { + "https://www.metrixlab.com": [ + "adoftheyear.com", + "crm-metrix.com", + "customerconversio.com", + "metrixlab.com", + "opinionbar.com" + ] + } + }, + { + "MicroAd": { + "http://www.microad.jp/": [ + "microad.jp" + ] + } + }, + { + "Microsoft": { + "http://www.microsoft.com/": [ + "adbureau.net", + "adecn.com", + "aquantive.com", + "msads.net", + "netconversions.com", + "roiservice.com" + ] + } + }, + { + "Millennial Media": { + "http://www.millennialmedia.com/": [ + "decktrade.com", + "millennialmedia.com", + "mydas.mobi" + ] + } + }, + { + "Mindset Media": { + "http://www.mindset-media.com/": [ + "mindset-media.com", + "mmismm.com" + ] + } + }, + { + "Mirando": { + "http://www.mirando.de/": [ + "mirando.de" + ] + } + }, + { + "Mixpo": { + "http://www.mixpo.com/": [ + "mixpo.com" + ] + } + }, + { + "Moat": { + "http://www.moat.com/": [ + "moat.com", + "moatads.com" + ] + } + }, + { + "MobFox": { + "http://www.mobfox.com/": [ + "mobfox.com" + ] + } + }, + { + "Mobials": { + "http://mobials.com": [ + "mobials.com" + ] + } + }, + { + "MobileAdTrading": { + "https://mobileadtrading.com/": [ + "mobileadtrading.com" + ] + } + }, + { + "Mobile Meteor": { + "http://mobilemeteor.com/": [ + "mobilemeteor.com", + "showmeinn.com" + ] + } + }, + { + "Mobile Storm": { + "http://mobilestorm.com/": [ + "mobilestorm.com" + ] + } + }, + { + "MobVision": { + "http://www.mobvision.com/": [ + "admoda.com", + "mobvision.com" + ] + } + }, + { + "Mocean Mobile": { + "http://www.moceanmobile.com/": [ + "moceanmobile.com" + ] + } + }, + { + "Mochila": { + "http://www.mochila.com/": [ + "mochila.com" + ] + } + }, + { + "Mojiva": { + "http://www.mojiva.com/": [ + "mojiva.com" + ] + } + }, + { + "Monetate": { + "http://monetate.com/": [ + "monetate.com", + "monetate.net" + ] + } + }, + { + "MONETIZEdigital": { + "https://www.cpalead.com/": [ + "cpalead.com" + ] + } + }, + { + "Monetize More": { + "http://monetizemore.com/": [ + "monetizemore.com" + ] + } + }, + { + "Monoloop": { + "http://www.monoloop.com/": [ + "monoloop.com" + ] + } + }, + { + "Monster": { + "http://www.monster.com/": [ + "monster.com" + ] + } + }, + { + "Moolah Media": { + "http://www.moolahmedia.com/": [ + "moolah-media.com", + "moolahmedia.com" + ] + } + }, + { + "MoPub": { + "http://www.mopub.com/": [ + "mopub.com" + ] + } + }, + { + "MovieLush.com": { + "https://www.movielush.com/": [ + "affbuzzads.com", + "movielush.com" + ] + } + }, + { + "Multiple Stream Media": { + "http://www.multiplestreammktg.com/": [ + "adclickmedia.com", + "multiplestreammktg.com" + ] + } + }, + { + "MUNDO Media": { + "http://www.mundomedia.com/": [ + "mundomedia.com", + "silver-path.com" + ] + } + }, + { + "MyCounter": { + "http://mycounter.com.ua/": [ + "mycounter.com.ua" + ] + } + }, + { + "MyPressPlus": { + "http://www.mypressplus.com/": [ + "mypressplus.com", + "ppjol.net" + ] + } + }, + { + "myThings": { + "http://www.mythings.com/": [ + "mythings.com", + "mythingsmedia.com" + ] + } + }, + { + "MyWebGrocer": { + "http://www.mywebgrocer.com/": [ + "mywebgrocer.com" + ] + } + }, + { + "Nanigans": { + "http://www.nanigans.com/": [ + "nanigans.com" + ] + } + }, + { + "NativeAds": { + "https://nativeads.com/": [ + "nativeads.com" + ] + } + }, + { + "Nativo": { + "http://www.nativo.net/": [ + "postrelease.com" + ] + } + }, + { + "Navegg": { + "http://www.navegg.com/": [ + "navdmp.com", + "navegg.com" + ] + } + }, + { + "NetAffiliation": { + "http://www.netaffiliation.com/": [ + "netaffiliation.com" + ] + } + }, + { + "NetBina": { + "http://www.netbina.com/": [ + "netbina.com" + ] + } + }, + { + "NetElixir": { + "http://www.netelixir.com/": [ + "adelixir.com", + "netelixir.com" + ] + } + }, + { + "Netmining": { + "http://www.netmining.com/": [ + "netmining.com", + "netmng.com" + ] + } + }, + { + "Net-Results": { + "http://www.net-results.com/": [ + "cdnma.com", + "net-results.com", + "nr7.us" + ] + } + }, + { + "NetSeer": { + "http://www.netseer.com/": [ + "netseer.com" + ] + } + }, + { + "NetShelter": { + "http://netshelter.com/": [ + "netshelter.com", + "netshelter.net" + ] + } + }, + { + "Neustar": { + "http://www.neustar.biz/": [ + "adadvisor.net", + "neustar.biz" + ] + } + }, + { + "newtention": { + "http://newtention.de/": [ + "newtention.de", + "newtention.net", + "newtentionassets.net" + ] + } + }, + { + "Nexage": { + "http://nexage.com/": [ + "nexage.com" + ] + } + }, + { + "Nextag": { + "http://www.nextag.com/": [ + "nextag.com" + ] + } + }, + { + "NextPerformance": { + "http://www.nextperformance.com/": [ + "nextperformance.com", + "nxtck.com" + ] + } + }, + { + "Nielsen": { + "http://www.nielsen.com/": [ + "imrworldwide.com", + "imrworldwide.net" + ] + } + }, + { + "Ninua": { + "http://www.ninua.com/": [ + "networkedblogs.com", + "ninua.com" + ] + } + }, + { + "Nokta": { + "http://www.noktamedya.com/": [ + "noktamedya.com", + "virgul.com" + ] + } + }, + { + "NowSpots": { + "http://nowspots.com/": [ + "nowspots.com" + ] + } + }, + { + "nrelate": { + "http://nrelate.com/": [ + "nrelate.com" + ] + } + }, + { + "Nuffnang": { + "http://www.nuffnang.com.my/": [ + "nuffnang.com", + "nuffnang.com.my" + ] + } + }, + { + "nugg.ad": { + "http://www.nugg.ad/": [ + "nugg.ad", + "nuggad.net" + ] + } + }, + { + "Ohana Media": { + "http://www.ohana-media.com/": [ + "adohana.com", + "ohana-media.com", + "ohanaqb.com" + ] + } + }, + { + "Omnicom Group": { + "http://www.omnicomgroup.com/": [ + "accuenmedia.com", + "omnicomgroup.com", + "p-td.com" + ] + } + }, + { + "onAd": { + "http://www.onad.eu/": [ + "onad.eu" + ] + } + }, + { + "Onclusive": { + "https://onclusive.com/": [ + "airpr.com" + ] + } + }, + { + "OneAd": { + "https://www.onead.com.tw/": [ + "guoshipartners.com", + "onevision.com.tw" + ] + } + }, + { + "One iota": { + "http://www.itsoneiota.com/": [ + "itsoneiota.com", + "oneiota.co.uk" + ] + } + }, + { + "Oneupweb": { + "http://www.oneupweb.com/": [ + "oneupweb.com", + "sodoit.com" + ] + } + }, + { + "OnlineMetrix": { + "http://h.online-metrix.net": [ + "online-metrix.net" + ] + } + }, + { + "Open New Media": { + "http://www.onm.de/": [ + "onm.de" + ] + } + }, + { + "OpenX": { + "http://openx.com/": [ + "liftdna.com", + "openx.com", + "openx.net", + "openx.org", + "openxenterprise.com", + "servedbyopenx.com" + ] + } + }, + { + "Opera": { + "http://www.opera.com/": [ + "mobiletheory.com", + "opera.com", + "operamediaworks.com", + "operasoftware.com" + ] + } + }, + { + "OPT": { + "http://www.opt.ne.jp/": [ + "advg.jp", + "opt.ne.jp", + "p-advg.com" + ] + } + }, + { + "Optify": { + "http://www.optify.net/": [ + "optify.net" + ] + } + }, + { + "Optimal": { + "http://optim.al/": [ + "cpmadvisors.com", + "cpmatic.com", + "nprove.com", + "optim.al", + "orbengine.com", + "xa.net" + ] + } + }, + { + "OptimumResponse": { + "http://www.optimumresponse.com/": [ + "optimumresponse.com" + ] + } + }, + { + "OptinMonster": { + "https://optinmonster.com/": [ + "optinmonster.com", + "optnmstr.com" + ] + } + }, + { + "OptMD": { + "http://optmd.com/": [ + "optmd.com" + ] + } + }, + { + "Oracle": { + "http://www.oracle.com/": [ + "estara.com" + ] + } + }, + { + "OrangeSoda": { + "http://www.orangesoda.com/": [ + "orangesoda.com", + "otracking.com" + ] + } + }, + { + "Outbrain": { + "http://www.outbrain.com/": [ + "outbrain.com", + "sphere.com", + "visualrevenue.com" + ] + } + }, + { + "Out There Media": { + "http://www.out-there-media.com/": [ + "out-there-media.com" + ] + } + }, + { + "Oversee.net": { + "http://www.oversee.net/": [ + "dsnextgen.com", + "oversee.net" + ] + } + }, + { + "OwnerIQ": { + "http://www.owneriq.com/": [ + "owneriq.com", + "owneriq.net" + ] + } + }, + { + "OxaMedia": { + "http://www.oxamedia.com/": [ + "adconnexa.com", + "adsbwm.com", + "oxamedia.com" + ] + } + }, + { + "PageFair": { + "https://pagefair.com/": [ + "pagefair.com", + "pagefair.net" + ] + } + }, + { + "Paid-To-Promote.net": { + "http://www.paid-to-promote.net/": [ + "paid-to-promote.net" + ] + } + }, + { + "Pardot": { + "http://www.pardot.com/": [ + "pardot.com" + ] + } + }, + { + "PayHit": { + "http://www.payhit.com/": [ + "payhit.com" + ] + } + }, + { + "Paypopup.com": { + "http://www.paypopup.com/": [ + "lzjl.com", + "paypopup.com" + ] + } + }, + { + "PebblePost": { + "https://www.pebblepost.com/": [ + "pbbl.co" + ] + } + }, + { + "Peer39": { + "http://www.peer39.com/": [ + "peer39.com", + "peer39.net" + ] + } + }, + { + "PeerFly": { + "http://peerfly.com/": [ + "peerfly.com" + ] + } + }, + { + "Performancing": { + "http://performancing.com/": [ + "performancing.com" + ] + } + }, + { + "PerimeterX": { + "https://www.perimeterx.com": [ + "perimeterx.net" + ] + } + }, + { + "Pheedo": { + "http://site.pheedo.com/": [ + "pheedo.com" + ] + } + }, + { + "Pictela": { + "http://www.pictela.com/": [ + "pictela.com", + "pictela.net" + ] + } + }, + { + "PinPoll": { + "https://pinpoll.com/": [ + "pinpoll.com" + ] + } + }, + { + "Pixel.sg": { + "http://www.pixel.sg/": [ + "pixel.sg" + ] + } + }, + { + "Piximedia": { + "http://www.piximedia.com/": [ + "piximedia.com" + ] + } + }, + { + "Pixlee": { + "https://www.pixlee.com/": [ + "pixlee.com" + ] + } + }, + { + "PLATFORM ONE": { + "http://www.platform-one.co.jp/": [ + "platform-one.co.jp" + ] + } + }, + { + "plista": { + "http://www.plista.com/": [ + "plista.com" + ] + } + }, + { + "PocketCents": { + "http://pocketcents.com/": [ + "pocketcents.com" + ] + } + }, + { + "Polar Mobile": { + "http://polarmobile.com": [ + "mediavoice.com", + "polarmobile.com" + ] + } + }, + { + "Politads": { + "http://politads.com/": [ + "politads.com" + ] + } + }, + { + "Polymorph": { + "http://getpolymorph.com/": [ + "adsnative.com", + "getpolymorph.com" + ] + } + }, + { + "Pontiflex": { + "http://www.pontiflex.com/": [ + "pontiflex.com" + ] + } + }, + { + "PopAds": { + "https://www.popads.net/": [ + "popads.net", + "popadscdn.net" + ] + } + }, + { + "PopRule": { + "http://poprule.com/": [ + "gocampaignlive.com", + "poprule.com" + ] + } + }, + { + "Popunder.ru": { + "http://popunder.ru/": [ + "popunder.ru" + ] + } + }, + { + "Po.st": { + "http://www.po.st/": [ + "po.st" + ] + } + }, + { + "Powerlinks": { + "https://www.powerlinks.com/": [ + "powerlinks.com" + ] + } + }, + { + "PPCProtect": { + "https://ppcprotect.com": [ + "ppcprotect.com" + ] + } + }, + { + "PrecisionClick": { + "http://www.precisionclick.com/": [ + "precisionclick.com" + ] + } + }, + { + "PredictAd": { + "http://www.predictad.com/": [ + "predictad.com" + ] + } + }, + { + "Pressflex": { + "http://www.pressflex.com/": [ + "blogads.com", + "pressflex.com" + ] + } + }, + { + "Prime Visibility": { + "http://www.primevisibility.com/": [ + "adcde.com", + "addlvr.com", + "adonnetwork.com", + "adonnetwork.net", + "adtrgt.com", + "bannertgt.com", + "cptgt.com", + "cpvfeed.com", + "cpvtgt.com", + "dashboardad.net", + "popcde.com", + "primevisibility.com", + "sdfje.com", + "urtbk.com" + ] + } + }, + { + "Primis": { + "https://www.primis.tech": [ + "sekindo.com" + ] + } + }, + { + "PrismApp": { + "https://www.prismapp.io/": [ + "prismapp.io" + ] + } + }, + { + "Proclivity": { + "http://www.proclivitymedia.com/": [ + "proclivitymedia.com", + "proclivitysystems.com", + "pswec.com" + ] + } + }, + { + "Project Wonderful": { + "http://www.projectwonderful.com/": [ + "projectwonderful.com" + ] + } + }, + { + "PrometheusIntelligenceTechnology": { + "https://prometheusintelligencetechnology.com/": [ + "prometheusintelligencetechnology.com" + ] + } + }, + { + "Propeller Ads": { + "http://propellerads.com/": [ + "propellerads.com" + ] + } + }, + { + "Prosperent": { + "http://prosperent.com/": [ + "prosperent.com" + ] + } + }, + { + "Protected Media": { + "http://www.protected.media/": [ + "ad-score.com", + "protected.media" + ] + } + }, + { + "Provers": { + "http://provers.pro": [ + "provers.pro" + ] + } + }, + { + "Psonstrentie": { + "http://psonstrentie.info": [ + "psonstrentie.info" + ] + } + }, + { + "Public-Idées": { + "http://www.publicidees.com/": [ + "publicidees.com" + ] + } + }, + { + "Publishers Clearing House": { + "http://www.pch.com/": [ + "pch.com" + ] + } + }, + { + "PubMatic": { + "http://www.pubmatic.com/": [ + "pubmatic.com", + "revinet.com" + ] + } + }, + { + "PulsePoint": { + "https://www.pulsepoint.com/": [ + "pulsepoint.com" + ] + } + }, + { + "quadrantOne": { + "http://www.quadrantone.com/": [ + "quadrantone.com" + ] + } + }, + { + "Quake Marketing": { + "http://quakemarketing.com/": [ + "quakemarketing.com" + ] + } + }, + { + "Quantcast": { + "http://www.quantcast.com/": [ + "quantcast.com", + "quantcount.com", + "quantserve.com" + ] + } + }, + { + "QuantumAdvertising": { + "http://quantum-advertising.com": [ + "quantum-advertising.com" + ] + } + }, + { + "QuinStreet": { + "http://quinstreet.com/": [ + "qnsr.com", + "qsstats.com", + "quinstreet.com" + ] + } + }, + { + "QUISMA": { + "https://quisma.com/": [ + "iaded.com", + "quisma.com", + "quismatch.com", + "xaded.com", + "xmladed.com" + ] + } + }, + { + "Radial": { + "https://www.radial.com": [ + "gsicommerce.com", + "gsimedia.net" + ] + } + }, + { + "Radiate Media": { + "http://www.radiatemedia.com/": [ + "matchbin.com", + "radiatemedia.com" + ] + } + }, + { + "RadiumOne": { + "http://www.radiumone.com/": [ + "gwallet.com", + "radiumone.com" + ] + } + }, + { + "Radius Marketing": { + "http://www.radiusmarketing.com/": [ + "radiusmarketing.com" + ] + } + }, + { + "Rambler": { + "http://www.rambler.ru/": [ + "rambler.ru" + ] + } + }, + { + "Rapleaf": { + "http://www.rapleaf.com/": [ + "rapleaf.com", + "rlcdn.com" + ] + } + }, + { + "ReachLocal": { + "http://www.reachlocal.com/": [ + "reachlocal.com", + "rlcdn.net" + ] + } + }, + { + "React2Media": { + "http://www.react2media.com/": [ + "react2media.com" + ] + } + }, + { + "Redux Media": { + "http://reduxmedia.com/": [ + "reduxmedia.com" + ] + } + }, + { + "Rekko": { + "http://rekko.com/": [ + "convertglobal.com", + "rekko.com" + ] + } + }, + { + "Reklamport": { + "http://www.reklamport.com/": [ + "reklamport.com" + ] + } + }, + { + "Reklam Store": { + "http://reklamstore.com/": [ + "reklamstore.com" + ] + } + }, + { + "Reklamz": { + "http://www.reklamz.com/": [ + "reklamz.com" + ] + } + }, + { + "Relevad": { + "http://www.relevad.com/": [ + "relestar.com", + "relevad.com" + ] + } + }, + { + "Renegade Internet": { + "http://www.renegadeinternet.com/": [ + "advertserve.com", + "renegadeinternet.com" + ] + } + }, + { + "Reporo": { + "http://www.reporo.com/": [ + "buzzcity.com" + ] + } + }, + { + "ResolutionMedia": { + "https://nonstoppartner.net/": [ + "nonstoppartner.net" + ] + } + }, + { + "Resolution Media": { + "http://resolutionmedia.com/": [ + "resolutionmedia.com" + ] + } + }, + { + "Resonate": { + "http://www.resonateinsights.com/": [ + "reson8.com", + "resonateinsights.com", + "resonatenetworks.com" + ] + } + }, + { + "Responsys": { + "http://www.responsys.com/": [ + "responsys.com" + ] + } + }, + { + "ReTargeter": { + "http://www.retargeter.com/": [ + "retargeter.com" + ] + } + }, + { + "Retirement Living": { + "www.retirement-living.com/": [ + "blvdstatus.com", + "retirement-living.com" + ] + } + }, + { + "RevContent": { + "http://revcontent.com/": [ + "revcontent.com" + ] + } + }, + { + "RevenueMax": { + "http://revenuemax.de/": [ + "revenuemax.de" + ] + } + }, + { + "Rhythm": { + "http://rhythmnewmedia.com/": [ + "1rx.io", + "rhythmnewmedia.com", + "rhythmone.com", + "rhythmxchange.com", + "rnmd.net" + ] + } + }, + { + "RichAudience": { + "https://richaudience.com/": [ + "richaudience.com" + ] + } + }, + { + "RichRelevance": { + "http://www.richrelevance.com/": [ + "richrelevance.com" + ] + } + }, + { + "RightAction": { + "http://rightaction.com/": [ + "rightaction.com" + ] + } + }, + { + "RMBN": { + "http://rmbn.net/": [ + "rmbn.net", + "rmbn.ru" + ] + } + }, + { + "RMM": { + "http://www.rmmonline.com/": [ + "rmmonline.com" + ] + } + }, + { + "Rocket Fuel": { + "http://rocketfuel.com/": [ + "rfihub.com", + "rfihub.net", + "rocketfuel.com", + "ru4.com", + "xplusone.com" + ] + } + }, + { + "Rovion": { + "http://www.rovion.com/": [ + "rovion.com" + ] + } + }, + { + "rtk": { + "http://rtk.io/": [ + "rtk.io" + ] + } + }, + { + "RubiconProject": { + "http://rubiconproject.com/": [ + "adsbyisocket.com", + "isocket.com", + "rubiconproject.com" + ] + } + }, + { + "RunAds": { + "http://www.runads.com/": [ + "runads.com", + "rundsp.com" + ] + } + }, + { + "RuTarget": { + "http://www.rutarget.ru/": [ + "rutarget.ru" + ] + } + }, + { + "Sabavision": { + "http://www.sabavision.com": [ + "sabavision.com" + ] + } + }, + { + "Sabre": { + "http://www.sabre.com/": [ + "reztrack.com", + "sabre.com", + "sabrehospitality.com" + ] + } + }, + { + "Salesforce.com": { + "http://www.salesforce.com/": [ + "salesforce.com" + ] + } + }, + { + "Samurai Factory": { + "http://www.samurai-factory.jp/": [ + "samurai-factory.jp", + "shinobi.jp" + ] + } + }, + { + "SAP": { + "https://www.sap.com": [ + "seewhy.com" + ] + } + }, + { + "Sapient": { + "http://www.sapient.com/": [ + "bridgetrack.com", + "sapient.com" + ] + } + }, + { + "SAS": { + "http://www.sas.com/": [ + "aimatch.com", + "sas.com" + ] + } + }, + { + "Scandinavian AdNetworks": { + "http://www.scandinavianadnetworks.com/": [ + "scandinavianadnetworks.com" + ] + } + }, + { + "Scribol": { + "http://scribol.com/": [ + "scribol.com" + ] + } + }, + { + "SearchForce": { + "http://www.searchforce.com/": [ + "searchforce.com", + "searchforce.net" + ] + } + }, + { + "Seevast": { + "http://www.seevast.com/": [ + "kanoodle.com", + "pulse360.com", + "seevast.com", + "syndigonetworks.com" + ] + } + }, + { + "Selectable Media": { + "http://selectablemedia.com/": [ + "nabbr.com", + "selectablemedia.com" + ] + } + }, + { + "Semantiqo": { + "http://semantiqo.com/": [ + "semantiqo.com" + ] + } + }, + { + "Semasio": { + "http://www.semasio.com/": [ + "semasio.com", + "semasio.net" + ] + } + }, + { + "SevenAds": { + "http://www.sevenads.net/": [ + "sevenads.net" + ] + } + }, + { + "SexInYourCity": { + "http://www.sexinyourcity.com/": [ + "sexinyourcity.com" + ] + } + }, + { + "ShaftTraffic": { + "https://shafttraffic.com": [ + "libertystmedia.com" + ] + } + }, + { + "ShareASale": { + "http://www.shareasale.com/": [ + "shareasale.com" + ] + } + }, + { + "Sharethrough": { + "http://sharethrough.com/": [ + "sharethrough.com" + ] + } + }, + { + "Shopzilla": { + "http://www.shopzilla.com/": [ + "shopzilla.com" + ] + } + }, + { + "Shortest": { + "http://shorte.st/": [ + "shorte.st" + ] + } + }, + { + "Silverpop": { + "http://www.silverpop.com/": [ + "mkt51.net", + "pages05.net", + "silverpop.com", + "vtrenz.net" + ] + } + }, + { + "Simpli.fi": { + "http://www.simpli.fi/": [ + "simpli.fi" + ] + } + }, + { + "SiteScout": { + "http://www.sitescout.com/": [ + "sitescout.com" + ] + } + }, + { + "Skimlinks": { + "http://skimlinks.com/": [ + "skimlinks.com", + "skimresources.com" + ] + } + }, + { + "Skupe Net": { + "http://www.skupenet.com/": [ + "adcentriconline.com", + "skupenet.com" + ] + } + }, + { + "Smaato": { + "http://www.smaato.com/": [ + "smaato.com" + ] + } + }, + { + "SmartAdServer": { + "http://smartadserver.com/": [ + "smartadserver.com" + ] + } + }, + { + "SmartyAds": { + "https://smartyads.com/": [ + "smartyads.com" + ] + } + }, + { + "Smiley Media": { + "http://www.smileymedia.com/": [ + "smileymedia.com" + ] + } + }, + { + "Smowtion": { + "http://smowtion.com/": [ + "smowtion.com" + ] + } + }, + { + "Snap": { + "http://www.snap.com/": [ + "snap.com" + ] + } + }, + { + "SocialChorus": { + "http://www.socialchorus.com/": [ + "halogenmediagroup.com", + "halogennetwork.com", + "socialchorus.com" + ] + } + }, + { + "SocialInterface": { + "http://socialinterface.com/": [ + "ratevoice.com", + "socialinterface.com" + ] + } + }, + { + "SocialTwist": { + "http://tellafriend.socialtwist.com/": [ + "socialtwist.com" + ] + } + }, + { + "sociomantic labs": { + "http://www.sociomantic.com/": [ + "sociomantic.com" + ] + } + }, + { + "Socital": { + "https://www.socital.com": [ + "socital.com" + ] + } + }, + { + "Sojern": { + "https://www.sojern.com": [ + "sojern.com" + ] + } + }, + { + "SomoAudience": { + "https://somoaudience.com/": [ + "somoaudience.com" + ] + } + }, + { + "Sonobi": { + "http://sonobi.com/": [ + "sonobi.com" + ] + } + }, + { + "sophus3": { + "http://www.sophus3.com/": [ + "sophus3.co.uk", + "sophus3.com" + ] + } + }, + { + "Sortable": { + "https://www.sortable.com/": [ + "deployads.com" + ] + } + }, + { + "Sovrn": { + "https://www.sovrn.com/": [ + "sovrn.com" + ] + } + }, + { + "Space Chimp Media": { + "http://spacechimpmedia.com/": [ + "spacechimpmedia.com" + ] + } + }, + { + "Sparklit": { + "http://www.sparklit.com/": [ + "adbutler.com", + "sparklit.com" + ] + } + }, + { + "Spark Studios": { + "http://www.sparkstudios.com/": [ + "sparkstudios.com" + ] + } + }, + { + "Specific Media": { + "http://www.specificmedia.com/": [ + "adviva.co.uk", + "adviva.net", + "sitemeter.com", + "specificclick.net", + "specificmedia.co.uk", + "specificmedia.com" + ] + } + }, + { + "Spectate": { + "http://spectate.com/": [ + "spectate.com" + ] + } + }, + { + "Sponge": { + "http://spongegroup.com/": [ + "spongegroup.com" + ] + } + }, + { + "Spongecell": { + "http://www.spongecell.com/": [ + "spongecell.com" + ] + } + }, + { + "SponsorAds": { + "http://www.sponsorads.de/": [ + "sponsorads.de" + ] + } + }, + { + "Spot200": { + "http://spot200.com/": [ + "spot200.com" + ] + } + }, + { + "SpotX": { + "https://www.spotx.tv": [ + "spotx.tv" + ] + } + }, + { + "SpotXchange": { + "http://www.spotxchange.com/": [ + "spotxchange.com" + ] + } + }, + { + "SpringServe": { + "https://springserve.com/": [ + "springserve.com" + ] + } + }, + { + "StackAdapt": { + "https://www.stackadapt.com/": [ + "stackadapt.com" + ] + } + }, + { + "StarGames": { + "https://www.stargames.net/": [ + "stargamesaffiliate.com" + ] + } + }, + { + "SteelHouse": { + "http://www.steelhouse.com/": [ + "steelhouse.com", + "steelhousemedia.com" + ] + } + }, + { + "Storygize": { + "http://www.storygize.com/": [ + "storygize.com", + "storygize.net" + ] + } + }, + { + "Streamray": { + "http://streamray.com/": [ + "cams.com", + "streamray.com" + ] + } + }, + { + "StrikeAd": { + "http://www.strikead.com/": [ + "strikead.com" + ] + } + }, + { + "StrongMail": { + "http://www.strongmail.com/": [ + "popularmedia.com" + ] + } + }, + { + "Struq": { + "http://struq.com/": [ + "struq.com" + ] + } + }, + { + "Sublime Skinz": { + "http://sublime.xyz/": [ + "ayads.co", + "sublime.xyz" + ] + } + }, + { + "Suite 66": { + "http://www.suite66.com/": [ + "suite66.com" + ] + } + }, + { + "Summit": { + "http://www.summit.co.uk/": [ + "summitmedia.co.uk" + ] + } + }, + { + "Superfish": { + "http://www.superfish.com/": [ + "superfish.com" + ] + } + }, + { + "SupersonicAds": { + "http://www.supersonicads.com/": [ + "supersonicads.com" + ] + } + }, + { + "Survata": { + "https://www.survata.com/": [ + "survata.com" + ] + } + }, + { + "Switch": { + "http://www.switchconcepts.com/": [ + "ethicalads.net", + "switchadhub.com", + "switchconcepts.co.uk", + "switchconcepts.com" + ] + } + }, + { + "Swoop": { + "http://swoop.com/": [ + "swoop.com" + ] + } + }, + { + "SymphonyAM": { + "http://www.factortg.com/": [ + "factortg.com" + ] + } + }, + { + "Syncapse": { + "http://www.syncapse.com/": [ + "clickable.net", + "syncapse.com" + ] + } + }, + { + "Syrup Ad": { + "http://adotsolution.com/": [ + "adotsolution.com" + ] + } + }, + { + "Taboola": { + "https://www.taboola.com/": [ + "perfectmarket.com", + "taboola.com" + ] + } + }, + { + "Tailsweep": { + "http://www.tailsweep.com/": [ + "tailsweep.com" + ] + } + }, + { + "Taleria": { + "https://outstream.telaria.com/": [ + "freeskreen.com" + ] + } + }, + { + "Tapad": { + "http://www.tapad.com/": [ + "tapad.com" + ] + } + }, + { + "Tapgage": { + "http://www.tapgage.com/": [ + "bizmey.com", + "tapgage.com" + ] + } + }, + { + "TapIt!": { + "http://tapit.com/": [ + "tapit.com" + ] + } + }, + { + "Tap.me": { + "http://tap.me/": [ + "tap.me" + ] + } + }, + { + "Targetix": { + "http://targetix.net/": [ + "targetix.net" + ] + } + }, + { + "Tatto Media": { + "http://tattomedia.com/": [ + "quicknoodles.com", + "tattomedia.com" + ] + } + }, + { + "Teadma": { + "http://www.teadma.com/": [ + "teadma.com" + ] + } + }, + { + "Teads.tv": { + "http://teads.tv/": [ + "ebuzzing.com", + "teads.tv" + ] + } + }, + { + "Technorati": { + "http://technorati.com/": [ + "technorati.com", + "technoratimedia.com" + ] + } + }, + { + "TellApart": { + "http://tellapart.com/": [ + "tellapart.com", + "tellapt.com" + ] + } + }, + { + "Telstra": { + "http://www.telstra.com.au/": [ + "sensis.com.au", + "sensisdata.com.au", + "sensisdigitalmedia.com.au", + "telstra.com.au" + ] + } + }, + { + "Terra": { + "http://www.terra.com.br/": [ + "eztargetmedia.com", + "terra.com.br" + ] + } + }, + { + "The Numa Group": { + "http://www.thenumagroup.com/": [ + "hittail.com", + "thenumagroup.com" + ] + } + }, + { + "The Search Agency": { + "http://www.thesearchagency.com/": [ + "thesearchagency.com", + "thesearchagency.net" + ] + } + }, + { + "The Trade Desk": { + "http://thetradedesk.com/": [ + "adsrvr.org", + "thetradedesk.com" + ] + } + }, + { + "Think Realtime": { + "http://www.thinkrealtime.com/": [ + "echosearch.com", + "esm1.net", + "thinkrealtime.com" + ] + } + }, + { + "Tinder": { + "http://tinder.com/": [ + "carbonads.com", + "tinder.com" + ] + } + }, + { + "TiqIQ": { + "http://www.tiqiq.com/": [ + "tiqiq.com" + ] + } + }, + { + "Tisoomi": { + "http://www.tisoomi.com/": [ + "adternal.com", + "tisoomi.com" + ] + } + }, + { + "TLVMedia": { + "http://tlvmedia.com/": [ + "tlvmedia.com" + ] + } + }, + { + "Todacell": { + "http://www.todacell.com/": [ + "todacell.com" + ] + } + }, + { + "ToneFuse": { + "http://tonefuse.com/": [ + "tonefuse.com" + ] + } + }, + { + "ToneMedia": { + "http://tonemedia.com/": [ + "clickfuse.com", + "tonemedia.com" + ] + } + }, + { + "TouchCommerce": { + "http://www.touchcommerce.com/": [ + "inq.com", + "touchcommerce.com" + ] + } + }, + { + "TrackingSoft": { + "http://trackingsoft.com/": [ + "trackingsoft.com" + ] + } + }, + { + "Tradedoubler": { + "http://www.tradedoubler.com/": [ + "tradedoubler.com" + ] + } + }, + { + "TradeTracker": { + "http://www.tradetracker.com/": [ + "tradetracker.com", + "tradetracker.net" + ] + } + }, + { + "TrafficHaus": { + "http://www.traffichaus.com/": [ + "traffichaus.com", + "traffichouse.com" + ] + } + }, + { + "TrafficRevenue": { + "http://www.trafficrevenue.net/": [ + "trafficrevenue.net" + ] + } + }, + { + "Traffiq": { + "http://www.traffiq.com/": [ + "traffiq.com" + ] + } + }, + { + "Trafmag": { + "http://trafmag.com/": [ + "trafmag.com" + ] + } + }, + { + "Traverse": { + "http://www.traversedata.com/": [ + "traversedlp.com" + ] + } + }, + { + "Travora Media": { + "http://www.travoramedia.com/": [ + "traveladnetwork.com", + "traveladvertising.com", + "travoramedia.com" + ] + } + }, + { + "Tremor Video": { + "http://www.tremorvideo.com/": [ + "scanscout.com", + "tmnetads.com", + "tremorhub.com", + "tremormedia.com", + "tremorvideo.com" + ] + } + }, + { + "Triggit": { + "http://triggit.com/": [ + "triggit.com" + ] + } + }, + { + "TripleLift": { + "http://triplelift.com/": [ + "3lift.com", + "triplelift.com" + ] + } + }, + { + "TruEffect": { + "http://www.trueffect.com/": [ + "adlegend.com", + "trueffect.com" + ] + } + }, + { + "TrustX": { + "https://trustx.org/": [ + "trustx.org" + ] + } + }, + { + "TubeMogul": { + "http://www.tubemogul.com/": [ + "tmogul.com", + "tubemogul.com" + ] + } + }, + { + "Twelvefold": { + "http://www.twelvefold.com/": [ + "buzzlogic.com", + "twelvefold.com" + ] + } + }, + { + "Twitter": { + "https://twitter.com/": [ + "ads-twitter.com" + ] + } + }, + { + "Twyn Group": { + "http://www.twyn.com/": [ + "twyn-group.com", + "twyn.com" + ] + } + }, + { + "Tyroo": { + "http://www.tyroo.com/": [ + "tyroo.com" + ] + } + }, + { + "ucfunnel": { + "https://www.ucfunnel.com/": [ + "aralego.com", + "ucfunnel.com" + ] + } + }, + { + "uCoz": { + "http://www.ucoz.com/": [ + "ucoz.ae", + "ucoz.br", + "ucoz.com", + "ucoz.du", + "ucoz.fr", + "ucoz.net", + "ucoz.ru" + ] + } + }, + { + "Unanimis": { + "http://www.unanimis.co.uk/": [ + "unanimis.co.uk" + ] + } + }, + { + "Underdog Media": { + "http://www.underdogmedia.com/": [ + "udmserve.net", + "underdogmedia.com" + ] + } + }, + { + "Undertone": { + "http://www.undertone.com/": [ + "undertone.com", + "undertonenetworks.com", + "undertonevideo.com" + ] + } + }, + { + "UniQlick": { + "http://www.uniqlick.com/": [ + "51network.com", + "uniqlick.com", + "wanmo.com" + ] + } + }, + { + "Unruly": { + "https://unruly.co/": [ + "unrulymedia.com" + ] + } + }, + { + "Upland": { + "https://uplandsoftware.com/": [ + "leadlander.com", + "trackalyzer.com" + ] + } + }, + { + "up-value": { + "http://www.up-value.de/": [ + "up-value.de" + ] + } + }, + { + "Value Ad": { + "http://valuead.com/": [ + "valuead.com" + ] + } + }, + { + "Various": { + "http://www.various.com/": [ + "amigos.com", + "getiton.com", + "medley.com", + "nostringsattached.com", + "various.com" + ] + } + }, + { + "Vdopia": { + "http://www.vdopia.com/": [ + "ivdopia.com", + "vdopia.com" + ] + } + }, + { + "Veeseo": { + "http://veeseo.com": [ + "veeseo.com" + ] + } + }, + { + "Velocity Media": { + "http://adsvelocity.com/": [ + "adsvelocity.com" + ] + } + }, + { + "Velti": { + "http://www.velti.com/": [ + "mobclix.com", + "velti.com" + ] + } + }, + { + "Vemba": { + "https://www.vemba.com/": [ + "vemba.com" + ] + } + }, + { + "Venatus Media": { + "http://venatusmedia.com": [ + "venatusmedia.com" + ] + } + }, + { + "Vendemore": { + "https://vendemore.com/": [ + "vendemore.com" + ] + } + }, + { + "Vendio": { + "http://www.vendio.com/": [ + "singlefeed.com", + "vendio.com" + ] + } + }, + { + "Veoxa": { + "http://www.veoxa.com/": [ + "veoxa.com" + ] + } + }, + { + "Veremedia": { + "http://www.veremedia.com/": [ + "veremedia.com" + ] + } + }, + { + "VerticalHealth": { + "https://www.verticalhealth.com/": [ + "verticalhealth.net" + ] + } + }, + { + "VerticalResponse": { + "http://www.verticalresponse.com/": [ + "verticalresponse.com", + "vresp.com" + ] + } + }, + { + "Vibrant Media": { + "http://www.vibrantmedia.com/": [ + "intellitxt.com", + "picadmedia.com", + "vibrantmedia.com" + ] + } + }, + { + "VideoIntelligence": { + "https://www.vi.ai/": [ + "vi.ai" + ] + } + }, + { + "VigLink": { + "http://www.viglink.com/": [ + "viglink.com" + ] + } + }, + { + "VisibleBrands": { + "http://www.visbrands.com/": [ + "visbrands.com" + ] + } + }, + { + "Visible Measures": { + "http://www.visiblemeasures.com/": [ + "viewablemedia.net", + "visiblemeasures.com" + ] + } + }, + { + "VisualDNA": { + "http://www.visualdna.com/": [ + "vdna-assets.com", + "visualdna-stats.com", + "visualdna.com" + ] + } + }, + { + "Vizu": { + "http://www.vizu.com/": [ + "vizu.com" + ] + } + }, + { + "Vizury": { + "http://www.vizury.com/": [ + "vizury.com" + ] + } + }, + { + "Vserv": { + "http://www.vserv.com/": [ + "vserv.com", + "vserv.mobi" + ] + } + }, + { + "Vuble": { + "https://vuble.tv/us/": [ + "mediabong.com" + ] + } + }, + { + "Wahoha": { + "http://wahoha.com/": [ + "contentwidgets.net", + "wahoha.com" + ] + } + }, + { + "Wayfair": { + "https://www.wayfair.com/": [ + "wayfair.com" + ] + } + }, + { + "WebAds": { + "http://www.webads.co.uk/": [ + "webads.co.uk" + ] + } + }, + { + "Web.com": { + "http://www.web.com/": [ + "feedperfect.com", + "web.com" + ] + } + }, + { + "WebGozar.com": { + "http://www.webgozar.com/": [ + "webgozar.com", + "webgozar.ir" + ] + } + }, + { + "Webmecanik": { + "https://www.webmecanik.com/": [ + "webmecanik.com" + ] + } + }, + { + "WebMetro": { + "http://www.webmetro.com/": [ + "dsmmadvantage.com", + "webmetro.com" + ] + } + }, + { + "Weborama": { + "http://weborama.com/": [ + "weborama.com", + "weborama.fr" + ] + } + }, + { + "Webtraffic": { + "http://www.webtraffic.se/": [ + "webtraffic.no", + "webtraffic.se" + ] + } + }, + { + "WideOrbit": { + "https://www.wideorbit.com/": [ + "dep-x.com" + ] + } + }, + { + "WiredMinds": { + "http://www.wiredminds.com/": [ + "wiredminds.com", + "wiredminds.de" + ] + } + }, + { + "Wishabi": { + "http://wishabi.com": [ + "wishabi.com", + "wishabi.net" + ] + } + }, + { + "WordStream": { + "http://www.wordstream.com/": [ + "wordstream.com" + ] + } + }, + { + "WPP": { + "http://www.wpp.com/": [ + "247realmedia.com", + "accelerator-media.com", + "acceleratorusa.com", + "decdna.net", + "decideinteractive.com", + "gmads.net", + "groupm.com", + "kantarmedia.com", + "mecglobal.com", + "mindshare.nl", + "mookie1.com", + "pm14.com", + "realmedia.com", + "targ.ad", + "themig.com", + "wpp.com", + "xaxis.com" + ] + } + }, + { + "xAd": { + "http://www.xad.com/": [ + "xad.com" + ] + } + }, + { + "Xertive Media": { + "http://www.xertivemedia.com/": [ + "admanager-xertive.com", + "xertivemedia.com" + ] + } + }, + { + "xplosion interactive": { + "http://www.xplosion.de/": [ + "xplosion.de" + ] + } + }, + { + "Xrost DS": { + "http://www.adplan-ds.com/": [ + "adplan-ds.com" + ] + } + }, + { + "Yabuka": { + "http://www.yabuka.com/": [ + "yabuka.com" + ] + } + }, + { + "Yahoo!": { + "http://www.yahoo.com/": [ + "adinterax.com", + "adrevolver.com", + "ads.yahoo.com", + "adserver.yahoo.com", + "advertising.yahoo.com", + "bluelithium.com", + "dapper.net", + "flurry.com", + "interclick.com", + "marketingsolutions.yahoo.com", + "overture.com", + "rightmedia.com", + "rmxads.com", + "secure-adserver.com", + "thewheelof.com", + "yieldmanager.com", + "yieldmanager.net", + "yldmgrimg.net" + ] + } + }, + { + "Yandex": { + "http://www.yandex.com/": [ + "adfox.yandex.ru", + "an.yandex.ru", + "awaps.yandex.ru", + "mc.yandex.ru", + "moikrug.ru", + "web-visor.com", + "yandex.ru/clck/click", + "yandex.ru/clck/counter", + "yandex.ru/cycounter", + "yandex.ru/portal/set/any", + "yandex.ru/set/s/rsya-tag-users/data" + ] + } + }, + { + "Ybrant Digital": { + "http://www.ybrantdigital.com/": [ + "addynamix.com", + "adserverplus.com", + "oridian.com", + "ybrantdigital.com" + ] + } + }, + { + "YD": { + "http://www.ydworld.com/": [ + "ydworld.com", + "yieldivision.com" + ] + } + }, + { + "YellowHammer": { + "http://www.yhmg.com/": [ + "attracto.com", + "clickhype.com", + "yellowhammermg.com", + "yhmg.com" + ] + } + }, + { + "Yes Ads": { + "http://yesads.com/": [ + "yesads.com" + ] + } + }, + { + "YieldAds": { + "http://yieldads.com/": [ + "yieldads.com" + ] + } + }, + { + "YieldBids": { + "http://ybx.io/": [ + "ybx.io" + ] + } + }, + { + "YieldBot": { + "http://yieldbot.com/": [ + "yldbt.com" + ] + } + }, + { + "YieldBuild": { + "http://yieldbuild.com/": [ + "yieldbuild.com" + ] + } + }, + { + "Yieldify": { + "https://www.yieldify.com/": [ + "yieldify.com" + ] + } + }, + { + "Yieldlab": { + "http://www.yieldlab.de/": [ + "yieldlab.de", + "yieldlab.net" + ] + } + }, + { + "Yieldmo": { + "https://yieldmo.com": [ + "yieldmo.com" + ] + } + }, + { + "YieldNexus": { + "https://www.yieldnexus.com/": [ + "ynxs.io" + ] + } + }, + { + "YOC": { + "http://group.yoc.com/": [ + "yoc-performance.com", + "yoc.com" + ] + } + }, + { + "Yoggrt": { + "http://www.yoggrt.com/": [ + "yoggrt.com" + ] + } + }, + { + "youknowbest": { + "http://www.youknowbest.com/": [ + "youknowbest.com" + ] + } + }, + { + "YuMe": { + "http://www.yume.com/": [ + "yume.com", + "yumenetworks.com" + ] + } + }, + { + "ZafulAffiliate": { + "https://affiliate.zaful.com/": [ + "affasi.com", + "gw-ec.com", + "zaful.com" + ] + } + }, + { + "Zango": { + "http://www.zango.com/": [ + "metricsdirect.com", + "zango.com" + ] + } + }, + { + "zanox": { + "http://www.zanox.com/": [ + "buy.at", + "zanox-affiliate.de", + "zanox.com" + ] + } + }, + { + "zapunited": { + "http://www.zapunited.com/": [ + "zaparena.com", + "zapunited.com" + ] + } + }, + { + "ZEDO": { + "http://www.zedo.com/": [ + "zedo.com", + "zincx.com" + ] + } + }, + { + "Zefir": { + "https://ze-fir.com/": [ + "ze-fir.com" + ] + } + }, + { + "Zemanta": { + "http://www.zemanta.com/": [ + "zemanta.com" + ] + } + }, + { + "ZestAd": { + "http://www.zestad.com/": [ + "zestad.com" + ] + } + }, + { + "Zeta Email Solutions": { + "http://www.zetaemailsolutions.com/": [ + "insightgrit.com", + "zetaemailsolutions.com" + ] + } + }, + { + "Zumobi": { + "http://www.zumobi.com/": [ + "zumobi.com" + ] + } + }, + { + "ZypMedia": { + "http://www.zypmedia.com/": [ + "extend.tv", + "zypmedia.com" + ] + } + } + ], + "Content": [ + { + "33Across": { + "http://33across.com/": [ + "tynt.com" + ] + } + }, + { + "ActivEngage": { + "http://www.activengage.com/": [ + "activengage.com" + ] + } + }, + { + "Adap.tv": { + "http://adap.tv/": [ + "adap.tv" + ] + } + }, + { + "Adobe": { + "http://www.adobe.com/": [ + "adobe.com", + "fyre.co", + "livefyre.com", + "typekit.com" + ] + } + }, + { + "Akamai": { + "http://www.akamai.com/": [ + "abmr.net", + "akamai.com", + "edgesuite.net" + ] + } + }, + { + "AKQA": { + "http://www.akqa.com/": [ + "akqa.com", + "srtk.net" + ] + } + }, + { + "Amazon.com": { + "http://www.amazon.com/": [ + "alexa.com", + "amazon.com", + "cloudfront.net" + ] + } + }, + { + "AOL": { + "http://www.aol.com/": [ + "5min.com", + "aim.com", + "aol.com", + "aolanswers.com", + "aolcdn.com", + "aoltechguru.com", + "autoblog.com", + "cambio.com", + "dailyfinance.com", + "editions.com", + "engadget.com", + "games.com", + "homesessive.com", + "huffingtonpost.com", + "joystiq.com", + "kitchendaily.com", + "makers.com", + "mandatory.com", + "mapquest.com", + "moviefone.com", + "noisecreep.com", + "patch.com", + "pawnation.com", + "shortcuts.com", + "shoutcast.com", + "spinner.com", + "stylelist.com", + "stylemepretty.com", + "surphace.com", + "techcrunch.com", + "theboombox.com", + "theboot.com", + "tuaw.com", + "userplane.com", + "winamp.com" + ] + } + }, + { + "Automattic": { + "http://automattic.com/": [ + "automattic.com", + "gravatar.com", + "intensedebate.com" + ] + } + }, + { + "Baynote": { + "http://www.baynote.com/": [ + "baynote.com", + "baynote.net" + ] + } + }, + { + "Bazaarvoice": { + "http://www.bazaarvoice.com/": [ + "bazaarvoice.com" + ] + } + }, + { + "BigDoor": { + "http://www.bigdoor.com/": [ + "bigdoor.com", + "onetruefan.com" + ] + } + }, + { + "Brightcove": { + "http://www.brightcove.com/": [ + "brightcove.com" + ] + } + }, + { + "Browser-Update.org": { + "www.browser-update.org/": [ + "browser-update.org" + ] + } + }, + { + "BTBuckets": { + "http://btbuckets.com/": [ + "btbuckets.com" + ] + } + }, + { + "Buffer": { + "http://bufferapp.com/": [ + "bufferapp.com" + ] + } + }, + { + "Bunchball": { + "http://www.bunchball.com/": [ + "bunchball.com" + ] + } + }, + { + "buySAFE": { + "http://www.buysafe.com/": [ + "buysafe.com" + ] + } + }, + { + "BuzzFeed": { + "http://www.buzzfeed.com/": [ + "buzzfed.com", + "buzzfeed.com" + ] + } + }, + { + "Cbox": { + "http://www.cbox.ws/": [ + "cbox.ws" + ] + } + }, + { + "CBS Interactive": { + "http://www.cbsinteractive.com/": [ + "cbsinteractive.com", + "com.com" + ] + } + }, + { + "Cedexis": { + "http://www.cedexis.com/": [ + "cedexis.com", + "cedexis.net" + ] + } + }, + { + "Certona": { + "http://www.certona.com/": [ + "certona.com", + "res-x.com" + ] + } + }, + { + "ClipSyndicate": { + "http://www.clipsyndicate.com/": [ + "clipsyndicate.com" + ] + } + }, + { + "Collarity": { + "http://www.collarity.com/": [ + "collarity.com" + ] + } + }, + { + "Conduit": { + "http://www.conduit.com/": [ + "conduit-banners.com", + "conduit-services.com", + "conduit.com", + "wibiya.com" + ] + } + }, + { + "Congoo": { + "http://www.congoo.com/": [ + "congoo.com" + ] + } + }, + { + "Contact At Once!": { + "http://www.contactatonce.com/": [ + "contactatonce.com" + ] + } + }, + { + "Conviva": { + "http://www.conviva.com/": [ + "conviva.com" + ] + } + }, + { + "DailyMe": { + "http://dailyme.com/": [ + "dailyme.com", + "newstogram.com" + ] + } + }, + { + "DataSift": { + "http://datasift.com/": [ + "datasift.com", + "tweetmeme.com" + ] + } + }, + { + "Disqus": { + "http://disqus.com/": [ + "disqus.com" + ] + } + }, + { + "Echo": { + "http://aboutecho.com/": [ + "aboutecho.com", + "haloscan.com", + "js-kit.com" + ] + } + }, + { + "Facebook": { + "http://www.facebook.com/": [ + "fbcdn.net", + "instagram.com", + "messenger.com" + ] + } + }, + { + "Flattr": { + "http://flattr.com/": [ + "flattr.com" + ] + } + }, + { + "FreeWheel": { + "http://www.freewheel.tv/": [ + "freewheel.tv", + "fwmrm.net" + ] + } + }, + { + "Genius.com": { + "http://www.genius.com/": [ + "genius.com" + ] + } + }, + { + "Get Satisfaction": { + "https://getsatisfaction.com/": [ + "getsatisfaction.com" + ] + } + }, + { + "Gigya": { + "http://www.gigya.com/": [ + "gigcount.com", + "gigya.com" + ] + } + }, + { + "Global Takeoff": { + "http://www.globaltakeoff.com/": [ + "globaltakeoff.com", + "globaltakeoff.net" + ] + } + }, + { + "GoGrid": { + "http://www.gogrid.com/": [ + "formalyzer.com", + "gogrid.com", + "komli.net" + ] + } + }, + { + "Google": { + "http://www.google.com/": [ + "accounts.google.com", + "apis.google.com", + "appengine.google.com", + "apture.com", + "blogger.com", + "books.google.com", + "checkout.google.com", + "chrome.google.com", + "code.google.com", + "codesearch.google.com", + "docs.google.com", + "drive.google.com", + "earth.google.com", + "encrypted.google.com", + "feedburner.com", + "feedburner.google.com", + "feedproxy.google.com", + "finance.google.com", + "ggpht.com", + "gmodules.com", + "google-melange.com", + "google.ad", + "google.ae", + "google.al", + "google.am", + "google.as", + "google.at", + "google.az", + "google.ba", + "google.be", + "google.bf", + "google.bg", + "google.bi", + "google.bj", + "google.bs", + "google.bt", + "google.by", + "google.ca", + "google.cat", + "google.cd", + "google.cf", + "google.cg", + "google.ch", + "google.ci", + "google.cl", + "google.cm", + "google.cn", + "google.co.ao", + "google.co.bw", + "google.co.ck", + "google.co.cr", + "google.co.id", + "google.co.il", + "google.co.in", + "google.co.jp", + "google.co.ke", + "google.co.kr", + "google.co.ls", + "google.co.ma", + "google.co.mz", + "google.co.nz", + "google.co.th", + "google.co.tz", + "google.co.ug", + "google.co.uk", + "google.co.uz", + "google.co.ve", + "google.co.vi", + "google.co.za", + "google.co.zm", + "google.co.zw", + "google.com", + "google.com.af", + "google.com.ag", + "google.com.ai", + "google.com.ar", + "google.com.au", + "google.com.bd", + "google.com.bh", + "google.com.bn", + "google.com.bo", + "google.com.br", + "google.com.bz", + "google.com.co", + "google.com.cu", + "google.com.cy", + "google.com.do", + "google.com.ec", + "google.com.eg", + "google.com.et", + "google.com.fj", + "google.com.gh", + "google.com.gi", + "google.com.gt", + "google.com.hk", + "google.com.jm", + "google.com.kh", + "google.com.kw", + "google.com.lb", + "google.com.ly", + "google.com.mm", + "google.com.mt", + "google.com.mx", + "google.com.my", + "google.com.na", + "google.com.nf", + "google.com.ng", + "google.com.ni", + "google.com.np", + "google.com.om", + "google.com.pa", + "google.com.pe", + "google.com.pg", + "google.com.ph", + "google.com.pk", + "google.com.pr", + "google.com.py", + "google.com.qa", + "google.com.sa", + "google.com.sb", + "google.com.sg", + "google.com.sl", + "google.com.sv", + "google.com.tj", + "google.com.tr", + "google.com.tw", + "google.com.ua", + "google.com.uy", + "google.com.vc", + "google.com.vn", + "google.cv", + "google.cz", + "google.de", + "google.dj", + "google.dk", + "google.dm", + "google.dz", + "google.ee", + "google.es", + "google.fi", + "google.fm", + "google.fr", + "google.ga", + "google.ge", + "google.gg", + "google.gl", + "google.gm", + "google.gp", + "google.gr", + "google.gy", + "google.hn", + "google.hr", + "google.ht", + "google.hu", + "google.ie", + "google.im", + "google.iq", + "google.is", + "google.it", + "google.je", + "google.jo", + "google.kg", + "google.ki", + "google.kz", + "google.la", + "google.li", + "google.lk", + "google.lt", + "google.lu", + "google.lv", + "google.md", + "google.me", + "google.mg", + "google.mk", + "google.ml", + "google.mn", + "google.ms", + "google.mu", + "google.mv", + "google.mw", + "google.ne", + "google.nl", + "google.no", + "google.nr", + "google.nu", + "google.pl", + "google.pn", + "google.ps", + "google.pt", + "google.ro", + "google.rs", + "google.ru", + "google.rw", + "google.sc", + "google.se", + "google.sh", + "google.si", + "google.sk", + "google.sm", + "google.sn", + "google.so", + "google.st", + "google.td", + "google.tg", + "google.tk", + "google.tl", + "google.tm", + "google.tn", + "google.to", + "google.tt", + "google.vg", + "google.vu", + "google.ws", + "googleapis.com", + "googleartproject.com", + "googleusercontent.com", + "groups.google.com", + "gstatic.com", + "health.google.com", + "images.google.com", + "investor.google.com", + "knol.google.com", + "maps.google.com", + "music.google.com", + "news.google.com", + "panoramio.com", + "picasa.google.com", + "picasaweb.google.com", + "play.google.com", + "postini.com", + "recaptcha.net", + "script.google.com", + "shopping.google.com", + "sites.google.com", + "sketchup.google.com", + "support.google.com", + "talk.google.com", + "talkgadget.google.com", + "toolbar.google.com", + "translate.google.com", + "trends.google.com", + "video.google.com", + "videos.google.com", + "wallet.google.com", + "youtube.com" + ] + } + }, + { + "Gravity": { + "http://www.gravity.com/": [ + "gravity.com", + "grvcdn.com" + ] + } + }, + { + "Heyzap": { + "http://www.heyzap.com/": [ + "heyzap.com" + ] + } + }, + { + "HubSpot": { + "http://www.hubspot.com/": [ + "hubspot.com" + ] + } + }, + { + "IBM": { + "http://www.ibm.com/": [ + "xtify.com" + ] + } + }, + { + "iovation": { + "http://www.iovation.com/": [ + "iesnare.com", + "iovation.com" + ] + } + }, + { + "Kaltura": { + "http://corp.kaltura.com/": [ + "kaltura.com" + ] + } + }, + { + "kikin": { + "http://www.kikin.com/": [ + "kikin.com" + ] + } + }, + { + "Limelight Networks": { + "http://www.limelight.com/": [ + "clickability.com", + "limelight.com", + "llnwd.net" + ] + } + }, + { + "LivePerson": { + "http://www.liveperson.net/": [ + "liveperson.net" + ] + } + }, + { + "LiveRail": { + "http://liverail.com/": [ + "liverail.com" + ] + } + }, + { + "LongTail Video": { + "http://www.longtailvideo.com/": [ + "longtailvideo.com", + "ltassrv.com" + ] + } + }, + { + "Markit": { + "http://www.markit.com/": [ + "markit.com", + "wsod.com" + ] + } + }, + { + "MashLogic": { + "http://www.mashlogic.com/": [ + "mashlogic.com" + ] + } + }, + { + "McAfee": { + "http://www.mcafee.com/": [ + "mcafee.com", + "scanalert.com" + ] + } + }, + { + "Microsoft": { + "http://www.microsoft.com/": [ + "bing.com", + "gamesforwindows.com", + "getgamesmart.com", + "healthvault.com", + "ieaddons.com", + "iegallery.com", + "live.com", + "microsoft.com", + "microsoftalumni.com", + "microsoftalumni.org", + "microsoftstore.com", + "msn.com", + "msndirect.com", + "office.com", + "officelive.com", + "outlook.com", + "s-msn.com", + "skype.com", + "windowsphone.com", + "worldwidetelescope.org", + "xbox.com", + "zune.com", + "zune.net" + ] + } + }, + { + "NDN": { + "http://www.newsinc.com/": [ + "newsinc.com" + ] + } + }, + { + "Oberon Media": { + "http://www.oberon-media.com/": [ + "blaze.com", + "oberon-media.com" + ] + } + }, + { + "Ooyala": { + "http://www.ooyala.com/": [ + "oo4.com", + "ooyala.com" + ] + } + }, + { + "Oracle": { + "http://www.oracle.com/": [ + "atgsvcs.com", + "instantservice.com", + "istrack.com", + "oracle.com" + ] + } + }, + { + "Peerius": { + "http://www.peerius.com/": [ + "peerius.com" + ] + } + }, + { + "Pinterest": { + "http://pinterest.com/": [ + "pinimg.com", + "pinterest.com" + ] + } + }, + { + "PunchTab": { + "http://www.punchtab.com/": [ + "punchtab.com" + ] + } + }, + { + "RIM": { + "http://www.rim.com/": [ + "rim.com", + "scoreloop.com" + ] + } + }, + { + "Salesforce.com": { + "http://www.salesforce.com/": [ + "salesforceliveagent.com" + ] + } + }, + { + "SAY": { + "http://saymedia.com/": [ + "saymedia.com", + "typepad.com", + "videoegg.com" + ] + } + }, + { + "ScribeFire": { + "http://www.scribefire.com/": [ + "scribefire.com" + ] + } + }, + { + "Six Apart": { + "http://www.sixapart.com/": [ + "sixapart.com" + ] + } + }, + { + "Skribit": { + "http://skribit.com/": [ + "skribit.com" + ] + } + }, + { + "SnapEngage": { + "http://www.snapengage.com/": [ + "snapengage.com" + ] + } + }, + { + "Spring Metrics": { + "http://www.springmetrics.com/": [ + "springmetrics.com" + ] + } + }, + { + "Synacor": { + "http://www.synacor.com/": [ + "synacor.com" + ] + } + }, + { + "ThingLink": { + "http://www.thinglink.com/": [ + "thinglink.com" + ] + } + }, + { + "Thismoment": { + "http://www.thismoment.com/": [ + "thismoment.com" + ] + } + }, + { + "Thummit": { + "http://www.thummit.com/": [ + "thummit.com" + ] + } + }, + { + "Topsy": { + "http://topsy.com/": [ + "topsy.com" + ] + } + }, + { + "TraceMyIP.org": { + "http://www.tracemyip.org/": [ + "tracemyip.org" + ] + } + }, + { + "Trackset": { + "http://www.trackset.com/": [ + "trackset.com" + ] + } + }, + { + "Trovus": { + "http://www.trovus.co.uk/": [ + "trovus.co.uk" + ] + } + }, + { + "Trumba": { + "http://www.trumba.com/": [ + "trumba.com" + ] + } + }, + { + "TRUSTe": { + "http://www.truste.com/": [ + "truste.com" + ] + } + }, + { + "TurnTo": { + "http://www.turntonetworks.com/": [ + "turnto.com", + "turntonetworks.com" + ] + } + }, + { + "Tweetboard": { + "http://tweetboard.com/": [ + "tweetboard.com" + ] + } + }, + { + "Twitter Counter": { + "http://twittercounter.com/": [ + "twittercounter.com" + ] + } + }, + { + "UberMedia": { + "http://ubermedia.com/": [ + "tweetup.com", + "ubermedia.com" + ] + } + }, + { + "UberTags": { + "http://ubertags.com/": [ + "ubertags.com" + ] + } + }, + { + "Unbounce": { + "http://unbounce.com/": [ + "unbounce.com" + ] + } + }, + { + "Uptrends": { + "http://www.uptrends.com/": [ + "uptrends.com" + ] + } + }, + { + "Usability Sciences": { + "http://www.usabilitysciences.com/": [ + "usabilitysciences.com", + "webiqonline.com" + ] + } + }, + { + "UserVoice": { + "http://www.uservoice.com/": [ + "uservoice.com" + ] + } + }, + { + "Vertical Acuity": { + "http://www.verticalacuity.com/": [ + "verticalacuity.com" + ] + } + }, + { + "VG WORT": { + "http://www.vgwort.de/": [ + "vgwort.de" + ] + } + }, + { + "Videology": { + "http://www.videologygroup.com/": [ + "tidaltv.com", + "videologygroup.com" + ] + } + }, + { + "Viewbix": { + "http://www.viewbix.com/": [ + "qoof.com", + "viewbix.com" + ] + } + }, + { + "Vimeo": { + "http://vimeo.com/": [ + "vimeo.com", + "vimeocdn.com" + ] + } + }, + { + "VINDICO": { + "http://vindicogroup.com/": [ + "vindicogroup.com", + "vindicosuite.com" + ] + } + }, + { + "Voice2Page": { + "http://www.voice2page.com/": [ + "voice2page.com" + ] + } + }, + { + "WebsiteAlive": { + "http://www.websitealive.com/": [ + "websitealive.com", + "websitealive0.com", + "websitealive1.com", + "websitealive2.com", + "websitealive3.com", + "websitealive4.com", + "websitealive5.com", + "websitealive6.com", + "websitealive7.com", + "websitealive8.com", + "websitealive9.com" + ] + } + }, + { + "Yahoo!": { + "http://www.yahoo.com/": [ + "answers.yahoo.com", + "apps.yahoo.com", + "autos.yahoo.com", + "biz.yahoo.com", + "developer.yahoo.com", + "everything.yahoo.com", + "finance.yahoo.com", + "flickr.com", + "games.yahoo.com", + "groups.yahoo.com", + "help.yahoo.com", + "hotjobs.yahoo.com", + "info.yahoo.com", + "local.yahoo.com", + "luminate.com", + "messages.yahoo.com", + "movies.yahoo.com", + "msg.yahoo.com", + "news.yahoo.com", + "omg.yahoo.com", + "pipes.yahoo.com", + "pixazza.com", + "realestate.yahoo.com", + "search.yahoo.com", + "shine.yahoo.com", + "smallbusiness.yahoo.com", + "sports.yahoo.com", + "staticflickr.com", + "suggestions.yahoo.com", + "travel.yahoo.com", + "tumblr.com", + "upcoming.yahoo.com", + "webhosting.yahoo.com", + "widgets.yahoo.com", + "www.yahoo.com", + "yahooapis.com", + "yahoofs.com", + "yimg.com", + "ypolicyblog.com", + "yuilibrary.com", + "zenfs.com" + ] + } + }, + { + "Yandex": { + "http://www.yandex.com/": [ + "kinopoisk.ru", + "yandex.by", + "yandex.com", + "yandex.com.tr", + "yandex.ru", + "yandex.st", + "yandex.ua" + ] + } + }, + { + "Zendesk": { + "http://www.zendesk.com/": [ + "zendesk.com" + ] + } + }, + { + "Zopim": { + "https://www.zopim.com/": [ + "zopim.com" + ] + } + } + ], + "Analytics": [ + { + "63 Squares": { + "http://63squares.com/": [ + "63squares.com", + "i-stats.com" + ] + } + }, + { + "Acxiom": { + "http://www.acxiom.com/": [ + "acxiom.com", + "acxiomapac.com", + "mm7.net", + "pippio.com" + ] + } + }, + { + "AddFreeStats": { + "http://www.addfreestats.com/": [ + "3dstats.com", + "addfreestats.com" + ] + } + }, + { + "Adloox": { + "http://www.adloox.com/": [ + "adloox.com", + "adlooxtracking.com" + ] + } + }, + { + "Adventori": { + "https://adventori.com": [ + "adventori.com" + ] + } + }, + { + "AIData": { + "http://www.aidata.me/": [ + "advombat.ru", + "aidata.me" + ] + } + }, + { + "AivaLabs": { + "https://aivalabs.com": [ + "aivalabs.com" + ] + } + }, + { + "Akamai": { + "http://www.akamai.com/": [ + "go-mpulse.net" + ] + } + }, + { + "Amadesa": { + "http://www.amadesa.com/": [ + "amadesa.com" + ] + } + }, + { + "Amazing Counters": { + "http://amazingcounters.com/": [ + "amazingcounters.com" + ] + } + }, + { + "Amazon.com": { + "http://www.amazon.com/": [ + "alexametrics.com" + ] + } + }, + { + "Amplitude": { + "https://amplitude.com/": [ + "amplitude.com" + ] + } + }, + { + "anormal-media.de": { + "http://anormal-media.de/": [ + "anormal-media.de", + "anormal-tracker.de" + ] + } + }, + { + "AT Internet": { + "http://www.atinternet.com/": [ + "at-o.net", + "atinternet.com", + "xiti.com" + ] + } + }, + { + "Attracta": { + "https://www.attracta.com/": [ + "attracta.com" + ] + } + }, + { + "Automattic": { + "http://automattic.com/": [ + "polldaddy.com" + ] + } + }, + { + "AvantLink": { + "http://www.avantlink.com/": [ + "avmws.com" + ] + } + }, + { + "Awio": { + "http://www.awio.com/": [ + "awio.com", + "w3counter.com", + "w3roi.com" + ] + } + }, + { + "Belstat": { + "http://www.belstat.com/": [ + "belstat.be", + "belstat.com", + "belstat.de", + "belstat.fr", + "belstat.nl" + ] + } + }, + { + "BetssonPalantir": { + "https://betssonpalantir.com/": [ + "betssonpalantir.com" + ] + } + }, + { + "BlogCounter.com": { + "http://www.blogcounter.de/": [ + "blogcounter.de" + ] + } + }, + { + "BloomReach": { + "http://www.bloomreach.com/": [ + "p.brsrvr.com" + ] + } + }, + { + "BlueCava": { + "http://www.bluecava.com/": [ + "bluecava.com" + ] + } + }, + { + "Bluemetrix": { + "http://www.bluemetrix.com/": [ + "bluemetrix.com", + "bmmetrix.com" + ] + } + }, + { + "Bombora": { + "https://bombora.com/": [ + "ml314.com" + ] + } + }, + { + "Branch": { + "https://branch.io/": [ + "branch.io" + ] + } + }, + { + "Branica": { + "http://www.branica.com/": [ + "branica.com" + ] + } + }, + { + "BrightEdge": { + "http://www.brightedge.com/": [ + "b0e8.com", + "brightedge.com" + ] + } + }, + { + "Bubblestat": { + "http://www.bubblestat.com/": [ + "bubblestat.com" + ] + } + }, + { + "Cardlytics": { + "http://www.cardlytics.com/": [ + "cardlytics.com" + ] + } + }, + { + "Chartbeat": { + "http://chartbeat.com/": [ + "chartbeat.com", + "chartbeat.net" + ] + } + }, + { + "Clickdensity": { + "http://www.clickdensity.com/": [ + "clickdensity.com" + ] + } + }, + { + "ClickGuard": { + "https://www.clickguard.com/": [ + "clickguard.com" + ] + } + }, + { + "ClickTale": { + "http://www.clicktale.com/": [ + "clicktale.com", + "clicktale.net", + "pantherssl.com" + ], + "session-replay": "true" + } + }, + { + "ClixMetrix": { + "http://www.clixmetrix.com/": [ + "clixmetrix.com" + ] + } + }, + { + "Clixpy": { + "http://clixpy.com/": [ + "clixpy.com" + ] + } + }, + { + "ClustrMaps": { + "http://www.clustrmaps.com/": [ + "clustrmaps.com" + ] + } + }, + { + "CNZZ": { + "http://www.cnzz.com/": [ + "cnzz.com" + ] + } + }, + { + "Compuware": { + "http://www.compuware.com/": [ + "axf8.net", + "compuware.com", + "gomez.com" + ] + } + }, + { + "comScore": { + "http://www.comscore.com/": [ + "certifica.com", + "comscore.com", + "mdotlabs.com", + "scorecardresearch.com", + "sitestat.com", + "voicefive.com" + ] + } + }, + { + "Connexity": { + "http://www.connexity.com/": [ + "connexity.com", + "connexity.net" + ] + } + }, + { + "Convert Insights": { + "http://www.convert.com/": [ + "convert.com", + "reedge.com" + ] + } + }, + { + "Convertro": { + "http://www.convertro.com/": [ + "convertro.com" + ] + } + }, + { + "Crazy Egg": { + "http://www.crazyegg.com/": [ + "cetrk.com", + "crazyegg.com" + ] + } + }, + { + "Crowd Science": { + "http://crowdscience.com/": [ + "crowdscience.com" + ] + } + }, + { + "Cya2": { + "http://cya2.net/": [ + "cya2.net" + ] + } + }, + { + "Dataium": { + "http://www.dataium.com/": [ + "collserve.com", + "dataium.com" + ] + } + }, + { + "Deep Intent": { + "https://www.deepintent.com/": [ + "deepintent.com" + ] + } + }, + { + "Demandbase": { + "http://www.demandbase.com/": [ + "company-target.com", + "demandbase.com" + ] + } + }, + { + "DirectCORP": { + "http://www.directcorp.de/": [ + "ipcounter.de" + ] + } + }, + { + "DistilNetworks": { + "https://www.distilnetworks.com/": [ + "distiltag.com" + ] + } + }, + { + "DoubleVerify": { + "http://www.doubleverify.com/": [ + "doubleverify.com" + ] + } + }, + { + "dwstat.com": { + "http://www.dwstat.cn/": [ + "dwstat.cn" + ] + } + }, + { + "ECSAnalytics": { + "https://www.theecsinc.com/": [ + "ecsanalytics.com" + ] + } + }, + { + "EFF": { + "https://www.eff.org/": [ + "do-not-tracker.org", + "eviltracker.net", + "trackersimulator.org" + ] + } + }, + { + "eProof.com": { + "http://www.eproof.com/": [ + "eproof.com" + ] + } + }, + { + "etracker": { + "http://www.etracker.com/": [ + "etracker.com", + "etracker.de", + "sedotracker.com", + "sedotracker.de" + ] + } + }, + { + "Eulerian Technologies": { + "http://www.eulerian.com/": [ + "eulerian.com", + "eulerian.net" + ] + } + }, + { + "eXTReMe digital": { + "http://extremetracking.com/": [ + "extreme-dm.com", + "extremetracking.com" + ] + } + }, + { + "Eyeota": { + "http://eyeota.net/": [ + "eyeota.net" + ] + } + }, + { + "Feedjit": { + "http://feedjit.com/": [ + "feedjit.com" + ] + } + }, + { + "Flashtalking": { + "http://www.flashtalking.com/": [ + "encoremetrics.com", + "sitecompass.com" + ] + } + }, + { + "Footprint": { + "http://www.footprintlive.com/": [ + "footprintlive.com" + ] + } + }, + { + "Free Online Users": { + "http://www.freeonlineusers.com/": [ + "freeonlineusers.com" + ] + } + }, + { + "Free-PageRank.com": { + "http://www.free-pagerank.com/": [ + "free-pagerank.com" + ] + } + }, + { + "Friends2Follow": { + "https://friends2follow.com/": [ + "antifraudjs.friends2follow.com" + ] + } + }, + { + "Fullstory": { + "https://www.fullstory.com/": [ + "fullstory.com" + ], + "session-replay": "true" + } + }, + { + "GetSiteControl": { + "https://getsitecontrol.com/": [ + "getsitecontrol.com" + ] + } + }, + { + "GfK Group": { + "http://www.gfk.com/": [ + "daphnecm.com", + "gfk.com", + "gfkdaphne.com" + ] + } + }, + { + "GitHub": { + "https://github.com/": [ + "gaug.es" + ] + } + }, + { + "Go Daddy": { + "http://www.godaddy.com/": [ + "godaddy.com", + "trafficfacts.com" + ] + } + }, + { + "Google": { + "http://www.google.com/": [ + "google-analytics.com", + "postrank.com" + ] + } + }, + { + "GoSquared": { + "https://www.gosquared.com/": [ + "gosquared.com" + ] + } + }, + { + "GoStats": { + "http://gostats.com/": [ + "gostats.com" + ] + } + }, + { + "GrapheneMedia": { + "http://graphenemedia.in/": [ + "graphenedigitalanalytics.in" + ] + } + }, + { + "GTop": { + "http://www.gtop.ro/": [ + "gtop.ro", + "gtopstats.com" + ] + } + }, + { + "Hearst": { + "http://www.hearst.com/": [ + "raasnet.com", + "redaril.com" + ] + } + }, + { + "Histats": { + "http://www.histats.com/": [ + "histats.com" + ] + } + }, + { + "HitsLink": { + "http://www.hitslink.com/": [ + "hitslink.com" + ] + } + }, + { + "Hit Sniffer": { + "http://www.hitsniffer.com/": [ + "hitsniffer.com" + ] + } + }, + { + "Hotjar": { + "https://www.hotjar.com": [ + "hotjar.com" + ] + } + }, + { + "HubSpot": { + "http://www.hubspot.com/": [ + "hs-analytics.net" + ] + } + }, + { + "IBM": { + "http://www.ibm.com/": [ + "cmcore.com", + "coremetrics.com", + "ibm.com" + ] + } + }, + { + "InboundWriter": { + "http://www.inboundwriter.com/": [ + "enquisite.com", + "inboundwriter.com" + ] + } + }, + { + "Infernotions": { + "https://infernotions.com/": [ + "infernotions.com" + ] + } + }, + { + "INFOnline": { + "https://www.infonline.de/": [ + "infonline.de", + "ioam.de", + "ivwbox.de" + ] + } + }, + { + "InfoStars": { + "http://infostars.ru/": [ + "hotlog.ru", + "infostars.ru" + ] + } + }, + { + "Inspectlet": { + "http://www.inspectlet.com/": [ + "inspectlet.com" + ] + } + }, + { + "IntelligenceFocus": { + "http://www.intelligencefocus.com/": [ + "domodomain.com", + "intelligencefocus.com" + ] + } + }, + { + "iPerceptions": { + "http://www.iperceptions.com/": [ + "iperceptions.com" + ] + } + }, + { + "IslayTech": { + "http://islay.tech": [ + "islay.tech" + ] + } + }, + { + "ItIsATracker": { + "https://itisatracker.com/": [ + "itisatracker.com" + ], + "dnt": "eff" + } + }, + { + "KeyMetric": { + "http://www.keymetric.net/": [ + "keymetric.net" + ] + } + }, + { + "KISSmetrics": { + "http://kissmetrics.com/": [ + "kissmetrics.com" + ] + } + }, + { + "Kitcode": { + "http://src.kitcode.net/": [ + "src.kitcode.net" + ] + } + }, + { + "LeadForensics": { + "https://www.leadforensics.com": [ + "leadforensics.com" + ] + } + }, + { + "LineZing": { + "http://www.linezing.com/": [ + "linezing.com" + ] + } + }, + { + "LivePerson": { + "http://www.liveperson.net/": [ + "liveperson.com", + "nuconomy.com" + ] + } + }, + { + "Logdy": { + "http://logdy.com/": [ + "logdy.com" + ] + } + }, + { + "Lotame": { + "http://www.lotame.com/": [ + "crwdcntrl.net", + "lotame.com" + ] + } + }, + { + "LuckyOrange": { + "https://www.luckyorange.com": [ + "luckyorange.com", + "luckyorange.net" + ], + "session-replay": "true" + } + }, + { + "Lynchpin": { + "http://www.lynchpin.com/": [ + "lynchpin.com", + "lypn.com" + ] + } + }, + { + "Lyris": { + "http://www.lyris.com/": [ + "clicktracks.com", + "lyris.com" + ] + } + }, + { + "Lytiks": { + "http://www.lytiks.com/": [ + "lytiks.com" + ] + } + }, + { + "MarkMonitor": { + "https://www.markmonitor.com": [ + "9c9media.ca", + "markmonitor.com" + ] + } + }, + { + "Marktest": { + "http://www.marktest.com/": [ + "marktest.com", + "marktest.pt" + ] + } + }, + { + "MaxMind": { + "https://www.maxmind.com/en/home": [ + "maxmind.com", + "mmapiws.com" + ] + } + }, + { + "Médiamétrie-eStat": { + "http://www.mediametrie-estat.com/": [ + "estat.com", + "mediametrie-estat.com" + ] + } + }, + { + "Merkle": { + "https://www.merkleinc.com/": [ + "merkleinc.com", + "rkdms.com" + ] + } + }, + { + "Mixpanel": { + "https://mixpanel.com/": [ + "mixpanel.com", + "mxpnl.com" + ] + } + }, + { + "Mongoose Metrics": { + "http://www.mongoosemetrics.com/": [ + "mongoosemetrics.com" + ] + } + }, + { + "Monitus": { + "http://www.monitus.net/": [ + "monitus.net" + ] + } + }, + { + "motigo": { + "http://motigo.com/": [ + "motigo.com", + "nedstatbasic.net" + ] + } + }, + { + "Mouseflow": { + "http://mouseflow.com/": [ + "mouseflow.com" + ] + } + }, + { + "MyPagerank.Net": { + "http://www.mypagerank.net/": [ + "mypagerank.net" + ] + } + }, + { + "Mystighty": { + "http://mystighty.info/": [ + "mystighty.info", + "sweeterge.info" + ] + } + }, + { + "Narrative": { + "http://narrative.io/2/": [ + "narrative.io" + ] + } + }, + { + "Net Applications": { + "http://www.netapplications.com/": [ + "hitsprocessor.com", + "netapplications.com" + ] + } + }, + { + "New Relic": { + "http://newrelic.com/": [ + "newrelic.com", + "nr-data.net" + ] + } + }, + { + "NewsRight": { + "http://www.newsright.com/": [ + "apnewsregistry.com" + ] + } + }, + { + "NextSTAT": { + "http://www.nextstat.com/": [ + "nextstat.com" + ] + } + }, + { + "Nielsen": { + "http://www.nielsen.com/": [ + "glanceguide.com", + "nielsen.com" + ] + } + }, + { + "NuDataSecurity": { + "https://nudatasecurity.com/": [ + "nudatasecurity.com" + ] + } + }, + { + "nurago": { + "http://www.nurago.com/": [ + "nurago.com", + "nurago.de", + "sensic.net" + ] + } + }, + { + "Observer": { + "http://observerapp.com/": [ + "observerapp.com" + ] + } + }, + { + "OnAudience": { + "http://www.onaudience.com/": [ + "behavioralengine.com", + "onaudience.com" + ] + } + }, + { + "OneStat": { + "http://www.onestat.com/": [ + "onestat.com" + ] + } + }, + { + "Openstat": { + "https://www.openstat.ru/": [ + "openstat.ru", + "spylog.com" + ] + } + }, + { + "Opentracker": { + "http://www.opentracker.net/": [ + "opentracker.net" + ] + } + }, + { + "Opolen": { + "https://opolen.com.br": [ + "opolen.com.br" + ] + } + }, + { + "Optimizely": { + "https://www.optimizely.com/": [ + "optimizely.com" + ] + } + }, + { + "Oracle": { + "http://www.oracle.com/": [ + "eloqua.com", + "maxymiser.com" + ] + } + }, + { + "ÖWA": { + "http://www.oewa.at/": [ + "oewa.at", + "oewabox.at" + ] + } + }, + { + "Parse.ly": { + "http://parsely.com/": [ + "parsely.com" + ] + } + }, + { + "PersianStat.com": { + "http://www.persianstat.com/": [ + "persianstat.com" + ] + } + }, + { + "Phonalytics": { + "http://www.phonalytics.com/": [ + "phonalytics.com" + ] + } + }, + { + "phpMyVisites": { + "http://www.phpmyvisites.us/": [ + "phpmyvisites.us" + ] + } + }, + { + "Piwik": { + "http://piwik.org/": [ + "piwik.org" + ] + } + }, + { + "PixAnalytics": { + "https://pixanalytics.com/": [ + "pixanalytics.com" + ] + } + }, + { + "Poool": { + "http://poool.fr/": [ + "poool.fr" + ] + } + }, + { + "Pronunciator": { + "http://www.pronunciator.com/": [ + "pronunciator.com", + "visitorville.com" + ] + } + }, + { + "Qualaroo": { + "http://qualaroo.com/": [ + "kissinsights.com", + "qualaroo.com" + ] + } + }, + { + "QuinStreet": { + "http://quinstreet.com/": [ + "thecounter.com" + ] + } + }, + { + "Quintelligence": { + "http://www.quintelligence.com/": [ + "quintelligence.com" + ] + } + }, + { + "RadarURL": { + "http://radarurl.com/": [ + "radarurl.com" + ] + } + }, + { + "Research Now": { + "http://www.researchnow.com/": [ + "researchnow.com", + "valuedopinions.co.uk" + ] + } + }, + { + "Retail Automata": { + "https://retailautomata.com": [ + "retailautomata.com" + ] + } + }, + { + "Revtracks": { + "http://revtrax.com/": [ + "revtrax.com" + ] + } + }, + { + "Ringier": { + "http://ringier.cz/": [ + "ringier.cz" + ] + } + }, + { + "Rollick": { + "https://gorollick.com": [ + "rollick.io" + ] + } + }, + { + "Roxr": { + "http://roxr.net/": [ + "getclicky.com", + "roxr.net", + "staticstuff.net" + ] + } + }, + { + "Safecount": { + "http://www.safecount.net/": [ + "dl-rms.com", + "dlqm.net", + "questionmarket.com", + "safecount.net" + ] + } + }, + { + "SageMetrics": { + "http://www.sagemetrics.com/": [ + "sageanalyst.net", + "sagemetrics.com" + ] + } + }, + { + "Salesintelligence": { + "https://salesintelligence.pl/": [ + "plugin.management" + ] + } + }, + { + "SeeVolution": { + "https://www.seevolution.com/": [ + "seevolution.com", + "svlu.net" + ] + } + }, + { + "Segment.io": { + "https://segment.io/": [ + "segment.io" + ] + } + }, + { + "SendPulse": { + "https://sendpulse.com/": [ + "sendpulse.com" + ] + } + }, + { + "SessionCam": { + "https://sessioncam.com/": [ + "sessioncam.com" + ], + "session-replay": "true" + } + }, + { + "ShinyStat": { + "http://www.shinystat.com/": [ + "shinystat.com" + ] + } + }, + { + "Smartlook": { + "https://www.smartlook.com/": [ + "smartlook.com" + ], + "session-replay": "true" + } + }, + { + "Snoobi": { + "http://www.snoobi.com/": [ + "snoobi.com" + ] + } + }, + { + "Sourcepoint": { + "https://www.sourcepoint.com/": [ + "summerhamster.com" + ] + } + }, + { + "Sputnik.ru": { + "http://sputnik.ru": [ + "sputnik.ru" + ] + } + }, + { + "StackTrack": { + "http://stat-track.com": [ + "stat-track.com" + ] + } + }, + { + "stat4u": { + "http://stat.4u.pl/": [ + "4u.pl" + ] + } + }, + { + "StatCounter": { + "http://statcounter.com/": [ + "statcounter.com" + ] + } + }, + { + "Statisfy": { + "http://statisfy.net": [ + "statisfy.net" + ] + } + }, + { + "STATSIT": { + "http://www.statsit.com/": [ + "statsit.com" + ] + } + }, + { + "Storeland": { + "https://storeland.ru/": [ + "storeland.ru" + ] + } + }, + { + "Stratigent": { + "http://www.stratigent.com/": [ + "stratigent.com" + ] + } + }, + { + "Tealium": { + "https://tealium.com": [ + "tealiumiq.com" + ] + } + }, + { + "TechSolutions": { + "https://www.techsolutions.com.tw/": [ + "techsolutions.com.tw" + ] + } + }, + { + "TENSQUARE": { + "http://www.tensquare.com/": [ + "tensquare.com" + ] + } + }, + { + "The Heron Partnership": { + "http://www.heronpartners.com.au/": [ + "heronpartners.com.au", + "marinsm.com" + ] + } + }, + { + "TNS": { + "http://www.tnsglobal.com/": [ + "sesamestats.com", + "statistik-gallup.net", + "tns-counter.ru", + "tns-cs.net", + "tnsglobal.com" + ] + } + }, + { + "TrackingSoft": { + "http://trackingsoft.com/": [ + "roia.biz", + "trackingsoft.com" + ] + } + }, + { + "TrafficScore": { + "https://trafficscore.com/": [ + "trafficscore.com" + ] + } + }, + { + "Twitter": { + "https://twitter.com/": [ + "crashlytics.com", + "tweetdeck.com" + ] + } + }, + { + "Umbel": { + "https://www.umbel.com/": [ + "umbel.com" + ] + } + }, + { + "User Local": { + "http://nakanohito.jp/": [ + "nakanohito.jp" + ] + } + }, + { + "V12 Data": { + "https://www.v12data.com/": [ + "v12data.com", + "v12group.com" + ] + } + }, + { + "Vertster": { + "http://www.vertster.com/": [ + "vertster.com" + ] + } + }, + { + "VisiStat": { + "http://www.visistat.com/": [ + "sa-as.com", + "visistat.com" + ] + } + }, + { + "Visit Streamer": { + "http://www.visitstreamer.com/": [ + "visitstreamer.com" + ] + } + }, + { + "vistrac": { + "http://vistrac.com/": [ + "vistrac.com" + ] + } + }, + { + "ViziSense": { + "http://www.vizisense.com/": [ + "vizisense.com", + "vizisense.net" + ] + } + }, + { + "Webclicktracker": { + "http://www.webclicktracker.com/": [ + "webclicktracker.com" + ] + } + }, + { + "Web Stats": { + "http://www.onlinewebstats.com/": [ + "onlinewebstats.com" + ] + } + }, + { + "Web Tracking Services": { + "http://www.webtrackingservices.com/": [ + "web-stat.com", + "webtrackingservices.com" + ] + } + }, + { + "Web Traxs": { + "http://www.webtraxs.com/": [ + "webtraxs.com" + ] + } + }, + { + "Webtrekk": { + "http://www.webtrekk.com/": [ + "webtrekk.com", + "webtrekk.net" + ] + } + }, + { + "Webtrends": { + "http://webtrends.com/": [ + "reinvigorate.net", + "webtrends.com", + "webtrendslive.com" + ] + } + }, + { + "White Ops": { + "https://www.whiteops.com/": [ + "adzmath.com", + "whiteops.com" + ] + } + }, + { + "whos.amung.us": { + "http://whos.amung.us/": [ + "amung.us" + ] + } + }, + { + "Wingify": { + "http://wingify.com/": [ + "visualwebsiteoptimizer.com", + "wingify.com" + ] + } + }, + { + "Woopra": { + "http://www.woopra.com/": [ + "woopra-ns.com", + "woopra.com" + ] + } + }, + { + "WOW Analytics": { + "http://www.wowanalytics.co.uk/": [ + "wowanalytics.co.uk" + ] + } + }, + { + "WPP": { + "http://www.wpp.com/": [ + "compete.com" + ] + } + }, + { + "Wysistat": { + "http://www.wysistat.com/": [ + "wysistat.com" + ] + } + }, + { + "Yahoo!": { + "http://www.yahoo.com/": [ + "analytics.yahoo.com" + ] + } + }, + { + "YellowTracker": { + "http://www.yellowtracker.com/": [ + "yellowtracker.com" + ] + } + }, + { + "YSance": { + "https://www.ysance.com/data-services/fr/home/": [ + "y-track.com" + ] + } + } + ], + "Fingerprinting": [ + { + "Adabra": { + "https://www.adabra.com/": [ + "adabra.com" + ] + } + }, + { + "Adbot": { + "https://adbot.tw/": [ + "adbot.tw" + ] + } + }, + { + "AdGainerSolutions": { + "http://adgainersolutions.com/adgainer/": [ + "adgainersolutions.com" + ] + } + }, + { + "AdMaven": { + "https://ad-maven.com/": [ + "ad-maven.com", + "agreensdistra.info", + "boudja.com", + "rensovetors.info", + "wrethicap.info" + ] + } + }, + { + "Admicro": { + "http://www.admicro.vn/": [ + "admicro.vn", + "vcmedia.vn" + ] + } + }, + { + "Adnium": { + "https://adnium.com": [ + "adnium.com", + "montwam.top" + ] + } + }, + { + "AdScore": { + "http://www.adscoremarketing.com/": [ + "adsco.re" + ] + } + }, + { + "AdYouLike": { + "https://www.adyoulike.com/": [ + "pulpix.com" + ] + } + }, + { + "AivaLabs": { + "https://aivalabs.com": [ + "aivalabs.com" + ] + } + }, + { + "Albacross": { + "https://albacross.com": [ + "albacross.com" + ] + } + }, + { + "AppCast": { + "https://appcast.io/": [ + "appcast.io" + ] + } + }, + { + "AuditedMedia": { + "https://auditedmedia.com/": [ + "aamapi.com", + "aamsitecertifier.com", + "auditedmedia.com" + ] + } + }, + { + "Augur": { + "http://www.augur.io/": [ + "augur.io" + ] + } + }, + { + "Azet": { + "http://mediaimpact.sk/": [ + "azetklik.sk", + "rsz.sk" + ] + } + }, + { + "BetssonPalantir": { + "https://betssonpalantir.com/": [ + "betssonpalantir.com" + ] + } + }, + { + "BigClick": { + "http://bigclick.me/": [ + "bgclck.me", + "xcvgdf.party" + ] + } + }, + { + "BitMedia": { + "https://bitmedia.io/": [ + "bitmedia.io" + ] + } + }, + { + "BlueCava": { + "http://www.bluecava.com/": [ + "bluecava.com" + ] + } + }, + { + "BoostBox": { + "https://www.boostbox.com.br/": [ + "boostbox.com.br" + ] + } + }, + { + "Brandcrumb": { + "http://www.brandcrumb.com": [ + "brandcrumb.com" + ] + } + }, + { + "BreakTime": { + "https://www.breaktime.com.tw/": [ + "breaktime.com.tw" + ] + } + }, + { + "BrightEdge": { + "http://www.brightedge.com/": [ + "b0e8.com" + ] + } + }, + { + "C3 Metrics": { + "http://c3metrics.com/": [ + "attributionmodel.com", + "c3metrics.com", + "c3tag.com" + ] + } + }, + { + "CallSource": { + "https://www.callsource.com/": [ + "leadtrackingdata.com" + ] + } + }, + { + "CartsGuru": { + "https://carts.guru/": [ + "carts.guru" + ] + } + }, + { + "ClearLink": { + "https://www.clearlink.com/": [ + "clearlink.com" + ] + } + }, + { + "Clickayab": { + "http://www.clickyab.com": [ + "clickyab.com" + ] + } + }, + { + "ClickFrog": { + "https://clickfrog.ru/": [ + "bashirian.biz", + "buckridge.link", + "franecki.net", + "quitzon.net", + "reichelcormier.bid", + "wisokykulas.bid" + ] + } + }, + { + "ClickGuard": { + "https://www.clickguard.com/": [ + "clickguard.com" + ] + } + }, + { + "Clixtell": { + "https://www.clixtell.com/": [ + "clixtell.com" + ] + } + }, + { + "Consumable": { + "http://consumable.com/": [ + "consumable.com" + ] + } + }, + { + "dmpxs": { + "http://bob.dmpxs.com": [ + "dmpxs.com" + ] + } + }, + { + "ECSAnalytics": { + "https://www.theecsinc.com/": [ + "ecsanalytics.com" + ] + } + }, + { + "EroAdvertising": { + "http://www.ero-advertising.com/": [ + "ero-advertising.com" + ] + } + }, + { + "eyeReturn Marketing": { + "http://www.eyereturnmarketing.com/": [ + "eyereturn.com", + "eyereturnmarketing.com" + ] + } + }, + { + "Fanplayr": { + "https://fanplayr.com/": [ + "fanplayr.com" + ] + } + }, + { + "Foresee": { + "https://www.foresee.com": [ + "answerscloud.com", + "foresee.com" + ] + } + }, + { + "Friends2Follow": { + "https://friends2follow.com/": [ + "antifraudjs.friends2follow.com" + ] + } + }, + { + "FuelX": { + "https://fuelx.com/": [ + "fuel451.com", + "fuelx.com" + ] + } + }, + { + "Gleam": { + "https://gleam.io/": [ + "fraudjs.io" + ] + } + }, + { + "GrapheneMedia": { + "http://graphenemedia.in/": [ + "graphenedigitalanalytics.in" + ] + } + }, + { + "Gruner + Jahr": { + "http://www.guj.de/": [ + "ligatus.com" + ] + } + }, + { + "HilltopAds": { + "https://hilltopads.com/": [ + "hilltopads.net", + "shoporielder.pro" + ] + } + }, + { + "HotelChamp": { + "https://www.hotelchamp.com": [ + "hotelchamp.com" + ] + } + }, + { + "iMedia": { + "http://www.imedia.cz": [ + "imedia.cz" + ] + } + }, + { + "IslayTech": { + "http://islay.tech": [ + "islay.tech" + ] + } + }, + { + "ismatlab.com": { + "http://ismatlab.com": [ + "ismatlab.com" + ] + } + }, + { + "Itch": { + "https://itch.io/": [ + "itch.io" + ] + } + }, + { + "justuno": { + "https://www.justuno.com/": [ + "justuno.com" + ] + } + }, + { + "Konduto": { + "http://konduto.com": [ + "k-analytix.com", + "konduto.com" + ] + } + }, + { + "LeadsHub": { + "https://ztsrv.com/": [ + "ztsrv.com" + ] + } + }, + { + "lptracker": { + "https://lptracker.io/": [ + "lptracker.io" + ] + } + }, + { + "MaxMind": { + "https://www.maxmind.com/en/home": [ + "maxmind.com", + "mmapiws.com" + ] + } + }, + { + "Mercadopago": { + "https://www.mercadopago.com/": [ + "mercadopago.com" + ] + } + }, + { + "Mobials": { + "http://mobials.com": [ + "mobials.com" + ] + } + }, + { + "Mystighty": { + "http://mystighty.info/": [ + "mystighty.info", + "sweeterge.info" + ] + } + }, + { + "Negishim": { + "http://www.negishim.org": [ + "negishim.org" + ] + } + }, + { + "NuDataSecurity": { + "https://nudatasecurity.com/": [ + "nudatasecurity.com" + ] + } + }, + { + "OneAd": { + "https://www.onead.com.tw/": [ + "guoshipartners.com", + "onevision.com.tw" + ] + } + }, + { + "OnlineMetrix": { + "http://h.online-metrix.net": [ + "online-metrix.net" + ] + } + }, + { + "Opolen": { + "https://opolen.com.br": [ + "opolen.com.br" + ] + } + }, + { + "PaymentsMB": { + "https://paymentsmb.com": [ + "paymentsmb.com" + ] + } + }, + { + "Paypal": { + "https://www.paypal.com": [ + "simility.com" + ] + } + }, + { + "PerimeterX": { + "https://www.perimeterx.com": [ + "perimeterx.net" + ] + } + }, + { + "PixAnalytics": { + "https://pixanalytics.com/": [ + "pixanalytics.com" + ] + } + }, + { + "Pixlee": { + "https://www.pixlee.com/": [ + "pixlee.com" + ] + } + }, + { + "Poool": { + "http://poool.fr/": [ + "poool.fr" + ] + } + }, + { + "PPCProtect": { + "https://ppcprotect.com": [ + "ppcprotect.com" + ] + } + }, + { + "PrismApp": { + "https://www.prismapp.io/": [ + "prismapp.io" + ] + } + }, + { + "PrometheusIntelligenceTechnology": { + "https://prometheusintelligencetechnology.com/": [ + "prometheusintelligencetechnology.com" + ] + } + }, + { + "Provers": { + "http://provers.pro": [ + "provers.pro" + ] + } + }, + { + "Psonstrentie": { + "http://psonstrentie.info": [ + "psonstrentie.info" + ] + } + }, + { + "Rollick": { + "https://gorollick.com": [ + "rollick.io" + ] + } + }, + { + "SAP": { + "https://www.sap.com": [ + "seewhy.com" + ] + } + }, + { + "Selectable Media": { + "http://selectablemedia.com/": [ + "nabbr.com", + "selectablemedia.com" + ] + } + }, + { + "Semantiqo": { + "http://semantiqo.com/": [ + "semantiqo.com" + ] + } + }, + { + "SendPulse": { + "https://sendpulse.com/": [ + "sendpulse.com" + ] + } + }, + { + "ShaftTraffic": { + "https://shafttraffic.com": [ + "libertystmedia.com" + ] + } + }, + { + "Shortest": { + "http://shorte.st/": [ + "shorte.st" + ] + } + }, + { + "SiftScience": { + "https://sift.com/": [ + "siftscience.com" + ] + } + }, + { + "Signifyd": { + "https://www.signifyd.com/": [ + "signifyd.com" + ] + } + }, + { + "Smi": { + "http://24smi.net": [ + "24smi.net" + ] + } + }, + { + "Socital": { + "https://www.socital.com": [ + "socital.com" + ] + } + }, + { + "Storeland": { + "https://storeland.ru/": [ + "storeland.ru" + ] + } + }, + { + "Stripe": { + "https://stripe.com": [ + "stripe.network" + ] + } + }, + { + "TechSolutions": { + "https://www.techsolutions.com.tw/": [ + "techsolutions.com.tw" + ] + } + }, + { + "tongdun.cn": { + "https://www.tongdun.cn/?lan=EN": [ + "fraudmetrix.cn", + "tongdun.net" + ] + } + }, + { + "Upland": { + "https://uplandsoftware.com/": [ + "leadlander.com", + "sf14g.com" + ] + } + }, + { + "Vendemore": { + "https://vendemore.com/": [ + "vendemore.com" + ] + } + }, + { + "VerticalHealth": { + "https://www.verticalhealth.com/": [ + "verticalhealth.net" + ] + } + }, + { + "Webmecanik": { + "https://www.webmecanik.com/": [ + "webmecanik.com" + ] + } + }, + { + "WideOrbit": { + "https://www.wideorbit.com/": [ + "dep-x.com" + ] + } + }, + { + "YSance": { + "https://www.ysance.com/data-services/fr/home/": [ + "y-track.com" + ] + } + }, + { + "ZafulAffiliate": { + "https://affiliate.zaful.com/": [ + "affasi.com", + "gw-ec.com", + "zaful.com" + ] + } + }, + { + "Zefir": { + "https://ze-fir.com/": [ + "ze-fir.com" + ] + } + } + ], + "Social": [ + { + "AddThis": { + "http://www.addthis.com/": [ + "addthis.com", + "addthiscdn.com", + "addthisedge.com", + "clearspring.com", + "connectedads.net", + "xgraph.com", + "xgraph.net" + ] + } + }, + { + "Causes": { + "http://www.causes.com/": [ + "causes.com" + ] + } + }, + { + "Digg": { + "http://digg.com/": [ + "digg.com" + ] + } + }, + { + "Facebook": { + "http://www.facebook.com/": [ + "apps.fbsbx.com", + "atdmt.com", + "facebook.com", + "facebook.de", + "facebook.fr", + "facebook.net", + "fb.com", + "fbsbx.com", + "friendfeed.com" + ] + } + }, + { + "Google": { + "http://www.google.com/": [ + "developers.google.com", + "gmail.com", + "googlemail.com", + "inbox.google.com", + "mail.google.com", + "orkut.com", + "plus.google.com", + "plusone.google.com", + "smartlock.google.com", + "voice.google.com", + "wave.google.com" + ] + } + }, + { + "LinkedIn": { + "http://www.linkedin.com/": [ + "licdn.com", + "linkedin.com" + ] + } + }, + { + "Lockerz": { + "http://lockerz.com/": [ + "lockerz.com" + ] + } + }, + { + "Mail.Ru": { + "http://mail.ru/": [ + "list.ru", + "mail.ru" + ] + } + }, + { + "Meebo": { + "https://www.meebo.com/": [ + "meebo.com", + "meebocdn.net" + ] + } + }, + { + "Papaya": { + "http://papayamobile.com/": [ + "papayamobile.com" + ] + } + }, + { + "reddit": { + "http://www.reddit.com/": [ + "reddit.com" + ] + } + }, + { + "Shareaholic": { + "http://www.shareaholic.com/": [ + "shareaholic.com" + ] + } + }, + { + "ShareThis": { + "http://sharethis.com/": [ + "sharethis.com" + ] + } + }, + { + "StumbleUpon": { + "http://www.stumbleupon.com/": [ + "stumble-upon.com", + "stumbleupon.com" + ] + } + }, + { + "Twitter": { + "https://twitter.com/": [ + "twimg.com", + "twitter.com", + "twitter.jp" + ] + } + }, + { + "VKontakte": { + "http://vk.com/": [ + "userapi.com", + "vk.com", + "vkontakte.ru" + ] + } + }, + { + "Yahoo!": { + "http://www.yahoo.com/": [ + "address.yahoo.com", + "alerts.yahoo.com", + "avatars.yahoo.com", + "buzz.yahoo.com", + "calendar.yahoo.com", + "edit.yahoo.com", + "legalredirect.yahoo.com", + "login.yahoo.com", + "mail.yahoo.com", + "my.yahoo.com", + "mybloglog.com", + "notepad.yahoo.com", + "pulse.yahoo.com", + "rocketmail.com", + "webmessenger.yahoo.com", + "ymail.com" + ] + } + } + ], + "Cryptomining": [ + { + "a.js": { + "http://zymerget.bid": [ + "alflying.date", + "alflying.win", + "anybest.site", + "flightsy.bid", + "flightsy.win", + "flightzy.bid", + "flightzy.date", + "flightzy.win", + "zymerget.bid", + "zymerget.faith" + ], + "performance": "true" + } + }, + { + "CashBeet": { + "http://cashbeet.com": [ + "cashbeet.com", + "serv1swork.com" + ] + } + }, + { + "CoinHive": { + "https://coinhive.com": [ + "ad-miner.com", + "authedmine.com", + "bmst.pw", + "cnhv.co", + "coin-hive.com", + "coinhive.com", + "wsservices.org" + ], + "performance": "true" + } + }, + { + "CoinPot": { + "http://coinpot.co": [ + "coinpot.co" + ], + "performance": "true" + } + }, + { + "CryptoLoot": { + "https://crypto-loot.com": [ + "cryptaloot.pro", + "crypto-loot.com", + "cryptolootminer.com", + "flashx.pw", + "gitgrub.pro", + "reauthenticator.com", + "statdynamic.com", + "webmine.pro" + ], + "performance": "true" + } + }, + { + "CryptoWebMiner": { + "https://www.crypto-webminer.com": [ + "bitcoin-pay.eu", + "crypto-webminer.com", + "ethpocket.de", + "ethtrader.de" + ] + } + }, + { + "Gridcash": { + "https://www.gridcash.net/": [ + "adless.io", + "gridcash.net" + ], + "performance": "true" + } + }, + { + "JSE": { + "http://jsecoin.com": [ + "freecontent.bid", + "freecontent.date", + "freecontent.stream", + "hashing.win", + "hostingcloud.racing", + "hostingcloud.science", + "jsecoin.com" + ], + "performance": "true" + } + }, + { + "MinerAlt": { + "http://mineralt.io": [ + "1q2w3.website", + "analytics.blue", + "aster18cdn.nl", + "belicimo.pw", + "besstahete.info", + "dinorslick.icu", + "feesocrald.com", + "gramombird.com", + "istlandoll.com", + "mepirtedic.com", + "mineralt.io", + "pampopholf.com", + "tercabilis.info", + "tulip18.com", + "vidzi.tv", + "yololike.space" + ], + "performance": "true" + } + }, + { + "Minescripts": { + "http://minescripts.info": [ + "minescripts.info", + "sslverify.info" + ], + "performance": "true" + } + }, + { + "MineXMR": { + "http://minexmr.stream": [ + "minexmr.stream" + ], + "performance": "true" + } + }, + { + "NeroHut": { + "https://nerohut.com": [ + "nerohut.com", + "nhsrv.cf" + ], + "performance": "true" + } + }, + { + "Service4refresh": { + "https://service4refresh.info": [ + "service4refresh.info" + ] + } + }, + { + "SpareChange": { + "http://sparechange.io": [ + "sparechange.io" + ], + "performance": "true" + } + }, + { + "SwiftMining": { + "https://swiftmining.win/": [ + "swiftmining.win" + ] + } + }, + { + "Webmine": { + "https://webmine.cz/": [ + "authedwebmine.cz", + "webmine.cz" + ] + } + }, + { + "WebminePool": { + "http://webminepool.com": [ + "webminepool.com" + ], + "performance": "true" + } + }, + { + "Webmining": { + "https://webmining.co/": [ + "webmining.co" + ] + } + } + ] + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/raw/domain_safelist.json b/mobile/android/android-components/components/browser/engine-system/src/main/res/raw/domain_safelist.json new file mode 100644 index 0000000000..a2c80176b6 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/raw/domain_safelist.json @@ -0,0 +1,12347 @@ +{ + "2leep.com": { + "properties": [ + "2leep.com" + ], + "resources": [ + "2leep.com" + ] + }, + "33Across": { + "properties": [ + "33across.com", + "tynt.com" + ], + "resources": [ + "33across.com", + "tynt.com" + ] + }, + "365Media": { + "properties": [ + "aggregateintelligence.com" + ], + "resources": [ + "365media.com", + "aggregateintelligence.com" + ] + }, + "4INFO": { + "properties": [ + "4info.com", + "adhaven.com" + ], + "resources": [ + "4info.com", + "adhaven.com" + ] + }, + "4mads": { + "properties": [ + "4mads.com" + ], + "resources": [ + "4mads.com" + ] + }, + "63 Squares": { + "properties": [ + "63labs.com" + ], + "resources": [ + "63labs.com", + "63squares.com", + "i-stats.com" + ] + }, + "Abax Interactive": { + "properties": [ + "abaxinteractive.com" + ], + "resources": [ + "abaxinteractive.com" + ] + }, + "Accelia": { + "properties": [ + "accelia.net", + "durasite.net" + ], + "resources": [ + "accelia.net", + "durasite.net" + ] + }, + "Accordant Media": { + "properties": [ + "accordantmedia.com" + ], + "resources": [ + "accordantmedia.com" + ] + }, + "Acquisio": { + "properties": [ + "acquisio.com", + "clickequations.net" + ], + "resources": [ + "acquisio.com", + "clickequations.net" + ] + }, + "Actisens": { + "properties": [ + "actisens.com", + "gestionpub.com" + ], + "resources": [ + "actisens.com", + "gestionpub.com" + ] + }, + "ActiveConversion": { + "properties": [ + "activeconversion.com", + "activemeter.com" + ], + "resources": [ + "activeconversion.com", + "activemeter.com" + ] + }, + "ActivEngage": { + "properties": [ + "activengage.com" + ], + "resources": [ + "activengage.com" + ] + }, + "Act-On": { + "properties": [ + "act-on.com", + "actonsoftware.com" + ], + "resources": [ + "act-on.com", + "actonsoftware.com" + ] + }, + "Acuity": { + "properties": [ + "acuity.com", + "acuityads.com", + "acuityplatform.com" + ], + "resources": [ + "acuity.com", + "acuityads.com", + "acuityplatform.com" + ] + }, + "Acxiom": { + "properties": [ + "acxiom.com", + "mm7.net" + ], + "resources": [ + "acxiom.com", + "acxiomapac.com", + "mm7.net", + "pippio.com" + ] + }, + "AD2ONE": { + "properties": [ + "ad2onegroup.com" + ], + "resources": [ + "ad2onegroup.com" + ] + }, + "Ad4Game": { + "properties": [ + "ad4game.com" + ], + "resources": [ + "ad4game.com" + ] + }, + "ad6media": { + "properties": [ + "ad6media.fr" + ], + "resources": [ + "ad6media.fr" + ] + }, + "Adabra": { + "properties": [ + "adabra.com" + ], + "resources": [ + "adabra.com" + ] + }, + "Adality": { + "properties": [ + "adality.de" + ], + "resources": [ + "adality.de", + "adrtx.net" + ] + }, + "AdaptiveAds": { + "properties": [ + "adaptiveads.com" + ], + "resources": [ + "adaptiveads.com" + ] + }, + "Adaptly": { + "properties": [ + "adaptly.com" + ], + "resources": [ + "adaptly.com" + ] + }, + "Adap.tv": { + "properties": [ + "adap.tv" + ], + "resources": [ + "adap.tv" + ] + }, + "Adara Media": { + "properties": [ + "adaramedia.com", + "opinmind.com", + "yieldoptimizer.com" + ], + "resources": [ + "adaramedia.com", + "opinmind.com", + "yieldoptimizer.com" + ] + }, + "Adatus": { + "properties": [ + "adatus.com" + ], + "resources": [ + "adatus.com" + ] + }, + "Adbot": { + "properties": [ + "adbot.tw" + ], + "resources": [ + "adbot.tw" + ] + }, + "Adbrain": { + "properties": [ + "adbrain.com" + ], + "resources": [ + "adbrain.com", + "adbrn.com" + ] + }, + "adBrite": { + "properties": [ + "adbrite.com" + ], + "resources": [ + "adbrite.com" + ] + }, + "Adbroker.de": { + "properties": [ + "adbroker.de" + ], + "resources": [ + "adbroker.de" + ] + }, + "Adchemy": { + "properties": [ + "adchemy.com" + ], + "resources": [ + "adchemy.com" + ] + }, + "AdCirrus": { + "properties": [ + "adcirrus.com" + ], + "resources": [ + "adcirrus.com" + ] + }, + "Ad Decisive": { + "properties": [ + "a2dfp.net", + "addecisive.com" + ], + "resources": [ + "a2dfp.net", + "addecisive.com" + ] + }, + "AddFreeStats": { + "properties": [ + "3dstats.com", + "addfreestats.com" + ], + "resources": [ + "3dstats.com", + "addfreestats.com" + ] + }, + "addGloo": { + "properties": [ + "addgloo.com" + ], + "resources": [ + "addgloo.com" + ] + }, + "AddThis": { + "properties": [ + "addthis.com" + ], + "resources": [ + "addthis.com", + "addthiscdn.com", + "addthisedge.com", + "clearspring.com", + "connectedads.net", + "xgraph.com", + "xgraph.net" + ] + }, + "Addvantage Media": { + "properties": [ + "addvantagemedia.com" + ], + "resources": [ + "addvantagemedia.com" + ] + }, + "Ad Dynamo": { + "properties": [ + "addynamo.com" + ], + "resources": [ + "addynamo.com", + "addynamo.net" + ] + }, + "Adelphic": { + "properties": [ + "adelphic.com" + ], + "resources": [ + "adelphic.com", + "ipredictive.com" + ] + }, + "AdEngage": { + "properties": [ + "adengage.com" + ], + "resources": [ + "adengage.com" + ] + }, + "AD Europe": { + "properties": [ + "adeurope.com" + ], + "resources": [ + "adeurope.com" + ] + }, + "AdExtent": { + "properties": [ + "adextent.com" + ], + "resources": [ + "adextent.com" + ] + }, + "AdF.ly": { + "properties": [ + "adf.ly" + ], + "resources": [ + "adf.ly" + ] + }, + "Adfonic": { + "properties": [ + "adfonic.com" + ], + "resources": [ + "adfonic.com" + ] + }, + "Adforge": { + "properties": [ + "adforgeinc.com" + ], + "resources": [ + "adforgeinc.com" + ] + }, + "Adform": { + "properties": [ + "adform.com" + ], + "resources": [ + "adform.com", + "adform.net", + "adformdsp.net" + ] + }, + "AdFox": { + "properties": [ + "adfox.ru" + ], + "resources": [ + "adfox.ru" + ] + }, + "AdFrontiers": { + "properties": [ + "adfrontiers.com" + ], + "resources": [ + "adfrontiers.com" + ] + }, + "Adfunky": { + "properties": [ + "adfunky.com", + "adfunkyserver.com" + ], + "resources": [ + "adfunky.com", + "adfunkyserver.com" + ] + }, + "Adfusion": { + "properties": [ + "adfusion.com" + ], + "resources": [ + "adfusion.com" + ] + }, + "AdGainerSolutions": { + "properties": [ + "adgainersolutions.com" + ], + "resources": [ + "adgainersolutions.com" + ] + }, + "AdGent Digital": { + "properties": [ + "adgentdigital.com" + ], + "resources": [ + "adgentdigital.com", + "shorttailmedia.com" + ] + }, + "AdGibbon": { + "properties": [ + "adgibbon.com" + ], + "resources": [ + "adgibbon.com" + ] + }, + "Adglare": { + "properties": [ + "adglare.com" + ], + "resources": [ + "adglare.com", + "adglare.net" + ] + }, + "adhood": { + "properties": [ + "adhood.com" + ], + "resources": [ + "adhood.com" + ] + }, + "Adiant": { + "properties": [ + "adblade.com", + "adiant.com" + ], + "resources": [ + "adblade.com", + "adiant.com" + ] + }, + "AdInsight": { + "properties": [ + "responsetap.com" + ], + "resources": [ + "adinsight.com", + "adinsight.eu", + "responsetap.com" + ] + }, + "AdIQuity": { + "properties": [ + "adiquity.com" + ], + "resources": [ + "adiquity.com" + ] + }, + "ADITION": { + "properties": [ + "adition.com" + ], + "resources": [ + "adition.com" + ] + }, + "AdJug": { + "properties": [ + "adjug.com" + ], + "resources": [ + "adjug.com" + ] + }, + "AdJuggler": { + "properties": [ + "adjuggler.com", + "adjuggler.net" + ], + "resources": [ + "adjuggler.com", + "adjuggler.net" + ] + }, + "Adjust": { + "properties": [ + "adjust.com" + ], + "resources": [ + "adjust.com" + ] + }, + "AdKeeper": { + "properties": [ + "keep.com" + ], + "resources": [ + "adkeeper.com", + "akncdn.com", + "keep.com" + ] + }, + "AdKernel": { + "properties": [ + "adkernel.com" + ], + "resources": [ + "adkernel.com" + ] + }, + "Ad Knife": { + "properties": [ + "adknife.com" + ], + "resources": [ + "adknife.com" + ] + }, + "Adknowledge": { + "properties": [ + "adknowledge.com", + "adparlor.com", + "bidsystem.com", + "cubics.com", + "lookery.com" + ], + "resources": [ + "adknowledge.com", + "adparlor.com", + "bidsystem.com", + "cubics.com", + "lookery.com" + ] + }, + "AdLantis": { + "properties": [ + "adimg.net", + "adlantis.jp", + "www.adlantis.jp" + ], + "resources": [ + "adimg.net", + "adlantis.jp", + "www.adlantis.jp" + ] + }, + "AdLeave": { + "properties": [ + "adleave.com" + ], + "resources": [ + "adleave.com" + ] + }, + "Adlibrium": { + "properties": [ + "adlibrium.com" + ], + "resources": [ + "adlibrium.com" + ] + }, + "Adloox": { + "properties": [ + "adloox.com" + ], + "resources": [ + "adloox.com", + "adlooxtracking.com" + ] + }, + "Adlucent": { + "properties": [ + "adlucent.com" + ], + "resources": [ + "adlucent.com" + ] + }, + "Ad Magnet": { + "properties": [ + "admagnet.com", + "admagnet.net" + ], + "resources": [ + "admagnet.com", + "admagnet.net" + ] + }, + "Admarketplace": { + "properties": [ + "admarketplace.com" + ], + "resources": [ + "admarketplace.com", + "admarketplace.net", + "ampxchange.com" + ] + }, + "AdMarvel": { + "properties": [ + "admarvel.com" + ], + "resources": [ + "admarvel.com" + ] + }, + "AdMatrix": { + "properties": [ + "admatrix.jp" + ], + "resources": [ + "admatrix.jp" + ] + }, + "AdMaven": { + "properties": [ + "ad-maven.com" + ], + "resources": [ + "ad-maven.com", + "agreensdistra.info", + "boudja.com", + "rensovetors.info", + "wrethicap.info" + ] + }, + "AdMaximizer Network": { + "properties": [ + "admaximizer.com" + ], + "resources": [ + "admaximizer.com" + ] + }, + "AdMedia": { + "properties": [ + "admedia.com" + ], + "resources": [ + "admedia.com" + ] + }, + "Admeta": { + "properties": [ + "admeta.com", + "atemda.com" + ], + "resources": [ + "admeta.com", + "atemda.com" + ] + }, + "Admicro": { + "properties": [ + "admicro.vn" + ], + "resources": [ + "admicro.vn", + "vcmedia.vn" + ] + }, + "Admixer": { + "properties": [ + "admixer.co.kr" + ], + "resources": [ + "admixer.co.kr" + ] + }, + "Admized": { + "properties": [ + "admized.com" + ], + "resources": [ + "admized.com" + ] + }, + "Admobile": { + "properties": [ + "admobile.com" + ], + "resources": [ + "admobile.com" + ] + }, + "Admotion": { + "properties": [ + "admotion.com" + ], + "resources": [ + "admotion.com", + "nspmotion.com" + ] + }, + "Adnetik": { + "properties": [ + "wtp101.com" + ], + "resources": [ + "adnetik.com", + "wtp101.com" + ] + }, + "AdNetwork.net": { + "properties": [ + "adnetwork.net" + ], + "resources": [ + "adnetwork.net" + ] + }, + "Adnium": { + "properties": [ + "adnium.com" + ], + "resources": [ + "adnium.com", + "montwam.top" + ] + }, + "adnologies": { + "properties": [ + "adnologies.com", + "heias.com" + ], + "resources": [ + "adnologies.com", + "heias.com" + ] + }, + "Adobe": { + "properties": [ + "adobe.com", + "livefyre.com", + "typekit.com" + ], + "resources": [ + "2o7.net", + "adobe.com", + "auditude.com", + "demdex.com", + "demdex.net", + "dmtracker.com", + "efrontier.com", + "everestads.net", + "everestjs.net", + "everesttech.net", + "fyre.co", + "hitbox.com", + "livefyre.com", + "omniture.com", + "omtrdc.net", + "touchclarity.com", + "typekit.com" + ] + }, + "AdOcean": { + "properties": [ + "adocean-global.com", + "adocean.pl" + ], + "resources": [ + "adocean-global.com", + "adocean.pl" + ] + }, + "Adometry": { + "properties": [ + "adometry.com" + ], + "resources": [ + "adometry.com", + "dmtry.com" + ] + }, + "Adomik": { + "properties": [ + "adomik.com" + ], + "resources": [ + "adomik.com" + ] + }, + "AdOnion": { + "properties": [ + "adonion.com" + ], + "resources": [ + "adonion.com" + ] + }, + "Adorika": { + "properties": [ + "clickotmedia.com" + ], + "resources": [ + "clickotmedia.com" + ] + }, + "Adotmob": { + "properties": [ + "adotmob.com" + ], + "resources": [ + "adotmob.com" + ] + }, + "ADP Dealer Services": { + "properties": [ + "cdkglobal.com" + ], + "resources": [ + "admission.net", + "adpdealerservices.com", + "cdkglobal.com", + "cobalt.com" + ] + }, + "ad pepper media": { + "properties": [ + "adpepper.com", + "adpepper.us" + ], + "resources": [ + "adpepper.com", + "adpepper.us" + ] + }, + "AdPerfect": { + "properties": [ + "adperfect.com" + ], + "resources": [ + "adperfect.com" + ] + }, + "Adperium": { + "properties": [ + "adperium.com" + ], + "resources": [ + "adperium.com" + ] + }, + "Adpersia": { + "properties": [ + "adpersia.com" + ], + "resources": [ + "adpersia.com" + ] + }, + "adPrecision": { + "properties": [ + "adprecision.net", + "adprs.net" + ], + "resources": [ + "adprecision.net", + "adprs.net", + "aprecision.net" + ] + }, + "AdPredictive": { + "properties": [ + "adpredictive.com" + ], + "resources": [ + "adpredictive.com" + ] + }, + "AdReactor": { + "properties": [ + "adreactor.com" + ], + "resources": [ + "adreactor.com" + ] + }, + "AdReady": { + "properties": [ + "digitalremedy.com" + ], + "resources": [ + "adready.com", + "adreadytractions.com", + "digitalremedy" + ] + }, + "AdRevolution": { + "properties": [ + "adrevolution.com" + ], + "resources": [ + "adrevolution.com" + ] + }, + "AdRiver": { + "properties": [ + "adriver.ru" + ], + "resources": [ + "adriver.ru" + ] + }, + "adrolays": { + "properties": [ + "contactimpact.de" + ], + "resources": [ + "adrolays.com", + "adrolays.de", + "contactimpact.de" + ] + }, + "AdRoll": { + "properties": [ + "adroll.com" + ], + "resources": [ + "adroll.com" + ] + }, + "adscale": { + "properties": [ + "stroeer.de" + ], + "resources": [ + "adscale.de", + "stroeer.de" + ] + }, + "Adscience": { + "properties": [ + "adscience.nl" + ], + "resources": [ + "adscience.nl" + ] + }, + "AdScore": { + "properties": [ + "adscoremarketing.com" + ], + "resources": [ + "adsco.re" + ] + }, + "AdServerPub": { + "properties": [ + "adserverpub.com" + ], + "resources": [ + "adserverpub.com" + ] + }, + "AdShuffle": { + "properties": [ + "adshuffle.com" + ], + "resources": [ + "adshuffle.com" + ] + }, + "AdSide": { + "properties": [ + "adside.com", + "doclix.com" + ], + "resources": [ + "adside.com", + "doclix.com" + ] + }, + "AdSpeed": { + "properties": [ + "adspeed.com", + "adspeed.net" + ], + "resources": [ + "adspeed.com", + "adspeed.net" + ] + }, + "Adsperity": { + "properties": [ + "adsperity.com" + ], + "resources": [ + "adsperity.com" + ] + }, + "AdSpirit": { + "properties": [ + "adspirit.com", + "adspirit.de", + "adspirit.net" + ], + "resources": [ + "adspirit.com", + "adspirit.de", + "adspirit.net" + ] + }, + "Adsrevenue.net": { + "properties": [ + "adsrevenue.net" + ], + "resources": [ + "adsrevenue.net" + ] + }, + "AdStir": { + "properties": [ + "ad-stir.com" + ], + "resources": [ + "ad-stir.com" + ] + }, + "AdsTours": { + "properties": [ + "adstours.com", + "clickintext.net" + ], + "resources": [ + "adstours.com", + "clickintext.net" + ] + }, + "Adsty": { + "properties": [ + "adsty.com", + "adx1.com" + ], + "resources": [ + "adsty.com", + "adx1.com" + ] + }, + "Adsupply": { + "properties": [ + "4dsply.com", + "adsupply.com" + ], + "resources": [ + "4dsply.com", + "adsupply.com" + ] + }, + "Adswizz": { + "properties": [ + "adswizz.com" + ], + "resources": [ + "adswizz.com" + ] + }, + "ADTECH": { + "properties": [ + "adtech.com", + "adtech.de", + "adtechus.com" + ], + "resources": [ + "adtech.com", + "adtech.de", + "adtechus.com" + ] + }, + "Adtegrity.com": { + "properties": [ + "adtegrity.com", + "adtegrity.net" + ], + "resources": [ + "adtegrity.com", + "adtegrity.net" + ] + }, + "ADTELLIGENCE": { + "properties": [ + "adtelligence.de" + ], + "resources": [ + "adtelligence.de" + ] + }, + "Adthink": { + "properties": [ + "adthink.com" + ], + "resources": [ + "adthink.com", + "audienceinsights.net" + ] + }, + "AdTiger": { + "properties": [ + "adtiger.de" + ], + "resources": [ + "adtiger.de" + ] + }, + "AdTruth": { + "properties": [ + "adtruth.com" + ], + "resources": [ + "adtruth.com" + ] + }, + "Adult AdWorld": { + "properties": [ + "adultadworld.com" + ], + "resources": [ + "adultadworld.com" + ] + }, + "Adultmoda": { + "properties": [ + "adultmoda.com" + ], + "resources": [ + "adultmoda.com" + ] + }, + "Adventive": { + "properties": [ + "adventive.com" + ], + "resources": [ + "adventive.com" + ] + }, + "Adventori": { + "properties": [ + "adventori.com" + ], + "resources": [ + "adventori.com" + ] + }, + "Adverline": { + "properties": [ + "adnext.fr", + "adverline.com" + ], + "resources": [ + "adnext.fr", + "adverline.com" + ] + }, + "Adversal.com": { + "properties": [ + "adv-adserver.com", + "adversal.com" + ], + "resources": [ + "adv-adserver.com", + "adversal.com" + ] + }, + "Adverticum": { + "properties": [ + "adsmart.com", + "adverticum.com", + "adverticum.net" + ], + "resources": [ + "adsmart.com", + "adverticum.com", + "adverticum.net" + ] + }, + "Advertise.com": { + "properties": [ + "advertise.com" + ], + "resources": [ + "advertise.com" + ] + }, + "AdvertiseSpace": { + "properties": [ + "advertisespace.com" + ], + "resources": [ + "advertisespace.com" + ] + }, + "Advert Stream": { + "properties": [ + "advertstream.com" + ], + "resources": [ + "advertstream.com" + ] + }, + "Advisor Media": { + "properties": [ + "advisormedia.cz" + ], + "resources": [ + "advisormedia.cz" + ] + }, + "Adworx": { + "properties": [ + "adworx.at", + "adworx.be", + "adworx.nl" + ], + "resources": [ + "adworx.at", + "adworx.be", + "adworx.nl" + ] + }, + "AdXpansion": { + "properties": [ + "adxpansion.com" + ], + "resources": [ + "adxpansion.com" + ] + }, + "Adxvalue": { + "properties": [ + "adxvalue.com", + "adxvalue.de" + ], + "resources": [ + "adxvalue.com", + "adxvalue.de" + ] + }, + "adyard": { + "properties": [ + "adyard.de" + ], + "resources": [ + "adyard.de" + ] + }, + "AdYield": { + "properties": [ + "adyield.com" + ], + "resources": [ + "adxyield.com", + "adyield.com" + ] + }, + "AdYouLike": { + "properties": [ + "adyoulike.com" + ], + "resources": [ + "adyoulike.com", + "omnitagjs.com", + "pulpix.com" + ] + }, + "ADZ": { + "properties": [ + "adzcentral.com" + ], + "resources": [ + "adzcentral.com" + ] + }, + "Adzerk": { + "properties": [ + "adzerk.com" + ], + "resources": [ + "adzerk.com", + "adzerk.net" + ] + }, + "adzly": { + "properties": [ + "adzly.com" + ], + "resources": [ + "adzly.com" + ] + }, + "Aegis Group": { + "properties": [ + "aemedia.com", + "bluestreak.com", + "dentsuaegisnetwork.com" + ], + "resources": [ + "aemedia.com", + "bluestreak.com", + "dentsuaegisnetwork.com" + ] + }, + "AERIFY MEDIA": { + "properties": [ + "aerifymedia.com", + "anonymous-media.com" + ], + "resources": [ + "aerifymedia.com", + "anonymous-media.com" + ] + }, + "Affectv": { + "properties": [ + "affectv.co.uk" + ], + "resources": [ + "affectv.co.uk" + ] + }, + "affilinet": { + "properties": [ + "affili.net", + "affilinet-inside.de" + ], + "resources": [ + "affili.net", + "affilinet-inside.de", + "banner-rotation.com", + "successfultogether.co.uk" + ] + }, + "Affine": { + "properties": [ + "affine.tv", + "affinesystems.com" + ], + "resources": [ + "affine.tv", + "affinesystems.com" + ] + }, + "Affinity": { + "properties": [ + "affinity.com" + ], + "resources": [ + "affinity.com" + ] + }, + "AfterDownload": { + "properties": [ + "afdads.com", + "afterdownload.com" + ], + "resources": [ + "afdads.com", + "afterdownload.com" + ] + }, + "AIData": { + "properties": [ + "advombat.ru", + "aidata.me" + ], + "resources": [ + "advombat.ru", + "aidata.me" + ] + }, + "Aim4Media": { + "properties": [ + "aim4media.com" + ], + "resources": [ + "aim4media.com" + ] + }, + "Airpush": { + "properties": [ + "airpush.com" + ], + "resources": [ + "airpush.com" + ] + }, + "AivaLabs": { + "properties": [ + "aivalabs.com" + ], + "resources": [ + "aivalabs.com" + ] + }, + "a.js": { + "properties": [ + "alflying.date", + "alflying.win", + "anybest.site", + "flightsy.bid", + "flightsy.win", + "flightzy.bid", + "flightzy.date", + "flightzy.win", + "zymerget.bid", + "zymerget.faith" + ], + "resources": [ + "alflying.date", + "alflying.win", + "anybest.site", + "flightsy.bid", + "flightsy.win", + "flightzy.bid", + "flightzy.date", + "flightzy.win", + "zymerget.bid", + "zymerget.faith" + ] + }, + "AK": { + "properties": [ + "aggregateknowledge.com", + "agkn.com" + ], + "resources": [ + "aggregateknowledge.com", + "agkn.com" + ] + }, + "Akamai": { + "properties": [ + "akamai.com" + ], + "resources": [ + "abmr.net", + "akamai.com", + "edgesuite.net", + "go-mpulse.net", + "imiclk.com" + ] + }, + "AKQA": { + "properties": [ + "akqa.com" + ], + "resources": [ + "akqa.com", + "srtk.net" + ] + }, + "Albacross": { + "properties": [ + "albacross.com" + ], + "resources": [ + "albacross.com" + ] + }, + "AllStarMediaGroup": { + "properties": [ + "allstarmediagroup.com" + ], + "resources": [ + "allstarmediagroup.com" + ] + }, + "Aloodo": { + "properties": [ + "aloodo.com" + ], + "resources": [ + "aloodo.com" + ] + }, + "AlterGeo": { + "properties": [ + "altergeo.ru" + ], + "resources": [ + "altergeo.ru" + ] + }, + "Amadesa": { + "properties": [ + "amadesa.com" + ], + "resources": [ + "amadesa.com" + ] + }, + "Amazing Counters": { + "properties": [ + "amazingcounters.com" + ], + "resources": [ + "amazingcounters.com" + ] + }, + "Amazon.com": { + "properties": [ + "6pm.com", + "abebooks.co.uk", + "abebooks.com", + "abebooks.de", + "abebooks.fr", + "abebooks.it", + "acx.com", + "alexa.com", + "amazon.ae", + "amazon.ca", + "amazon.cn", + "amazon.co.jp", + "amazon.co.uk", + "amazon.com", + "amazon.com.au", + "amazon.com.br", + "amazon.com.mx", + "amazon.com.sg", + "amazon.com.tr", + "amazon.de", + "amazon.es", + "amazon.fr", + "amazon.in", + "amazon.it", + "amazon.nl", + "amazon.sa", + "amazonaws.com", + "amazoninspire.com", + "assoc-amazon.com", + "audible.co.jp", + "audible.co.uk", + "audible.com", + "audible.de", + "audible.fr", + "audible.in", + "audible.it", + "bookdepository.com", + "boxofficemojo.com", + "brilliancepublishing.com", + "comixology.com", + "createspace.com", + "dpreview.com", + "dpreview.in", + "eastdane.com", + "fabric.com", + "goodreads.com", + "iberlibro.com", + "imdb.com", + "imdb.de", + "junglee.com", + "look.com", + "pillpack.com", + "shopbop.com", + "souq.com", + "twitch.com", + "twitch.tv", + "wholefoodsmarket.com", + "withoutabox.com", + "woot.com", + "yoyo.com", + "zappos.com", + "zvab.com" + ], + "resources": [ + "alexa.com", + "alexametrics.com", + "amazon-adsystem.com", + "amazon.ca", + "amazon.co.jp", + "amazon.co.uk", + "amazon.com", + "amazon.de", + "amazon.es", + "amazon.fr", + "amazon.it", + "amazonaws.com", + "assoc-amazon.com", + "cloudfront.net", + "ssl-images-amazon.com" + ] + }, + "Ambient Digital": { + "properties": [ + "adnetwork.vn", + "ambientdigital.com.vn" + ], + "resources": [ + "adnetwork.vn", + "ambientdigital.com.vn" + ] + }, + "Amobee": { + "properties": [ + "amobee.com", + "smartclip.com" + ], + "resources": [ + "adconion.com", + "amgdgt.com", + "amobee.com", + "euroclick.com", + "smartclip.com", + "turn.com" + ] + }, + "Amplitude": { + "properties": [ + "amplitude.com" + ], + "resources": [ + "amplitude.com" + ] + }, + "AndBeyond": { + "properties": [ + "andbeyond.media" + ], + "resources": [ + "andbeyond.media" + ] + }, + "anormal-media.de": { + "properties": [ + "anormal-media.de", + "primawebtools.de" + ], + "resources": [ + "anormal-media.de", + "anormal-tracker.de", + "primawebtools.de" + ] + }, + "Answers.com": { + "properties": [ + "answers.com", + "dsply.com" + ], + "resources": [ + "dsply.com" + ] + }, + "AOL": { + "properties": [ + "5min.com", + "adsonar.com", + "advertising.com", + "aim.com", + "aol.com", + "aolcdn.com", + "aoltechguru.com", + "atwola.com", + "autoblog.com", + "cambio.com", + "dailyfinance.com", + "editions.com", + "engadget.com", + "games.com", + "homesessive.com", + "huffingtonpost.com", + "leadback.com", + "makers.com", + "mandatory.com", + "mapquest.com", + "moviefone.com", + "noisecreep.com", + "patch.com", + "pawnation.com", + "shortcuts.com", + "shoutcast.com", + "spinner.com", + "stylelist.com", + "stylemepretty.com", + "surphace.com", + "tacoda.net", + "techcrunch.com", + "theboombox.com", + "theboot.com", + "userplane.com", + "winamp.com" + ], + "resources": [ + "5min.com", + "adsonar.com", + "adtechjp.com", + "advertising.com", + "aim.com", + "aol.com", + "aolcdn.com", + "aolcloud.net", + "atwola.com", + "editions.com", + "leadback.com", + "mapquest.com", + "patch.com", + "shortcuts.com", + "shoutcast.com", + "spinner.com", + "surphace.com", + "tacoda.net", + "userplane.com", + "vidible.tv", + "winamp.com" + ] + }, + "AppCast": { + "properties": [ + "appcast.io" + ], + "resources": [ + "appcast.io" + ] + }, + "Appenda": { + "properties": [ + "appenda.com" + ], + "resources": [ + "appenda.com" + ] + }, + "AppFlood": { + "properties": [ + "appflood.com" + ], + "resources": [ + "appflood.com" + ] + }, + "Appier": { + "properties": [ + "appier.com" + ], + "resources": [ + "appier.com" + ] + }, + "Applifier": { + "properties": [ + "applifier.com" + ], + "resources": [ + "applifier.com" + ] + }, + "Applovin": { + "properties": [ + "applovin.com" + ], + "resources": [ + "applovin.com" + ] + }, + "AppNexus": { + "properties": [ + "adlantic.nl", + "adnxs.com", + "adrdgt.com", + "appnexus.com" + ], + "resources": [ + "adlantic.nl", + "adnxs.com", + "adrdgt.com", + "appnexus.com" + ] + }, + "AppsFlyer": { + "properties": [ + "appsflyer.com" + ], + "resources": [ + "appsflyer.com" + ] + }, + "appssavvy": { + "properties": [ + "appssavvy.com" + ], + "resources": [ + "appssavvy.com" + ] + }, + "Arkwrights Homebrew": { + "properties": [ + "whiskyandwines.com" + ], + "resources": [ + "arkwrightshomebrew.com", + "ctasnet.com", + "whiskyandwines.com" + ] + }, + "AT Internet": { + "properties": [ + "atinternet.com", + "xiti.com" + ], + "resources": [ + "at-o.net", + "atinternet.com", + "hit-parade.com", + "xiti.com" + ] + }, + "ATN": { + "properties": [ + "affiliatetracking.com" + ], + "resources": [ + "affiliatetracking.com" + ] + }, + "Atoomic.com": { + "properties": [ + "atoomic.com" + ], + "resources": [ + "atoomic.com" + ] + }, + "Atrinsic": { + "properties": [ + "atrinsic.com" + ], + "resources": [ + "atrinsic.com" + ] + }, + "AT&T": { + "properties": [ + "att.com", + "yp.com" + ], + "resources": [ + "att.com", + "yp.com" + ] + }, + "Attracta": { + "properties": [ + "attracta.com" + ], + "resources": [ + "attracta.com" + ] + }, + "Audience2Media": { + "properties": [ + "audience2media.com" + ], + "resources": [ + "audience2media.com" + ] + }, + "Audience Ad Network": { + "properties": [ + "audienceadnetwork.com" + ], + "resources": [ + "audienceadnetwork.com" + ] + }, + "AudienceScience": { + "properties": [ + "audiencescience.com" + ], + "resources": [ + "audiencescience.com", + "revsci.net", + "targetingmarketplace.com", + "wunderloop.net" + ] + }, + "AuditedMedia": { + "properties": [ + "auditedmedia.com" + ], + "resources": [ + "aamapi.com", + "aamsitecertifier.com", + "auditedmedia.com" + ] + }, + "Augme": { + "properties": [ + "hipcricket.com" + ], + "resources": [ + "augme.com", + "hipcricket.com" + ] + }, + "Augur": { + "properties": [ + "augur.io" + ], + "resources": [ + "augur.io" + ] + }, + "AUTOCENTRE.UA": { + "properties": [ + "am.ua", + "autocentre.ua" + ], + "resources": [ + "am.ua", + "autocentre.ua" + ] + }, + "Automattic": { + "properties": [ + "automattic.com", + "gravatar.com", + "intensedebate.com", + "polldaddy.com" + ], + "resources": [ + "automattic.com", + "gravatar.com", + "intensedebate.com", + "polldaddy.com", + "pubmine.com" + ] + }, + "Avalanchers": { + "properties": [ + "avalanchers.com" + ], + "resources": [ + "avalanchers.com" + ] + }, + "AvantLink": { + "properties": [ + "avantlink.com", + "avantmetrics.com" + ], + "resources": [ + "avantlink.com", + "avmws.com" + ] + }, + "Avocet": { + "properties": [ + "avocet.io" + ], + "resources": [ + "avocet.io" + ] + }, + "Avsads": { + "properties": [ + "avsads.com" + ], + "resources": [ + "avsads.com" + ] + }, + "AWeber": { + "properties": [ + "aweber.com" + ], + "resources": [ + "aweber.com" + ] + }, + "Awin": { + "properties": [ + "awin.com" + ], + "resources": [ + "awin.com", + "digitalwindow.com", + "dwin1.com", + "perfiliate.com" + ] + }, + "Awio": { + "properties": [ + "awio.com", + "w3counter.com" + ], + "resources": [ + "awio.com", + "w3counter.com", + "w3roi.com" + ] + }, + "Azet": { + "properties": [ + "azet.sk", + "mediaimpact.sk" + ], + "resources": [ + "azet.sk", + "azetklik.sk", + "mediaimpact.sk", + "rsz.sk" + ] + }, + "BackBeat Media": { + "properties": [ + "backbeatmedia.com" + ], + "resources": [ + "backbeatmedia.com" + ] + }, + "Bannerconnect": { + "properties": [ + "bannerconnect.net" + ], + "resources": [ + "bannerconnect.net" + ] + }, + "Barilliance": { + "properties": [ + "barilliance.com" + ], + "resources": [ + "barilliance.com" + ] + }, + "BaronsNetworks": { + "properties": [ + "baronsoffers.com" + ], + "resources": [ + "baronsoffers.com" + ] + }, + "Batanga Network": { + "properties": [ + "batanga.com", + "corp.vix.com", + "vix.com" + ], + "resources": [ + "batanga.com", + "batanganetwork.com", + "vix.com" + ] + }, + "Baynote": { + "properties": [ + "baynote.com" + ], + "resources": [ + "baynote.com", + "baynote.net" + ] + }, + "Bazaarvoice": { + "properties": [ + "bazaarvoice.com" + ], + "resources": [ + "bazaarvoice.com" + ] + }, + "BeachFront": { + "properties": [ + "beachfront.com" + ], + "resources": [ + "beachfront.com" + ] + }, + "Beanstock Media": { + "properties": [ + "beanstockmedia.com" + ], + "resources": [ + "beanstockmedia.com" + ] + }, + "beencounter": { + "properties": [ + "beencounter.com" + ], + "resources": [ + "beencounter.com" + ] + }, + "Begun": { + "properties": [ + "begun.ru" + ], + "resources": [ + "begun.ru" + ] + }, + "belboon": { + "properties": [ + "belboon.com" + ], + "resources": [ + "adbutler.de", + "belboon.com" + ] + }, + "Belstat": { + "properties": [ + "belstat.be", + "belstat.com", + "belstat.de", + "belstat.fr", + "belstat.nl" + ], + "resources": [ + "belstat.be", + "belstat.com", + "belstat.de", + "belstat.fr", + "belstat.nl" + ] + }, + "Betgenius": { + "properties": [ + "betgenius.com", + "connextra.com" + ], + "resources": [ + "betgenius.com", + "connextra.com" + ] + }, + "BetssonPalantir": { + "properties": [ + "betssonpalantir.com" + ], + "resources": [ + "betssonpalantir.com" + ] + }, + "BetweenDigital": { + "properties": [ + "betweendigital.com" + ], + "resources": [ + "betweendigital.com" + ] + }, + "Bidfluence": { + "properties": [ + "bidfluence.com" + ], + "resources": [ + "bidfluence.com" + ] + }, + "Bidr": { + "properties": [ + "bidr.io" + ], + "resources": [ + "bidr.io" + ] + }, + "BidSwitch": { + "properties": [ + "bidswitch.com" + ], + "resources": [ + "bidswitch.net", + "mfadsrvr.com" + ] + }, + "Bidtellect": { + "properties": [ + "bidtellect.com", + "bttrack.com" + ], + "resources": [ + "bidtellect.com", + "bttrack.com" + ] + }, + "BidVertiser": { + "properties": [ + "bidvertiser.com" + ], + "resources": [ + "bidvertiser.com" + ] + }, + "BigClick": { + "properties": [ + "bigclick.me" + ], + "resources": [ + "bgclck.me", + "xcvgdf.party" + ] + }, + "BigDoor": { + "properties": [ + "bigdoor.com" + ], + "resources": [ + "bigdoor.com", + "onetruefan.com" + ] + }, + "bigmirnet": { + "properties": [ + "bigmir.net" + ], + "resources": [ + "bigmir.net" + ] + }, + "BinLayer": { + "properties": [ + "binlayer.com" + ], + "resources": [ + "binlayer.com" + ] + }, + "Bitcoin Plus": { + "properties": [ + "bitcoinplus.com" + ], + "resources": [ + "bitcoinplus.com" + ] + }, + "BitMedia": { + "properties": [ + "bitmedia.io" + ], + "resources": [ + "bitmedia.io" + ] + }, + "BittAds": { + "properties": [ + "bittads.com" + ], + "resources": [ + "bittads.com" + ] + }, + "Bizo": { + "properties": [ + "bizo.com", + "bizographics.com" + ], + "resources": [ + "bizo.com", + "bizographics.com" + ] + }, + "Black Label Ads": { + "properties": [ + "blacklabelads.com" + ], + "resources": [ + "blacklabelads.com" + ] + }, + "BlogCatalog": { + "properties": [ + "blogcatalog.com" + ], + "resources": [ + "blogcatalog.com" + ] + }, + "BlogCounter.com": { + "properties": [ + "blogcounter.de" + ], + "resources": [ + "blogcounter.de" + ] + }, + "BlogFrog": { + "properties": [ + "theblogfrog.com" + ], + "resources": [ + "theblogfrog.com" + ] + }, + "BlogHer": { + "properties": [ + "blogher.com", + "blogherads.com" + ], + "resources": [ + "blogher.com", + "blogherads.com" + ] + }, + "BlogRollr": { + "properties": [ + "blogrollr.com" + ], + "resources": [ + "blogrollr.com" + ] + }, + "BLOOM Digital Platforms": { + "properties": [ + "adgear.com", + "bloom-hq.com" + ], + "resources": [ + "adgear.com", + "adgrx.com", + "bloom-hq.com" + ] + }, + "BloomReach": { + "properties": [ + "bloomreach.com", + "brcdn.com" + ], + "resources": [ + "bloomreach.com", + "brcdn.com", + "brsrvr.com" + ] + }, + "BlueCava": { + "properties": [ + "bluecava.com" + ], + "resources": [ + "bluecava.com" + ] + }, + "BlueKai": { + "properties": [ + "bluekai.com", + "tracksimple.com" + ], + "resources": [ + "bkrtx.com", + "bluekai.com", + "tracksimple.com" + ] + }, + "Bluemetrix": { + "properties": [ + "bluemetrix.com", + "bmmetrix.com" + ], + "resources": [ + "bluemetrix.com", + "bmmetrix.com" + ] + }, + "Blu Trumpet": { + "properties": [ + "blutrumpet.com" + ], + "resources": [ + "blutrumpet.com" + ] + }, + "Bombora": { + "properties": [ + "bombora.com" + ], + "resources": [ + "ml314.com" + ] + }, + "Boo-Box": { + "properties": [ + "boo-box.com" + ], + "resources": [ + "boo-box.com" + ] + }, + "BoostBox": { + "properties": [ + "boostbox.com.br" + ], + "resources": [ + "boostbox.com.br" + ] + }, + "Bouncex": { + "properties": [ + "bouncex.com" + ], + "resources": [ + "bounceexchange.com", + "bouncex.com", + "bouncex.net" + ] + }, + "Brainient": { + "properties": [ + "brainient.com" + ], + "resources": [ + "brainient.com" + ] + }, + "Branch": { + "properties": [ + "branch.io" + ], + "resources": [ + "branch.io" + ] + }, + "Brand Affinity Technologies": { + "properties": [ + "brandaffinity.net" + ], + "resources": [ + "brandaffinity.net" + ] + }, + "Brandcrumb": { + "properties": [ + "brandcrumb.com" + ], + "resources": [ + "brandcrumb.com" + ] + }, + "Brand.net": { + "properties": [ + "brand.net" + ], + "resources": [ + "brand.net" + ] + }, + "Brandscreen": { + "properties": [ + "brandscreen.com", + "rtbidder.net" + ], + "resources": [ + "brandscreen.com", + "rtbidder.net" + ] + }, + "Branica": { + "properties": [ + "branica.com" + ], + "resources": [ + "branica.com" + ] + }, + "BreakTime": { + "properties": [ + "breaktime.com.tw" + ], + "resources": [ + "breaktime.com.tw" + ] + }, + "Brightcove": { + "properties": [ + "brightcove.com" + ], + "resources": [ + "brightcove.com" + ] + }, + "BrightEdge": { + "properties": [ + "brightedge.com" + ], + "resources": [ + "b0e8.com", + "brightedge.com" + ] + }, + "BrightRoll": { + "properties": [ + "brightroll.com" + ], + "resources": [ + "brightroll.com", + "btrll.com" + ] + }, + "BrightTag": { + "properties": [ + "brighttag.com", + "btstatic.com", + "thebrighttag.com" + ], + "resources": [ + "brighttag.com", + "btstatic.com", + "thebrighttag.com" + ] + }, + "Brilig": { + "properties": [ + "brilig.com" + ], + "resources": [ + "brilig.com" + ] + }, + "Browser-Update.org": { + "properties": [ + "browser-update.org" + ], + "resources": [ + "browser-update.org" + ] + }, + "BTBuckets": { + "properties": [ + "btbuckets.com" + ], + "resources": [ + "btbuckets.com" + ] + }, + "Bubblestat": { + "properties": [ + "bubblestat.com" + ], + "resources": [ + "bubblestat.com" + ] + }, + "BuckSense": { + "properties": [ + "bucksense.com" + ], + "resources": [ + "bucksense.com" + ] + }, + "Buffer": { + "properties": [ + "bufferapp.com" + ], + "resources": [ + "bufferapp.com" + ] + }, + "Bunchball": { + "properties": [ + "bunchball.com" + ], + "resources": [ + "bunchball.com" + ] + }, + "Burstly": { + "properties": [ + "burstly.com" + ], + "resources": [ + "burstly.com" + ] + }, + "Burst Media": { + "properties": [ + "burstbeacon.com", + "burstdirectads.com", + "burstmedia.com", + "burstnet.com", + "giantrealm.com" + ], + "resources": [ + "burstbeacon.com", + "burstdirectads.com", + "burstmedia.com", + "burstnet.com", + "giantrealm.com" + ] + }, + "BusinessOnline": { + "properties": [ + "businessol.com" + ], + "resources": [ + "businessol.com" + ] + }, + "Button": { + "properties": [ + "usebutton.com" + ], + "resources": [ + "usebutton.com" + ] + }, + "buySAFE": { + "properties": [ + "buysafe.com" + ], + "resources": [ + "buysafe.com" + ] + }, + "BuySellAds": { + "properties": [ + "beaconads.com", + "buysellads.com" + ], + "resources": [ + "beaconads.com", + "buysellads.com" + ] + }, + "Buysight": { + "properties": [ + "buysight.com", + "permuto.com", + "pulsemgr.com" + ], + "resources": [ + "buysight.com", + "permuto.com", + "pulsemgr.com" + ] + }, + "BuzzFeed": { + "properties": [ + "buzzfeed.com" + ], + "resources": [ + "buzzfed.com", + "buzzfeed.com" + ] + }, + "BuzzParadise": { + "properties": [ + "buzzparadise.com" + ], + "resources": [ + "buzzparadise.com" + ] + }, + "BV! MEDIA": { + "properties": [ + "branchez-vous.com", + "bvmedia.ca" + ], + "resources": [ + "branchez-vous.com", + "bvmedia.ca", + "networldmedia.com", + "networldmedia.net" + ] + }, + "c1exchange": { + "properties": [ + "c1exchange.com" + ], + "resources": [ + "c1exchange.com" + ] + }, + "C3 Metrics": { + "properties": [ + "attributionmodel.com", + "c3metrics.com", + "c3tag.com" + ], + "resources": [ + "attributionmodel.com", + "c3metrics.com", + "c3tag.com" + ] + }, + "Cadreon": { + "properties": [ + "cadreon.com" + ], + "resources": [ + "cadreon.com" + ] + }, + "CallSource": { + "properties": [ + "callsource.com" + ], + "resources": [ + "leadtrackingdata.com" + ] + }, + "CampaignGrid": { + "properties": [ + "campaigngrid.com" + ], + "resources": [ + "campaigngrid.com" + ] + }, + "CAPITALDATA": { + "properties": [ + "capitaldata.fr" + ], + "resources": [ + "capitaldata.fr" + ] + }, + "Carambola": { + "properties": [ + "carambola.com" + ], + "resources": [ + "carambo.la" + ] + }, + "Caraytech": { + "properties": [ + "caraytech.com.ar", + "e-planning.net", + "www.caraytech.com.ar" + ], + "resources": [ + "caraytech.com.ar", + "e-planning.net", + "www.caraytech.com.ar" + ] + }, + "Cardlytics": { + "properties": [ + "cardlytics.com" + ], + "resources": [ + "cardlytics.com" + ] + }, + "Cart.ro": { + "properties": [ + "cart.ro" + ], + "resources": [ + "cart.ro", + "statistics.ro" + ] + }, + "CartsGuru": { + "properties": [ + "carts.guru" + ], + "resources": [ + "carts.guru" + ] + }, + "Casale Media": { + "properties": [ + "casalemedia.com", + "medianet.com" + ], + "resources": [ + "casalemedia.com", + "medianet.com" + ] + }, + "CashBeet": { + "properties": [ + "cashbeet.com" + ], + "resources": [ + "cashbeet.com", + "serv1swork.com" + ] + }, + "Causes": { + "properties": [ + "causes.com" + ], + "resources": [ + "causes.com" + ] + }, + "Cbox": { + "properties": [ + "cbox.ws" + ], + "resources": [ + "cbox.ws" + ] + }, + "CBproADS": { + "properties": [ + "cbproads.com" + ], + "resources": [ + "cbproads.com" + ] + }, + "CBS Interactive": { + "properties": [ + "cbsinteractive.com", + "com.com" + ], + "resources": [ + "cbsinteractive.com", + "com.com" + ] + }, + "Cedato": { + "properties": [ + "cedato.com" + ], + "resources": [ + "cedato.com" + ] + }, + "Cedexis": { + "properties": [ + "cedexis.com" + ], + "resources": [ + "cedexis.com", + "cedexis.net" + ] + }, + "Certona": { + "properties": [ + "certona.com", + "res-x.com" + ], + "resources": [ + "certona.com", + "res-x.com" + ] + }, + "Chango": { + "properties": [ + "chango.ca", + "chango.com" + ], + "resources": [ + "chango.ca", + "chango.com" + ] + }, + "ChannelAdvisor": { + "properties": [ + "channeladvisor.com", + "searchmarketing.com" + ], + "resources": [ + "channeladvisor.com", + "searchmarketing.com" + ] + }, + "Channel Intelligence": { + "properties": [ + "channelintelligence.com" + ], + "resources": [ + "channelintelligence.com" + ] + }, + "Chartbeat": { + "properties": [ + "chartbeat.com", + "chartbeat.net" + ], + "resources": [ + "chartbeat.com", + "chartbeat.net" + ] + }, + "Chartboost": { + "properties": [ + "chartboost.com" + ], + "resources": [ + "chartboost.com" + ] + }, + "CheckM8": { + "properties": [ + "checkm8.com" + ], + "resources": [ + "checkm8.com" + ] + }, + "Chitika": { + "properties": [ + "chitika.com" + ], + "resources": [ + "chitika.com", + "chitika.net" + ] + }, + "ChoiceStream": { + "properties": [ + "choicestream.com" + ], + "resources": [ + "choicestream.com" + ] + }, + "ClearLink": { + "properties": [ + "clearlink.com" + ], + "resources": [ + "clearlink.com" + ] + }, + "ClearSaleing": { + "properties": [ + "clearsaleing.com" + ], + "resources": [ + "clearsaleing.com", + "csdata1.com", + "csdata2.com", + "csdata3.com" + ] + }, + "Clearsearch Media": { + "properties": [ + "pathinteractive.com" + ], + "resources": [ + "clearsearchmedia.com", + "csm-secure.com", + "pathinteractive.com" + ] + }, + "ClearSight Interactive": { + "properties": [ + "clearsightinteractive.com", + "csi-tracking.com" + ], + "resources": [ + "clearsightinteractive.com", + "csi-tracking.com" + ] + }, + "ClickAider": { + "properties": [ + "clickaider.com" + ], + "resources": [ + "clickaider.com" + ] + }, + "Clickayab": { + "properties": [ + "clickyab.com" + ], + "resources": [ + "clickyab.com" + ] + }, + "Clickbooth": { + "properties": [ + "clickbooth.com" + ], + "resources": [ + "adtoll.com", + "clickbooth.com" + ] + }, + "Clickdensity": { + "properties": [ + "clickdensity.com" + ], + "resources": [ + "clickdensity.com" + ] + }, + "ClickDimensions": { + "properties": [ + "clickdimensions.com" + ], + "resources": [ + "clickdimensions.com" + ] + }, + "ClickDistrict": { + "properties": [ + "clickdistrict.com", + "creative-serving.com" + ], + "resources": [ + "clickdistrict.com", + "creative-serving.com" + ] + }, + "ClickFrog": { + "properties": [ + "clickfrog.ru" + ], + "resources": [ + "bashirian.biz", + "buckridge.link", + "clickfrog.ru", + "franecki.net", + "quitzon.net", + "reichelcormier.bid", + "wisokykulas.bid" + ] + }, + "ClickFuel": { + "properties": [ + "clickfuel.com", + "myconversionlab.com" + ], + "resources": [ + "clickfuel.com", + "conversiondashboard.com", + "myconversionlab.com" + ] + }, + "ClickGuard": { + "properties": [ + "clickguard.com" + ], + "resources": [ + "clickguard.com" + ] + }, + "ClickInc": { + "properties": [ + "clickinc.com" + ], + "resources": [ + "clickinc.com" + ] + }, + "Clicksor": { + "properties": [ + "clicksor.com", + "clicksor.net" + ], + "resources": [ + "clicksor.com", + "clicksor.net" + ] + }, + "ClickTale": { + "properties": [ + "clicktale.com" + ], + "resources": [ + "clicktale.com", + "clicktale.net", + "pantherssl.com" + ] + }, + "Clickwinks": { + "properties": [ + "clickwinks.com" + ], + "resources": [ + "clickwinks.com" + ] + }, + "ClicManager": { + "properties": [ + "clicmanager.fr" + ], + "resources": [ + "clicmanager.fr" + ] + }, + "ClipSyndicate": { + "properties": [ + "clipsyndicate.com" + ], + "resources": [ + "clipsyndicate.com" + ] + }, + "ClixMetrix": { + "properties": [ + "clixmetrix.com" + ], + "resources": [ + "clixmetrix.com" + ] + }, + "Clixpy": { + "properties": [ + "clixpy.com" + ], + "resources": [ + "clixpy.com" + ] + }, + "Clixtell": { + "properties": [ + "clixtell.com" + ], + "resources": [ + "clixtell.com" + ] + }, + "Clove Network": { + "properties": [ + "clovenetwork.com" + ], + "resources": [ + "clovenetwork.com" + ] + }, + "ClustrMaps": { + "properties": [ + "clustrmaps.com" + ], + "resources": [ + "clustrmaps.com" + ] + }, + "CNZZ": { + "properties": [ + "cnzz.com" + ], + "resources": [ + "cnzz.com" + ] + }, + "Cognitive Match": { + "properties": [ + "cmads.com.tw", + "cmadsasia.com", + "cmadseu.com", + "cmmeglobal.com", + "cognitivematch.com" + ], + "resources": [ + "cmads.com.tw", + "cmadsasia.com", + "cmadseu.com", + "cmmeglobal.com", + "cognitivematch.com" + ] + }, + "CoinHive": { + "properties": [ + "authedmine.com", + "coinhive.com" + ], + "resources": [ + "ad-miner.com", + "authedmine.com", + "bmst.pw", + "cnhv.co", + "coin-hive.com", + "coinhive.com", + "wsservices.org" + ] + }, + "CoinPot": { + "properties": [ + "coinpot.co" + ], + "resources": [ + "coinpot.co" + ] + }, + "Collarity": { + "properties": [ + "collarity.com" + ], + "resources": [ + "collarity.com" + ] + }, + "Collective": { + "properties": [ + "collective.com" + ], + "resources": [ + "collective-media.net", + "collective.com", + "oggifinogi.com", + "tumri.com", + "tumri.net", + "yt1187.net" + ] + }, + "Commission Junction": { + "properties": [ + "cj.com" + ], + "resources": [ + "apmebf.com", + "awltovhc.com", + "cj.com", + "ftjcfx.com", + "kcdwa.com", + "qksz.com", + "qksz.net", + "tqlkg.com", + "yceml.net" + ] + }, + "Communicator Corp": { + "properties": [ + "communicatorcorp.com" + ], + "resources": [ + "communicatorcorp.com" + ] + }, + "Compass Labs": { + "properties": [ + "compasslabs.com" + ], + "resources": [ + "compasslabs.com" + ] + }, + "Complex Media": { + "properties": [ + "collider.com", + "complex.com", + "complexmedianetwork.com", + "firstwefeast.com", + "pigeonsandplanes.com", + "solecollector.com", + "theridechannel.com" + ], + "resources": [ + "complex.com", + "complexmedianetwork.com" + ] + }, + "Compuware": { + "properties": [ + "axf8.net", + "compuware.com", + "dynatrace.com" + ], + "resources": [ + "axf8.net", + "compuware.com", + "dynatrace.com", + "gomez.com" + ] + }, + "comScore": { + "properties": [ + "adxpose.com", + "comscore.com", + "scorecardresearch.com", + "sitestat.com", + "voicefive.com" + ], + "resources": [ + "adxpose.com", + "certifica.com", + "comscore.com", + "mdotlabs.com", + "proxilinks.com", + "proximic.com", + "proximic.net", + "scorecardresearch.com", + "sitestat.com", + "voicefive.com" + ] + }, + "Conduit": { + "properties": [ + "conduit-banners.com", + "conduit.com" + ], + "resources": [ + "conduit-banners.com", + "conduit-services.com", + "conduit.com", + "wibiya.com" + ] + }, + "Congoo": { + "properties": [ + "congoo.com" + ], + "resources": [ + "congoo.com" + ] + }, + "Connatix.com": { + "properties": [ + "connatix.com" + ], + "resources": [ + "connatix.com" + ] + }, + "Connexity": { + "properties": [ + "connexity.com", + "pricegrabber.com" + ], + "resources": [ + "connexity.com", + "connexity.net", + "pricegrabber.com" + ] + }, + "Consilium Media": { + "properties": [ + "consiliummedia.com" + ], + "resources": [ + "consiliummedia.com" + ] + }, + "Consumable": { + "properties": [ + "consumable.com" + ], + "resources": [ + "consumable.com" + ] + }, + "Contact At Once!": { + "properties": [ + "contactatonce.com" + ], + "resources": [ + "contactatonce.com" + ] + }, + "CONTAXE": { + "properties": [ + "contaxe.com" + ], + "resources": [ + "contaxe.com" + ] + }, + "ContentABC": { + "properties": [ + "contentabc.com" + ], + "resources": [ + "contentabc.com" + ] + }, + "CONTEXTin": { + "properties": [ + "admailtiser.com", + "contextin.com" + ], + "resources": [ + "admailtiser.com", + "contextin.com" + ] + }, + "ContextuAds": { + "properties": [ + "agencytradingdesk.net", + "contextuads.com" + ], + "resources": [ + "agencytradingdesk.net", + "contextuads.com" + ] + }, + "CONTEXTWEB": { + "properties": [ + "contextweb.com" + ], + "resources": [ + "contextweb.com" + ] + }, + "ConvergeDirect": { + "properties": [ + "convergedirect.com", + "convergetrack.com" + ], + "resources": [ + "convergedirect.com", + "convergetrack.com" + ] + }, + "ConversantMedia": { + "properties": [ + "conversantmedia.com" + ], + "resources": [ + "adserver.com", + "conversantmedia.com", + "dotomi.com", + "dtmpub.com", + "emjcd.com", + "fastclick.com", + "fastclick.net", + "greystripe.com", + "lduhtrp.net", + "mediaplex.com", + "valueclick.com", + "valueclick.net", + "valueclickmedia.com" + ] + }, + "ConversionRuler": { + "properties": [ + "conversionruler.com" + ], + "resources": [ + "conversionruler.com" + ] + }, + "Conversive": { + "properties": [ + "conversive.nl" + ], + "resources": [ + "conversive.nl" + ] + }, + "Convert Insights": { + "properties": [ + "convert.com", + "reedge.com" + ], + "resources": [ + "convert.com", + "reedge.com" + ] + }, + "Convertro": { + "properties": [ + "convertro.com" + ], + "resources": [ + "convertro.com" + ] + }, + "Conviva": { + "properties": [ + "conviva.com" + ], + "resources": [ + "conviva.com" + ] + }, + "CoreMotives": { + "properties": [ + "coremotives.com" + ], + "resources": [ + "coremotives.com" + ] + }, + "Cox Digital Solutions": { + "properties": [ + "adify.com", + "coxdigitalsolutions.com", + "novomotus.com" + ], + "resources": [ + "adify.com", + "afy11.net", + "coxdigitalsolutions.com", + "novomotus.com" + ] + }, + "CPMStar": { + "properties": [ + "cpmstar.com" + ], + "resources": [ + "cpmstar.com" + ] + }, + "CPX Interactive": { + "properties": [ + "cpxadroit.com" + ], + "resources": [ + "adreadypixels.com", + "cpxadroit.com", + "cpxinteractive.com" + ] + }, + "Crazy Egg": { + "properties": [ + "cetrk.com", + "crazyegg.com" + ], + "resources": [ + "cetrk.com", + "crazyegg.com" + ] + }, + "Creafi": { + "properties": [ + "creafi.com" + ], + "resources": [ + "creafi.com" + ] + }, + "Crimtan": { + "properties": [ + "crimtan.com" + ], + "resources": [ + "crimtan.com" + ] + }, + "Crisp Media": { + "properties": [ + "crispmedia.com" + ], + "resources": [ + "crispmedia.com" + ] + }, + "Criteo": { + "properties": [ + "criteo.com", + "criteo.net" + ], + "resources": [ + "criteo.com", + "criteo.net", + "hlserve.com", + "hooklogic.com", + "storetail.io" + ] + }, + "Cross Pixel": { + "properties": [ + "crosspixel.net" + ], + "resources": [ + "crosspixel.net", + "crosspixelmedia.com", + "crsspxl.com" + ] + }, + "Crowd Science": { + "properties": [ + "crowdscience.com" + ], + "resources": [ + "crowdscience.com" + ] + }, + "CryptoLoot": { + "properties": [ + "crypto-loot.com" + ], + "resources": [ + "cryptaloot.pro", + "crypto-loot.com", + "cryptolootminer.com", + "flashx.pw", + "gitgrub.pro", + "reauthenticator.com", + "statdynamic.com", + "webmine.pro" + ] + }, + "CryptoWebMiner": { + "properties": [ + "crypto-webminer.com" + ], + "resources": [ + "bitcoin-pay.eu", + "crypto-webminer.com", + "ethpocket.de", + "ethtrader.de" + ] + }, + "cXense": { + "properties": [ + "cxense.com" + ], + "resources": [ + "cxense.com", + "emediate.biz", + "emediate.com", + "emediate.dk", + "emediate.eu" + ] + }, + "Cya2": { + "properties": [ + "cya2.net" + ], + "resources": [ + "cya2.net" + ] + }, + "Cyberplex": { + "properties": [ + "cyberplex.com" + ], + "resources": [ + "cyberplex.com" + ] + }, + "Dada": { + "properties": [ + "dada.eu", + "dada.pro", + "simply.com" + ], + "resources": [ + "dada.eu", + "dada.pro", + "simply.com" + ] + }, + "DailyMe": { + "properties": [ + "dailyme.com", + "newstogram.com" + ], + "resources": [ + "dailyme.com", + "newstogram.com" + ] + }, + "Dataium": { + "properties": [ + "collserve.com", + "ihs.com" + ], + "resources": [ + "collserve.com", + "dataium.com", + "ihs.com" + ] + }, + "Datalogix": { + "properties": [ + "datalogix.com", + "nexac.com" + ], + "resources": [ + "datalogix.com", + "nexac.com", + "nextaction.net" + ] + }, + "DataSift": { + "properties": [ + "datasift.com", + "tweetmeme.com" + ], + "resources": [ + "datasift.com", + "tweetmeme.com" + ] + }, + "DataXu": { + "properties": [ + "dataxu.com", + "mexad.com", + "w55c.net" + ], + "resources": [ + "dataxu.com", + "dataxu.net", + "mexad.com", + "w55c.net" + ] + }, + "Datonics": { + "properties": [ + "datonics.com" + ], + "resources": [ + "datonics.com", + "pro-market.net" + ] + }, + "Datran Media": { + "properties": [ + "datranmedia.com", + "displaymarketplace.com" + ], + "resources": [ + "datranmedia.com", + "displaymarketplace.com" + ] + }, + "Datvantage": { + "properties": [ + "datvantage.com" + ], + "resources": [ + "datvantage.com" + ] + }, + "DC Storm": { + "properties": [ + "dc-storm.com", + "stormiq.com" + ], + "resources": [ + "dc-storm.com", + "stormiq.com" + ] + }, + "Dedicated Media": { + "properties": [ + "dedicatedmedia.com", + "dedicatednetworks.com" + ], + "resources": [ + "dedicatedmedia.com", + "dedicatednetworks.com" + ] + }, + "Deep Intent": { + "properties": [ + "deepintent.com" + ], + "resources": [ + "deepintent.com" + ] + }, + "Delivr": { + "properties": [ + "delivr.com" + ], + "resources": [ + "delivr.com", + "percentmobile.com" + ] + }, + "Delta Projects": { + "properties": [ + "deltaprojects.com" + ], + "resources": [ + "adaction.se", + "de17a.com", + "deltaprojects.com", + "deltaprojects.se" + ] + }, + "Demandbase": { + "properties": [ + "demandbase.com" + ], + "resources": [ + "company-target.com", + "demandbase.com" + ] + }, + "Demand Media": { + "properties": [ + "leafgroup.com" + ], + "resources": [ + "demandmedia.com", + "indieclick.com" + ] + }, + "Deutsche Post DHL": { + "properties": [ + "dpdhl.com" + ], + "resources": [ + "adcloud.com", + "adcloud.net", + "dp-dhl.com", + "dpdhl.com" + ] + }, + "Developer Media": { + "properties": [ + "developermedia.com" + ], + "resources": [ + "developermedia.com", + "lqcdn.com" + ] + }, + "DG": { + "properties": [ + "dgit.com", + "sizmek.com" + ], + "resources": [ + "dgit.com", + "eyeblaster.com", + "eyewonder.com", + "mdadx.com", + "serving-sys.com", + "unicast.com" + ] + }, + "dianomi": { + "properties": [ + "dianomi.com" + ], + "resources": [ + "dianomi.com" + ] + }, + "Didit": { + "properties": [ + "didit.com" + ], + "resources": [ + "did-it.com", + "didit.com" + ] + }, + "Digg": { + "properties": [ + "digg.com" + ], + "resources": [ + "digg.com" + ] + }, + "DigitalAdConsortium": { + "properties": [ + "dac.co.jp" + ], + "resources": [ + "impact-ad.jp" + ] + }, + "Digital River": { + "properties": [ + "digitalriver.com", + "keywordmax.com", + "netflame.cc" + ], + "resources": [ + "digitalriver.com", + "keywordmax.com", + "netflame.cc" + ] + }, + "Digital Target": { + "properties": [ + "digitaltarget.ru" + ], + "resources": [ + "digitaltarget.ru" + ] + }, + "Digitize": { + "properties": [ + "digitize.ie" + ], + "resources": [ + "digitize.ie" + ] + }, + "DirectAdvert": { + "properties": [ + "directadvert.ru" + ], + "resources": [ + "directadvert.ru" + ] + }, + "DirectCORP": { + "properties": [ + "directcorp.de", + "ipcounter.de" + ], + "resources": [ + "directcorp.de", + "ipcounter.de" + ] + }, + "Direct Response Group": { + "properties": [ + "directresponsegroup.com" + ], + "resources": [ + "directresponsegroup.com", + "ppctracking.net" + ] + }, + "Directtrack": { + "properties": [ + "directtrack.com" + ], + "resources": [ + "directtrack.com" + ] + }, + "Disqus": { + "properties": [ + "disqus.com", + "disqusads.com" + ], + "resources": [ + "disqus.com", + "disqusads.com" + ] + }, + "DistilNetworks": { + "properties": [ + "distilnetworks.com" + ], + "resources": [ + "distilnetworks.com", + "distiltag.com" + ] + }, + "DistrictM": { + "properties": [ + "districtm.net" + ], + "resources": [ + "districtm.io" + ] + }, + "dmpxs": { + "properties": [ + "dmpxs.com" + ], + "resources": [ + "dmpxs.com" + ] + }, + "DoublePimp": { + "properties": [ + "doublepimp.com" + ], + "resources": [ + "doublepimp.com" + ] + }, + "DoublePositive": { + "properties": [ + "doublepositive.com" + ], + "resources": [ + "bid-tag.com", + "doublepositive.com" + ] + }, + "DoubleVerify": { + "properties": [ + "doubleverify.com" + ], + "resources": [ + "doubleverify.com" + ] + }, + "Drawbridge": { + "properties": [ + "drawbridge.com" + ], + "resources": [ + "adsymptotic.com", + "drawbrid.ge", + "drawbridge.com" + ] + }, + "DS-IQ": { + "properties": [ + "ds-iq.com" + ], + "resources": [ + "ds-iq.com" + ] + }, + "DSNR Group": { + "properties": [ + "dsnrgroup.com", + "dsnrmg.com", + "traffiliate.com", + "z5x.net" + ], + "resources": [ + "dsnrgroup.com", + "dsnrmg.com", + "traffiliate.com", + "z5x.com", + "z5x.net" + ] + }, + "dwstat.com": { + "properties": [ + "dwstat.cn" + ], + "resources": [ + "dwstat.cn" + ] + }, + "DynAdmic": { + "properties": [ + "dynadmic.com" + ], + "resources": [ + "dynadmic.com", + "dyntrk.com" + ] + }, + "DynamicOxygen": { + "properties": [ + "dynamicoxygen.com", + "exitjunction.com" + ], + "resources": [ + "dynamicoxygen.com", + "exitjunction.com" + ] + }, + "DynamicYield": { + "properties": [ + "dynamicyield.com" + ], + "resources": [ + "dynamicyield.com" + ] + }, + "Earnify": { + "properties": [ + "earnify.com" + ], + "resources": [ + "earnify.com" + ] + }, + "eBay": { + "properties": [ + "ebay.at", + "ebay.ba", + "ebay.be", + "ebay.ca", + "ebay.ch", + "ebay.cn", + "ebay.co.jp", + "ebay.co.kr", + "ebay.co.uk", + "ebay.com", + "ebay.com.au", + "ebay.com.hk", + "ebay.com.my", + "ebay.com.ph", + "ebay.com.sg", + "ebay.com.tw", + "ebay.de", + "ebay.es", + "ebay.fr", + "ebay.ie", + "ebay.in", + "ebay.it", + "ebay.nl", + "ebay.pl" + ], + "resources": [ + "ebay.com" + ] + }, + "Echo": { + "properties": [ + "aboutecho.com", + "haloscan.com", + "js-kit.com" + ], + "resources": [ + "aboutecho.com", + "haloscan.com", + "js-kit.com" + ] + }, + "ECSAnalytics": { + "properties": [ + "ecsanalytics.com", + "theecsinc.com" + ], + "resources": [ + "ecsanalytics.com" + ] + }, + "EFF": { + "properties": [ + "do-not-tracker.org", + "eff.org", + "eviltracker.net", + "trackersimulator.org" + ], + "resources": [ + "do-not-tracker.org", + "eff.org", + "eviltracker.net", + "trackersimulator.org" + ] + }, + "Effective Measure": { + "properties": [ + "effectivemeasure.com", + "effectivemeasure.net" + ], + "resources": [ + "effectivemeasure.com", + "effectivemeasure.net" + ] + }, + "ekolay": { + "properties": [ + "hurriyet.com.tr" + ], + "resources": [ + "e-kolay.net", + "ekolay.net", + "hurriyet.com.tr" + ] + }, + "Eleavers": { + "properties": [ + "eleavers.com" + ], + "resources": [ + "eleavers.com" + ] + }, + "Emego": { + "properties": [ + "usemax.de" + ], + "resources": [ + "usemax.de" + ] + }, + "Emerse": { + "properties": [ + "emerse.com" + ], + "resources": [ + "emerse.com" + ] + }, + "EMX": { + "properties": [ + "emxdigital.com" + ], + "resources": [ + "brealtime.com", + "clearstream.tv", + "emxdgt.com", + "emxdigital.com" + ] + }, + "Enecto": { + "properties": [ + "enecto.com" + ], + "resources": [ + "enecto.com" + ] + }, + "engage:BDR": { + "properties": [ + "engagebdr.com" + ], + "resources": [ + "bnmla.com", + "engagebdr.com" + ] + }, + "Engago Technology": { + "properties": [ + "engago.com" + ], + "resources": [ + "appmetrx.com", + "engago.com" + ] + }, + "Engine Network": { + "properties": [ + "enginenetwork.com" + ], + "resources": [ + "enginenetwork.com" + ] + }, + "Ensighten": { + "properties": [ + "ensighten.com" + ], + "resources": [ + "ensighten.com" + ] + }, + "Entireweb": { + "properties": [ + "entireweb.com" + ], + "resources": [ + "entireweb.com" + ] + }, + "Epic Media Group": { + "properties": [ + "epicadvertising.com", + "epicmarketplace.com", + "theepicmediagroup.com" + ], + "resources": [ + "epicadvertising.com", + "epicmarketplace.com", + "epicmobileads.com", + "theepicmediagroup.com", + "trafficmp.com" + ] + }, + "eProof.com": { + "properties": [ + "eproof.com" + ], + "resources": [ + "eproof.com" + ] + }, + "Epsilon": { + "properties": [ + "epsilon.com" + ], + "resources": [ + "epsilon.com" + ] + }, + "EQ Ads": { + "properties": [ + "eqads.com" + ], + "resources": [ + "eqads.com" + ] + }, + "EroAdvertising": { + "properties": [ + "ero-advertising.com" + ], + "resources": [ + "ero-advertising.com" + ] + }, + "Etarget": { + "properties": [ + "etarget.net", + "etargetnet.com" + ], + "resources": [ + "etarget.net", + "etargetnet.com" + ] + }, + "Etineria": { + "properties": [ + "adwitserver.com", + "etineria.com" + ], + "resources": [ + "adwitserver.com", + "etineria.com" + ] + }, + "etracker": { + "properties": [ + "etracker.com", + "etracker.de" + ], + "resources": [ + "etracker.com", + "etracker.de", + "sedotracker.com", + "sedotracker.de" + ] + }, + "eTrigue": { + "properties": [ + "etrigue.com" + ], + "resources": [ + "etrigue.com" + ] + }, + "Eulerian Technologies": { + "properties": [ + "eulerian.com" + ], + "resources": [ + "eulerian.com", + "eulerian.net" + ] + }, + "Evergage": { + "properties": [ + "evergage.com" + ], + "resources": [ + "mybuys.com", + "veruta.com" + ] + }, + "Everyday Health": { + "properties": [ + "everydayhealth.com", + "waterfrontmedia.com" + ], + "resources": [ + "everydayhealth.com", + "waterfrontmedia.com" + ] + }, + "Evisions Marketing": { + "properties": [ + "engineseeker.com", + "evisionsmarketing.com" + ], + "resources": [ + "engineseeker.com", + "evisionsmarketing.com" + ] + }, + "Evolve": { + "properties": [ + "evolvemediacorp.com", + "gorillanation.com" + ], + "resources": [ + "evolvemediacorp.com", + "evolvemediametrics.com", + "gorillanation.com" + ] + }, + "eWayDirect": { + "properties": [ + "ewaydirect.com" + ], + "resources": [ + "ewaydirect.com", + "ixs1.net" + ] + }, + "ewebse": { + "properties": [ + "777seo.com", + "ewebse.com" + ], + "resources": [ + "777seo.com", + "ewebse.com" + ] + }, + "excitad": { + "properties": [ + "excitad.com" + ], + "resources": [ + "excitad.com" + ] + }, + "eXelate": { + "properties": [ + "exelate.com" + ], + "resources": [ + "exelate.com", + "exelator.com" + ] + }, + "ExoClick": { + "properties": [ + "exoclick.com" + ], + "resources": [ + "exoclick.com" + ] + }, + "Exosrv": { + "properties": [ + "exosrv.com" + ], + "resources": [ + "exosrv.com" + ] + }, + "Experian": { + "properties": [ + "experian.com" + ], + "resources": [ + "audienceiq.com", + "experian.com" + ] + }, + "expo-MAX": { + "properties": [ + "expo-max.com" + ], + "resources": [ + "expo-max.com" + ] + }, + "Exponential Interactive": { + "properties": [ + "exponential.com", + "fulltango.com" + ], + "resources": [ + "adotube.com", + "exponential.com", + "fulltango.com", + "tribalfusion.com" + ] + }, + "Extension Factory": { + "properties": [ + "extensionfactory.com" + ], + "resources": [ + "extensionfactory.com" + ] + }, + "EXTENSIONS.RU": { + "properties": [ + "extensions.ru" + ], + "resources": [ + "extensions.ru" + ] + }, + "eXTReMe digital": { + "properties": [ + "extremetracking.com" + ], + "resources": [ + "extreme-dm.com", + "extremetracking.com" + ] + }, + "Eyeconomy": { + "properties": [ + "eyeconomy.co.uk" + ], + "resources": [ + "eyeconomy.co.uk", + "eyeconomy.com", + "sublimemedia.net", + "www.eyeconomy.co.uk" + ] + }, + "EyeNewton": { + "properties": [ + "eyenewton.ru" + ], + "resources": [ + "eyenewton.ru" + ] + }, + "Eyeota": { + "properties": [ + "eyeota.net" + ], + "resources": [ + "eyeota.net" + ] + }, + "eyeReturn Marketing": { + "properties": [ + "eyereturnmarketing.com" + ], + "resources": [ + "eyereturn.com", + "eyereturnmarketing.com" + ] + }, + "Eyeviewdigital": { + "properties": [ + "eyeviewdigital.com" + ], + "resources": [ + "eyeviewads.com", + "eyeviewdigital.com" + ] + }, + "Facebook": { + "properties": [ + "atlassolutions.com", + "facebook.com", + "facebook.de", + "facebook.fr", + "facebook.net", + "fb.com", + "fb.me", + "fbcdn.net", + "friendfeed.com", + "instagram.com", + "internalfb.com", + "messenger.com", + "oculus.com", + "whatsapp.com", + "workplace.com" + ], + "resources": [ + "apps.fbsbx.com", + "atdmt.com", + "atlassolutions.com", + "facebook.com", + "facebook.de", + "facebook.fr", + "facebook.net", + "fb.com", + "fb.me", + "fbcdn.net", + "fbsbx.com", + "friendfeed.com", + "instagram.com", + "messenger.com" + ] + }, + "Facilitate Digital": { + "properties": [ + "adsfac.eu", + "adsfac.net", + "adsfac.us", + "facilitatedigital.com" + ], + "resources": [ + "adsfac.eu", + "adsfac.info", + "adsfac.net", + "adsfac.sg", + "adsfac.us", + "facilitatedigital.com" + ] + }, + "Fairfax Media": { + "properties": [ + "fairfax.com.au", + "fxj.com.au", + "www.fxj.com.au" + ], + "resources": [ + "fairfax.com.au", + "fxj.com.au", + "www.fxj.com.au" + ] + }, + "faithadnet": { + "properties": [ + "faithadnet.com" + ], + "resources": [ + "faithadnet.com" + ] + }, + "Fanplayr": { + "properties": [ + "fanplayr.com" + ], + "resources": [ + "fanplayr.com" + ] + }, + "Fathom": { + "properties": [ + "fathomdelivers.com", + "fathomseo.com" + ], + "resources": [ + "fathomdelivers.com", + "fathomseo.com" + ] + }, + "Federated Media": { + "properties": [ + "hyfn.com", + "lijit.com" + ], + "resources": [ + "federatedmedia.net", + "fmpub.net", + "hyfn.com", + "lijit.com" + ] + }, + "Feedjit": { + "properties": [ + "feedjit.com" + ], + "resources": [ + "feedjit.com" + ] + }, + "FetchBack": { + "properties": [ + "fetchback.com" + ], + "resources": [ + "fetchback.com" + ] + }, + "Fiksu": { + "properties": [ + "fiksu.com" + ], + "resources": [ + "fiksu.com" + ] + }, + "FinancialContent": { + "properties": [ + "financialcontent.com" + ], + "resources": [ + "financialcontent.com" + ] + }, + "Fizz-Buzz Media": { + "properties": [ + "fizzbuzzmedia.com", + "fizzbuzzmedia.net" + ], + "resources": [ + "fizzbuzzmedia.com", + "fizzbuzzmedia.net" + ] + }, + "Flashtalking": { + "properties": [ + "flashtalking.com" + ], + "resources": [ + "encoremetrics.com", + "flashtalking.com", + "sitecompass.com" + ] + }, + "Flattr": { + "properties": [ + "flattr.com" + ], + "resources": [ + "flattr.com" + ] + }, + "Flite": { + "properties": [ + "flite.com", + "widgetserver.com" + ], + "resources": [ + "flite.com", + "widgetserver.com" + ] + }, + "Fluct": { + "properties": [ + "adingo.jp", + "fluct.jp" + ], + "resources": [ + "adingo.jp", + "fluct.jp" + ] + }, + "Flytxt": { + "properties": [ + "flytxt.com" + ], + "resources": [ + "flytxt.com" + ] + }, + "Footprint": { + "properties": [ + "footprintlive.com" + ], + "resources": [ + "footprintlive.com" + ] + }, + "Forbes": { + "properties": [ + "brandsideplatform.com", + "forbes.com" + ], + "resources": [ + "brandsideplatform.com", + "forbes.com" + ] + }, + "Foresee": { + "properties": [ + "foresee.com" + ], + "resources": [ + "answerscloud.com" + ] + }, + "Fox One Stop Media": { + "properties": [ + "fimserve.com", + "foxnetworks.com", + "foxonestop.com", + "mobsmith.com", + "myads.com", + "othersonline.com" + ], + "resources": [ + "fimserve.com", + "foxnetworks.com", + "foxonestop.com", + "mobsmith.com", + "myads.com", + "othersonline.com" + ] + }, + "FreakOut": { + "properties": [ + "fout.jp" + ], + "resources": [ + "fout.jp" + ] + }, + "Freedom Communications": { + "properties": [ + "freedom.com" + ], + "resources": [ + "freedom.com" + ] + }, + "Free Online Users": { + "properties": [ + "freeonlineusers.com" + ], + "resources": [ + "freeonlineusers.com" + ] + }, + "Free-PageRank.com": { + "properties": [ + "free-pagerank.com" + ], + "resources": [ + "free-pagerank.com" + ] + }, + "FreeWheel": { + "properties": [ + "freewheel.tv", + "fwmrm.net" + ], + "resources": [ + "freewheel.tv", + "fwmrm.net", + "stickyadstv.com" + ] + }, + "FriendFinder Networks": { + "properties": [ + "adultfriendfinder.com", + "ffn.com", + "pop6.com" + ], + "resources": [ + "adultfriendfinder.com", + "ffn.com", + "pop6.com" + ] + }, + "Friends2Follow": { + "properties": [ + "friends2follow.com" + ], + "resources": [ + "friends2follow.com" + ] + }, + "Frog Sex": { + "properties": [ + "double-check.com", + "frogsex.com" + ], + "resources": [ + "double-check.com", + "frogsex.com" + ] + }, + "FuelX": { + "properties": [ + "fuelx.com" + ], + "resources": [ + "fuel451.com" + ] + }, + "Fullstory": { + "properties": [ + "fullstory.com" + ], + "resources": [ + "fullstory.com" + ] + }, + "Future Ads": { + "properties": [ + "futureads.com", + "resultlinks.com" + ], + "resources": [ + "futureads.com", + "resultlinks.com" + ] + }, + "Fyber": { + "properties": [ + "fyber.com" + ], + "resources": [ + "fyber.com" + ] + }, + "Game Advertising Online": { + "properties": [ + "game-advertising-online.com" + ], + "resources": [ + "game-advertising-online.com" + ] + }, + "Games2win": { + "properties": [ + "games2win.com", + "inviziads.com" + ], + "resources": [ + "games2win.com", + "inviziads.com" + ] + }, + "Gamned": { + "properties": [ + "gamned.com" + ], + "resources": [ + "gamned.com" + ] + }, + "Gannett": { + "properties": [ + "gannett.com", + "pointroll.com" + ], + "resources": [ + "gannett.com", + "pointroll.com" + ] + }, + "GB-World": { + "properties": [ + "gb-world.net" + ], + "resources": [ + "gb-world.net" + ] + }, + "Gemius": { + "properties": [ + "gemius.com", + "gemius.pl" + ], + "resources": [ + "gemius.com", + "gemius.pl" + ] + }, + "Genesis Media": { + "properties": [ + "genesismedia.com" + ], + "resources": [ + "genesismedia.com", + "genesismediaus.com" + ] + }, + "GENIEE": { + "properties": [ + "geniee.co.jp" + ], + "resources": [ + "geniee.co.jp", + "gssprt.jp" + ] + }, + "GENIE GROUP": { + "properties": [ + "geniegroupltd.co.uk", + "www.geniegroupltd.co.uk" + ], + "resources": [ + "geniegroupltd.co.uk", + "www.geniegroupltd.co.uk" + ] + }, + "Genius.com": { + "properties": [ + "genius.com", + "rsvpgenius.com" + ], + "resources": [ + "genius.com", + "rsvpgenius.com" + ] + }, + "GeoAds": { + "properties": [ + "geoads.com" + ], + "resources": [ + "geoads.com" + ] + }, + "GetGlue": { + "properties": [ + "elfie.com", + "smrtlnks.com" + ], + "resources": [ + "getglue.com", + "smrtlnks.com" + ] + }, + "GetIntent": { + "properties": [ + "adhigh.net", + "getintent.com" + ], + "resources": [ + "adhigh.net", + "getintent.com" + ] + }, + "Get Satisfaction": { + "properties": [ + "getsatisfaction.com" + ], + "resources": [ + "getsatisfaction.com" + ] + }, + "GetSiteControl": { + "properties": [ + "getsitecontrol.com" + ], + "resources": [ + "getsitecontrol.com" + ] + }, + "GfK Group": { + "properties": [ + "gfk.com" + ], + "resources": [ + "daphnecm.com", + "gfk.com", + "gfkdaphne.com" + ] + }, + "Gigya": { + "properties": [ + "gigya.com" + ], + "resources": [ + "gigcount.com", + "gigya.com" + ] + }, + "GISMAds": { + "properties": [ + "gismads.jp" + ], + "resources": [ + "gismads.jp" + ] + }, + "GitHub": { + "properties": [ + "gaug.es", + "github.com" + ], + "resources": [ + "gaug.es", + "github.com" + ] + }, + "Glam Media": { + "properties": [ + "glam.com", + "glammedia.com" + ], + "resources": [ + "glam.com", + "glammedia.com" + ] + }, + "Gleam": { + "properties": [ + "gleam.io" + ], + "resources": [ + "fraudjs.io", + "gleam.io" + ] + }, + "Global Takeoff": { + "properties": [ + "globaltakeoff.com", + "globaltakeoff.net" + ], + "resources": [ + "globaltakeoff.com", + "globaltakeoff.net" + ] + }, + "Globe7": { + "properties": [ + "globe7.com" + ], + "resources": [ + "globe7.com" + ] + }, + "Go Daddy": { + "properties": [ + "godaddy.com", + "trafficfacts.com" + ], + "resources": [ + "godaddy.com", + "trafficfacts.com" + ] + }, + "GoDataFeed": { + "properties": [ + "godatafeed.com" + ], + "resources": [ + "godatafeed.com" + ] + }, + "GoGrid": { + "properties": [ + "datapipe.com", + "formalyzer.com" + ], + "resources": [ + "datapipe.com", + "formalyzer.com", + "gogrid.com", + "komli.net" + ] + }, + "Goldbach": { + "properties": [ + "goldbachgroup.com" + ], + "resources": [ + "goldbach.com", + "goldbachgroup.com" + ] + }, + "GoldSpot Media": { + "properties": [ + "goldspotmedia.com" + ], + "resources": [ + "goldspotmedia.com" + ] + }, + "Google": { + "properties": [ + "abc.xyz", + "admeld.com", + "blogger.com", + "blogspot.com", + "crashlytics.com", + "google-melange.com", + "google.ac", + "google.ad", + "google.ae", + "google.al", + "google.am", + "google.as", + "google.at", + "google.az", + "google.ba", + "google.be", + "google.bf", + "google.bg", + "google.bi", + "google.bj", + "google.bs", + "google.bt", + "google.by", + "google.ca", + "google.cat", + "google.cd", + "google.cf", + "google.cg", + "google.ch", + "google.ci", + "google.cl", + "google.cm", + "google.cn", + "google.co.ao", + "google.co.bw", + "google.co.ck", + "google.co.cr", + "google.co.id", + "google.co.il", + "google.co.in", + "google.co.jp", + "google.co.ke", + "google.co.kr", + "google.co.ls", + "google.co.ma", + "google.co.mz", + "google.co.nz", + "google.co.th", + "google.co.tz", + "google.co.ug", + "google.co.uk", + "google.co.uz", + "google.co.ve", + "google.co.vi", + "google.co.za", + "google.co.zm", + "google.co.zw", + "google.com", + "google.com.af", + "google.com.ag", + "google.com.ai", + "google.com.ar", + "google.com.au", + "google.com.bd", + "google.com.bh", + "google.com.bn", + "google.com.bo", + "google.com.br", + "google.com.bz", + "google.com.co", + "google.com.cu", + "google.com.cy", + "google.com.do", + "google.com.ec", + "google.com.eg", + "google.com.et", + "google.com.fj", + "google.com.gh", + "google.com.gi", + "google.com.gt", + "google.com.hk", + "google.com.jm", + "google.com.kh", + "google.com.kw", + "google.com.lb", + "google.com.ly", + "google.com.mm", + "google.com.mt", + "google.com.mx", + "google.com.my", + "google.com.na", + "google.com.nf", + "google.com.ng", + "google.com.ni", + "google.com.np", + "google.com.om", + "google.com.pa", + "google.com.pe", + "google.com.pg", + "google.com.ph", + "google.com.pk", + "google.com.pr", + "google.com.py", + "google.com.qa", + "google.com.sa", + "google.com.sb", + "google.com.sg", + "google.com.sl", + "google.com.sv", + "google.com.tj", + "google.com.tr", + "google.com.tw", + "google.com.ua", + "google.com.uy", + "google.com.vc", + "google.com.vn", + "google.cv", + "google.cz", + "google.de", + "google.dj", + "google.dk", + "google.dm", + "google.dz", + "google.ee", + "google.es", + "google.fi", + "google.fm", + "google.fr", + "google.ga", + "google.ge", + "google.gg", + "google.gl", + "google.gm", + "google.gp", + "google.gr", + "google.gy", + "google.hn", + "google.hr", + "google.ht", + "google.hu", + "google.ie", + "google.im", + "google.iq", + "google.is", + "google.it", + "google.je", + "google.jo", + "google.kg", + "google.ki", + "google.kz", + "google.la", + "google.li", + "google.lk", + "google.lt", + "google.lu", + "google.lv", + "google.md", + "google.me", + "google.mg", + "google.mk", + "google.ml", + "google.mn", + "google.ms", + "google.mu", + "google.mv", + "google.mw", + "google.ne", + "google.nl", + "google.no", + "google.nr", + "google.nu", + "google.pl", + "google.pn", + "google.ps", + "google.pt", + "google.ro", + "google.rs", + "google.ru", + "google.rw", + "google.sc", + "google.se", + "google.sh", + "google.si", + "google.sk", + "google.sm", + "google.sn", + "google.so", + "google.st", + "google.td", + "google.tg", + "google.tk", + "google.tl", + "google.tm", + "google.tn", + "google.to", + "google.tt", + "google.vg", + "google.vu", + "google.ws", + "googlesource.com", + "ingress.com", + "nest.com", + "panoramio.com", + "pinpoint-dot-chromeperf.appspot.com", + "youtube.com" + ], + "resources": [ + "2mdn.net", + "admeld.com", + "admob.com", + "apture.com", + "blogger.com", + "cc-dt.com", + "crashlytics.com", + "destinationurl.com", + "doubleclick.net", + "ggpht.com", + "gmail.com", + "gmodules.com", + "google-analytics.com", + "google.ac", + "google.ad", + "google.ae", + "google.al", + "google.am", + "google.as", + "google.at", + "google.az", + "google.ba", + "google.be", + "google.bf", + "google.bg", + "google.bi", + "google.bj", + "google.bs", + "google.bt", + "google.by", + "google.ca", + "google.cat", + "google.cc", + "google.cd", + "google.cf", + "google.cg", + "google.ch", + "google.ci", + "google.cl", + "google.cm", + "google.cn", + "google.co.ao", + "google.co.bw", + "google.co.ck", + "google.co.cr", + "google.co.id", + "google.co.il", + "google.co.in", + "google.co.jp", + "google.co.ke", + "google.co.kr", + "google.co.ls", + "google.co.ma", + "google.co.mz", + "google.co.nz", + "google.co.th", + "google.co.tz", + "google.co.ug", + "google.co.uk", + "google.co.uz", + "google.co.ve", + "google.co.vi", + "google.co.za", + "google.co.zm", + "google.co.zw", + "google.com", + "google.com.af", + "google.com.ag", + "google.com.ai", + "google.com.ar", + "google.com.au", + "google.com.bd", + "google.com.bh", + "google.com.bn", + "google.com.bo", + "google.com.br", + "google.com.bz", + "google.com.co", + "google.com.cu", + "google.com.cy", + "google.com.do", + "google.com.ec", + "google.com.eg", + "google.com.et", + "google.com.fj", + "google.com.gh", + "google.com.gi", + "google.com.gt", + "google.com.hk", + "google.com.jm", + "google.com.kh", + "google.com.kw", + "google.com.lb", + "google.com.lc", + "google.com.ly", + "google.com.mm", + "google.com.mt", + "google.com.mx", + "google.com.my", + "google.com.na", + "google.com.nf", + "google.com.ng", + "google.com.ni", + "google.com.np", + "google.com.om", + "google.com.pa", + "google.com.pe", + "google.com.pg", + "google.com.ph", + "google.com.pk", + "google.com.pr", + "google.com.py", + "google.com.qa", + "google.com.sa", + "google.com.sb", + "google.com.sg", + "google.com.sl", + "google.com.sv", + "google.com.tj", + "google.com.tn", + "google.com.tr", + "google.com.tw", + "google.com.ua", + "google.com.uy", + "google.com.vc", + "google.com.vn", + "google.cv", + "google.cz", + "google.de", + "google.dj", + "google.dk", + "google.dm", + "google.dz", + "google.ee", + "google.es", + "google.fi", + "google.fm", + "google.fr", + "google.ga", + "google.ge", + "google.gf", + "google.gg", + "google.gl", + "google.gm", + "google.gp", + "google.gr", + "google.gy", + "google.hn", + "google.hr", + "google.ht", + "google.hu", + "google.ie", + "google.im", + "google.io", + "google.iq", + "google.is", + "google.it", + "google.je", + "google.jo", + "google.kg", + "google.ki", + "google.kz", + "google.la", + "google.li", + "google.lk", + "google.lt", + "google.lu", + "google.lv", + "google.md", + "google.me", + "google.mg", + "google.mk", + "google.ml", + "google.mn", + "google.ms", + "google.mu", + "google.mv", + "google.mw", + "google.ne", + "google.nl", + "google.no", + "google.nr", + "google.nu", + "google.pl", + "google.pn", + "google.ps", + "google.pt", + "google.ro", + "google.rs", + "google.ru", + "google.rw", + "google.sc", + "google.se", + "google.sh", + "google.si", + "google.sk", + "google.sm", + "google.sn", + "google.so", + "google.st", + "google.td", + "google.tg", + "google.tk", + "google.tl", + "google.tm", + "google.tn", + "google.to", + "google.tt", + "google.vg", + "google.vu", + "google.ws", + "googleadservices.com", + "googleapis.com", + "googlemail.com", + "googlesyndication.com", + "googletagservices.com", + "googleusercontent.com", + "googlevideo.com", + "gstatic.com", + "invitemedia.com", + "postrank.com", + "recaptcha.net", + "smtad.net", + "youtube.com" + ] + }, + "GoSquared": { + "properties": [ + "gosquared.com" + ], + "resources": [ + "gosquared.com" + ] + }, + "GoStats": { + "properties": [ + "gostats.com" + ], + "resources": [ + "gostats.com" + ] + }, + "Grapeshot": { + "properties": [ + "grapeshot.co.uk", + "www.grapeshot.co.uk" + ], + "resources": [ + "grapeshot.co.uk", + "www.grapeshot.co.uk" + ] + }, + "GrapheneMedia": { + "properties": [ + "graphenemedia.in" + ], + "resources": [ + "graphenedigitalanalytics.in" + ] + }, + "Graphnium": { + "properties": [ + "graphinium.com" + ], + "resources": [ + "crm4d.com" + ] + }, + "Gravity": { + "properties": [ + "gravity.com", + "grvcdn.com" + ], + "resources": [ + "gravity.com", + "grvcdn.com" + ] + }, + "Gridcash": { + "properties": [ + "adless.io", + "gridcash.net" + ], + "resources": [ + "adless.io", + "gridcash.net" + ] + }, + "Grocery Shopping Network": { + "properties": [ + "groceryshopping.net" + ], + "resources": [ + "groceryshopping.net" + ] + }, + "GroovinAds": { + "properties": [ + "groovinads.com" + ], + "resources": [ + "groovinads.com" + ] + }, + "Gruner + Jahr": { + "properties": [ + "guj.de", + "ligatus.com" + ], + "resources": [ + "guj.de", + "ligatus.com" + ] + }, + "GTop": { + "properties": [ + "arenaweb.ro" + ], + "resources": [ + "arenaweb.ro", + "gtop.ro", + "gtopstats.com" + ] + }, + "GumGum": { + "properties": [ + "gumgum.com" + ], + "resources": [ + "gumgum.com" + ] + }, + "Gunggo": { + "properties": [ + "gunggo.com" + ], + "resources": [ + "gunggo.com" + ] + }, + "Hands Mobile": { + "properties": [ + "hands.com.br", + "www.hands.com.br" + ], + "resources": [ + "hands.com.br", + "www.hands.com.br" + ] + }, + "Harrenmedia": { + "properties": [ + "harrenmedia.com", + "harrenmedianetwork.com" + ], + "resources": [ + "harrenmedia.com", + "harrenmedianetwork.com" + ] + }, + "HealthPricer": { + "properties": [ + "adacado.com", + "healthpricer.com" + ], + "resources": [ + "adacado.com", + "healthpricer.com" + ] + }, + "Hearst": { + "properties": [ + "hearst.com", + "ic-live.com", + "iclive.com", + "icrossing.com", + "raasnet.com" + ], + "resources": [ + "hearst.com", + "ic-live.com", + "iclive.com", + "icrossing.com", + "raasnet.com", + "redaril.com", + "sptag.com", + "sptag1.com", + "sptag2.com", + "sptag3.com" + ] + }, + "Heyzap": { + "properties": [ + "heyzap.com" + ], + "resources": [ + "heyzap.com" + ] + }, + "HilltopAds": { + "properties": [ + "hilltopads.com" + ], + "resources": [ + "hilltopads.com", + "hilltopads.net", + "shoporielder.pro" + ] + }, + "Hi-media": { + "properties": [ + "himediagroup.com" + ], + "resources": [ + "comclick.com", + "hi-media.com", + "himediagroup.com" + ] + }, + "Histats": { + "properties": [ + "histats.com" + ], + "resources": [ + "histats.com" + ] + }, + "HitsLink": { + "properties": [ + "hitslink.com" + ], + "resources": [ + "hitslink.com" + ] + }, + "Hit Sniffer": { + "properties": [ + "hitsniffer.com" + ], + "resources": [ + "hitsniffer.com" + ] + }, + "Horyzon Media": { + "properties": [ + "horyzon-media.com" + ], + "resources": [ + "horyzon-media.com" + ] + }, + "HotelChamp": { + "properties": [ + "hotelchamp.com" + ], + "resources": [ + "hotelchamp.com" + ] + }, + "Hotjar": { + "properties": [ + "hotjar.com" + ], + "resources": [ + "hotjar.com" + ] + }, + "HotMart": { + "properties": [ + "hotmart.com" + ], + "resources": [ + "hotmart.com" + ] + }, + "HOTWords": { + "properties": [ + "hotwords.com", + "hotwords.es" + ], + "resources": [ + "hotwords.com", + "hotwords.es" + ] + }, + "HP": { + "properties": [ + "hp.com", + "opentext.com", + "optimost.com" + ], + "resources": [ + "hp.com", + "optimost.com" + ] + }, + "Httpool": { + "properties": [ + "httpool.com" + ], + "resources": [ + "httpool.com" + ] + }, + "HubSpot": { + "properties": [ + "hubspot.com" + ], + "resources": [ + "hs-analytics.net", + "hubspot.com" + ] + }, + "HUNT Mobile Ads": { + "properties": [ + "huntmads.com" + ], + "resources": [ + "huntmads.com" + ] + }, + "Hurra.com": { + "properties": [ + "hurra.com" + ], + "resources": [ + "hurra.com" + ] + }, + "IAB": { + "properties": [ + "digitru.st", + "iabtechlab.com" + ], + "resources": [ + "digitru.st" + ] + }, + "IAC": { + "properties": [ + "iac.com", + "iacadvertising.com" + ], + "resources": [ + "iac.com", + "iacadvertising.com" + ] + }, + "iBehavior": { + "properties": [ + "i-behavior.com", + "ib-ibi.com" + ], + "resources": [ + "i-behavior.com", + "ib-ibi.com" + ] + }, + "IBM": { + "properties": [ + "ibm.com", + "multicloud-ibm.com" + ], + "resources": [ + "cmcore.com", + "coremetrics.com", + "ibm.com", + "unica.com", + "xtify.com" + ] + }, + "ID5": { + "properties": [ + "id5.io" + ], + "resources": [ + "id5-sync.com" + ] + }, + "IDG": { + "properties": [ + "idg.com", + "idgtechnetwork.com" + ], + "resources": [ + "idg.com", + "idgtechnetwork.com" + ] + }, + "iEntry": { + "properties": [ + "600z.com", + "ientry.com" + ], + "resources": [ + "600z.com", + "ientry.com" + ] + }, + "IgnitAd": { + "properties": [ + "ignitad.com" + ], + "resources": [ + "ignitad.com" + ] + }, + "IgnitionOne": { + "properties": [ + "ignitionone.com", + "ignitionone.net", + "searchignite.com" + ], + "resources": [ + "ignitionone.com", + "ignitionone.net", + "searchignite.com" + ] + }, + "iMedia": { + "properties": [ + "imedia.cz" + ], + "resources": [ + "imedia.cz" + ] + }, + "Improve Digital": { + "properties": [ + "360yield.com", + "improvedigital.com" + ], + "resources": [ + "360yield.com", + "improvedigital.com" + ] + }, + "Inadco": { + "properties": [ + "inadco.com" + ], + "resources": [ + "anadcoads.com", + "inadco.com", + "inadcoads.com" + ] + }, + "InboundWriter": { + "properties": [ + "enquisite.com", + "inboundwriter.com" + ], + "resources": [ + "enquisite.com", + "inboundwriter.com" + ] + }, + "IndexExchange": { + "properties": [ + "indexexchange.com" + ], + "resources": [ + "indexexchange.com" + ] + }, + "Infectious Media": { + "properties": [ + "infectiousmedia.com" + ], + "resources": [ + "impressiondesk.com", + "infectiousmedia.com" + ] + }, + "Infernotions": { + "properties": [ + "infernotions.com" + ], + "resources": [ + "infernotions.com" + ] + }, + "Inflection Point Media": { + "properties": [ + "inflectionpointmedia.com" + ], + "resources": [ + "inflectionpointmedia.com" + ] + }, + "Infogroup": { + "properties": [ + "infogroup.com" + ], + "resources": [ + "infogroup.com" + ] + }, + "Infolinks": { + "properties": [ + "infolinks.com" + ], + "resources": [ + "infolinks.com" + ] + }, + "INFOnline": { + "properties": [ + "infonline.de" + ], + "resources": [ + "infonline.de", + "ioam.de", + "ivwbox.de" + ] + }, + "InfoStars": { + "properties": [ + "hotlog.ru", + "infostars.ru" + ], + "resources": [ + "hotlog.ru", + "infostars.ru" + ] + }, + "Infra-Ad": { + "properties": [ + "infra-ad.com" + ], + "resources": [ + "infra-ad.com" + ] + }, + "InMobi": { + "properties": [ + "aerserv.com", + "inmobi.com", + "sproutinc.com" + ], + "resources": [ + "aerserv.com", + "inmobi.com", + "sproutinc.com" + ] + }, + "inneractive": { + "properties": [ + "inner-active.com" + ], + "resources": [ + "inner-active.com" + ] + }, + "Innity": { + "properties": [ + "innity.com" + ], + "resources": [ + "innity.com" + ] + }, + "InsightExpress": { + "properties": [ + "insightexpress.com" + ], + "resources": [ + "insightexpress.com", + "insightexpressai.com" + ] + }, + "InSkin Media": { + "properties": [ + "inskinmedia.com" + ], + "resources": [ + "inskinmedia.com" + ] + }, + "Inspectlet": { + "properties": [ + "inspectlet.com" + ], + "resources": [ + "inspectlet.com" + ] + }, + "Instinctive": { + "properties": [ + "instinctive.io" + ], + "resources": [ + "instinctive.io", + "instinctiveads.com" + ] + }, + "Integral Ad Science": { + "properties": [ + "integralads.com" + ], + "resources": [ + "adsafemedia.com", + "adsafeprotected.com", + "iasds01.com", + "integralads.com" + ] + }, + "IntelligenceFocus": { + "properties": [ + "intelligencefocus.com", + "leadchampion.com" + ], + "resources": [ + "domodomain.com", + "intelligencefocus.com", + "leadchampion.com" + ] + }, + "Intent Media": { + "properties": [ + "intentmedia.com" + ], + "resources": [ + "intentmedia.com", + "intentmedia.net" + ] + }, + "Intergi": { + "properties": [ + "intergi.com" + ], + "resources": [ + "intergi.com" + ] + }, + "Intermarkets": { + "properties": [ + "intermarkets.net" + ], + "resources": [ + "intermarkets.net" + ] + }, + "Intermundo Media": { + "properties": [ + "intermundomedia.com" + ], + "resources": [ + "intermundomedia.com" + ] + }, + "Internet Brands": { + "properties": [ + "ibpxl.com", + "internetbrands.com" + ], + "resources": [ + "ibpxl.com", + "internetbrands.com" + ] + }, + "Interpolls": { + "properties": [ + "interpolls.com" + ], + "resources": [ + "interpolls.com" + ] + }, + "Inuvo": { + "properties": [ + "inuvo.com" + ], + "resources": [ + "inuvo.com" + ] + }, + "InvestingChannel": { + "properties": [ + "investingchannel.com" + ], + "resources": [ + "investingchannel.com" + ] + }, + "iovation": { + "properties": [ + "iovation.com" + ], + "resources": [ + "iesnare.com", + "iovation.com" + ] + }, + "iPerceptions": { + "properties": [ + "iperceptions.com" + ], + "resources": [ + "iperceptions.com" + ] + }, + "IponWeb": { + "properties": [ + "iponweb.com" + ], + "resources": [ + "iponweb.com", + "iponweb.net" + ] + }, + "iPROM": { + "properties": [ + "centraliprom.com", + "iprom.net", + "iprom.si", + "mediaiprom.com" + ], + "resources": [ + "centraliprom.com", + "iprom.net", + "iprom.si", + "mediaiprom.com" + ] + }, + "iPromote": { + "properties": [ + "ipromote.com" + ], + "resources": [ + "ipromote.com" + ] + }, + "iProspect": { + "properties": [ + "iprospect.com" + ], + "resources": [ + "clickmanage.com", + "iprospect.com" + ] + }, + "ISI Technologies": { + "properties": [ + "adversalservers.com", + "digbro.com" + ], + "resources": [ + "adversalservers.com", + "digbro.com" + ] + }, + "IslayTech": { + "properties": [ + "islay.tech" + ], + "resources": [ + "islay.tech" + ] + }, + "ismatlab.com": { + "properties": [ + "ismatlab.com" + ], + "resources": [ + "ismatlab.com" + ] + }, + "Itch": { + "properties": [ + "itch.io" + ], + "resources": [ + "itch.io" + ] + }, + "ItIsATracker": { + "properties": [ + "itisatracker.com" + ], + "resources": [ + "itisatracker.com" + ] + }, + "I.UA": { + "properties": [ + "i.ua" + ], + "resources": [ + "i.ua" + ] + }, + "Jaroop": { + "properties": [ + "jaroop.com" + ], + "resources": [ + "jaroop.com" + ] + }, + "JasperLabs": { + "properties": [ + "jasperlabs.com" + ], + "resources": [ + "jasperlabs.com" + ] + }, + "Jemm": { + "properties": [ + "jemmgroup.com" + ], + "resources": [ + "jemmgroup.com" + ] + }, + "Jink": { + "properties": [ + "jink.de", + "jinkads.com" + ], + "resources": [ + "jink.de", + "jinkads.com" + ] + }, + "Jirbo": { + "properties": [ + "adcolony.com" + ], + "resources": [ + "adcolony.com", + "jirbo.com" + ] + }, + "Jivox": { + "properties": [ + "jivox.com" + ], + "resources": [ + "jivox.com" + ] + }, + "JobThread": { + "properties": [ + "jobthread.com" + ], + "resources": [ + "jobthread.com" + ] + }, + "JSE": { + "properties": [ + "jsecoin.com" + ], + "resources": [ + "freecontent.bid", + "freecontent.date", + "freecontent.stream", + "hashing.win", + "hostingcloud.racing", + "hostingcloud.science", + "jsecoin.com" + ] + }, + "JuicyAds": { + "properties": [ + "juicyads.com" + ], + "resources": [ + "juicyads.com" + ] + }, + "Jumptap": { + "properties": [ + "jumptap.com" + ], + "resources": [ + "jumptap.com" + ] + }, + "justuno": { + "properties": [ + "justuno.com" + ], + "resources": [ + "justuno.com" + ] + }, + "Kaltura": { + "properties": [ + "kaltura.com" + ], + "resources": [ + "kaltura.com" + ] + }, + "Kargo": { + "properties": [ + "kargo.com" + ], + "resources": [ + "kargo.com" + ] + }, + "Kenshoo": { + "properties": [ + "kenshoo.com", + "xg4ken.com" + ], + "resources": [ + "kenshoo.com", + "xg4ken.com" + ] + }, + "Keyade": { + "properties": [ + "keyade.com" + ], + "resources": [ + "keyade.com" + ] + }, + "KeyMetric": { + "properties": [ + "keymetric.net" + ], + "resources": [ + "keymetric.net" + ] + }, + "Keywee": { + "properties": [ + "keywee.co" + ], + "resources": [ + "keywee.co" + ] + }, + "kikin": { + "properties": [ + "kikin.com" + ], + "resources": [ + "kikin.com" + ] + }, + "KISSmetrics": { + "properties": [ + "kissmetrics.com" + ], + "resources": [ + "kissmetrics.com" + ] + }, + "KissMyAds": { + "properties": [ + "kissmyads.com" + ], + "resources": [ + "kissmyads.com" + ] + }, + "Kitara Media": { + "properties": [ + "103092804.com", + "kitaramedia.com" + ], + "resources": [ + "103092804.com", + "kitaramedia.com" + ] + }, + "Kitcode": { + "properties": [ + "kitcode.net" + ], + "resources": [ + "kitcode.net" + ] + }, + "KIT digital": { + "properties": [ + "kitd.com" + ], + "resources": [ + "keewurd.com", + "kitd.com", + "peerset.com" + ] + }, + "Kokteyl": { + "properties": [ + "admost.com", + "kokteyl.com" + ], + "resources": [ + "admost.com", + "kokteyl.com" + ] + }, + "Komli": { + "properties": [ + "komli.com" + ], + "resources": [ + "komli.com" + ] + }, + "Konduto": { + "properties": [ + "konduto.com" + ], + "resources": [ + "k-analytix.com", + "konduto.com" + ] + }, + "Kontera": { + "properties": [ + "kontera.com" + ], + "resources": [ + "kontera.com" + ] + }, + "Korrelate": { + "properties": [ + "korrelate.com" + ], + "resources": [ + "adsummos.com", + "adsummos.net", + "korrelate.com" + ] + }, + "Krux": { + "properties": [ + "krux.com", + "kruxdigital.com" + ], + "resources": [ + "krux.com", + "kruxdigital.com", + "krxd.net" + ] + }, + "Lakana": { + "properties": [ + "lakana.com" + ], + "resources": [ + "ibsys.com", + "lakana.com" + ] + }, + "Layer-Ad.org": { + "properties": [ + "layer-ad.org" + ], + "resources": [ + "layer-ad.org" + ] + }, + "Layer Ads": { + "properties": [ + "layer-ads.net" + ], + "resources": [ + "layer-ads.net" + ] + }, + "LeadBolt": { + "properties": [ + "leadbolt.com" + ], + "resources": [ + "leadbolt.com" + ] + }, + "LeadForensics": { + "properties": [ + "leadforensics.com" + ], + "resources": [ + "leadforensics.com" + ] + }, + "LeadFormix": { + "properties": [ + "calliduscloud.com", + "leadforce1.com", + "leadformix.com" + ], + "resources": [ + "calliduscloud.com", + "leadforce1.com", + "leadformix.com" + ] + }, + "LeadsHub": { + "properties": [ + "ztsrv.com" + ], + "resources": [ + "ztsrv.com" + ] + }, + "LeanPlum": { + "properties": [ + "leanplum.com" + ], + "resources": [ + "leanplum.com" + ] + }, + "Legolas Media": { + "properties": [ + "legolas-media.com" + ], + "resources": [ + "legolas-media.com" + ] + }, + "Levexis": { + "properties": [ + "levexis.com" + ], + "resources": [ + "levexis.com" + ] + }, + "Lexos Media": { + "properties": [ + "adbull.com", + "lexosmedia.com" + ], + "resources": [ + "adbull.com", + "lexosmedia.com" + ] + }, + "LifeStreet": { + "properties": [ + "lfstmedia.com", + "lifestreetmedia.com" + ], + "resources": [ + "lfstmedia.com", + "lifestreetmedia.com" + ] + }, + "Limelight Networks": { + "properties": [ + "limelight.com" + ], + "resources": [ + "clickability.com", + "limelight.com", + "llnwd.net" + ] + }, + "LineZing": { + "properties": [ + "linezing.com" + ], + "resources": [ + "linezing.com" + ] + }, + "LinkConnector": { + "properties": [ + "linkconnector.com" + ], + "resources": [ + "linkconnector.com" + ] + }, + "LinkedIn": { + "properties": [ + "linkedin.com" + ], + "resources": [ + "licdn.com", + "linkedin.com" + ] + }, + "LinkShare": { + "properties": [ + "rakutenmarketing.com" + ], + "resources": [ + "linkshare.com", + "linksynergy.com", + "rakutenmarketing.com" + ] + }, + "Linkz": { + "properties": [ + "linkz.net" + ], + "resources": [ + "linkz.net" + ] + }, + "Listrak": { + "properties": [ + "listrak.com", + "listrakbi.com" + ], + "resources": [ + "listrak.com", + "listrakbi.com" + ] + }, + "LiveIntent": { + "properties": [ + "liveintent.com" + ], + "resources": [ + "liadm.com", + "liveintent.com" + ] + }, + "LiveInternet": { + "properties": [ + "liveinternet.ru", + "yadro.ru" + ], + "resources": [ + "liveinternet.ru", + "yadro.ru" + ] + }, + "LivePerson": { + "properties": [ + "liveperson.com" + ], + "resources": [ + "liveperson.com", + "liveperson.net", + "nuconomy.com" + ] + }, + "LiveRail": { + "properties": [ + "liverail.com" + ], + "resources": [ + "liverail.com" + ] + }, + "LiveRamp": { + "properties": [ + "liveramp.com" + ], + "resources": [ + "liveramp.com", + "tvpixel.com" + ] + }, + "LKQD": { + "properties": [ + "lkqd.com", + "lkqd.net" + ], + "resources": [ + "lkqd.com", + "lkqd.net" + ] + }, + "Local Yokel Media": { + "properties": [ + "localyokelmedia.com" + ], + "resources": [ + "localyokelmedia.com" + ] + }, + "Localytics": { + "properties": [ + "localytics.com" + ], + "resources": [ + "localytics.com" + ] + }, + "LockerDome": { + "properties": [ + "lockerdome.com" + ], + "resources": [ + "lockerdome.com" + ] + }, + "Lockerz": { + "properties": [ + "lockerz.com" + ], + "resources": [ + "lockerz.com" + ] + }, + "Logdy": { + "properties": [ + "logdy.com" + ], + "resources": [ + "logdy.com" + ] + }, + "Longboard Media": { + "properties": [ + "longboardmedia.com" + ], + "resources": [ + "longboardmedia.com" + ] + }, + "LongTail Video": { + "properties": [ + "jwplayer.com" + ], + "resources": [ + "longtailvideo.com", + "ltassrv.com" + ] + }, + "Loomia": { + "properties": [ + "loomia.com" + ], + "resources": [ + "loomia.com" + ] + }, + "LoopFuse": { + "properties": [ + "lfov.net", + "loopfuse.net" + ], + "resources": [ + "lfov.net", + "loopfuse.net" + ] + }, + "LoopMe": { + "properties": [ + "loopme.com" + ], + "resources": [ + "loopme.com" + ] + }, + "Lotame": { + "properties": [ + "crwdcntrl.net", + "lotame.com" + ], + "resources": [ + "crwdcntrl.net", + "lotame.com" + ] + }, + "LotLinx": { + "properties": [ + "lotlinx.com" + ], + "resources": [ + "lotlinx.com" + ] + }, + "Lower My Bills": { + "properties": [ + "lowermybills.com" + ], + "resources": [ + "lowermybills.com" + ] + }, + "lptracker": { + "properties": [ + "lptracker.io" + ], + "resources": [ + "lptracker.io" + ] + }, + "LucidMedia": { + "properties": [ + "lucidmedia.com" + ], + "resources": [ + "lucidmedia.com" + ] + }, + "LuckyOrange": { + "properties": [ + "luckyorange.com" + ], + "resources": [ + "luckyorange.com", + "luckyorange.net" + ] + }, + "Lynchpin": { + "properties": [ + "lynchpin.com" + ], + "resources": [ + "lynchpin.com", + "lypn.com" + ] + }, + "Lyris": { + "properties": [ + "aurea.com" + ], + "resources": [ + "aurea.com", + "clicktracks.com", + "lyris.com" + ] + }, + "Lytiks": { + "properties": [ + "lytiks.com" + ], + "resources": [ + "lytiks.com" + ] + }, + "m6d": { + "properties": [ + "dstillery.com" + ], + "resources": [ + "dstillery.com", + "m6d.com", + "media6degrees.com" + ] + }, + "Madhouse": { + "properties": [ + "madhouse.cn" + ], + "resources": [ + "madhouse.cn" + ] + }, + "Madison Logic": { + "properties": [ + "dinclinx.com", + "madisonlogic.com" + ], + "resources": [ + "dinclinx.com", + "madisonlogic.com" + ] + }, + "madvertise": { + "properties": [ + "madvertise.com" + ], + "resources": [ + "madvertise.com" + ] + }, + "Magnetic": { + "properties": [ + "domdex.net", + "magnetic.com" + ], + "resources": [ + "domdex.com", + "domdex.net", + "magnetic.com", + "qjex.net" + ] + }, + "Magnify360": { + "properties": [ + "dialogmgr.com", + "magnify360.com" + ], + "resources": [ + "dialogmgr.com", + "magnify360.com" + ] + }, + "MailChimp": { + "properties": [ + "campaign-archive1.com", + "mailchi.mp", + "mailchimp.com" + ], + "resources": [ + "campaign-archive1.com", + "list-manage.com", + "mailchi.mp", + "mailchimp.com" + ] + }, + "Mail.Ru": { + "properties": [ + "list.ru", + "mail.ru" + ], + "resources": [ + "list.ru", + "mail.ru" + ] + }, + "Manifest": { + "properties": [ + "bannerbank.ru", + "manifest.ru" + ], + "resources": [ + "bannerbank.ru", + "manifest.ru" + ] + }, + "Marchex": { + "properties": [ + "industrybrains.com", + "marchex.com" + ], + "resources": [ + "industrybrains.com", + "marchex.com" + ] + }, + "Marimedia": { + "properties": [ + "marimedia.net" + ], + "resources": [ + "marimedia.net" + ] + }, + "MarketGid": { + "properties": [ + "dt00.net", + "dt07.net", + "marketgid.com" + ], + "resources": [ + "dt00.net", + "dt07.net", + "marketgid.com" + ] + }, + "Marketo": { + "properties": [ + "marketo.com" + ], + "resources": [ + "marketo.com", + "marketo.net" + ] + }, + "Markit": { + "properties": [ + "markit.com", + "wsod.com" + ], + "resources": [ + "markit.com", + "wsod.com" + ] + }, + "MarkMonitor": { + "properties": [ + "9c9media.ca", + "markmonitor.com" + ], + "resources": [ + "9c9media.ca", + "markmonitor.com" + ] + }, + "Marktest": { + "properties": [ + "marktest.com", + "marktest.pt" + ], + "resources": [ + "marktest.com", + "marktest.pt" + ] + }, + "Martini Media": { + "properties": [ + "martiniadnetwork.com" + ], + "resources": [ + "martiniadnetwork.com", + "martinimedianetwork.com" + ] + }, + "mashero": { + "properties": [ + "mashero.com" + ], + "resources": [ + "mashero.com" + ] + }, + "MashLogic": { + "properties": [ + "mashlogic.com" + ], + "resources": [ + "mashlogic.com" + ] + }, + "Match.com": { + "properties": [ + "chemistry.com", + "match.com" + ], + "resources": [ + "chemistry.com", + "match.com", + "meetic-partners.com" + ] + }, + "Matomy": { + "properties": [ + "matomy.com" + ], + "resources": [ + "adnetinteractive.com", + "adsmarket.com", + "matomy.com", + "matomymarket.com", + "matomymedia.com", + "mediawhiz.com", + "optimatic.com", + "xtendmedia.com" + ] + }, + "MaxBounty": { + "properties": [ + "maxbounty.com", + "mb01.com" + ], + "resources": [ + "maxbounty.com", + "mb01.com" + ] + }, + "MaxMind": { + "properties": [ + "maxmind.com" + ], + "resources": [ + "maxmind.com", + "mmapiws.com" + ] + }, + "MaxPoint": { + "properties": [ + "maxpointinteractive.com", + "maxusglobal.com", + "mxptint.net" + ], + "resources": [ + "maxpointinteractive.com", + "maxusglobal.com", + "mxptint.net" + ] + }, + "McAfee": { + "properties": [ + "mcafee.com", + "mcafeesecure.com" + ], + "resources": [ + "mcafee.com", + "mcafeesecure.com", + "scanalert.com" + ] + }, + "MdotM": { + "properties": [ + "mdotm.com" + ], + "resources": [ + "mdotm.com" + ] + }, + "MediaBrix": { + "properties": [ + "mediabrix.com" + ], + "resources": [ + "mediabrix.com" + ] + }, + "MediaCom": { + "properties": [ + "mediacom.com" + ], + "resources": [ + "mediacom.com" + ] + }, + "mediaFORGE": { + "properties": [ + "mediaforge.com" + ], + "resources": [ + "mediaforge.com" + ] + }, + "Medialets": { + "properties": [ + "medialets.com" + ], + "resources": [ + "medialets.com" + ] + }, + "MediaMath": { + "properties": [ + "mediamath.com" + ], + "resources": [ + "adroitinteractive.com", + "designbloxlive.com", + "mathtag.com", + "mediamath.com" + ] + }, + "Médiamétrie-eStat": { + "properties": [ + "mediametrie-estat.com" + ], + "resources": [ + "estat.com", + "mediametrie-estat.com" + ] + }, + "media.net": { + "properties": [ + "media.net" + ], + "resources": [ + "media.net" + ] + }, + "Mediaocean": { + "properties": [ + "adbuyer.com", + "mediaocean.com" + ], + "resources": [ + "adbuyer.com", + "mediaocean.com" + ] + }, + "MediaShakers": { + "properties": [ + "media-servers.net", + "mediashakers.com" + ], + "resources": [ + "media-servers.net", + "mediashakers.com" + ] + }, + "MediaTrust": { + "properties": [ + "mediatrust.com" + ], + "resources": [ + "mediatrust.com" + ] + }, + "Medicx Media Solutions": { + "properties": [ + "medicxmedia.com" + ], + "resources": [ + "medicxmedia.com" + ] + }, + "Meebo": { + "properties": [ + "meebo.com" + ], + "resources": [ + "meebo.com", + "meebocdn.net" + ] + }, + "MegaIndex": { + "properties": [ + "megaindex.ru" + ], + "resources": [ + "megaindex.ru" + ] + }, + "Mercadopago": { + "properties": [ + "mercadolibre.cl", + "mercadolibre.co.cr", + "mercadolibre.com", + "mercadolibre.com.ar", + "mercadolibre.com.bo", + "mercadolibre.com.co", + "mercadolibre.com.do", + "mercadolibre.com.ec", + "mercadolibre.com.gt", + "mercadolibre.com.hn", + "mercadolibre.com.mx", + "mercadolibre.com.ni", + "mercadolibre.com.pa", + "mercadolibre.com.pe", + "mercadolibre.com.py", + "mercadolibre.com.sv", + "mercadolibre.com.uy", + "mercadolibre.com.ve", + "mercadolivre.com.br", + "mercadopago.com" + ], + "resources": [ + "mercadopago.com" + ] + }, + "Mercent": { + "properties": [ + "mercent.com" + ], + "resources": [ + "mercent.com" + ] + }, + "MerchantAdvantage": { + "properties": [ + "merchantadvantage.com" + ], + "resources": [ + "merchantadvantage.com" + ] + }, + "Merchenta": { + "properties": [ + "merchenta.com" + ], + "resources": [ + "merchenta.com" + ] + }, + "Merkle": { + "properties": [ + "merkleinc.com", + "rkdms.com" + ], + "resources": [ + "merkleinc.com", + "rimmkaufman.com", + "rkdms.com" + ] + }, + "Meta Network": { + "properties": [ + "metanetwork.com" + ], + "resources": [ + "metanetwork.com" + ] + }, + "Meteor": { + "properties": [ + "meteorsolutions.com" + ], + "resources": [ + "meteorsolutions.com" + ] + }, + "MetrixLab": { + "properties": [ + "crm-metrix.com", + "customerconversio.com", + "metrixlab.com", + "opinionbar.com" + ], + "resources": [ + "adoftheyear.com", + "crm-metrix.com", + "customerconversio.com", + "metrixlab.com", + "opinionbar.com" + ] + }, + "MicroAd": { + "properties": [ + "microad.jp", + "www.microad.jp" + ], + "resources": [ + "microad.jp", + "www.microad.jp" + ] + }, + "Microsoft": { + "properties": [ + "acompli.net", + "aka.ms", + "azure.com", + "azure.net", + "azurerms.com", + "bing.com", + "cloudappsecurity.com", + "gamesforwindows.com", + "getgamesmart.com", + "gfx.ms", + "healthvault.com", + "hockeyapp.net", + "ieaddons.com", + "iegallery.com", + "live.com", + "microsoft.com", + "microsoftalumni.com", + "microsoftalumni.org", + "microsoftazuread-sso.com", + "microsoftedgeinsiders.com", + "microsoftonline-p.com", + "microsoftonline-p.net", + "microsoftonline.com", + "microsoftstore.com", + "microsoftstream.com", + "msappproxy.net", + "msft.net", + "msftidentity.com", + "msidentity.com", + "msn.com", + "o365weve.com", + "oaspapps.com", + "office.com", + "office365.com", + "officelive.com", + "onedrive.com", + "onenote.com", + "outlook.com", + "outlookmobile.com", + "phonefactor.net", + "s-msn.com", + "sfx.ms", + "sharepoint.com", + "skype.com", + "skypeforbusiness.com", + "staffhub.ms", + "sway-extensions.com", + "sway.com", + "trafficmanager.net", + "virtualearth.net", + "visualstudio.com", + "windows.net", + "windowsazure.com", + "windowsphone.com", + "worldwidetelescope.org", + "wunderlist.com", + "xbox.com", + "yammer.com" + ], + "resources": [ + "aadrm.com", + "adbureau.net", + "adecn.com", + "aquantive.com", + "aspnetcdn.com", + "assets-yammer.com", + "azure.com", + "azureedge.net", + "bing.com", + "cloudapp.net", + "gamesforwindows.com", + "getgamesmart.com", + "gfx.ms", + "healthvault.com", + "live.com", + "microsoft.com", + "microsoftazuread-sso.com", + "microsoftonline-p.com", + "microsoftonline-p.net", + "microsoftonline.com", + "microsoftstore.com", + "msads.net", + "msauthimages.net", + "msecnd.net", + "msedge.net", + "msndirect.com", + "msocdn.com", + "netconversions.com", + "oaspapps.com", + "office.com", + "office.net", + "officelive.com", + "onenote.net", + "onestore.ms", + "onmicrosoft.com", + "outlook.com", + "roiservice.com", + "s-msn.com", + "sfbassets.com", + "sharepoint.com", + "skype.com", + "skypeassets.com", + "sway-cdn.com", + "sway-extensions.com", + "windows.net", + "windowsazure.com", + "yammerusercontent.com" + ] + }, + "Millennial Media": { + "properties": [ + "decktrade.com", + "millennialmedia.com", + "mydas.mobi" + ], + "resources": [ + "decktrade.com", + "millennialmedia.com", + "mydas.mobi" + ] + }, + "Mindset Media": { + "properties": [ + "mindset-media.com" + ], + "resources": [ + "mindset-media.com", + "mmismm.com" + ] + }, + "MinerAlt": { + "properties": [ + "mineralt.io", + "vidzi.nu", + "vidzi.tv" + ], + "resources": [ + "1q2w3.website", + "analytics.blue", + "aster18cdn.nl", + "belicimo.pw", + "besstahete.info", + "dinorslick.icu", + "feesocrald.com", + "gramombird.com", + "istlandoll.com", + "mepirtedic.com", + "mineralt.io", + "pampopholf.com", + "tercabilis.info", + "tulip18.com", + "vidzi.tv", + "yololike.space" + ] + }, + "Minescripts": { + "properties": [ + "minescripts.info" + ], + "resources": [ + "minescripts.info", + "sslverify.info" + ] + }, + "MineXMR": { + "properties": [ + "minexmr.stream" + ], + "resources": [ + "minexmr.stream" + ] + }, + "Mirando": { + "properties": [ + "mirando.de" + ], + "resources": [ + "mirando.de" + ] + }, + "Mixpanel": { + "properties": [ + "mixpanel.com" + ], + "resources": [ + "mixpanel.com", + "mxpnl.com" + ] + }, + "Mixpo": { + "properties": [ + "mixpo.com" + ], + "resources": [ + "mixpo.com" + ] + }, + "Moat": { + "properties": [ + "moat.com", + "moatads.com" + ], + "resources": [ + "moat.com", + "moatads.com" + ] + }, + "MobFox": { + "properties": [ + "mobfox.com" + ], + "resources": [ + "mobfox.com" + ] + }, + "Mobials": { + "properties": [ + "mobials.com" + ], + "resources": [ + "mobials.com" + ] + }, + "MobileAdTrading": { + "properties": [ + "mobileadtrading.com" + ], + "resources": [ + "mobileadtrading.com" + ] + }, + "Mobile Meteor": { + "properties": [ + "mobilemeteor.com" + ], + "resources": [ + "mobilemeteor.com", + "showmeinn.com" + ] + }, + "Mobile Storm": { + "properties": [ + "mobilestorm.com" + ], + "resources": [ + "mobilestorm.com" + ] + }, + "MobVision": { + "properties": [ + "admoda.com" + ], + "resources": [ + "admoda.com", + "mobvision.com" + ] + }, + "Mocean Mobile": { + "properties": [ + "moceanmobile.com" + ], + "resources": [ + "moceanmobile.com" + ] + }, + "Mochila": { + "properties": [ + "mochila.com" + ], + "resources": [ + "mochila.com" + ] + }, + "Mojiva": { + "properties": [ + "mojiva.com" + ], + "resources": [ + "mojiva.com" + ] + }, + "Monetate": { + "properties": [ + "monetate.com", + "monetate.net" + ], + "resources": [ + "monetate.com", + "monetate.net" + ] + }, + "MONETIZEdigital": { + "properties": [ + "cpalead.com" + ], + "resources": [ + "cpalead.com" + ] + }, + "Monetize More": { + "properties": [ + "monetizemore.com" + ], + "resources": [ + "monetizemore.com" + ] + }, + "Mongoose Metrics": { + "properties": [ + "mongoosemetrics.com" + ], + "resources": [ + "mongoosemetrics.com" + ] + }, + "Monitus": { + "properties": [ + "monitus.net" + ], + "resources": [ + "monitus.net" + ] + }, + "Monoloop": { + "properties": [ + "monoloop.com" + ], + "resources": [ + "monoloop.com" + ] + }, + "Monster": { + "properties": [ + "monster.com" + ], + "resources": [ + "monster.com" + ] + }, + "Moolah Media": { + "properties": [ + "moolah-media.com", + "moolahmedia.com" + ], + "resources": [ + "moolah-media.com", + "moolahmedia.com" + ] + }, + "MoPub": { + "properties": [ + "mopub.com" + ], + "resources": [ + "mopub.com" + ] + }, + "motigo": { + "properties": [ + "motigo.com" + ], + "resources": [ + "motigo.com", + "nedstatbasic.net" + ] + }, + "Mouseflow": { + "properties": [ + "mouseflow.com" + ], + "resources": [ + "mouseflow.com" + ] + }, + "MovieLush.com": { + "properties": [ + "affbuzzads.com", + "movielush.com" + ], + "resources": [ + "affbuzzads.com", + "movielush.com" + ] + }, + "Multiple Stream Media": { + "properties": [ + "adclickmedia.com", + "multiplestreammktg.com" + ], + "resources": [ + "adclickmedia.com", + "multiplestreammktg.com" + ] + }, + "MUNDO Media": { + "properties": [ + "mundomedia.com", + "silver-path.com" + ], + "resources": [ + "mundomedia.com", + "silver-path.com" + ] + }, + "MyCounter": { + "properties": [ + "mycounter.com.ua" + ], + "resources": [ + "mycounter.com.ua" + ] + }, + "MyPagerank.Net": { + "properties": [ + "mypagerank.net" + ], + "resources": [ + "mypagerank.net" + ] + }, + "MyPressPlus": { + "properties": [ + "mypressplus.com", + "ppjol.net" + ], + "resources": [ + "mypressplus.com", + "ppjol.net" + ] + }, + "Mystighty": { + "properties": [ + "mystighty.info" + ], + "resources": [ + "mystighty.info", + "sweeterge.info" + ] + }, + "myThings": { + "properties": [ + "mythings.com", + "mythingsmedia.com" + ], + "resources": [ + "mythings.com", + "mythingsmedia.com" + ] + }, + "MyWebGrocer": { + "properties": [ + "mywebgrocer.com" + ], + "resources": [ + "mywebgrocer.com" + ] + }, + "Nanigans": { + "properties": [ + "nanigans.com" + ], + "resources": [ + "nanigans.com" + ] + }, + "Narrative": { + "properties": [ + "narrative.io" + ], + "resources": [ + "narrative.io" + ] + }, + "NativeAds": { + "properties": [ + "nativeads.com" + ], + "resources": [ + "nativeads.com" + ] + }, + "Nativo": { + "properties": [ + "nativo.com", + "postrelease.com" + ], + "resources": [ + "nativo.com", + "postrelease.com" + ] + }, + "Navegg": { + "properties": [ + "navdmp.com", + "navegg.com" + ], + "resources": [ + "navdmp.com", + "navegg.com" + ] + }, + "NDN": { + "properties": [ + "newsinc.com" + ], + "resources": [ + "newsinc.com" + ] + }, + "Negishim": { + "properties": [ + "negishim.org" + ], + "resources": [ + "negishim.org" + ] + }, + "NeroHut": { + "properties": [ + "nerohut.com" + ], + "resources": [ + "nerohut.com", + "nhsrv.cf" + ] + }, + "NetAffiliation": { + "properties": [ + "netaffiliation.com" + ], + "resources": [ + "netaffiliation.com" + ] + }, + "Net Applications": { + "properties": [ + "netapplications.com" + ], + "resources": [ + "hitsprocessor.com", + "netapplications.com" + ] + }, + "NetBina": { + "properties": [ + "netbina.com" + ], + "resources": [ + "netbina.com" + ] + }, + "NetElixir": { + "properties": [ + "adelixir.com", + "netelixir.com" + ], + "resources": [ + "adelixir.com", + "netelixir.com" + ] + }, + "Netmining": { + "properties": [ + "netmining.com", + "netmng.com" + ], + "resources": [ + "netmining.com", + "netmng.com" + ] + }, + "Net-Results": { + "properties": [ + "net-results.com", + "nr7.us" + ], + "resources": [ + "cdnma.com", + "net-results.com", + "nr7.us" + ] + }, + "NetSeer": { + "properties": [ + "netseer.com" + ], + "resources": [ + "netseer.com" + ] + }, + "NetShelter": { + "properties": [ + "ziffdavistech.com" + ], + "resources": [ + "netshelter.com", + "netshelter.net", + "ziffdavistech.com" + ] + }, + "Neustar": { + "properties": [ + "adadvisor.net", + "home.neustar", + "neustar.biz" + ], + "resources": [ + "adadvisor.net", + "neustar.biz" + ] + }, + "New Relic": { + "properties": [ + "newrelic.com" + ], + "resources": [ + "newrelic.com", + "nr-data.net" + ] + }, + "NewsRight": { + "properties": [ + "apnewsregistry.com", + "newsright.com" + ], + "resources": [ + "apnewsregistry.com", + "newsright.com" + ] + }, + "newtention": { + "properties": [ + "newtention.de", + "newtention.net", + "newtentionassets.net" + ], + "resources": [ + "newtention.de", + "newtention.net", + "newtentionassets.net" + ] + }, + "Nexage": { + "properties": [ + "nexage.com" + ], + "resources": [ + "nexage.com" + ] + }, + "Nextag": { + "properties": [ + "nextag.com" + ], + "resources": [ + "nextag.com" + ] + }, + "NextPerformance": { + "properties": [ + "nextperf.com", + "nextperformance.com", + "nxtck.com" + ], + "resources": [ + "nextperf.com", + "nextperformance.com", + "nxtck.com" + ] + }, + "NextSTAT": { + "properties": [ + "nextstat.com" + ], + "resources": [ + "nextstat.com" + ] + }, + "Nielsen": { + "properties": [ + "glanceguide.com", + "imrworldwide.com", + "imrworldwide.net", + "nielsen.com" + ], + "resources": [ + "glanceguide.com", + "imrworldwide.com", + "imrworldwide.net", + "nielsen.com" + ] + }, + "Ninua": { + "properties": [ + "networkedblogs.com", + "ninua.com" + ], + "resources": [ + "networkedblogs.com", + "ninua.com" + ] + }, + "Nokta": { + "properties": [ + "noktamedya.com", + "virgul.com" + ], + "resources": [ + "noktamedya.com", + "virgul.com" + ] + }, + "NowSpots": { + "properties": [ + "nowspots.com" + ], + "resources": [ + "nowspots.com" + ] + }, + "nrelate": { + "properties": [ + "nrelate.com" + ], + "resources": [ + "nrelate.com" + ] + }, + "NuDataSecurity": { + "properties": [ + "nudatasecurity.com" + ], + "resources": [ + "nudatasecurity.com" + ] + }, + "Nuffnang": { + "properties": [ + "nuffnang.com", + "nuffnang.com.my", + "www.nuffnang.com.my" + ], + "resources": [ + "nuffnang.com", + "nuffnang.com.my", + "www.nuffnang.com.my" + ] + }, + "nugg.ad": { + "properties": [ + "nugg.ad" + ], + "resources": [ + "nugg.ad", + "nuggad.net" + ] + }, + "nurago": { + "properties": [ + "sensic.net" + ], + "resources": [ + "nurago.com", + "nurago.de", + "sensic.net" + ] + }, + "Oberon Media": { + "properties": [ + "iwin.com" + ], + "resources": [ + "blaze.com", + "iwin.com", + "oberon-media.com" + ] + }, + "Observer": { + "properties": [ + "observerapp.com" + ], + "resources": [ + "observerapp.com" + ] + }, + "Ohana Media": { + "properties": [ + "adohana.com", + "ohana-media.com", + "ohanaqb.com" + ], + "resources": [ + "adohana.com", + "ohana-media.com", + "ohanaqb.com" + ] + }, + "Omnicom Group": { + "properties": [ + "accuenmedia.com", + "omnicomgroup.com" + ], + "resources": [ + "accuenmedia.com", + "omnicomgroup.com", + "p-td.com" + ] + }, + "onAd": { + "properties": [ + "onad.eu" + ], + "resources": [ + "onad.eu" + ] + }, + "OnAudience": { + "properties": [ + "behavioralengine.com", + "onaudience.com" + ], + "resources": [ + "behavioralengine.com", + "onaudience.com" + ] + }, + "Onclusive": { + "properties": [ + "onclusive.com" + ], + "resources": [ + "airpr.com" + ] + }, + "OneAd": { + "properties": [ + "onead.com.tw" + ], + "resources": [ + "guoshipartners.com", + "onevision.com.tw" + ] + }, + "One iota": { + "properties": [ + "itsoneiota.com", + "oneiota.co.uk" + ], + "resources": [ + "itsoneiota.com", + "oneiota.co.uk" + ] + }, + "OneStat": { + "properties": [ + "onestat.com" + ], + "resources": [ + "onestat.com" + ] + }, + "Oneupweb": { + "properties": [ + "oneupweb.com", + "sodoit.com" + ], + "resources": [ + "oneupweb.com", + "sodoit.com" + ] + }, + "OnlineMetrix": { + "properties": [ + "online-metrix.net" + ], + "resources": [ + "online-metrix.net" + ] + }, + "Ooyala": { + "properties": [ + "ooyala.com" + ], + "resources": [ + "oo4.com", + "ooyala.com" + ] + }, + "Open New Media": { + "properties": [ + "onm.de" + ], + "resources": [ + "onm.de" + ] + }, + "Openstat": { + "properties": [ + "openstat.com" + ], + "resources": [ + "openstat.com", + "openstat.ru", + "spylog.com" + ] + }, + "Opentracker": { + "properties": [ + "opentracker.net" + ], + "resources": [ + "opentracker.net" + ] + }, + "OpenX": { + "properties": [ + "openx.com", + "openx.net" + ], + "resources": [ + "liftdna.com", + "openx.com", + "openx.net", + "openx.org", + "openxenterprise.com", + "servedbyopenx.com" + ] + }, + "Opera": { + "properties": [ + "opera.com" + ], + "resources": [ + "mobiletheory.com", + "opera.com" + ] + }, + "Opolen": { + "properties": [ + "opolen.com.br" + ], + "resources": [ + "opolen.com.br" + ] + }, + "OPT": { + "properties": [ + "www.opt.ne.jp" + ], + "resources": [ + "advg.jp", + "opt.ne.jp", + "p-advg.com", + "www.opt.ne.jp" + ] + }, + "Optify": { + "properties": [ + "optify.net" + ], + "resources": [ + "optify.net" + ] + }, + "Optimal": { + "properties": [ + "bn.co" + ], + "resources": [ + "cpmadvisors.com", + "cpmatic.com", + "nprove.com", + "optim.al", + "orbengine.com", + "xa.net" + ] + }, + "Optimizely": { + "properties": [ + "optimizely.com" + ], + "resources": [ + "optimizely.com" + ] + }, + "OptimumResponse": { + "properties": [ + "optimumresponse.com" + ], + "resources": [ + "optimumresponse.com" + ] + }, + "OptinMonster": { + "properties": [ + "optinmonster.com", + "optnmstr.com" + ], + "resources": [ + "optinmonster.com", + "optnmstr.com" + ] + }, + "OptMD": { + "properties": [ + "optmd.com" + ], + "resources": [ + "optmd.com" + ] + }, + "Oracle": { + "properties": [ + "oracle.com" + ], + "resources": [ + "atgsvcs.com", + "eloqua.com", + "estara.com", + "instantservice.com", + "istrack.com", + "maxymiser.com", + "oracle.com" + ] + }, + "OrangeSoda": { + "properties": [ + "orangesoda.com", + "otracking.com" + ], + "resources": [ + "orangesoda.com", + "otracking.com" + ] + }, + "Outbrain": { + "properties": [ + "outbrain.com", + "sphere.com" + ], + "resources": [ + "outbrain.com", + "sphere.com", + "visualrevenue.com" + ] + }, + "Out There Media": { + "properties": [ + "out-there-media.com" + ], + "resources": [ + "out-there-media.com" + ] + }, + "Oversee.net": { + "properties": [ + "dsnextgen.com", + "oversee.net" + ], + "resources": [ + "dsnextgen.com", + "oversee.net" + ] + }, + "ÖWA": { + "properties": [ + "oewa.at" + ], + "resources": [ + "oewa.at", + "oewabox.at" + ] + }, + "OwnerIQ": { + "properties": [ + "owneriq.com", + "owneriq.net" + ], + "resources": [ + "owneriq.com", + "owneriq.net" + ] + }, + "OxaMedia": { + "properties": [ + "oxamedia.com" + ], + "resources": [ + "adconnexa.com", + "adsbwm.com", + "oxamedia.com" + ] + }, + "PageFair": { + "properties": [ + "pagefair.com", + "pagefair.net" + ], + "resources": [ + "pagefair.com", + "pagefair.net" + ] + }, + "Paid-To-Promote.net": { + "properties": [ + "paid-to-promote.net" + ], + "resources": [ + "paid-to-promote.net" + ] + }, + "Papaya": { + "properties": [ + "papayamobile.com" + ], + "resources": [ + "papayamobile.com" + ] + }, + "Pardot": { + "properties": [ + "pardot.com" + ], + "resources": [ + "pardot.com" + ] + }, + "Parse.ly": { + "properties": [ + "parsely.com" + ], + "resources": [ + "parsely.com" + ] + }, + "PayHit": { + "properties": [ + "payhit.com" + ], + "resources": [ + "payhit.com" + ] + }, + "PaymentsMB": { + "properties": [ + "paymentsmb.com" + ], + "resources": [ + "paymentsmb.com" + ] + }, + "Paypal": { + "properties": [ + "paypal.com", + "simility.com" + ], + "resources": [ + "paypal.com", + "simility.com" + ] + }, + "Paypopup.com": { + "properties": [ + "paypopup.com" + ], + "resources": [ + "lzjl.com", + "paypopup.com" + ] + }, + "PebblePost": { + "properties": [ + "pebblepost.com" + ], + "resources": [ + "pbbl.co" + ] + }, + "Peer39": { + "properties": [ + "peer39.com", + "peer39.net" + ], + "resources": [ + "peer39.com", + "peer39.net" + ] + }, + "PeerFly": { + "properties": [ + "peerfly.com" + ], + "resources": [ + "peerfly.com" + ] + }, + "Peerius": { + "properties": [ + "peerius.com" + ], + "resources": [ + "peerius.com" + ] + }, + "Performancing": { + "properties": [ + "performancing.com" + ], + "resources": [ + "performancing.com" + ] + }, + "PerimeterX": { + "properties": [ + "perimeterx.com" + ], + "resources": [ + "perimeterx.com" + ] + }, + "PersianStat.com": { + "properties": [ + "persianstat.com" + ], + "resources": [ + "persianstat.com" + ] + }, + "Pheedo": { + "properties": [ + "pheedo.com" + ], + "resources": [ + "pheedo.com" + ] + }, + "Phonalytics": { + "properties": [ + "phonalytics.com" + ], + "resources": [ + "phonalytics.com" + ] + }, + "phpMyVisites": { + "properties": [ + "phpmyvisites.us" + ], + "resources": [ + "phpmyvisites.us" + ] + }, + "Pictela": { + "properties": [ + "pictela.com", + "pictela.net" + ], + "resources": [ + "pictela.com", + "pictela.net" + ] + }, + "PinPoll": { + "properties": [ + "pinpoll.com" + ], + "resources": [ + "pinpoll.com" + ] + }, + "Pinterest": { + "properties": [ + "pinterest.at", + "pinterest.ca", + "pinterest.ch", + "pinterest.cl", + "pinterest.co.kr", + "pinterest.co.uk", + "pinterest.com", + "pinterest.com.au", + "pinterest.com.mx", + "pinterest.de", + "pinterest.dk", + "pinterest.es", + "pinterest.fr", + "pinterest.ie", + "pinterest.jp", + "pinterest.nz", + "pinterest.pt", + "pinterest.se" + ], + "resources": [ + "pinimg.com", + "pinterest.com" + ] + }, + "Piwik": { + "properties": [ + "piwik.org" + ], + "resources": [ + "piwik.org" + ] + }, + "PixAnalytics": { + "properties": [ + "pixanalytics.com" + ], + "resources": [ + "pixanalytics.com" + ] + }, + "Pixel.sg": { + "properties": [ + "pixel.sg" + ], + "resources": [ + "pixel.sg" + ] + }, + "Piximedia": { + "properties": [ + "piximedia.com" + ], + "resources": [ + "piximedia.com" + ] + }, + "Pixlee": { + "properties": [ + "pixlee.com" + ], + "resources": [ + "pixlee.com" + ] + }, + "PLATFORM ONE": { + "properties": [ + "platform-one.co.jp", + "www.platform-one.co.jp" + ], + "resources": [ + "platform-one.co.jp", + "www.platform-one.co.jp" + ] + }, + "plista": { + "properties": [ + "plista.com" + ], + "resources": [ + "plista.com" + ] + }, + "PocketCents": { + "properties": [ + "pocketcents.com" + ], + "resources": [ + "pocketcents.com" + ] + }, + "Polar Mobile": { + "properties": [ + "mediavoice.com" + ], + "resources": [ + "mediavoice.com", + "polarmobile.com" + ] + }, + "Politads": { + "properties": [ + "politads.com" + ], + "resources": [ + "politads.com" + ] + }, + "Polymorph": { + "properties": [ + "getpolymorph.com" + ], + "resources": [ + "adsnative.com", + "getpolymorph.com" + ] + }, + "Pontiflex": { + "properties": [ + "pontiflex.com" + ], + "resources": [ + "pontiflex.com" + ] + }, + "Poool": { + "properties": [ + "poool.fr" + ], + "resources": [ + "poool.fr" + ] + }, + "PopAds": { + "properties": [ + "popads.net" + ], + "resources": [ + "popads.net", + "popadscdn.net" + ] + }, + "PopRule": { + "properties": [ + "gocampaignlive.com", + "poprule.com" + ], + "resources": [ + "gocampaignlive.com", + "poprule.com" + ] + }, + "Popunder.ru": { + "properties": [ + "popunder.ru" + ], + "resources": [ + "popunder.ru" + ] + }, + "Po.st": { + "properties": [ + "po.st" + ], + "resources": [ + "po.st" + ] + }, + "Powerlinks": { + "properties": [ + "powerlinks.com" + ], + "resources": [ + "powerlinks.com" + ] + }, + "PPCProtect": { + "properties": [ + "ppcprotect.com" + ], + "resources": [ + "ppcprotect.com" + ] + }, + "PrecisionClick": { + "properties": [ + "precisionclick.com" + ], + "resources": [ + "precisionclick.com" + ] + }, + "PredictAd": { + "properties": [ + "predictad.com" + ], + "resources": [ + "predictad.com" + ] + }, + "Pressflex": { + "properties": [ + "blogads.com", + "pressflex.com" + ], + "resources": [ + "blogads.com", + "pressflex.com" + ] + }, + "Prime Visibility": { + "properties": [ + "primevisibility.com" + ], + "resources": [ + "adcde.com", + "addlvr.com", + "adonnetwork.com", + "adonnetwork.net", + "adtrgt.com", + "bannertgt.com", + "cptgt.com", + "cpvfeed.com", + "cpvtgt.com", + "dashboardad.net", + "popcde.com", + "primevisibility.com", + "sdfje.com", + "urtbk.com" + ] + }, + "Primis": { + "properties": [ + "primis.tech" + ], + "resources": [ + "sekindo.com" + ] + }, + "PrismApp": { + "properties": [ + "prismapp.io" + ], + "resources": [ + "prismapp.io" + ] + }, + "Proclivity": { + "properties": [ + "proclivitysystems.com", + "pswec.com" + ], + "resources": [ + "proclivitymedia.com", + "proclivitysystems.com", + "pswec.com" + ] + }, + "Project Wonderful": { + "properties": [ + "projectwonderful.com" + ], + "resources": [ + "projectwonderful.com" + ] + }, + "PrometheusIntelligenceTechnology": { + "properties": [ + "prometheusintelligencetechnology.com" + ], + "resources": [ + "prometheusintelligencetechnology.com" + ] + }, + "Pronunciator": { + "properties": [ + "pronunciator.com", + "visitorville.com" + ], + "resources": [ + "pronunciator.com", + "visitorville.com" + ] + }, + "Propeller Ads": { + "properties": [ + "propellerads.com" + ], + "resources": [ + "propellerads.com" + ] + }, + "Prosperent": { + "properties": [ + "prosperent.com" + ], + "resources": [ + "prosperent.com" + ] + }, + "Protected Media": { + "properties": [ + "ad-score.com", + "protected.media" + ], + "resources": [ + "ad-score.com", + "protected.media" + ] + }, + "Provers": { + "properties": [ + "provers.pro" + ], + "resources": [ + "provers.pro" + ] + }, + "Psonstrentie": { + "properties": [ + "psonstrentie.info" + ], + "resources": [ + "psonstrentie.info" + ] + }, + "Public-Idées": { + "properties": [ + "publicidees.com" + ], + "resources": [ + "publicidees.com" + ] + }, + "Publishers Clearing House": { + "properties": [ + "pch.com" + ], + "resources": [ + "pch.com" + ] + }, + "PubMatic": { + "properties": [ + "pubmatic.com" + ], + "resources": [ + "pubmatic.com", + "revinet.com" + ] + }, + "PulsePoint": { + "properties": [ + "pulsepoint.com" + ], + "resources": [ + "pulsepoint.com" + ] + }, + "PunchTab": { + "properties": [ + "punchtab.com" + ], + "resources": [ + "punchtab.com" + ] + }, + "quadrantOne": { + "properties": [ + "quadrantone.com" + ], + "resources": [ + "quadrantone.com" + ] + }, + "Quake Marketing": { + "properties": [ + "quakemarketing.com" + ], + "resources": [ + "quakemarketing.com" + ] + }, + "Qualaroo": { + "properties": [ + "qualaroo.com" + ], + "resources": [ + "kissinsights.com", + "qualaroo.com" + ] + }, + "Quantcast": { + "properties": [ + "quantcast.com", + "quantserve.com" + ], + "resources": [ + "quantcast.com", + "quantserve.com" + ] + }, + "QuantumAdvertising": { + "properties": [ + "quantum-advertising.com" + ], + "resources": [ + "quantum-advertising.com" + ] + }, + "QuinStreet": { + "properties": [ + "quinstreet.com", + "thecounter.com" + ], + "resources": [ + "qnsr.com", + "qsstats.com", + "quinstreet.com", + "thecounter.com" + ] + }, + "Quintelligence": { + "properties": [ + "quintelligence.com" + ], + "resources": [ + "quintelligence.com" + ] + }, + "QUISMA": { + "properties": [ + "quisma.com" + ], + "resources": [ + "iaded.com", + "quisma.com", + "quismatch.com", + "xaded.com", + "xmladed.com" + ] + }, + "RadarURL": { + "properties": [ + "radarurl.com" + ], + "resources": [ + "radarurl.com" + ] + }, + "Radial": { + "properties": [ + "radial.com" + ], + "resources": [ + "gsicommerce.com", + "gsimedia.net" + ] + }, + "Radiate Media": { + "properties": [ + "gtnetwork.com.au", + "solesolution.com" + ], + "resources": [ + "gtnetwork.com.au", + "matchbin.com", + "radiatemedia.com", + "solesolution.com" + ] + }, + "RadiumOne": { + "properties": [ + "radiumone.com" + ], + "resources": [ + "gwallet.com", + "radiumone.com" + ] + }, + "Radius Marketing": { + "properties": [ + "radiusmarketing.com" + ], + "resources": [ + "radiusmarketing.com" + ] + }, + "Rambler": { + "properties": [ + "rambler.ru" + ], + "resources": [ + "rambler.ru" + ] + }, + "Rapleaf": { + "properties": [ + "rapleaf.com", + "rlcdn.com" + ], + "resources": [ + "rapleaf.com", + "rlcdn.com" + ] + }, + "ReachLocal": { + "properties": [ + "reachlocal.com", + "rlcdn.net" + ], + "resources": [ + "reachlocal.com", + "rlcdn.net" + ] + }, + "React2Media": { + "properties": [ + "react2media.com" + ], + "resources": [ + "react2media.com" + ] + }, + "reddit": { + "properties": [ + "reddit.com" + ], + "resources": [ + "reddit.com" + ] + }, + "Redux Media": { + "properties": [ + "reduxmedia.com" + ], + "resources": [ + "reduxmedia.com" + ] + }, + "Rekko": { + "properties": [ + "convertglobal.com", + "rekko.com" + ], + "resources": [ + "convertglobal.com", + "rekko.com" + ] + }, + "Reklamport": { + "properties": [ + "reklamport.com" + ], + "resources": [ + "reklamport.com" + ] + }, + "Reklam Store": { + "properties": [ + "reklamstore.com" + ], + "resources": [ + "reklamstore.com" + ] + }, + "Reklamz": { + "properties": [ + "reklamz.com" + ], + "resources": [ + "reklamz.com" + ] + }, + "Relevad": { + "properties": [ + "relestar.com", + "relevad.com" + ], + "resources": [ + "relestar.com", + "relevad.com" + ] + }, + "Renegade Internet": { + "properties": [ + "advertserve.com", + "renegadeinternet.com" + ], + "resources": [ + "advertserve.com", + "renegadeinternet.com" + ] + }, + "Reporo": { + "properties": [ + "reporo.com" + ], + "resources": [ + "buzzcity.com" + ] + }, + "Research Now": { + "properties": [ + "researchnow.com", + "valuedopinions.co.uk" + ], + "resources": [ + "researchnow.com", + "valuedopinions.co.uk" + ] + }, + "ResolutionMedia": { + "properties": [ + "nonstoppartner.net" + ], + "resources": [ + "nonstoppartner.net" + ] + }, + "Resolution Media": { + "properties": [ + "resolutionmedia.com" + ], + "resources": [ + "resolutionmedia.com" + ] + }, + "Resonate": { + "properties": [ + "resonateinsights.com", + "resonatenetworks.com" + ], + "resources": [ + "reson8.com", + "resonateinsights.com", + "resonatenetworks.com" + ] + }, + "Responsys": { + "properties": [ + "responsys.com" + ], + "resources": [ + "responsys.com" + ] + }, + "Retail Automata": { + "properties": [ + "retailautomata.com" + ], + "resources": [ + "retailautomata.com" + ] + }, + "ReTargeter": { + "properties": [ + "retargeter.com" + ], + "resources": [ + "retargeter.com" + ] + }, + "Retirement Living": { + "properties": [ + "blvdstatus.com", + "retirement-living.com" + ], + "resources": [ + "blvdstatus.com", + "retirement-living.com" + ] + }, + "RevContent": { + "properties": [ + "revcontent.com" + ], + "resources": [ + "revcontent.com" + ] + }, + "RevenueMax": { + "properties": [ + "revenuemax.de" + ], + "resources": [ + "revenuemax.de" + ] + }, + "Revtracks": { + "properties": [ + "revtrax.com" + ], + "resources": [ + "revtrax.com" + ] + }, + "Rhythm": { + "properties": [ + "rhythmone.com" + ], + "resources": [ + "1rx.io", + "rhythmnewmedia.com", + "rhythmone.com", + "rhythmxchange.com", + "rnmd.net" + ] + }, + "RichAudience": { + "properties": [ + "richaudience.com" + ], + "resources": [ + "richaudience.com" + ] + }, + "RichRelevance": { + "properties": [ + "richrelevance.com" + ], + "resources": [ + "richrelevance.com" + ] + }, + "RightAction": { + "properties": [ + "rightaction.com" + ], + "resources": [ + "rightaction.com" + ] + }, + "RIM": { + "properties": [ + "global.blackberry.com", + "laptopverge.com" + ], + "resources": [ + "global.blackberry.com", + "laptopverge.com", + "rim.com", + "scoreloop.com" + ] + }, + "Ringier": { + "properties": [ + "ringier.cz" + ], + "resources": [ + "ringier.cz" + ] + }, + "RMBN": { + "properties": [ + "traforet.com" + ], + "resources": [ + "rmbn.net", + "rmbn.ru", + "traforet.com" + ] + }, + "RMM": { + "properties": [ + "rmmonline.com" + ], + "resources": [ + "rmmonline.com" + ] + }, + "Rocket Fuel": { + "properties": [ + "rfihub.com", + "rfihub.net", + "rocketfuel.com" + ], + "resources": [ + "rfihub.com", + "rfihub.net", + "rocketfuel.com", + "ru4.com", + "xplusone.com" + ] + }, + "Rollick": { + "properties": [ + "gorollick.com" + ], + "resources": [ + "rollick.io" + ] + }, + "Rovion": { + "properties": [ + "rovion.com" + ], + "resources": [ + "rovion.com" + ] + }, + "Roxr": { + "properties": [ + "clicky.com", + "roxr.net" + ], + "resources": [ + "clicky.com", + "getclicky.com", + "roxr.net", + "staticstuff.net" + ] + }, + "rtk": { + "properties": [ + "rtk.io" + ], + "resources": [ + "rtk.io" + ] + }, + "RubiconProject": { + "properties": [ + "rubiconproject.com" + ], + "resources": [ + "adsbyisocket.com", + "isocket.com", + "rubiconproject.com" + ] + }, + "RunAds": { + "properties": [ + "runads.com" + ], + "resources": [ + "runads.com", + "rundsp.com" + ] + }, + "RuTarget": { + "properties": [ + "rutarget.ru" + ], + "resources": [ + "rutarget.ru" + ] + }, + "Sabavision": { + "properties": [ + "sabavision.com" + ], + "resources": [ + "sabavision.com" + ] + }, + "Sabre": { + "properties": [ + "reztrack.com", + "sabre.com", + "sabrehospitality.com" + ], + "resources": [ + "reztrack.com", + "sabre.com", + "sabrehospitality.com" + ] + }, + "Safecount": { + "properties": [ + "safecount.net" + ], + "resources": [ + "dl-rms.com", + "dlqm.net", + "questionmarket.com", + "safecount.net" + ] + }, + "SageMetrics": { + "properties": [ + "sagemetrics.com" + ], + "resources": [ + "sageanalyst.net", + "sagemetrics.com" + ] + }, + "Salesforce.com": { + "properties": [ + "force.com", + "salesforce.com", + "trailblazer.me" + ], + "resources": [ + "documentforce.com", + "force.com", + "forcesslreports.com", + "forceusercontent.com", + "lightning.com", + "salesforce-communities.com", + "salesforce-hub.com", + "salesforce.com", + "salesforceliveagent.com", + "trailblazer.me", + "visualforce.com" + ] + }, + "Salesintelligence": { + "properties": [ + "salesintelligence.pl" + ], + "resources": [ + "plugin.management" + ] + }, + "Samurai Factory": { + "properties": [ + "samurai-factory.jp", + "shinobi.jp" + ], + "resources": [ + "samurai-factory.jp", + "shinobi.jp" + ] + }, + "SAP": { + "properties": [ + "sap.com" + ], + "resources": [ + "sap.com", + "seewhy.com" + ] + }, + "Sapient": { + "properties": [ + "bridgetrack.com", + "sapient.com" + ], + "resources": [ + "bridgetrack.com", + "sapient.com" + ] + }, + "SAS": { + "properties": [ + "aimatch.com", + "sas.com" + ], + "resources": [ + "aimatch.com", + "sas.com" + ] + }, + "SAY": { + "properties": [ + "saymedia.com", + "typepad.com", + "videoegg.com" + ], + "resources": [ + "saymedia.com", + "typepad.com", + "videoegg.com" + ] + }, + "Scandinavian AdNetworks": { + "properties": [ + "scandinavianadnetworks.com" + ], + "resources": [ + "scandinavianadnetworks.com" + ] + }, + "ScribeFire": { + "properties": [ + "scribefire.com" + ], + "resources": [ + "scribefire.com" + ] + }, + "Scribol": { + "properties": [ + "scribol.com" + ], + "resources": [ + "scribol.com" + ] + }, + "SearchForce": { + "properties": [ + "searchforce.com", + "searchforce.net" + ], + "resources": [ + "searchforce.com", + "searchforce.net" + ] + }, + "Seevast": { + "properties": [ + "kanoodle.com" + ], + "resources": [ + "kanoodle.com", + "pulse360.com", + "seevast.com", + "syndigonetworks.com" + ] + }, + "SeeVolution": { + "properties": [ + "seevolution.com", + "svlu.net" + ], + "resources": [ + "seevolution.com", + "svlu.net" + ] + }, + "Segment.io": { + "properties": [ + "segment.io" + ], + "resources": [ + "segment.io" + ] + }, + "Selectable Media": { + "properties": [ + "selectablemedia.com" + ], + "resources": [ + "nabbr.com", + "selectablemedia.com" + ] + }, + "Semantiqo": { + "properties": [ + "semantiqo.com" + ], + "resources": [ + "semantiqo.com" + ] + }, + "Semasio": { + "properties": [ + "semasio.com" + ], + "resources": [ + "semasio.com", + "semasio.net" + ] + }, + "SendPulse": { + "properties": [ + "sendpulse.com" + ], + "resources": [ + "sendpulse.com" + ] + }, + "Service4refresh": { + "properties": [ + "service4refresh.info" + ], + "resources": [ + "service4refresh.info" + ] + }, + "SessionCam": { + "properties": [ + "sessioncam.com" + ], + "resources": [ + "sessioncam.com" + ] + }, + "SevenAds": { + "properties": [ + "sevenads.net" + ], + "resources": [ + "sevenads.net" + ] + }, + "SexInYourCity": { + "properties": [ + "sexinyourcity.com" + ], + "resources": [ + "sexinyourcity.com" + ] + }, + "ShaftTraffic": { + "properties": [ + "shafttraffic.com" + ], + "resources": [ + "libertystmedia.com", + "shafttraffic.com" + ] + }, + "Shareaholic": { + "properties": [ + "shareaholic.com" + ], + "resources": [ + "shareaholic.com" + ] + }, + "ShareASale": { + "properties": [ + "shareasale.com" + ], + "resources": [ + "shareasale.com" + ] + }, + "ShareThis": { + "properties": [ + "sharethis.com" + ], + "resources": [ + "sharethis.com" + ] + }, + "Sharethrough": { + "properties": [ + "sharethrough.com" + ], + "resources": [ + "sharethrough.com" + ] + }, + "ShinyStat": { + "properties": [ + "shinystat.com" + ], + "resources": [ + "shinystat.com" + ] + }, + "Shopzilla": { + "properties": [ + "shopzilla.com" + ], + "resources": [ + "shopzilla.com" + ] + }, + "Shortest": { + "properties": [ + "shorte.st" + ], + "resources": [ + "shorte.st" + ] + }, + "SiftScience": { + "properties": [ + "sift.com" + ], + "resources": [ + "siftscience.com" + ] + }, + "Signifyd": { + "properties": [ + "signifyd.com" + ], + "resources": [ + "signifyd.com" + ] + }, + "Silverpop": { + "properties": [ + "mkt51.net", + "silverpop.com" + ], + "resources": [ + "mkt51.net", + "pages05.net", + "silverpop.com", + "vtrenz.net" + ] + }, + "Simpli.fi": { + "properties": [ + "simpli.fi" + ], + "resources": [ + "simpli.fi" + ] + }, + "SiteScout": { + "properties": [ + "sitescout.com" + ], + "resources": [ + "sitescout.com" + ] + }, + "Six Apart": { + "properties": [ + "movabletype.com", + "sixapart.com" + ], + "resources": [ + "movabletype.com", + "sixapart.com" + ] + }, + "Skimlinks": { + "properties": [ + "skimlinks.com", + "skimresources.com" + ], + "resources": [ + "skimlinks.com", + "skimresources.com" + ] + }, + "Skribit": { + "properties": [ + "paulstamatiou.com" + ], + "resources": [ + "paulstamatiou.com", + "skribit.com" + ] + }, + "Skupe Net": { + "properties": [ + "adcentriconline.com", + "skupenet.com" + ], + "resources": [ + "adcentriconline.com", + "skupenet.com" + ] + }, + "Smaato": { + "properties": [ + "smaato.com" + ], + "resources": [ + "smaato.com" + ] + }, + "SmartAdServer": { + "properties": [ + "smartadserver.com" + ], + "resources": [ + "smartadserver.com" + ] + }, + "Smartlook": { + "properties": [ + "smartlook.com" + ], + "resources": [ + "smartlook.com" + ] + }, + "SmartyAds": { + "properties": [ + "smartyads.com" + ], + "resources": [ + "smartyads.com" + ] + }, + "Smi": { + "properties": [ + "24smi.net" + ], + "resources": [ + "24smi.net" + ] + }, + "Smiley Media": { + "properties": [ + "smileymedia.com" + ], + "resources": [ + "smileymedia.com" + ] + }, + "Smowtion": { + "properties": [ + "smowtion.com" + ], + "resources": [ + "smowtion.com" + ] + }, + "Snap": { + "properties": [ + "snap.com" + ], + "resources": [ + "snap.com" + ] + }, + "SnapEngage": { + "properties": [ + "snapengage.com" + ], + "resources": [ + "snapengage.com" + ] + }, + "Snoobi": { + "properties": [ + "snoobi.fi" + ], + "resources": [ + "snoobi.com", + "snoobi.fi" + ] + }, + "SocialChorus": { + "properties": [ + "socialchorus.com" + ], + "resources": [ + "halogenmediagroup.com", + "halogennetwork.com", + "socialchorus.com" + ] + }, + "SocialInterface": { + "properties": [ + "socialinterface.com" + ], + "resources": [ + "ratevoice.com", + "socialinterface.com" + ] + }, + "SocialTwist": { + "properties": [ + "socialtwist.com" + ], + "resources": [ + "socialtwist.com" + ] + }, + "sociomantic labs": { + "properties": [ + "sociomantic.com" + ], + "resources": [ + "sociomantic.com" + ] + }, + "Socital": { + "properties": [ + "socital.com" + ], + "resources": [ + "socital.com" + ] + }, + "Sojern": { + "properties": [ + "sojern.com" + ], + "resources": [ + "sojern.com" + ] + }, + "SomoAudience": { + "properties": [ + "somoaudience.com" + ], + "resources": [ + "somoaudience.com" + ] + }, + "Sonobi": { + "properties": [ + "sonobi.com" + ], + "resources": [ + "sonobi.com" + ] + }, + "sophus3": { + "properties": [ + "sophus3.com" + ], + "resources": [ + "sophus3.co.uk", + "sophus3.com" + ] + }, + "Sortable": { + "properties": [ + "sortable.com" + ], + "resources": [ + "deployads.com" + ] + }, + "Sourcepoint": { + "properties": [ + "sourcepoint.com" + ], + "resources": [ + "summerhamster.com" + ] + }, + "Sovrn": { + "properties": [ + "sovrn.com" + ], + "resources": [ + "sovrn.com" + ] + }, + "Space Chimp Media": { + "properties": [ + "spacechimpmedia.com" + ], + "resources": [ + "spacechimpmedia.com" + ] + }, + "SpareChange": { + "properties": [ + "sparechange.io" + ], + "resources": [ + "sparechange.io" + ] + }, + "Sparklit": { + "properties": [ + "adbutler.com", + "sparklit.com" + ], + "resources": [ + "adbutler.com", + "sparklit.com" + ] + }, + "Spark Studios": { + "properties": [ + "sparkstudios.com" + ], + "resources": [ + "sparkstudios.com" + ] + }, + "Specific Media": { + "properties": [ + "sitemeter.com", + "specificmedia.com" + ], + "resources": [ + "adviva.co.uk", + "adviva.net", + "sitemeter.com", + "specificclick.net", + "specificmedia.com" + ] + }, + "Spectate": { + "properties": [ + "spectate.com" + ], + "resources": [ + "spectate.com" + ] + }, + "Sponge": { + "properties": [ + "spongegroup.com" + ], + "resources": [ + "spongegroup.com" + ] + }, + "Spongecell": { + "properties": [ + "spongecell.com" + ], + "resources": [ + "spongecell.com" + ] + }, + "SponsorAds": { + "properties": [ + "sponsorads.de" + ], + "resources": [ + "sponsorads.de" + ] + }, + "Spot200": { + "properties": [ + "spot200.com" + ], + "resources": [ + "spot200.com" + ] + }, + "SpotX": { + "properties": [ + "spotx.tv" + ], + "resources": [ + "spotx.tv" + ] + }, + "SpotXchange": { + "properties": [ + "spotxchange.com" + ], + "resources": [ + "spotxcdn.com", + "spotxchange.com" + ] + }, + "Spring Metrics": { + "properties": [ + "springmetrics.com" + ], + "resources": [ + "springmetrics.com" + ] + }, + "SpringServe": { + "properties": [ + "springserve.com" + ], + "resources": [ + "springserve.com" + ] + }, + "Sputnik.ru": { + "properties": [ + "sputnik.ru" + ], + "resources": [ + "sputnik.ru" + ] + }, + "StackAdapt": { + "properties": [ + "stackadapt.com" + ], + "resources": [ + "stackadapt.com" + ] + }, + "StackTrack": { + "properties": [ + "stat-track.com" + ], + "resources": [ + "stat-track.com" + ] + }, + "StarGames": { + "properties": [ + "stargames.net", + "stargamesaffiliate.com" + ], + "resources": [ + "stargames.net", + "stargamesaffiliate.com" + ] + }, + "stat4u": { + "properties": [ + "4u.pl" + ], + "resources": [ + "4u.pl" + ] + }, + "StatCounter": { + "properties": [ + "statcounter.com" + ], + "resources": [ + "statcounter.com" + ] + }, + "Statisfy": { + "properties": [ + "statisfy.net" + ], + "resources": [ + "statisfy.net" + ] + }, + "STATSIT": { + "properties": [ + "statsit.com" + ], + "resources": [ + "statsit.com" + ] + }, + "SteelHouse": { + "properties": [ + "steelhouse.com", + "steelhousemedia.com" + ], + "resources": [ + "steelhouse.com", + "steelhousemedia.com" + ] + }, + "Storeland": { + "properties": [ + "storeland.ru" + ], + "resources": [ + "storeland.ru" + ] + }, + "Storygize": { + "properties": [ + "storygize.com" + ], + "resources": [ + "storygize.com", + "storygize.net" + ] + }, + "Stratigent": { + "properties": [ + "stratigent.com" + ], + "resources": [ + "stratigent.com" + ] + }, + "Streamray": { + "properties": [ + "cams.com", + "streamray.com" + ], + "resources": [ + "cams.com", + "streamray.com" + ] + }, + "StrikeAd": { + "properties": [ + "strikead.com" + ], + "resources": [ + "strikead.com" + ] + }, + "Stripe": { + "properties": [ + "stripe.com" + ], + "resources": [ + "stripe.network" + ] + }, + "StrongMail": { + "properties": [ + "strongmail.com" + ], + "resources": [ + "popularmedia.com", + "strongmail.com" + ] + }, + "Struq": { + "properties": [ + "struq.com" + ], + "resources": [ + "struq.com" + ] + }, + "StumbleUpon": { + "properties": [ + "stumbleupon.com" + ], + "resources": [ + "stumble-upon.com", + "stumbleupon.com" + ] + }, + "Sublime Skinz": { + "properties": [ + "sublime.xyz" + ], + "resources": [ + "ayads.co", + "sublime.xyz" + ] + }, + "Suite 66": { + "properties": [ + "suite66.com" + ], + "resources": [ + "suite66.com" + ] + }, + "Summit": { + "properties": [ + "summitmedia.co.uk", + "www.summit.co.uk" + ], + "resources": [ + "summitmedia.co.uk", + "www.summit.co.uk" + ] + }, + "Superfish": { + "properties": [ + "superfish.com" + ], + "resources": [ + "superfish.com" + ] + }, + "SupersonicAds": { + "properties": [ + "supersonicads.com" + ], + "resources": [ + "supersonicads.com" + ] + }, + "Survata": { + "properties": [ + "survata.com" + ], + "resources": [ + "survata.com" + ] + }, + "SwiftMining": { + "properties": [ + "swiftmining.win" + ], + "resources": [ + "swiftmining.win" + ] + }, + "Switch": { + "properties": [ + "switchadhub.com", + "switchconcepts.com" + ], + "resources": [ + "switchadhub.com", + "switchads.com", + "switchconcepts.co.uk", + "switchconcepts.com" + ] + }, + "Swoop": { + "properties": [ + "swoop.com" + ], + "resources": [ + "swoop.com" + ] + }, + "SymphonyAM": { + "properties": [ + "factortg.com" + ], + "resources": [ + "factortg.com" + ] + }, + "Synacor": { + "properties": [ + "synacor.com" + ], + "resources": [ + "synacor.com" + ] + }, + "Syncapse": { + "properties": [ + "clickable.net", + "syncapse.com" + ], + "resources": [ + "clickable.net", + "syncapse.com" + ] + }, + "Syrup Ad": { + "properties": [ + "adotsolution.com" + ], + "resources": [ + "adotsolution.com" + ] + }, + "Taboola": { + "properties": [ + "taboola.com" + ], + "resources": [ + "perfectmarket.com", + "taboola.com" + ] + }, + "Tailsweep": { + "properties": [ + "tailsweep.com" + ], + "resources": [ + "tailsweep.com" + ] + }, + "Taleria": { + "properties": [ + "telaria.com" + ], + "resources": [ + "freeskreen.com" + ] + }, + "Tapad": { + "properties": [ + "tapad.com" + ], + "resources": [ + "tapad.com" + ] + }, + "Tapgage": { + "properties": [ + "bizmey.com", + "tapgage.com" + ], + "resources": [ + "bizmey.com", + "tapgage.com" + ] + }, + "TapIt!": { + "properties": [ + "tapit.com" + ], + "resources": [ + "tapit.com" + ] + }, + "Tap.me": { + "properties": [ + "tap.me" + ], + "resources": [ + "tap.me" + ] + }, + "Targetix": { + "properties": [ + "targetix.net" + ], + "resources": [ + "targetix.net" + ] + }, + "Tatto Media": { + "properties": [ + "tattomedia.com" + ], + "resources": [ + "quicknoodles.com", + "tattomedia.com" + ] + }, + "Teadma": { + "properties": [ + "teadma.com" + ], + "resources": [ + "teadma.com" + ] + }, + "Teads.tv": { + "properties": [ + "teads.tv" + ], + "resources": [ + "teads.tv" + ] + }, + "Tealium": { + "properties": [ + "tealium.com" + ], + "resources": [ + "tealiumiq.com" + ] + }, + "Technorati": { + "properties": [ + "technorati.com" + ], + "resources": [ + "technorati.com", + "technoratimedia.com" + ] + }, + "TechSolutions": { + "properties": [ + "techsolutions.com.tw" + ], + "resources": [ + "techsolutions.com.tw" + ] + }, + "TellApart": { + "properties": [ + "tellapart.com", + "tellapt.com" + ], + "resources": [ + "tellapart.com", + "tellapt.com" + ] + }, + "Telstra": { + "properties": [ + "sensis.com.au", + "sensisdata.com.au", + "telstra.com.au" + ], + "resources": [ + "sensis.com.au", + "sensisdata.com.au", + "sensisdigitalmedia.com.au", + "telstra.com.au" + ] + }, + "TENSQUARE": { + "properties": [ + "tensquare.com" + ], + "resources": [ + "tensquare.com" + ] + }, + "Terra": { + "properties": [ + "eztargetmedia.com", + "terra.com.br", + "www.terra.com.br" + ], + "resources": [ + "eztargetmedia.com", + "terra.com.br", + "www.terra.com.br" + ] + }, + "The Heron Partnership": { + "properties": [ + "marinsm.com" + ], + "resources": [ + "heronpartners.com.au", + "marinsm.com", + "marinsoftware.com" + ] + }, + "The Numa Group": { + "properties": [ + "hittail.com", + "thenumagroup.com" + ], + "resources": [ + "hittail.com", + "thenumagroup.com" + ] + }, + "The Search Agency": { + "properties": [ + "thesearchagency.com" + ], + "resources": [ + "thesearchagency.com", + "thesearchagency.net" + ] + }, + "The Trade Desk": { + "properties": [ + "thetradedesk.com" + ], + "resources": [ + "adsrvr.org", + "thetradedesk.com" + ] + }, + "ThingLink": { + "properties": [ + "thinglink.com" + ], + "resources": [ + "thinglink.com" + ] + }, + "Think Realtime": { + "properties": [ + "echosearch.com", + "thinkrealtime.com" + ], + "resources": [ + "echosearch.com", + "esm1.net", + "thinkrealtime.com" + ] + }, + "Thismoment": { + "properties": [ + "thismoment.com" + ], + "resources": [ + "thismoment.com" + ] + }, + "Thummit": { + "properties": [ + "thummit.com" + ], + "resources": [ + "thummit.com" + ] + }, + "Tinder": { + "properties": [ + "carbonads.com", + "tinder.com" + ], + "resources": [ + "carbonads.com", + "tinder.com" + ] + }, + "TiqIQ": { + "properties": [ + "tiqiq.com" + ], + "resources": [ + "tiqiq.com" + ] + }, + "Tisoomi": { + "properties": [ + "adternal.com", + "tisoomi.com" + ], + "resources": [ + "adternal.com", + "tisoomi.com" + ] + }, + "TLVMedia": { + "properties": [ + "tlvmedia.com" + ], + "resources": [ + "tlvmedia.com" + ] + }, + "TNS": { + "properties": [ + "statistik-gallup.net", + "tns-counter.ru", + "tns-cs.net", + "tnsglobal.com" + ], + "resources": [ + "sesamestats.com", + "statistik-gallup.net", + "tns-counter.ru", + "tns-cs.net", + "tnsglobal.com" + ] + }, + "Todacell": { + "properties": [ + "todacell.com" + ], + "resources": [ + "todacell.com" + ] + }, + "ToneFuse": { + "properties": [ + "tonefuse.com" + ], + "resources": [ + "tonefuse.com" + ] + }, + "ToneMedia": { + "properties": [ + "clickfuse.com" + ], + "resources": [ + "clickfuse.com", + "tonemedia.com" + ] + }, + "tongdun.cn": { + "properties": [ + "tongdun.cn" + ], + "resources": [ + "fraudmetrix.cn", + "tongdun.net" + ] + }, + "Topsy": { + "properties": [ + "topsy.com" + ], + "resources": [ + "topsy.com" + ] + }, + "TouchCommerce": { + "properties": [ + "nuance.com" + ], + "resources": [ + "inq.com", + "nuance.com", + "touchcommerce.com" + ] + }, + "TraceMyIP.org": { + "properties": [ + "tracemyip.org" + ], + "resources": [ + "tracemyip.org" + ] + }, + "TrackingSoft": { + "properties": [ + "roia.biz", + "trackingsoft.com" + ], + "resources": [ + "roia.biz", + "trackingsoft.com" + ] + }, + "Trackset": { + "properties": [ + "trackset.com" + ], + "resources": [ + "trackset.com" + ] + }, + "Tradedoubler": { + "properties": [ + "tradedoubler.com" + ], + "resources": [ + "tradedoubler.com" + ] + }, + "TradeTracker": { + "properties": [ + "tradetracker.com" + ], + "resources": [ + "tradetracker.com", + "tradetracker.net" + ] + }, + "TrafficHaus": { + "properties": [ + "traffichaus.com", + "traffichouse.com" + ], + "resources": [ + "traffichaus.com", + "traffichouse.com" + ] + }, + "TrafficRevenue": { + "properties": [ + "trafficrevenue.net" + ], + "resources": [ + "trafficrevenue.net" + ] + }, + "TrafficScore": { + "properties": [ + "trafficscore.com" + ], + "resources": [ + "trafficscore.com" + ] + }, + "Traffiq": { + "properties": [ + "traffiq.com" + ], + "resources": [ + "traffiq.com" + ] + }, + "Trafmag": { + "properties": [ + "trafmag.com" + ], + "resources": [ + "trafmag.com" + ] + }, + "Traverse": { + "properties": [ + "traversedata.com" + ], + "resources": [ + "traversedlp.com" + ] + }, + "Travora Media": { + "properties": [ + "travoramedia.com" + ], + "resources": [ + "traveladnetwork.com", + "traveladvertising.com", + "travoramedia.com" + ] + }, + "Tremor Video": { + "properties": [ + "tremorvideo.com" + ], + "resources": [ + "scanscout.com", + "tmnetads.com", + "tremorhub.com", + "tremormedia.com", + "tremorvideo.com" + ] + }, + "Triggit": { + "properties": [ + "triggit.com" + ], + "resources": [ + "triggit.com" + ] + }, + "TripleLift": { + "properties": [ + "triplelift.com" + ], + "resources": [ + "3lift.com", + "triplelift.com" + ] + }, + "Trovus": { + "properties": [ + "trovus.co.uk", + "www.trovus.co.uk" + ], + "resources": [ + "trovus.co.uk", + "www.trovus.co.uk" + ] + }, + "TruEffect": { + "properties": [ + "adlegend.com", + "trueffect.com" + ], + "resources": [ + "adlegend.com", + "trueffect.com" + ] + }, + "Trumba": { + "properties": [ + "trumba.com" + ], + "resources": [ + "trumba.com" + ] + }, + "TRUSTe": { + "properties": [ + "truste.com" + ], + "resources": [ + "truste.com" + ] + }, + "TrustX": { + "properties": [ + "trustx.org" + ], + "resources": [ + "trustx.org" + ] + }, + "TubeMogul": { + "properties": [ + "tmogul.com", + "tubemogul.com" + ], + "resources": [ + "tmogul.com", + "tubemogul.com" + ] + }, + "TurnTo": { + "properties": [ + "turntonetworks.com" + ], + "resources": [ + "turnto.com", + "turntonetworks.com" + ] + }, + "Tweetboard": { + "properties": [ + "tweetboard.com" + ], + "resources": [ + "tweetboard.com" + ] + }, + "Twelvefold": { + "properties": [ + "buzzlogic.com", + "twelvefold.com" + ], + "resources": [ + "buzzlogic.com", + "twelvefold.com" + ] + }, + "Twitter": { + "properties": [ + "digits.com", + "fabric.io", + "tweetdeck.com", + "twitter.com", + "twitter.jp" + ], + "resources": [ + "ads-twitter.com", + "fabric.io", + "tweetdeck.com", + "twimg.com", + "twitter.com", + "twitter.jp" + ] + }, + "Twitter Counter": { + "properties": [ + "twittercounter.com" + ], + "resources": [ + "twittercounter.com" + ] + }, + "Twyn Group": { + "properties": [ + "twyn-group.com", + "twyn.com" + ], + "resources": [ + "twyn-group.com", + "twyn.com" + ] + }, + "Tyroo": { + "properties": [ + "tyroo.com" + ], + "resources": [ + "tyroo.com" + ] + }, + "UberMedia": { + "properties": [ + "tweetup.com", + "ubermedia.com" + ], + "resources": [ + "tweetup.com", + "ubermedia.com" + ] + }, + "UberTags": { + "properties": [ + "ubertags.com" + ], + "resources": [ + "ubertags.com" + ] + }, + "ucfunnel": { + "properties": [ + "ucfunnel.com" + ], + "resources": [ + "aralego.com", + "ucfunnel.com" + ] + }, + "uCoz": { + "properties": [ + "ucoz.ae", + "ucoz.com", + "ucoz.fr", + "ucoz.net", + "ucoz.ru" + ], + "resources": [ + "ucoz.ae", + "ucoz.br", + "ucoz.com", + "ucoz.du", + "ucoz.fr", + "ucoz.net", + "ucoz.ru" + ] + }, + "Umbel": { + "properties": [ + "umbel.com" + ], + "resources": [ + "umbel.com" + ] + }, + "Unanimis": { + "properties": [ + "unanimis.co.uk", + "www.unanimis.co.uk" + ], + "resources": [ + "unanimis.co.uk", + "www.unanimis.co.uk" + ] + }, + "Unbounce": { + "properties": [ + "unbounce.com" + ], + "resources": [ + "unbounce.com" + ] + }, + "Underdog Media": { + "properties": [ + "udmserve.net", + "underdogmedia.com" + ], + "resources": [ + "udmserve.net", + "underdogmedia.com" + ] + }, + "Undertone": { + "properties": [ + "undertone.com", + "undertonevideo.com" + ], + "resources": [ + "undertone.com", + "undertonenetworks.com", + "undertonevideo.com" + ] + }, + "UniQlick": { + "properties": [ + "51network.com", + "uniqlick.com", + "wanmo.com" + ], + "resources": [ + "51network.com", + "uniqlick.com", + "wanmo.com" + ] + }, + "Unruly": { + "properties": [ + "unruly.co" + ], + "resources": [ + "unrulymedia.com" + ] + }, + "Upland": { + "properties": [ + "uplandsoftware.com" + ], + "resources": [ + "leadlander.com", + "sf14g.com", + "trackalyzer.com", + "uplandsoftware.com" + ] + }, + "Uptrends": { + "properties": [ + "uptrends.com" + ], + "resources": [ + "uptrends.com" + ] + }, + "up-value": { + "properties": [ + "up-value.de" + ], + "resources": [ + "up-value.de" + ] + }, + "Usability Sciences": { + "properties": [ + "usabilitysciences.com" + ], + "resources": [ + "usabilitysciences.com", + "webiqonline.com" + ] + }, + "User Local": { + "properties": [ + "nakanohito.jp" + ], + "resources": [ + "nakanohito.jp" + ] + }, + "UserVoice": { + "properties": [ + "uservoice.com" + ], + "resources": [ + "uservoice.com" + ] + }, + "V12 Data": { + "properties": [ + "v12group.com" + ], + "resources": [ + "v12data.com", + "v12group.com" + ] + }, + "Value Ad": { + "properties": [ + "valuead.com" + ], + "resources": [ + "valuead.com" + ] + }, + "Various": { + "properties": [ + "amigos.com", + "getiton.com", + "medley.com", + "nostringsattached.com", + "various.com" + ], + "resources": [ + "amigos.com", + "getiton.com", + "medley.com", + "nostringsattached.com", + "various.com" + ] + }, + "Vdopia": { + "properties": [ + "ivdopia.com", + "vdopia.com" + ], + "resources": [ + "ivdopia.com", + "vdopia.com" + ] + }, + "Veeseo": { + "properties": [ + "veeseo.com" + ], + "resources": [ + "veeseo.com" + ] + }, + "Velocity Media": { + "properties": [ + "adsvelocity.com" + ], + "resources": [ + "adsvelocity.com" + ] + }, + "Velti": { + "properties": [ + "mobclix.com", + "velti.com" + ], + "resources": [ + "mobclix.com", + "velti.com" + ] + }, + "Vemba": { + "properties": [ + "vemba.com" + ], + "resources": [ + "vemba.com" + ] + }, + "Venatus Media": { + "properties": [ + "venatusmedia.com" + ], + "resources": [ + "venatusmedia.com" + ] + }, + "Vendemore": { + "properties": [ + "vendemore.com" + ], + "resources": [ + "vendemore.com" + ] + }, + "Vendio": { + "properties": [ + "singlefeed.com", + "vendio.com" + ], + "resources": [ + "singlefeed.com", + "vendio.com" + ] + }, + "Veoxa": { + "properties": [ + "veoxa.com" + ], + "resources": [ + "veoxa.com" + ] + }, + "Veremedia": { + "properties": [ + "veremedia.com" + ], + "resources": [ + "veremedia.com" + ] + }, + "Vertical Acuity": { + "properties": [ + "verticalacuity.com" + ], + "resources": [ + "verticalacuity.com" + ] + }, + "VerticalHealth": { + "properties": [ + "verticalhealth.com" + ], + "resources": [ + "verticalhealth.net" + ] + }, + "VerticalResponse": { + "properties": [ + "verticalresponse.com", + "vresp.com" + ], + "resources": [ + "verticalresponse.com", + "vresp.com" + ] + }, + "Vertster": { + "properties": [ + "vertster.com" + ], + "resources": [ + "vertster.com" + ] + }, + "VG WORT": { + "properties": [ + "vgwort.de" + ], + "resources": [ + "vgwort.de" + ] + }, + "Vibrant Media": { + "properties": [ + "vibrantmedia.com" + ], + "resources": [ + "intellitxt.com", + "picadmedia.com", + "vibrantmedia.com" + ] + }, + "VideoIntelligence": { + "properties": [ + "vi.ai" + ], + "resources": [ + "vi.ai" + ] + }, + "Videology": { + "properties": [ + "tidaltv.com", + "videologygroup.com" + ], + "resources": [ + "tidaltv.com", + "videologygroup.com" + ] + }, + "Viewbix": { + "properties": [ + "qoof.com", + "viewbix.com" + ], + "resources": [ + "qoof.com", + "viewbix.com" + ] + }, + "VigLink": { + "properties": [ + "viglink.com" + ], + "resources": [ + "viglink.com" + ] + }, + "Vimeo": { + "properties": [ + "vimeo.com", + "vimeocdn.com" + ], + "resources": [ + "vimeo.com", + "vimeocdn.com" + ] + }, + "VINDICO": { + "properties": [ + "vindicogroup.com", + "vindicosuite.com" + ], + "resources": [ + "vindicogroup.com", + "vindicosuite.com" + ] + }, + "VisibleBrands": { + "properties": [ + "visbrands.com" + ], + "resources": [ + "visbrands.com" + ] + }, + "Visible Measures": { + "properties": [ + "visiblemeasures.com" + ], + "resources": [ + "viewablemedia.net", + "visiblemeasures.com" + ] + }, + "VisiStat": { + "properties": [ + "id.kickfire.com", + "sa-as.com" + ], + "resources": [ + "d.kickfire.com", + "sa-as.com", + "visistat.com" + ] + }, + "Visit Streamer": { + "properties": [ + "visitstreamer.com" + ], + "resources": [ + "visitstreamer.com" + ] + }, + "vistrac": { + "properties": [ + "vistrac.com" + ], + "resources": [ + "vistrac.com" + ] + }, + "VisualDNA": { + "properties": [ + "vdna-assets.com", + "visualdna-stats.com", + "visualdna.com" + ], + "resources": [ + "vdna-assets.com", + "visualdna-stats.com", + "visualdna.com" + ] + }, + "ViziSense": { + "properties": [ + "vizisense.com", + "vizisense.net" + ], + "resources": [ + "vizisense.com", + "vizisense.net" + ] + }, + "Vizu": { + "properties": [ + "vizu.com" + ], + "resources": [ + "vizu.com" + ] + }, + "Vizury": { + "properties": [ + "vizury.com" + ], + "resources": [ + "vizury.com" + ] + }, + "VKontakte": { + "properties": [ + "vk.com" + ], + "resources": [ + "userapi.com", + "vk.com", + "vkontakte.ru" + ] + }, + "Voice2Page": { + "properties": [ + "voice2page.com" + ], + "resources": [ + "voice2page.com" + ] + }, + "Vserv": { + "properties": [ + "vserv.com", + "vserv.mobi" + ], + "resources": [ + "vserv.com", + "vserv.mobi" + ] + }, + "Vuble": { + "properties": [ + "vuble.tv" + ], + "resources": [ + "mediabong.com" + ] + }, + "Wahoha": { + "properties": [ + "contentwidgets.net", + "wahoha.com" + ], + "resources": [ + "contentwidgets.net", + "wahoha.com" + ] + }, + "Wayfair": { + "properties": [ + "wayfair.com" + ], + "resources": [ + "wayfair.com" + ] + }, + "WebAds": { + "properties": [ + "webads.co.uk", + "www.webads.co.uk" + ], + "resources": [ + "webads.co.uk", + "www.webads.co.uk" + ] + }, + "Webclicktracker": { + "properties": [ + "webclicktracker.com" + ], + "resources": [ + "webclicktracker.com" + ] + }, + "Web.com": { + "properties": [ + "feedperfect.com", + "web.com" + ], + "resources": [ + "feedperfect.com", + "web.com" + ] + }, + "WebGozar.com": { + "properties": [ + "webgozar.com", + "webgozar.ir" + ], + "resources": [ + "webgozar.com", + "webgozar.ir" + ] + }, + "Webmecanik": { + "properties": [ + "webmecanik.com" + ], + "resources": [ + "webmecanik.com" + ] + }, + "WebMetro": { + "properties": [ + "dsmmadvantage.com", + "revanadigital.com" + ], + "resources": [ + "dsmmadvantage.com", + "revanadigital.com", + "webmetro.com" + ] + }, + "Webmine": { + "properties": [ + "webmine.cz" + ], + "resources": [ + "authedwebmine.cz", + "webmine.cz" + ] + }, + "WebminePool": { + "properties": [ + "webminepool.com" + ], + "resources": [ + "webminepool.com" + ] + }, + "Webmining": { + "properties": [ + "webmining.co" + ], + "resources": [ + "webmining.co" + ] + }, + "Weborama": { + "properties": [ + "weborama.com" + ], + "resources": [ + "weborama.com", + "weborama.fr" + ] + }, + "WebsiteAlive": { + "properties": [ + "websitealive.com", + "websitealive0.com", + "websitealive1.com", + "websitealive2.com", + "websitealive3.com", + "websitealive4.com", + "websitealive5.com", + "websitealive6.com", + "websitealive7.com", + "websitealive8.com", + "websitealive9.com" + ], + "resources": [ + "websitealive.com" + ] + }, + "Web Stats": { + "properties": [ + "onlinewebstats.com" + ], + "resources": [ + "onlinewebstats.com" + ] + }, + "Web Tracking Services": { + "properties": [ + "web-stat.com", + "webtrackingservices.com" + ], + "resources": [ + "web-stat.com", + "webtrackingservices.com" + ] + }, + "Webtraffic": { + "properties": [ + "webtraffic.no", + "webtraffic.se" + ], + "resources": [ + "webtraffic.no", + "webtraffic.se" + ] + }, + "Web Traxs": { + "properties": [ + "webtraxs.com" + ], + "resources": [ + "webtraxs.com" + ] + }, + "Webtrekk": { + "properties": [ + "webtrekk.com", + "webtrekk.net" + ], + "resources": [ + "webtrekk.com", + "webtrekk.net" + ] + }, + "Webtrends": { + "properties": [ + "webtrends.com" + ], + "resources": [ + "reinvigorate.net", + "webtrends.com", + "webtrendslive.com" + ] + }, + "White Ops": { + "properties": [ + "adzmath.com", + "whiteops.com" + ], + "resources": [ + "adzmath.com", + "whiteops.com" + ] + }, + "whos.amung.us": { + "properties": [ + "amung.us" + ], + "resources": [ + "amung.us" + ] + }, + "WideOrbit": { + "properties": [ + "wideorbit.com" + ], + "resources": [ + "dep-x.com" + ] + }, + "Wingify": { + "properties": [ + "vwo.com", + "wingify.com" + ], + "resources": [ + "visualwebsiteoptimizer.com", + "vwo.com", + "wingify.com" + ] + }, + "WiredMinds": { + "properties": [ + "wiredminds.de" + ], + "resources": [ + "wiredminds.com", + "wiredminds.de" + ] + }, + "Wishabi": { + "properties": [ + "wishabi.com", + "wishabi.net" + ], + "resources": [ + "flipp.com", + "wishabi.com", + "wishabi.net" + ] + }, + "Woopra": { + "properties": [ + "woopra-ns.com", + "woopra.com" + ], + "resources": [ + "woopra-ns.com", + "woopra.com" + ] + }, + "WordStream": { + "properties": [ + "wordstream.com" + ], + "resources": [ + "wordstream.com" + ] + }, + "WOW Analytics": { + "properties": [ + "wowanalytics.co.uk" + ], + "resources": [ + "wowanalytics.co.uk" + ] + }, + "WPP": { + "properties": [ + "compete.com", + "decdna.net", + "groupm.com", + "kantarmedia.com", + "mecglobal.com", + "mindshareworld.com", + "themig.com", + "wpp.com", + "xaxis.com" + ], + "resources": [ + "247realmedia.com", + "accelerator-media.com", + "acceleratorusa.com", + "compete.com", + "decdna.net", + "decideinteractive.com", + "gmads.net", + "groupm.com", + "kantarmedia.com", + "mecglobal.com", + "mindshare.nl", + "mindshareworld.com", + "mookie1.com", + "pm14.com", + "realmedia.com", + "targ.ad", + "themig.com", + "wpp.com", + "xaxis.com" + ] + }, + "Wysistat": { + "properties": [ + "wysistat.net" + ], + "resources": [ + "wysistat.com", + "wysistat.net" + ] + }, + "xAd": { + "properties": [ + "xad.com" + ], + "resources": [ + "xad.com" + ] + }, + "Xertive Media": { + "properties": [ + "xertivemedia.com" + ], + "resources": [ + "admanager-xertive.com", + "xertivemedia.com" + ] + }, + "xplosion interactive": { + "properties": [ + "xplosion.de" + ], + "resources": [ + "xplosion.de" + ] + }, + "Xrost DS": { + "properties": [ + "adplan-ds.com" + ], + "resources": [ + "adplan-ds.com" + ] + }, + "Yabuka": { + "properties": [ + "yabuka.com" + ], + "resources": [ + "yabuka.com" + ] + }, + "Yahoo!": { + "properties": [ + "flickr.com", + "flurry.com", + "tumblr.com", + "yahoo.co.jp", + "yahoo.com", + "yahoostudios.com", + "yuilibrary.com" + ], + "resources": [ + "adinterax.com", + "adrevolver.com", + "bluelithium.com", + "dapper.net", + "flickr.com", + "flurry.com", + "interclick.com", + "luminate.com", + "mybloglog.com", + "overture.com", + "pixazza.com", + "rightmedia.com", + "rmxads.com", + "rocketmail.com", + "secure-adserver.com", + "staticflickr.com", + "tumblr.com", + "yahoo.co.jp", + "yahoo.com", + "yahooapis.com", + "yahooapis.jp", + "yahoofs.com", + "yieldmanager.com", + "yieldmanager.net", + "yimg.com", + "yimg.jp", + "yldmgrimg.net", + "ymail.com", + "yuilibrary.com", + "zenfs.com" + ] + }, + "Yandex": { + "properties": [ + "kinopoisk.ru", + "moikrug.ru", + "yadi.sk", + "yandex.by", + "yandex.com", + "yandex.com.tr", + "yandex.ru", + "yandex.st", + "yandex.ua" + ], + "resources": [ + "api-maps.yandex.ru", + "moikrug.ru", + "web-visor.com", + "yandex.by", + "yandex.com", + "yandex.com.tr", + "yandex.ru", + "yandex.st", + "yandex.ua" + ] + }, + "Ybrant Digital": { + "properties": [ + "addynamix.com", + "brightcom.com", + "luj.sdsjweb.com" + ], + "resources": [ + "addynamix.com", + "adserverplus.com", + "brightcom.com", + "oridian.com", + "ybrantdigital.com" + ] + }, + "YD": { + "properties": [ + "ydworld.com", + "yieldivision.com" + ], + "resources": [ + "ydworld.com", + "yieldivision.com" + ] + }, + "YellowHammer": { + "properties": [ + "yhmg.com" + ], + "resources": [ + "attracto.com", + "clickhype.com", + "yellowhammermg.com", + "yhmg.com" + ] + }, + "YellowTracker": { + "properties": [ + "yellowtracker.com" + ], + "resources": [ + "yellowtracker.com" + ] + }, + "Yes Ads": { + "properties": [ + "yesads.com" + ], + "resources": [ + "yesads.com" + ] + }, + "YieldAds": { + "properties": [ + "yieldads.com" + ], + "resources": [ + "yieldads.com" + ] + }, + "YieldBids": { + "properties": [ + "ybx.io" + ], + "resources": [ + "ybx.io" + ] + }, + "YieldBot": { + "properties": [ + "yieldbot.com" + ], + "resources": [ + "yldbt.com" + ] + }, + "YieldBuild": { + "properties": [ + "yieldbuild.com" + ], + "resources": [ + "yieldbuild.com" + ] + }, + "Yieldify": { + "properties": [ + "yieldify.com" + ], + "resources": [ + "yieldify.com" + ] + }, + "Yieldlab": { + "properties": [ + "yieldlab.de", + "yieldlab.net" + ], + "resources": [ + "yieldlab.de", + "yieldlab.net" + ] + }, + "Yieldmo": { + "properties": [ + "yieldmo.com" + ], + "resources": [ + "yieldmo.com" + ] + }, + "YieldNexus": { + "properties": [ + "ynxs.io" + ], + "resources": [ + "ynxs.io" + ] + }, + "YOC": { + "properties": [ + "yoc.com" + ], + "resources": [ + "yoc.com" + ] + }, + "Yoggrt": { + "properties": [ + "yoggrt.com" + ], + "resources": [ + "yoggrt.com" + ] + }, + "youknowbest": { + "properties": [ + "youknowbest.com" + ], + "resources": [ + "youknowbest.com" + ] + }, + "YSance": { + "properties": [ + "ysance.com" + ], + "resources": [ + "y-track.com" + ] + }, + "YuMe": { + "properties": [ + "yume.com", + "yumenetworks.com" + ], + "resources": [ + "yume.com", + "yumenetworks.com" + ] + }, + "ZafulAffiliate": { + "properties": [ + "zaful.com" + ], + "resources": [ + "zaful.com" + ] + }, + "Zango": { + "properties": [ + "metricsdirect.com", + "zango.com" + ], + "resources": [ + "metricsdirect.com", + "zango.com" + ] + }, + "zanox": { + "properties": [ + "buy.at", + "zanox-affiliate.de", + "zanox.com" + ], + "resources": [ + "buy.at", + "zanox-affiliate.de", + "zanox.com" + ] + }, + "zapunited": { + "properties": [ + "zaparena.com", + "zapunited.com" + ], + "resources": [ + "zaparena.com", + "zapunited.com" + ] + }, + "ZEDO": { + "properties": [ + "zedo.com", + "zincx.com" + ], + "resources": [ + "zedo.com", + "zincx.com" + ] + }, + "Zefir": { + "properties": [ + "ze-fir.com" + ], + "resources": [ + "ze-fir.com" + ] + }, + "Zemanta": { + "properties": [ + "zemanta.com" + ], + "resources": [ + "zemanta.com" + ] + }, + "Zendesk": { + "properties": [ + "zendesk.com" + ], + "resources": [ + "zendesk.com" + ] + }, + "ZestAd": { + "properties": [ + "zestad.com" + ], + "resources": [ + "zestad.com" + ] + }, + "Zeta Email Solutions": { + "properties": [ + "insightgrit.com", + "zetaemailsolutions.com" + ], + "resources": [ + "insightgrit.com", + "zetaemailsolutions.com" + ] + }, + "Zopim": { + "properties": [ + "zopim.com" + ], + "resources": [ + "zopim.com" + ] + }, + "Zumobi": { + "properties": [ + "zumobi.com" + ], + "resources": [ + "zumobi.com" + ] + }, + "ZypMedia": { + "properties": [ + "zypmedia.com" + ], + "resources": [ + "extend.tv", + "zypmedia.com" + ] + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-am/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-am/strings.xml new file mode 100644 index 0000000000..e52c54b1a8 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-am/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">በ%1$s ላይ ያለው ገጽ እንዲህ ይላል፡-</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s የእርስዎን የተጠቃሚ ስም እና የይለፍ ቃል እየጠየቀ ነው። ድረ-ገፁ እንዲህ ይላል፡- "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s የእርስዎን የተጠቃሚ ስም እና የይለፍ ቃል እየጠየቀ ነው።</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-an/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-an/strings.xml new file mode 100644 index 0000000000..676854b02c --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-an/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La pachina en %1$s diz:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s te ye pedindo lo tuyo nombre d’usuario y clau. Lo puesto diz: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s te ye pedindo lo tuyo nombre d’usuario y clau.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ann/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ann/strings.xml new file mode 100644 index 0000000000..96cb729e73 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ann/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Akpọk òkup me %1$s ìbe:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ìkido erieen̄ òsikwaan̄ kwun̄ mè ikọ-atafia. Akpatan̄ ya ìbe: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ìkido erieen̄ òsikwaan̄ kwun̄ mè ikọ-atafia.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ar/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ar/strings.xml new file mode 100644 index 0000000000..7b36637be6 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ar/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">تقول الصفحة في %1$s:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">يطلب الوسيط %2$s اسم مستخدم و كلمة سر. يقول الموقع: ”%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">يطلب %1$s اسم المستخدم و كلمة السر.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ast/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ast/strings.xml new file mode 100644 index 0000000000..5cf994aa8e --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ast/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La páxina de «%1$s» diz:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">«%2$s» solicita un nome d\'usuariu y una contraseña. El sitiu diz «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">«%1$s» solicita un nome d\'usuariu y una contraseña.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-az/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-az/strings.xml new file mode 100644 index 0000000000..0f445744de --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-az/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s səhifəsi deyir ki:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s istifadəçi adı və parolunuzu istəyir. Sayt deyir ki: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s istifadəçi adı və parolunuzu istəyir.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-azb/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-azb/strings.xml new file mode 100644 index 0000000000..2c64a4e2ba --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-azb/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s یارپاغیندا دئییلیر:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s قوللانیجی آدینیزی و رمزینیزی ایستییر. سایتدا دئییلیر: ”%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s قوللانیجی آدینیزی و رمزینیزی ایستییر.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ban/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ban/strings.xml new file mode 100644 index 0000000000..ed2eb2520d --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ban/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Kaca ring %1$s mapikobet:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ngidih aran sang anganggé miwah kruna sandi Ragané. Situs nyuratang: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ngidih aran sang anganggé miwah sandi Ragané.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-be/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-be/strings.xml new file mode 100644 index 0000000000..d29e4e6dfa --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-be/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Старонка на %1$s паведамляе:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s запытвае імя карыстальніка і пароль. Сайт паведамляе: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s запытвае імя карыстальніка і пароль.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bg/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bg/strings.xml new file mode 100644 index 0000000000..e8b1b76299 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bg/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Страницата на %1$s казва:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s пита за вашето потребителско име и парола. Сайтът казва: „%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s пита за вашето потребителско име и парола.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bn/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000000..7cc8bde2c2 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bn/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s পাতায় বলা হয়েছে:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s আপনার ব্যবহারকারী নাম এবং পাসওয়ার্ডের জন্য অনুরোধ করছে। সাইটটি বলছে: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s আপনার ব্যবহারকারী নাম এবং পাসওয়ার্ড অনুরোধ করছে।</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-br/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-br/strings.xml new file mode 100644 index 0000000000..882b2fbab0 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-br/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Ar bajenn war %1$s a lâr:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">Emañ ar proksi %2$s ocʼh azgoulenn un anv arveriad hag ur ger-tremen. Emañ al lecʼhienn o lavarout: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">Emañ ar proksi %1$s ocʼh azgoulenn un anv arveriad hag ur ger-tremen.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bs/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bs/strings.xml new file mode 100644 index 0000000000..3d65bfb52b --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-bs/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Stranica pri %1$s kaže:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s zahtijeva korisničko ime i lozinku. Stranica vraća odgovor: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s zahtijeva tvoje korisničko ime i lozinku.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ca/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ca/strings.xml new file mode 100644 index 0000000000..488d0cf419 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ca/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La pàgina a %1$s diu:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s sol·licita el vostre nom d’usuari i contrasenya. El lloc diu: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s sol·licita el vostre nom d’usuari i contrasenya.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cak/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cak/strings.xml new file mode 100644 index 0000000000..2054981f69 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cak/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Ri ruxaq %1$s nub\'ij:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s nuk\'utuj rub\'i\' winäq chuqa\' jun ewan tzij. Ri ruxaq k\'amaya\'l nub\'ij: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s nuk\'utuj ri rub\'i\' ataqoya\'l chuqa\' ewan atzij.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ceb/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ceb/strings.xml new file mode 100644 index 0000000000..33ffd2b3d7 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ceb/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Ang page sa %1$s ingon:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s nangayo sa imong username ug password. Ang site ingon: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s nangayo sa imong username ug password.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ckb/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ckb/strings.xml new file mode 100644 index 0000000000..baa85b12a8 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ckb/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">پەڕە لە %1$s دەڵێت:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s داوای ناوی بەکارهێنەر و وشەی تێپەڕبوون دەکات. ماڵپەڕەکە دەڵێت: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s داوای ناوی بەکارهێنەر و ووشەی تێپەڕبوون دەکات.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-co/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-co/strings.xml new file mode 100644 index 0000000000..26e36461c0 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-co/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Annunziamentu di a pagina %1$s :find in page</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">U situ %2$s richiede u vostru nome d’utilizatore è a vostra parolla d’intesa. U site indica : « %1$s »</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">U situ %1$s richiede u vostru nome d’utilizatore è a vostra parolla d’intesa.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cs/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cs/strings.xml new file mode 100644 index 0000000000..bc5b5608f0 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cs/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Sdělení stránky %1$s:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s požaduje vaše uživatelské jméno a heslo. Sdělení serveru: „%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s požaduje vaše uživatelské jméno a heslo.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cy/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cy/strings.xml new file mode 100644 index 0000000000..0ab3645f16 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-cy/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Mae tudalen yn %1$s yn dweud:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">Mae %2$s yn gofyn am enw defnyddiwr a chyfrinair. Mae’r wefan yn dweud: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">Mae %1$s yn gofyn am eich enw defnyddiwr a chyfrinair.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-da/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-da/strings.xml new file mode 100644 index 0000000000..2e5451caef --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-da/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Siden på %1$s siger:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s beder om dit brugernavn og din adgangskode. Webstedet siger: "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s beder om dit brugernavn og din adgangskode.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-de/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-de/strings.xml new file mode 100644 index 0000000000..0fcf258a76 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-de/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Die Seite mit der Adresse %1$s meldet:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s verlangt einen Benutzernamen und ein Passwort. Ausgabe der Website: „%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s verlangt einen Benutzernamen und ein Passwort.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-dsb/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-dsb/strings.xml new file mode 100644 index 0000000000..187703d7c9 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-dsb/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Bok na %1$s groni:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s pomina wužywarske mě a gronidło. Sedło groni: "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s pomina wašo wužywarske mě a gronidło.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-el/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-el/strings.xml new file mode 100644 index 0000000000..7376515b82 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-el/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Η σελίδα στο %1$s δηλώνει:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">Το %2$s ζητά όνομα χρήστη και κωδικό πρόσβασής. Ο ιστότοπος δηλώνει: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">Το %1$s ζητά το όνομα χρήστη και τον κωδικό πρόσβασής σας.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-en-rCA/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-en-rCA/strings.xml new file mode 100644 index 0000000000..b70ef57248 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-en-rCA/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">The page at %1$s says:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s is requesting your username and password. The site says: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s is requesting your username and password.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-en-rGB/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-en-rGB/strings.xml new file mode 100644 index 0000000000..b70ef57248 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-en-rGB/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">The page at %1$s says:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s is requesting your username and password. The site says: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s is requesting your username and password.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-eo/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-eo/strings.xml new file mode 100644 index 0000000000..aaeb343bab --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-eo/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La paĝo ĉe %1$s diras:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s petas vian nomon de uzanto kaj pasvorton. La retejo diras "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s petas vian nomon de uzanto kaj pasvorton.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rAR/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rAR/strings.xml new file mode 100644 index 0000000000..8f0dde846d --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rAR/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La página en %1$s dice:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s está pidiendo tu nombre de usuario y contraseña. El sitio dice: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s te está pidiendo tu nombre de usuario y contraseña.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rCL/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rCL/strings.xml new file mode 100644 index 0000000000..f679763411 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rCL/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La página en %1$s dice:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s está solicitando tu usuario y contraseña. El sitio dice: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s está solicitando tu usuario y contraseña.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rES/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rES/strings.xml new file mode 100644 index 0000000000..a9ccba99c5 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rES/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La página en %1$s dice:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s te está pidiendo tu nombre de usuario y contraseña. El sitio dice: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s te está pidiendo tu nombre de usuario y contraseña.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rMX/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rMX/strings.xml new file mode 100644 index 0000000000..6c3bd9f23c --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es-rMX/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La página en %1$s dice:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s está solicitando tu nombre de usuario y contraseña. El sitio dice: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s está solicitando tu nombre de usuario y contraseña.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es/strings.xml new file mode 100644 index 0000000000..a9ccba99c5 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-es/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La página en %1$s dice:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s te está pidiendo tu nombre de usuario y contraseña. El sitio dice: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s te está pidiendo tu nombre de usuario y contraseña.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-et/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-et/strings.xml new file mode 100644 index 0000000000..2efd14bd0d --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-et/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Veebileht aadressil %1$s ütleb:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">Sait aadressil %2$s nõuab kasutajanime ja parooli. Teade saidilt: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s nõuab kasutajanime ja parooli.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-eu/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-eu/strings.xml new file mode 100644 index 0000000000..23cf8239d3 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-eu/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s helbideko orriak hau dio:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s guneak erabiltzaile-izen eta pasahitza eskatzen ditu. Guneak hau dio: "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s guneak erabiltzaile-izen eta pasahitza eskatzen ditu.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fa/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fa/strings.xml new file mode 100644 index 0000000000..ce4b29bd0e --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fa/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">صفحهٔ %1$s میگوید:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s درخواست نام کاربری و گذرواژهٔ شما را دارد. این پایگاه میگوید: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s درخواست نام کاربری و گذرواژهٔ شما را دارد.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ff/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ff/strings.xml new file mode 100644 index 0000000000..206ab42aa6 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ff/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Hello wonngo to %1$s wiyi:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ena naamnii innde kuutoro maa e finnde. Lowre ndee wiyi: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ena naamnii innde kuutoro maa e finnde.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fi/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fi/strings.xml new file mode 100644 index 0000000000..af33a323c4 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fi/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Sivu osoitteessa %1$s sanoo:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s pyytää käyttäjätunnusta ja salasanaa. Sivusto sanoo: ”%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s pyytää käyttäjätunnusta ja salasanaa.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fr/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fr/strings.xml new file mode 100644 index 0000000000..d4f313637b --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fr/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Annonce de la page %1$s :</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">Le site %2$s demande votre nom d’utilisateur et votre mot de passe. Le site indique : « %1$s »</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s demande votre nom d’utilisateur et votre mot de passe.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fur/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fur/strings.xml new file mode 100644 index 0000000000..96aef64c14 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fur/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La pagjine su %1$s e dîs:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s al domande il to non utent e la password. Il sît al dîs: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s al domande il to non utent e la password.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fy-rNL/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fy-rNL/strings.xml new file mode 100644 index 0000000000..3350940938 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-fy-rNL/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">De side op %1$s meldt:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s freget om jo brûkersnamme en wachtwurd. De website meldt: ‘%1$s’</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s freget om jo brûkersnamme en wachtwurd.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ga-rIE/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ga-rIE/strings.xml new file mode 100644 index 0000000000..5ff5ac4408 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ga-rIE/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Deir an leathanach ag %1$s:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">Tá %2$s ag iarraidh ainm úsáideora agus focal faire uait. Deir an suíomh: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">Tá %1$s ag iarraidh ainm úsáideora agus focal faire uait.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gd/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gd/strings.xml new file mode 100644 index 0000000000..00304632ce --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gd/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Tha an duilleag aig %1$s ag ràdh:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">Tha am progsaidh %2$s ag iarraidh ainm-cleachdaiche is facal-faire. Tha an làrach ag ràdh: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">Tha %1$s ag iarraidh an ainm-chleachdaiche is an fhacail-fhaire agad.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gl/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gl/strings.xml new file mode 100644 index 0000000000..2f63376db4 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gl/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">A páxina en %1$s di:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s solicita o seu nome de usuario e o contrasinal. O sitio di: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s solicita o seu nome de usuario e o contrasinal.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gn/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gn/strings.xml new file mode 100644 index 0000000000..0445a456da --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gn/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Pe kuatiarogue %1$s pegua he’i:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ojerure poruhára réra ha avei ñe’ẽñemi. Pe tenda he’i: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ojerure nde poruhára réra ha ñe’ẽñemi.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gu-rIN/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gu-rIN/strings.xml new file mode 100644 index 0000000000..73ee5f15b2 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-gu-rIN/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s આગળનું પાનું આમ કહે છે:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s તમારું વપરાશકર્તા નામ અને પાસવર્ડની વિનંતી કરી રહ્યું છે. આ સાઇટ કહે છે: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s તમારા વપરાશકર્તા નામ અને પાસવર્ડની વિનંતી કરી રહ્યું છે.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hi-rIN/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hi-rIN/strings.xml new file mode 100644 index 0000000000..99d77afaaa --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hi-rIN/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s पर मौजूद पृष्ठ का कहना है:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s आपके उपयोगकर्ता नाम और पासवर्ड का अनुरोध कर रहा है। साइट का कहना है: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s आपके उपयोगकर्ता नाम और पासवर्ड का अनुरोध कर रहा है।</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hil/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hil/strings.xml new file mode 100644 index 0000000000..fc19280ac9 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hil/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Ang pahina sa %1$s nagasiling:</string> + </resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hr/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hr/strings.xml new file mode 100644 index 0000000000..3d65bfb52b --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hr/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Stranica pri %1$s kaže:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s zahtijeva korisničko ime i lozinku. Stranica vraća odgovor: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s zahtijeva tvoje korisničko ime i lozinku.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hsb/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hsb/strings.xml new file mode 100644 index 0000000000..9886d5a970 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hsb/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Strona na %1$s praji:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s žada sej waše wužiwarske mjeno a hesło. Sydło praji: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s sej waše wužiwarske mjeno a hesło žada.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hu/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hu/strings.xml new file mode 100644 index 0000000000..61f9314eb1 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hu/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Az oldal a(z) %1$s helyen azt mondja:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">A(z) %2$s felhasználónevet és jelszót kér. A webhely üzenete: „%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">A(z) %1$s felhasználónevet és jelszót kér.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hy-rAM/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hy-rAM/strings.xml new file mode 100644 index 0000000000..3f5ca0b268 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-hy-rAM/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title"> %1$s-ից էջը հաղորդում է`</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s-ը պահանջում է օգտվողի անուն և գաղտնաբառ: Կայքը հաղորդում է` "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s-ը հարցնում է օգտվողի Ձեր անունը և գաղտնաբառը</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ia/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ia/strings.xml new file mode 100644 index 0000000000..6b1d1e8d3b --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ia/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Le pagina sur %1$s dice:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s requesta tu nomine de usator e contrasigno. Le sito dice: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s requesta tu nomine de usator e contrasigno.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-in/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-in/strings.xml new file mode 100644 index 0000000000..589964cfea --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-in/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Laman dari %1$s menjelaskan:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s meminta nama pengguna dan sandi anda. Situs ini berkata: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s meminta nama pengguna dan sandi anda.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-is/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-is/strings.xml new file mode 100644 index 0000000000..afc6c1bccc --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-is/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Síðan %1$s segir:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s er að biðja um notandanafnið þitt og lykilorð. Tilkynningin frá vefsvæðinu er: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s er að biðja um notandanafnið þitt og lykilorð.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-it/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-it/strings.xml new file mode 100644 index 0000000000..2bb6759963 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-it/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La pagina sul server %1$s riporta:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s richiede un nome utente e una password. Il sito riporta: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s richiede un nome utente e una password.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-iw/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-iw/strings.xml new file mode 100644 index 0000000000..c07e1eb199 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-iw/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">הדף %1$s אומר:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">השרת %2$s מבקש את שם המשתמש והססמה שלך. מהאתר נמסר: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">השרת %1$s מבקש את שם המשתמש והססמה שלך.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ja/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ja/strings.xml new file mode 100644 index 0000000000..f04da64533 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ja/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s のページから:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s がユーザー名とパスワードを要求しています。サイトからのメッセージ: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s がユーザー名とパスワードを要求しています。</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ka/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ka/strings.xml new file mode 100644 index 0000000000..b428118f78 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ka/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">გვერდი %1$s გამცნობთ:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ითხოვს მომხმარებლის სახელსა და პაროლს. საიტი გამცნობთ: „%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ითხოვს მომხმარებლის სახელსა და პაროლს.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kaa/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kaa/strings.xml new file mode 100644 index 0000000000..bca1aa650f --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kaa/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s betinde aytılıwınsha:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s paydalanıwshı atın hám parolin sorap atır. Sayt aytıwınsha: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s paydalanıwshı atın hám parolin sorap atır.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kab/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kab/strings.xml new file mode 100644 index 0000000000..92b5287629 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kab/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Asebter deg %1$s yeqqar-d:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s yessutur isem-ik n useqdac akked wawal-ik uffir. Asmel yaqqar: "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s yessutur isem-ik n useqdac akked wawal-ik uffir.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kk/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kk/strings.xml new file mode 100644 index 0000000000..8776c5afc5 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kk/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s адресіндегі бет хабарлайды:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s сайты сіздің пайдаланушы атын мен паролін сұрайды. Сайт айтады: "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s сіздің пайдаланушы атын және паролін сұрап тұр.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kmr/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kmr/strings.xml new file mode 100644 index 0000000000..7009b2f611 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kmr/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Rûpela %1$s’ê dibêje:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s navê bikarhêner û pêborîna te dixwaze. Malper dibêje: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s navê bikarhêner û pêborîna te dixwaze.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kn/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kn/strings.xml new file mode 100644 index 0000000000..f787624f46 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kn/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s ನಲ್ಲಿರುವ ಪುಟವು ಹೀಗೆ ಹೇಳುತ್ತದೆ:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ಎಂಬ ಪ್ರಾಕ್ಸಿಯು ಒಂದು ಬಳಕೆದಾರ ಪದ ಹಾಗು ಗುಪ್ತಪದಕ್ಕಾಗಿ ಮನವಿ ಸಲ್ಲಿಸಿದೆ. ತಾಣವು ಹೀಗೆ ಹೇಳುತ್ತದೆ: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ನಿಮ್ಮ ಬಳಕೆದಾರನ ಹೆಸರು ಮತ್ತು ಪ್ರವೇಶ ಪದ ಕೇಳುತ್ತಿದೆ.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ko/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ko/strings.xml new file mode 100644 index 0000000000..a7fa3f43ba --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ko/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s 페이지의 메세지:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s가 사용자 이름과 비밀번호를 요청하고 있습니다. 사이트 메시지: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s가 사용자 이름과 비밀번호를 요청하고 있습니다.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kw/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kw/strings.xml new file mode 100644 index 0000000000..533de3e828 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-kw/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">An folen orth %1$s a lever:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s a bys a\'th hanow devnydhyer ha ger tremena. An wiasva a lever: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s a bys a\'th hanow devnydhyer ha ger tremena.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lij/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lij/strings.xml new file mode 100644 index 0000000000..60d8ae087c --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lij/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">A pagina in %1$s a dixe:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s o domanda o teu nomme utente e paròlla segreta. O scito o dixe: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s o domanda o teu nomme utente e paròlla segreta.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lo/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lo/strings.xml new file mode 100644 index 0000000000..f6703c74e8 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lo/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">ຫນ້າເວັບທີ່ %1$s ລະບຸວ່າ:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ຕ້ອງການຊື່ຜູ້ໃຊ້ ແລະ ລະຫັດຜ່ານຂອງທ່ານ. ເວັບໄຊທລະບຸວ່າ: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ຕ້ອງການຊື່ຜູ້ໃຊ້ ແລະ ລະຫັດຜ່ານຂອງທ່ານ.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lt/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lt/strings.xml new file mode 100644 index 0000000000..f17c191cdc --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-lt/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Tinklalapis %1$s praneša:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s reikalauja jūsų vardo ir slaptažodžio. Svetainės pranešimas: „%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s reikalauja jūsų vardo iš slaptažodžio.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-mix/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-mix/strings.xml new file mode 100644 index 0000000000..372b02ea26 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-mix/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Página nu %1$s katyi:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s tsiki sivi tsi tu un se e. Ndaka tu in: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s tsiki sivi tsi tu un se e.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ml/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ml/strings.xml new file mode 100644 index 0000000000..0728f4572a --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ml/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s ൽ ഉള്ള പേജ് പറയുന്നത്:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ന് നിങ്ങളുടെ ഉപയോക്തൃനാമവും രഹസ്യവാക്കും ആവശ്യമാണ് . സൈറ്റ് പറയുന്നത് : “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s നിങ്ങളുടെ ഉപയോക്തൃനാമവും രഹസ്യവാക്കും ആവശ്യപെടുന്നു.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-mr/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-mr/strings.xml new file mode 100644 index 0000000000..c7cc6930a9 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-mr/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s वरील पृष्ठ म्हणते:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s वापरकर्तानाव व पासवर्डसाठी विनंती करत आहे. स्थळ असे म्हणते: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s आपल्या वापरकर्तानाव आणि पासवर्डसाठी विनंती करत आहे.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-my/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-my/strings.xml new file mode 100644 index 0000000000..9d9a02f29c --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-my/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s ရှိစာမျက်နှာက -</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s သည်သင်၏သုံးစွဲသူအမည်နှင့်စကားဝှက်ကိုတောင်းနေသည်။ ဆိုဒ်တွင်“%1$s” ဆိုထားသည်။</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s သည်သင်၏သုံးစွဲသူအမည်နှင့်စကားဝှက်ကိုတောင်းနေသည်။</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nb-rNO/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nb-rNO/strings.xml new file mode 100644 index 0000000000..ac3004ffc6 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nb-rNO/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Nettsiden på %1$s sier:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ber om brukernavn og passord. Nettstedet sier: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s krever brukernavn og passord.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ne-rNP/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ne-rNP/strings.xml new file mode 100644 index 0000000000..56135b2712 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ne-rNP/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title"> %1$s मा रहेको पृष्ठले भन्छ:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message"> %2$s ले प्रयोगकर्ता नाम र पासवर्ड अनुरोध गरिरहेको छ। साइट भन्छ: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message"> %1$s ले तपाईँको प्रयोगकर्ता नाम र पासवर्ड अनुरोध गरेको छ।</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nl/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nl/strings.xml new file mode 100644 index 0000000000..71b2ddf0b2 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nl/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">De pagina op %1$s zegt:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s vraagt om uw gebruikersnaam en wachtwoord. De website zegt: ‘%1$s’</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s vraagt om uw gebruikersnaam en wachtwoord.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nn-rNO/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nn-rNO/strings.xml new file mode 100644 index 0000000000..7763857a1f --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nn-rNO/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Nettsida på %1$s seier:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ber om brukarnamn og passord. Nettstaden seier: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s krev brukarnamn og passord.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nv/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nv/strings.xml new file mode 100644 index 0000000000..e305ee9534 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-nv/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Níłch’i naaltsoos -gi ’ání %1$s:</string> + </resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-oc/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-oc/strings.xml new file mode 100644 index 0000000000..cdf223c0f7 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-oc/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Anóncia de la pagina %1$s :</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s demanda un nom d’utilizaire e un senhal. Lo site indica : « %1$s »</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s demanda vòstres nom d’utilizaire e senhal.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-or/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-or/strings.xml new file mode 100644 index 0000000000..e93ac76cf2 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-or/strings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s ରେ ଥିବା ପୃଷ୍ଠା କହେ:</string> + </resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pa-rIN/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pa-rIN/strings.xml new file mode 100644 index 0000000000..8a6d8fac81 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pa-rIN/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s ਉੱਤੇ ਸਫ਼ਾ ਦਰਸਾਉਂਦਾ ਹੈ:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ਤੁਹਾਡੇ ਵਰਤੋਂਕਾਰ-ਨਾਂ ਅਤੇ ਪਾਸਵਰਡ ਦੀ ਮੰਗ ਕਰ ਰਹੀ ਹੈ। ਸਾਈਟ ਕਹਿੰਦੀ ਹੈ: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ਤੁਹਾਡੇ ਵਰਤੋਂਕਾਰ-ਨਾਂ ਅਤੇ ਪਾਸਵਰਡ ਦੀ ਮੰਗ ਕਰ ਰਹੀ ਹੈ। </string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pa-rPK/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pa-rPK/strings.xml new file mode 100644 index 0000000000..acabc7da8e --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pa-rPK/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">پتے %1$s دے صفحے توں سنیہا:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s تہاڈے ورتنوالے دا ناں تے پاسورڈ دی منگ کر رہی اے۔ ایتھوں سنیہا اے – ”%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s تہاڈے ورتنوالے دا ناں تے پاسورڈ دی منگ کر رہی اے۔</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pl/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pl/strings.xml new file mode 100644 index 0000000000..8a242ccbd4 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pl/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Komunikat ze strony %1$s:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s prosi o podanie nazwy użytkownika i hasła. Komunikat witryny: „%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s prosi o podanie nazwy użytkownika i hasła.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ppl/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ppl/strings.xml new file mode 100644 index 0000000000..8ed3d80af8 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ppl/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Ne iswat tik %1$s ina:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s metztajtanilia muusuariojtukay wan muichtakataketzalis. Ne sitioj ina: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s metztajtanilia muusuariojtukay wan muichtakataketzalis.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pt-rBR/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pt-rBR/strings.xml new file mode 100644 index 0000000000..0378158e27 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pt-rBR/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">A página em %1$s diz:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s está solicitando seu nome de usuário e senha. O site diz: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s está solicitando seu nome de usuário e senha.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pt-rPT/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pt-rPT/strings.xml new file mode 100644 index 0000000000..abac1094bb --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-pt-rPT/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">A página %1$s diz:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s está a solicitar o seu nome de utilizador e a palavra-passe. O site diz: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s está a solicitar o seu nome de utilizador e a palavra-passe.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-rm/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-rm/strings.xml new file mode 100644 index 0000000000..af664e1e22 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-rm/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">La pagina cun l\'adressa %1$s di:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s dumonda tes num d\'utilisader e pled-clav. La pagina di: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s dumonda tes num d\'utilisader e pled-clav.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ro/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ro/strings.xml new file mode 100644 index 0000000000..1dfac786f0 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ro/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Pagina de la %1$s spune:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s solicită numele de utilizator și parola. Site-ul spune: „%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s solicită numele de utilizator și parola.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ru/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ru/strings.xml new file mode 100644 index 0000000000..04b5fe508a --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ru/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Страница на %1$s сообщает:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s запрашивает имя пользователя и пароль. Сайт сообщает: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s запрашивает имя пользователя и пароль.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sat/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sat/strings.xml new file mode 100644 index 0000000000..e16abb8a6d --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sat/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s ᱴᱷᱮᱱ ᱢᱮᱱᱟᱜ ᱥᱟᱦᱴᱟ ᱢᱮᱱᱮᱜᱼᱟᱭ:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ᱫᱚ ᱟᱢᱟᱜ ᱭᱩᱡᱟᱹᱨᱱᱮᱢ ᱟᱨ ᱯᱟᱥᱥᱣᱟᱹᱨᱰ ᱠᱷᱚᱡ ᱠᱟᱱᱟᱭ ᱾ ᱥᱟᱭᱴ ᱮ ᱢᱮᱱᱮᱜᱼᱟᱭ: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ᱫᱚ ᱟᱢᱟᱜ ᱭᱩᱡᱟᱹᱨᱱᱮᱢ ᱟᱨ ᱯᱟᱥᱣᱟᱹᱨᱰ ᱠᱷᱚᱡ ᱠᱟᱱᱟᱭ ᱾</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sc/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sc/strings.xml new file mode 100644 index 0000000000..8088687053 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sc/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Sa pàgina in %1$s narat:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s rechedet su nòmine tuo e sa crae. Su situ narat: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s rechedet su nòmine tuo e sa crae.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-si/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-si/strings.xml new file mode 100644 index 0000000000..2cdf4bed5e --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-si/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s හි පිටුව මෙසේ පවසයි:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ඔබගේ පරිශීලක නාමය සහ මුරපදය ඉල්ලා සිටියි. අඩවිය පවසන්නේ: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ඔබගේ පරිශීලක නාමය සහ මුරපදය ඉල්ලා සිටියි.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sk/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sk/strings.xml new file mode 100644 index 0000000000..df5bf93d16 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sk/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Oznámenie stránky %1$s:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s požaduje zadanie vášho používateľského mena a hesla. Oznámenie stránky: „%1$s“</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s požaduje zadanie vášho používateľského mena a hesla.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-skr/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-skr/strings.xml new file mode 100644 index 0000000000..f5b21bf699 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-skr/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s تے ورقہ آہدے:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ورتݨ ناں تے پاسورڈ دی ارداس کریندا پئے۔ سائٹ آہدی ہے: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s تہاݙے ورتݨ ناں تے پاسورڈ دی ارداس کریندا پئے۔</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sl/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sl/strings.xml new file mode 100644 index 0000000000..8e839b6f40 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sl/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Stran na %1$s sporoča:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s zahteva uporabniško ime in geslo. Sporočilo strani: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s zahteva uporabniško ime in geslo.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sq/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sq/strings.xml new file mode 100644 index 0000000000..ba788eb520 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sq/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Faqja te %1$s thotë:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s po kërkon emrin tuaj të përdoruesit dhe fjalëkalimin. Sajti thotë: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s po kërkon emrin tuaj të përdoruesit dhe fjalëkalimin.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sr/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sr/strings.xml new file mode 100644 index 0000000000..f79607c7c3 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sr/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Порука са странице %1$s гласи:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s захтева ваше корисничко име и лозинку. Страница поручује: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s захтева ваше корисничко име и лозинку.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-su/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-su/strings.xml new file mode 100644 index 0000000000..f3256f67c6 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-su/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Kaca di %1$s nyebutkeun:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ménta sandiasma jeung kecap konci anjeun. Situsna nyebutkeun: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ménta sandiasma jeung kecap konci anjeun.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sv-rSE/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sv-rSE/strings.xml new file mode 100644 index 0000000000..f95ae52bd7 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-sv-rSE/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Sidan på %1$s säger:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s efterfrågar ditt användarnamn och lösenord. Webbplatsen säger: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s efterfrågar ditt användarnamn och lösenord.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-szl/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-szl/strings.xml new file mode 100644 index 0000000000..8d445f4a01 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-szl/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Strōna %1$s dowo znać:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s prosi ô twoje miano używocza i hasło. Kōmunikat strōny: „%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s prosi ô twoje miano używocza i hasło.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ta/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ta/strings.xml new file mode 100644 index 0000000000..a9878f95c9 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ta/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">பக்கம் %1$s இல் சொல்வது:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s உங்கள் பயனர்பெயர் மற்றும் கடவுச்சொல்லைக் கோருகிறது. தளம் கூறுகிறது: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s உங்கள் பயனர்பெயர் மற்றும் கடவுச்சொல்லைக் கோருகிறது.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-te/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-te/strings.xml new file mode 100644 index 0000000000..387402a8ba --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-te/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s వద్ద పేజీ అంటోంది:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s మీ వాడుకరి పేరును, సంకేతపదాన్ని అడుగుతోంది. సైటు ఇలా అంటుోంది: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s మీ వాడుకరి పేరును, సంకేతపదాన్ని అడుగుతోంది.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tg/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tg/strings.xml new file mode 100644 index 0000000000..4d5d623a07 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tg/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Саҳифа дар %1$s хабар медиҳад:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s номи корбар ва ниҳонвожаи шуморо дархост мекунад. Сомона хабар медиҳад: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s номи корбар ва ниҳонвожаи шуморо дархост мекунад.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-th/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-th/strings.xml new file mode 100644 index 0000000000..96dfa57c15 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-th/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">หน้าที่ %1$s ระบุว่า:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s กำลังขอชื่อผู้ใช้และรหัสผ่านของคุณ ไซต์ระบุว่า: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s กำลังขอชื่อผู้ใช้และรหัสผ่านของคุณ</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tl/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tl/strings.xml new file mode 100644 index 0000000000..520af991f2 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tl/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Ang pahina sa %1$s ay nagsasabi ng:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ay humihingi ng iyong username at password. Ang sabi ng site ay: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">Hinihingi ng %1$s ang iyong username at password.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tok/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tok/strings.xml new file mode 100644 index 0000000000..3c3cc21d77 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tok/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">lipu %1$s li toki:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s li wile e nimi sina e nimi open sina. lipu li toki e ni: "%1$s"</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s li wile e nimi sina e nimi open sina.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tr/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tr/strings.xml new file mode 100644 index 0000000000..e5011323c2 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tr/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s sayfası diyor ki:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s kullanıcı adı ve parolanızı istiyor. Site diyor ki: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s kullanıcı adı ve parolanızı istiyor.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-trs/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-trs/strings.xml new file mode 100644 index 0000000000..93075f89dd --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-trs/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Pajinâ nū riña %1$s taj:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s nachin\' man si yuguît ngà da\'nga\' huì arâj sunt. Sa tāj sitiô nan huin: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s nachin\' man si yuguît ngà da\'nga\' huì arâj sunt.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tt/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tt/strings.xml new file mode 100644 index 0000000000..8abc7cdffc --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tt/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s сәхифәсеннән хәбәр:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s кулланучы исемен һәм серсүзен сорый. Сайт хәбәре: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s кулланучы исемен һәм серсүзен сорый.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tzm/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tzm/strings.xml new file mode 100644 index 0000000000..6400a304ee --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-tzm/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Da tettini tasna g %1$s:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">Issutur-ak %2$s ism d tguri n uzray-nnek. Da ittini usit: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">Issutur-ak %1$s isem d tguri n uzray-nnek.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ug/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ug/strings.xml new file mode 100644 index 0000000000..67daade49d --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ug/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">بەت %1$s تە دېيىلگىنى:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ئىشلەتكۈچى ئىسمى بىلەن ئىمنى تەلەپ قىلىۋاتىدۇ. تور بېكەتتە دېيىلگىنى: «%1$s»</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ئىشلەتكۈچى ئىسمى بىلەن ئىمنى تەلەپ قىلىۋاتىدۇ.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-uk/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-uk/strings.xml new file mode 100644 index 0000000000..876a66cc03 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-uk/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Сторінка на %1$s повідомляє:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s запитує ваше ім’я користувача і пароль. Повідомлення сайту: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s запитує ім’я користувача і пароль.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ur/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ur/strings.xml new file mode 100644 index 0000000000..4af1973108 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-ur/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s پر صفحہ کہتا ہے:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s صارف کا نام اور پاسورڈ کی درخواست کر رہا ہے۔ سائٹ کہتی ہے: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s صارف کا نام اور پاسورڈ کی درخواست کر رہا ہے۔</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-uz/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-uz/strings.xml new file mode 100644 index 0000000000..0e3c13ca2c --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-uz/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s sahifasi xabari:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s login va parolingizni soʻrayapti. Sayt xabari: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s login va parolingizni soʻrayapti.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-vec/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-vec/strings.xml new file mode 100644 index 0000000000..9f85621706 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-vec/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Ƚa pàgina so’l server %1$s ƚa riporta:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s el dimanda on nòme utente e na password. El sito el riporta: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s dimanda on nòme utente e na password.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-vi/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-vi/strings.xml new file mode 100644 index 0000000000..573499c584 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-vi/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Trang %1$s cho biết:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s yêu cầu tên người dùng và mật khẩu của bạn. Trang web thông báo: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s yêu cầu tên người dùng và mật khẩu của bạn.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-yo/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-yo/strings.xml new file mode 100644 index 0000000000..5676053cd2 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-yo/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">Ojú-ìwé %1$s sọ pé:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s ń béèrè orúkọ ìṣàmúlò àti kóòdù rẹ. Ìkànnì náà sọ pé: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s ń béère orúkọ ìwọlé àti pásíwọọ̀dù </string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-zh-rCN/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-zh-rCN/strings.xml new file mode 100644 index 0000000000..52a4d15cfb --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-zh-rCN/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">域名为 %1$s 的页面提示:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s 要求您输入用户名和密码。该网站提示:“%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s 要求您输入用户名和密码。</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values-zh-rTW/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-zh-rTW/strings.xml new file mode 100644 index 0000000000..3c2573af95 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values-zh-rTW/strings.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">%1$s 這一頁說:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the realm, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s 要求您輸入帳號密碼。此網站說:「%1$s」</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s 要求您輸入帳號與密碼。</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/main/res/values/strings.xml b/mobile/android/android-components/components/browser/engine-system/src/main/res/values/strings.xml new file mode 100644 index 0000000000..7e80343142 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/main/res/values/strings.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- This Source Code Form is subject to the terms of the Mozilla Public + - License, v. 2.0. If a copy of the MPL was not distributed with this + - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> +<resources> + <!-- Text for the title of an alert dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_alert_title">The page at %1$s says:</string> + <!-- Text for the message of an auth dialog displayed by a web page. + %1$s will be replaced by the hostname or a description of the protected area/site, %2$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_message">%2$s is requesting your username and password. The site says: “%1$s”</string> + <!-- Text for the message of an auth dialog displayed by a web page. %1$s will be replaced with the URL of the current page (displaying the dialog). --> + <string name="mozac_browser_engine_system_auth_no_realm_message">%1$s is requesting your username and password.</string> +</resources> diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/NestedWebViewTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/NestedWebViewTest.kt new file mode 100644 index 0000000000..e822084cb3 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/NestedWebViewTest.kt @@ -0,0 +1,165 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.view.MotionEvent.ACTION_CANCEL +import android.view.MotionEvent.ACTION_DOWN +import android.view.MotionEvent.ACTION_MOVE +import android.view.MotionEvent.ACTION_UP +import androidx.core.view.NestedScrollingChildHelper +import androidx.core.view.ViewCompat.SCROLL_AXIS_VERTICAL +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import mozilla.components.support.test.mockMotionEvent +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class NestedWebViewTest { + + @Test + fun `NestedWebView must delegate NestedScrollingChild implementation to childHelper`() { + val nestedWebView = NestedWebView(testContext) + val mockChildHelper: NestedScrollingChildHelper = mock() + nestedWebView.childHelper = mockChildHelper + + doReturn(true).`when`(mockChildHelper).isNestedScrollingEnabled + doReturn(true).`when`(mockChildHelper).hasNestedScrollingParent() + + nestedWebView.isNestedScrollingEnabled = true + verify(mockChildHelper).isNestedScrollingEnabled = true + + assertTrue(nestedWebView.isNestedScrollingEnabled) + verify(mockChildHelper).isNestedScrollingEnabled + + nestedWebView.startNestedScroll(1) + verify(mockChildHelper).startNestedScroll(1) + + nestedWebView.stopNestedScroll() + verify(mockChildHelper).stopNestedScroll() + + assertTrue(nestedWebView.hasNestedScrollingParent()) + verify(mockChildHelper).hasNestedScrollingParent() + + nestedWebView.dispatchNestedScroll(0, 0, 0, 0, null) + verify(mockChildHelper).dispatchNestedScroll(0, 0, 0, 0, null) + + nestedWebView.dispatchNestedPreScroll(0, 0, null, null) + verify(mockChildHelper).dispatchNestedPreScroll(0, 0, null, null) + + nestedWebView.dispatchNestedFling(0f, 0f, true) + verify(mockChildHelper).dispatchNestedFling(0f, 0f, true) + + nestedWebView.dispatchNestedPreFling(0f, 0f) + verify(mockChildHelper).dispatchNestedPreFling(0f, 0f) + } + + @Test + fun `verify onTouchEvent when ACTION_DOWN`() { + val nestedWebView = NestedWebView(testContext) + val mockChildHelper: NestedScrollingChildHelper = mock() + nestedWebView.childHelper = mockChildHelper + + nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN)) + verify(mockChildHelper).startNestedScroll(SCROLL_AXIS_VERTICAL) + } + + @Test + fun `verify onTouchEvent when ACTION_MOVE`() { + val nestedWebView = NestedWebView(testContext) + val mockChildHelper: NestedScrollingChildHelper = mock() + nestedWebView.childHelper = mockChildHelper + + doReturn(true).`when`(mockChildHelper).dispatchNestedPreScroll( + anyInt(), + anyInt(), + any(), + any(), + ) + + nestedWebView.scrollOffset[0] = 1 + nestedWebView.scrollOffset[1] = 2 + + nestedWebView.onTouchEvent(mockMotionEvent(ACTION_MOVE, y = 10f)) + assertEquals(nestedWebView.nestedOffsetY, 2) + assertEquals(nestedWebView.lastY, 8) + + doReturn(true).`when`(mockChildHelper).dispatchNestedScroll( + anyInt(), + anyInt(), + anyInt(), + anyInt(), + any(), + ) + + nestedWebView.onTouchEvent(mockMotionEvent(ACTION_MOVE, y = 10f)) + assertEquals(nestedWebView.nestedOffsetY, 6) + assertEquals(nestedWebView.lastY, 6) + } + + @Test + fun `verify onTouchEvent when ACTION_UP or ACTION_CANCEL`() { + val nestedWebView = NestedWebView(testContext) + val mockChildHelper: NestedScrollingChildHelper = mock() + nestedWebView.childHelper = mockChildHelper + + nestedWebView.onTouchEvent(mockMotionEvent(ACTION_UP)) + verify(mockChildHelper).stopNestedScroll() + + nestedWebView.onTouchEvent(mockMotionEvent(ACTION_CANCEL)) + verify(mockChildHelper, times(2)).stopNestedScroll() + } + + @Test + fun `GIVEN NestedWebView WHEN a new instance is created THEN a properly configured InputResultDetail is created`() { + val nestedWebView = NestedWebView(testContext) + + assertTrue(nestedWebView.inputResultDetail.isTouchHandlingUnknown()) + assertFalse(nestedWebView.inputResultDetail.canScrollToLeft()) + assertFalse(nestedWebView.inputResultDetail.canScrollToTop()) + assertFalse(nestedWebView.inputResultDetail.canScrollToRight()) + assertFalse(nestedWebView.inputResultDetail.canScrollToBottom()) + assertFalse(nestedWebView.inputResultDetail.canOverscrollLeft()) + assertFalse(nestedWebView.inputResultDetail.canOverscrollTop()) + assertFalse(nestedWebView.inputResultDetail.canOverscrollRight()) + assertFalse(nestedWebView.inputResultDetail.canOverscrollBottom()) + } + + @Test + fun `GIVEN NestedWebView WHEN onTouchEvent is called THEN updateInputResult is called with the result of whether the touch is handled or not`() { + val nestedWebView = spy(NestedWebView(testContext)) + + doReturn(true).`when`(nestedWebView).callSuperOnTouchEvent(any()) + nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN)) + verify(nestedWebView).updateInputResult(true) + + doReturn(false).`when`(nestedWebView).callSuperOnTouchEvent(any()) + nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN)) + verify(nestedWebView).updateInputResult(false) + } + + @Test + fun `GIVEN an instance of InputResultDetail WHEN updateInputResult called THEN it sets whether the touch was handled`() { + val nestedWebView = NestedWebView(testContext) + + assertTrue(nestedWebView.inputResultDetail.isTouchHandlingUnknown()) + + nestedWebView.updateInputResult(true) + assertTrue(nestedWebView.inputResultDetail.isTouchHandledByBrowser()) + + nestedWebView.updateInputResult(false) + assertTrue(nestedWebView.inputResultDetail.isTouchUnhandled()) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineSessionStateTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineSessionStateTest.kt new file mode 100644 index 0000000000..a3aafae50e --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineSessionStateTest.kt @@ -0,0 +1,138 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.os.Bundle +import android.util.JsonReader +import android.util.JsonWriter +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.json.JSONArray +import org.json.JSONObject +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import java.io.ByteArrayOutputStream + +@RunWith(AndroidJUnit4::class) +class SystemEngineSessionStateTest { + @Test + fun fromJSON() { + val json = JSONObject().apply { + put("k0", "v0") + put("k1", 1) + put("k2", true) + put("k3", 5.0) + put("k4", 1.0f) + put("k5", JSONArray(listOf(1, 2, 3))) + } + + val state = SystemEngineSessionState.fromJSON(json) + val bundle = state.bundle!! + + assertEquals(5, bundle.size()) + + assertTrue(bundle.containsKey("k0")) + assertTrue(bundle.containsKey("k1")) + assertTrue(bundle.containsKey("k2")) + assertTrue(bundle.containsKey("k3")) + assertTrue(bundle.containsKey("k4")) + + assertEquals("v0", bundle.getString("k0")) + assertEquals(1, bundle.getInt("k1")) + assertEquals(true, bundle.getBoolean("k2")) + assertEquals(5.0, bundle.getDouble("k3"), 0.0) + assertEquals(1.0f, bundle.getFloat("k4")) + } + + @Test + fun writeToAndFromJSON() { + val state = SystemEngineSessionState( + Bundle().apply { + putString("k0", "v0") + putInt("k1", 1) + putBoolean("k2", true) + putStringArrayList("k3", ArrayList<String>(listOf("Hello", "World"))) + putDouble("k4", 5.0) + putFloat("k5", 1.0f) + putFloat("k6", 42.25f) + putDouble("k7", 23.23) + }, + ) + + val outputStream = ByteArrayOutputStream() + state.writeTo(JsonWriter(outputStream.writer())) + + val bundle = SystemEngineSessionState.fromJSON( + JSONObject(outputStream.toString()), + ).bundle + + assertNotNull(bundle!!) + + assertEquals(7, bundle.size()) + + assertTrue(bundle.containsKey("k0")) + assertTrue(bundle.containsKey("k1")) + assertTrue(bundle.containsKey("k2")) + assertFalse(bundle.containsKey("k3")) + assertTrue(bundle.containsKey("k4")) + assertTrue(bundle.containsKey("k5")) + assertTrue(bundle.containsKey("k6")) + assertTrue(bundle.containsKey("k7")) + + assertEquals("v0", bundle.getString("k0")) + assertEquals(1, bundle.getInt("k1")) + assertEquals(true, bundle.getBoolean("k2")) + assertEquals(5.0, bundle.getDouble("k4"), 0.0) + assertEquals(1.0, bundle.getDouble("k5"), 0.0) + assertEquals(42.25, bundle.getDouble("k6"), 0.0) + assertEquals(23.23, bundle.getDouble("k7"), 0.0) + } + + @Test + fun writeToAndReadFrom() { + val state = SystemEngineSessionState( + Bundle().apply { + putString("k0", "v0") + putInt("k1", 1) + putBoolean("k2", true) + putStringArrayList("k3", ArrayList<String>(listOf("Hello", "World"))) + putDouble("k4", 5.0) + putFloat("k5", 1.0f) + putFloat("k6", 42.25f) + putDouble("k7", 23.23) + }, + ) + + val outputStream = ByteArrayOutputStream() + state.writeTo(JsonWriter(outputStream.writer())) + + val reader = JsonReader(outputStream.toString().reader()) + val bundle = SystemEngineSessionState.from(reader).bundle + + assertNotNull(bundle!!) + + assertEquals(7, bundle.size()) + + assertTrue(bundle.containsKey("k0")) + assertTrue(bundle.containsKey("k1")) + assertTrue(bundle.containsKey("k2")) + assertFalse(bundle.containsKey("k3")) + assertTrue(bundle.containsKey("k4")) + assertTrue(bundle.containsKey("k5")) + assertTrue(bundle.containsKey("k6")) + assertTrue(bundle.containsKey("k7")) + + assertEquals("v0", bundle.getString("k0")) + assertEquals(1.0, bundle.getDouble("k1"), 0.0) // We only see token "number", so we have to read a double and can't know that this was an int. + assertEquals(true, bundle.getBoolean("k2")) + assertEquals(5.0, bundle.getDouble("k4"), 0.0) + assertEquals(1.0, bundle.getDouble("k5"), 0.0) + assertEquals(42.25, bundle.getDouble("k6"), 0.0) + assertEquals(23.23, bundle.getDouble("k7"), 0.0) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineSessionTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineSessionTest.kt new file mode 100644 index 0000000000..4f56d9c467 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineSessionTest.kt @@ -0,0 +1,1238 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.content.Context +import android.net.Uri +import android.os.Bundle +import android.webkit.WebChromeClient +import android.webkit.WebResourceRequest +import android.webkit.WebSettings +import android.webkit.WebStorage +import android.webkit.WebView +import android.webkit.WebViewClient +import android.webkit.WebViewDatabase +import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import mozilla.components.browser.engine.system.matcher.UrlMatcher +import mozilla.components.browser.errorpages.ErrorType +import mozilla.components.concept.engine.DefaultSettings +import mozilla.components.concept.engine.Engine.BrowsingData +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.request.RequestInterceptor +import mozilla.components.support.test.any +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.test.whenever +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyString +import org.mockito.Mockito +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.doThrow +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.robolectric.Shadows.shadowOf +import org.robolectric.annotation.LooperMode +import java.lang.reflect.Modifier +import org.mockito.ArgumentMatchers.any as mockitoAny + +@Suppress("DEPRECATION") // Suppress deprecation for LooperMode.Mode.LEGACY +@RunWith(AndroidJUnit4::class) +@LooperMode(LooperMode.Mode.LEGACY) +class SystemEngineSessionTest { + + @Test + fun webChromeClientNotifiesObservers() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + var observedProgress = 0 + engineSession.register( + object : EngineSession.Observer { + override fun onProgress(progress: Int) { observedProgress = progress } + }, + ) + + engineSession.webView.webChromeClient!!.onProgressChanged(null, 100) + assertEquals(100, observedProgress) + } + + @Test + fun loadUrl() { + var loadedUrl: String? = null + var loadHeaders: Map<String, String>? = null + + val engineSession = spy(SystemEngineSession(testContext)) + val webView = spy( + object : WebView(testContext) { + override fun loadUrl(url: String, additionalHttpHeaders: MutableMap<String, String>) { + loadedUrl = url + loadHeaders = additionalHttpHeaders + } + }, + ) + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.webView = webView + + engineSession.loadUrl("") + verify(webView, never()).loadUrl(anyString()) + + engineSession.loadUrl("http://mozilla.org") + verify(webView).loadUrl(eq("http://mozilla.org"), any()) + + assertEquals("http://mozilla.org", loadedUrl) + + assertNotNull(loadHeaders) + assertEquals(1, loadHeaders!!.size) + assertTrue(loadHeaders!!.containsKey("X-Requested-With")) + assertEquals("", loadHeaders!!["X-Requested-With"]) + + val extraHeaders = mapOf("X-Extra-Header" to "true") + engineSession.loadUrl("http://mozilla.org", additionalHeaders = extraHeaders) + assertNotNull(loadHeaders) + assertEquals(2, loadHeaders!!.size) + assertTrue(loadHeaders!!.containsKey("X-Extra-Header")) + assertEquals("true", loadHeaders!!["X-Extra-Header"]) + } + + @Test + fun `WHEN URL is loaded THEN URL load observer is notified`() { + var onLoadUrlTriggered = false + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + engineSession.register( + object : EngineSession.Observer { + override fun onLoadUrl() { + onLoadUrlTriggered = true + } + }, + ) + engineSession.webView = webView + + engineSession.loadUrl("http://mozilla.org") + + assertTrue(onLoadUrlTriggered) + } + + @Test + fun loadData() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.loadData("<html><body>Hello!</body></html>") + verify(webView, never()).loadData(anyString(), eq("text/html"), eq("UTF-8")) + + engineSession.webView = webView + + engineSession.loadData("<html><body>Hello!</body></html>") + verify(webView).loadData(eq("<html><body>Hello!</body></html>"), eq("text/html"), eq("UTF-8")) + + engineSession.loadData("Hello!", "text/plain", "UTF-8") + verify(webView).loadData(eq("Hello!"), eq("text/plain"), eq("UTF-8")) + + engineSession.loadData("ahr0cdovl21vemlsbgeub3jn==", "text/plain", "base64") + verify(webView).loadData(eq("ahr0cdovl21vemlsbgeub3jn=="), eq("text/plain"), eq("base64")) + } + + @Test + fun `WHEN data is loaded THEN data load observer is notified`() { + var onLoadDataTriggered = false + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + engineSession.register( + object : EngineSession.Observer { + override fun onLoadData() { + onLoadDataTriggered = true + } + }, + ) + engineSession.webView = webView + + engineSession.loadData("<html><body/></html>") + + assertTrue(onLoadDataTriggered) + } + + @Test + fun stopLoading() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.stopLoading() + verify(webView, never()).stopLoading() + + engineSession.webView = webView + + engineSession.stopLoading() + verify(webView).stopLoading() + } + + @Test + fun reload() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + engineSession.reload() + verify(webView, never()).reload() + + engineSession.webView = webView + + engineSession.reload() + verify(webView).reload() + } + + @Test + fun goBack() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.goBack() + verify(webView, never()).goBack() + + engineSession.webView = webView + + engineSession.goBack() + verify(webView).goBack() + } + + @Test + fun goForward() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.goForward() + verify(webView, never()).goForward() + + engineSession.webView = webView + + engineSession.goForward() + verify(webView).goForward() + } + + @Test + fun `GIVEN forward navigation is possible WHEN navigating forward THEN forward navigation observer is notified`() { + var observedOnNavigateForward = false + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + whenever(webView.canGoForward()).thenReturn(true) + engineSession.register( + object : EngineSession.Observer { + override fun onNavigateForward() { + observedOnNavigateForward = true + } + }, + ) + engineSession.webView = webView + + engineSession.goForward() + + assertTrue(observedOnNavigateForward) + } + + @Test + fun `GIVEN forward navigation is not possible WHEN navigating forward THEN forward navigation observer is not notified`() { + var observedOnNavigateForward = false + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + whenever(webView.canGoForward()).thenReturn(false) + engineSession.register( + object : EngineSession.Observer { + override fun onNavigateForward() { + observedOnNavigateForward = true + } + }, + ) + engineSession.webView = webView + + engineSession.goForward() + + assertFalse(observedOnNavigateForward) + } + + @Test + fun goToHistoryIndex() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + whenever(webView.copyBackForwardList()).thenReturn(mock()) + engineSession.goToHistoryIndex(0) + verify(webView, never()).goBackOrForward(0) + + engineSession.webView = webView + + engineSession.goToHistoryIndex(0) + verify(webView).goBackOrForward(0) + } + + @Test + fun `WHEN navigating to history index THEN the observer is notified`() { + var onGotoHistoryIndexTriggered = false + val engineSession = spy(SystemEngineSession(testContext)) + val settings = mock<WebSettings>() + val webView = mock<WebView>() { + whenever(this.settings).thenReturn(settings) + whenever(copyBackForwardList()).thenReturn(mock()) + } + engineSession.register( + object : EngineSession.Observer { + override fun onGotoHistoryIndex() { + onGotoHistoryIndexTriggered = true + } + }, + ) + engineSession.webView = webView + + engineSession.goToHistoryIndex(0) + + assertTrue(onGotoHistoryIndexTriggered) + } + + @Test + fun restoreState() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = spy(WebView(testContext)) + + try { + engineSession.restoreState(mock()) + fail("Expected IllegalArgumentException") + } catch (e: IllegalArgumentException) {} + assertFalse(engineSession.restoreState(SystemEngineSessionState(Bundle()))) + verify(webView, never()).restoreState(mockitoAny(Bundle::class.java)) + + engineSession.webView = webView + engineSession.webView.loadUrl("http://example.com") + + // update the WebView's history async. + shadowOf(webView).pushEntryToHistory("http://example.com") + + val bundle = Bundle() + webView.saveState(bundle) + val state = SystemEngineSessionState(bundle) + + assertTrue(engineSession.restoreState(state)) + verify(webView).restoreState(bundle) + } + + @ExperimentalCoroutinesApi + @Test + fun enableTrackingProtection() = runTest { + SystemEngineView.URL_MATCHER = UrlMatcher(arrayOf("")) + + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + + whenever(webView.settings).thenReturn(settings) + whenever(webView.context).thenReturn(testContext) + + engineSession.webView = webView + + var enabledObserved: Boolean? = null + engineSession.register( + object : EngineSession.Observer { + override fun onTrackerBlockingEnabledChange(enabled: Boolean) { + enabledObserved = enabled + } + }, + ) + + assertNull(engineSession.trackingProtectionPolicy) + engineSession.updateTrackingProtection() + assertEquals( + EngineSession.TrackingProtectionPolicy.strict(), + engineSession.trackingProtectionPolicy, + ) + assertNotNull(enabledObserved) + assertTrue(enabledObserved as Boolean) + } + + @Test + fun disableTrackingProtection() { + val engineSession = spy(SystemEngineSession(testContext)) + var enabledObserved: Boolean? = null + engineSession.register( + object : EngineSession.Observer { + override fun onTrackerBlockingEnabledChange(enabled: Boolean) { + enabledObserved = enabled + } + }, + ) + + engineSession.trackingProtectionPolicy = EngineSession.TrackingProtectionPolicy.strict() + + engineSession.disableTrackingProtection() + assertNull(engineSession.trackingProtectionPolicy) + assertNotNull(enabledObserved) + assertFalse(enabledObserved as Boolean) + } + + @Test + fun initSettings() { + val engineSession = spy(SystemEngineSession(testContext)) + assertNotNull(engineSession.internalSettings) + + val webViewSettings = mock<WebSettings>() + whenever(webViewSettings.displayZoomControls).thenReturn(true) + whenever(webViewSettings.allowContentAccess).thenReturn(true) + whenever(webViewSettings.allowFileAccess).thenReturn(true) + whenever(webViewSettings.mediaPlaybackRequiresUserGesture).thenReturn(true) + whenever(webViewSettings.supportMultipleWindows()).thenReturn(false) + + val webView = mock<WebView>() + whenever(webView.context).thenReturn(testContext) + whenever(webView.settings).thenReturn(webViewSettings) + whenever(webView.isVerticalScrollBarEnabled).thenReturn(true) + whenever(webView.isHorizontalScrollBarEnabled).thenReturn(true) + engineSession.webView = webView + + assertFalse(engineSession.settings.javascriptEnabled) + engineSession.settings.javascriptEnabled = true + verify(webViewSettings).javaScriptEnabled = true + + assertFalse(engineSession.settings.domStorageEnabled) + engineSession.settings.domStorageEnabled = true + verify(webViewSettings).domStorageEnabled = true + + assertNull(engineSession.settings.userAgentString) + engineSession.settings.userAgentString = "userAgent" + verify(webViewSettings).userAgentString = "userAgent" + + assertTrue(engineSession.settings.mediaPlaybackRequiresUserGesture) + engineSession.settings.mediaPlaybackRequiresUserGesture = false + verify(webViewSettings).mediaPlaybackRequiresUserGesture = false + + assertFalse(engineSession.settings.javaScriptCanOpenWindowsAutomatically) + engineSession.settings.javaScriptCanOpenWindowsAutomatically = true + verify(webViewSettings).javaScriptCanOpenWindowsAutomatically = true + + assertTrue(engineSession.settings.displayZoomControls) + engineSession.settings.javaScriptCanOpenWindowsAutomatically = false + verify(webViewSettings).javaScriptCanOpenWindowsAutomatically = false + + assertFalse(engineSession.settings.loadWithOverviewMode) + engineSession.settings.loadWithOverviewMode = true + verify(webViewSettings).loadWithOverviewMode = true + + assertNull(engineSession.settings.useWideViewPort) + engineSession.settings.useWideViewPort = false + verify(webViewSettings).useWideViewPort = false + + assertTrue(engineSession.settings.allowContentAccess) + engineSession.settings.allowContentAccess = false + verify(webViewSettings).allowContentAccess = false + + assertTrue(engineSession.settings.allowFileAccess) + engineSession.settings.allowFileAccess = false + verify(webViewSettings).allowFileAccess = false + + assertFalse(engineSession.settings.allowUniversalAccessFromFileURLs) + engineSession.settings.allowUniversalAccessFromFileURLs = true + verify(webViewSettings).allowUniversalAccessFromFileURLs = true + + assertFalse(engineSession.settings.allowFileAccessFromFileURLs) + engineSession.settings.allowFileAccessFromFileURLs = true + verify(webViewSettings).allowFileAccessFromFileURLs = true + + assertTrue(engineSession.settings.verticalScrollBarEnabled) + engineSession.settings.verticalScrollBarEnabled = false + verify(webView).isVerticalScrollBarEnabled = false + + assertTrue(engineSession.settings.horizontalScrollBarEnabled) + engineSession.settings.horizontalScrollBarEnabled = false + verify(webView).isHorizontalScrollBarEnabled = false + + assertFalse(engineSession.settings.supportMultipleWindows) + engineSession.settings.supportMultipleWindows = true + verify(webViewSettings).setSupportMultipleWindows(true) + + assertTrue(engineSession.webFontsEnabled) + assertTrue(engineSession.settings.webFontsEnabled) + engineSession.settings.webFontsEnabled = false + assertFalse(engineSession.webFontsEnabled) + assertFalse(engineSession.settings.webFontsEnabled) + + assertNull(engineSession.settings.trackingProtectionPolicy) + engineSession.settings.trackingProtectionPolicy = + EngineSession.TrackingProtectionPolicy.strict() + verify(engineSession).updateTrackingProtection(EngineSession.TrackingProtectionPolicy.strict()) + + engineSession.settings.trackingProtectionPolicy = null + verify(engineSession).disableTrackingProtection() + + verify(webViewSettings).cacheMode = WebSettings.LOAD_NO_CACHE + verify(webViewSettings).setGeolocationEnabled(false) + verify(webViewSettings).databaseEnabled = false + verify(webViewSettings).savePassword = false + verify(webViewSettings).saveFormData = false + verify(webViewSettings).builtInZoomControls = true + verify(webViewSettings).displayZoomControls = false + } + + @Test + fun withProvidedDefaultSettings() { + val defaultSettings = DefaultSettings( + javascriptEnabled = false, + domStorageEnabled = false, + webFontsEnabled = false, + trackingProtectionPolicy = EngineSession.TrackingProtectionPolicy.strict(), + userAgentString = "userAgent", + mediaPlaybackRequiresUserGesture = false, + javaScriptCanOpenWindowsAutomatically = true, + displayZoomControls = true, + loadWithOverviewMode = true, + useWideViewPort = true, + supportMultipleWindows = true, + ) + val engineSession = spy(SystemEngineSession(testContext, defaultSettings)) + + val webView = mock<WebView>() + whenever(webView.context).thenReturn(testContext) + + val webViewSettings = mock<WebSettings>() + whenever(webView.settings).thenReturn(webViewSettings) + + engineSession.webView = webView + + verify(webViewSettings).domStorageEnabled = false + verify(webViewSettings).javaScriptEnabled = false + verify(webViewSettings).userAgentString = "userAgent" + verify(webViewSettings).mediaPlaybackRequiresUserGesture = false + verify(webViewSettings).javaScriptCanOpenWindowsAutomatically = true + verify(webViewSettings).displayZoomControls = true + verify(webViewSettings).loadWithOverviewMode = true + verify(webViewSettings).useWideViewPort = true + verify(webViewSettings).setSupportMultipleWindows(true) + verify(engineSession).updateTrackingProtection(EngineSession.TrackingProtectionPolicy.strict()) + assertFalse(engineSession.webFontsEnabled) + } + + @Test + fun sharedFieldsAreVolatile() { + val internalSettings = SystemEngineSession::class.java.getDeclaredField("internalSettings") + val webFontsEnabledField = SystemEngineSession::class.java.getDeclaredField("webFontsEnabled") + val trackingProtectionField = SystemEngineSession::class.java.getDeclaredField("trackingProtectionPolicy") + val historyTrackingDelegate = SystemEngineSession::class.java.getDeclaredField("historyTrackingDelegate") + val fullScreenCallback = SystemEngineSession::class.java.getDeclaredField("fullScreenCallback") + val currentUrl = SystemEngineSession::class.java.getDeclaredField("currentUrl") + val webView = SystemEngineSession::class.java.getDeclaredField("webView") + + assertTrue(Modifier.isVolatile(internalSettings.modifiers)) + assertTrue(Modifier.isVolatile(webFontsEnabledField.modifiers)) + assertTrue(Modifier.isVolatile(trackingProtectionField.modifiers)) + assertTrue(Modifier.isVolatile(historyTrackingDelegate.modifiers)) + assertTrue(Modifier.isVolatile(fullScreenCallback.modifiers)) + assertTrue(Modifier.isVolatile(currentUrl.modifiers)) + assertTrue(Modifier.isVolatile(webView.modifiers)) + } + + @Test + fun settingInterceptorToProvideAlternativeContent() { + var interceptorCalledWithUri: String? = null + + val interceptor = object : RequestInterceptor { + override fun onLoadRequest( + engineSession: EngineSession, + uri: String, + lastUri: String?, + hasUserGesture: Boolean, + isSameDomain: Boolean, + isRedirect: Boolean, + isDirectNavigation: Boolean, + isSubframeRequest: Boolean, + ): RequestInterceptor.InterceptionResponse? { + interceptorCalledWithUri = uri + return RequestInterceptor.InterceptionResponse.Content("<h1>Hello World</h1>") + } + } + + val defaultSettings = DefaultSettings(requestInterceptor = interceptor) + + val engineSession = SystemEngineSession(testContext, defaultSettings) + engineSession.webView = spy(engineSession.webView) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val request: WebResourceRequest = mock() + doReturn(Uri.parse("sample:about")).`when`(request).url + + val response = engineSession.webView.webViewClient.shouldInterceptRequest( + engineSession.webView, + request, + ) + + assertEquals("sample:about", interceptorCalledWithUri) + + assertNotNull(response) + + assertEquals("<h1>Hello World</h1>", response!!.data.bufferedReader().use { it.readText() }) + assertEquals("text/html", response.mimeType) + assertEquals("UTF-8", response.encoding) + } + + @Test + fun `shouldInterceptRequest notifies observers if request was not intercepted`() { + val url = "sample:about" + val request: WebResourceRequest = mock() + doReturn(true).`when`(request).isForMainFrame + doReturn(true).`when`(request).hasGesture() + doReturn(Uri.parse(url)).`when`(request).url + + val engineSession = SystemEngineSession(testContext) + engineSession.webView = spy(engineSession.webView) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val observer: EngineSession.Observer = mock() + engineSession.register(observer) + + engineSession.webView.webViewClient.shouldInterceptRequest(engineSession.webView, request) + + verify(observer).onLoadRequest(anyString(), eq(true), eq(true)) + + val redirect: WebResourceRequest = mock() + doReturn(true).`when`(redirect).isForMainFrame + doReturn(false).`when`(redirect).hasGesture() + doReturn(Uri.parse("sample:about")).`when`(redirect).url + + engineSession.webView.webViewClient.shouldInterceptRequest(engineSession.webView, redirect) + + verify(observer).onLoadRequest(anyString(), eq(true), eq(true)) + } + + @Test + fun `shouldInterceptRequest does not notify observers if request was intercepted`() { + val request: WebResourceRequest = mock() + doReturn(true).`when`(request).isForMainFrame + doReturn(true).`when`(request).hasGesture() + doReturn(Uri.parse("sample:about")).`when`(request).url + + val interceptor = object : RequestInterceptor { + override fun onLoadRequest( + engineSession: EngineSession, + uri: String, + lastUri: String?, + hasUserGesture: Boolean, + isSameDomain: Boolean, + isRedirect: Boolean, + isDirectNavigation: Boolean, + isSubframeRequest: Boolean, + ): RequestInterceptor.InterceptionResponse? { + return RequestInterceptor.InterceptionResponse.Content("<h1>Hello World</h1>") + } + } + + val defaultSettings = DefaultSettings(requestInterceptor = interceptor) + + val engineSession = SystemEngineSession(testContext, defaultSettings) + engineSession.webView = spy(engineSession.webView) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val observer: EngineSession.Observer = mock() + engineSession.register(observer) + + engineSession.webView.webViewClient.shouldInterceptRequest( + engineSession.webView, + request, + ) + + verify(observer, never()).onLoadRequest(anyString(), anyBoolean(), anyBoolean()) + } + + @Test + fun settingInterceptorToProvideAlternativeUrl() { + var interceptorCalledWithUri: String? = null + + val interceptor = object : RequestInterceptor { + override fun onLoadRequest( + engineSession: EngineSession, + uri: String, + lastUri: String?, + hasUserGesture: Boolean, + isSameDomain: Boolean, + isRedirect: Boolean, + isDirectNavigation: Boolean, + isSubframeRequest: Boolean, + ): RequestInterceptor.InterceptionResponse? { + interceptorCalledWithUri = uri + return RequestInterceptor.InterceptionResponse.Url("https://mozilla.org") + } + } + + val defaultSettings = DefaultSettings(requestInterceptor = interceptor) + + val engineSession = SystemEngineSession(testContext, defaultSettings) + engineSession.webView = spy(engineSession.webView) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val request: WebResourceRequest = mock() + doReturn(Uri.parse("sample:about")).`when`(request).url + + val response = engineSession.webView.webViewClient.shouldInterceptRequest( + engineSession.webView, + request, + ) + + assertNull(response) + assertEquals("sample:about", interceptorCalledWithUri) + assertEquals("https://mozilla.org", engineSession.webView.url) + } + + @Test + fun onLoadRequestWithoutInterceptor() { + val defaultSettings = DefaultSettings() + + val engineSession = SystemEngineSession(testContext, defaultSettings) + engineSession.webView = spy(engineSession.webView) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val request: WebResourceRequest = mock() + doReturn(Uri.parse("sample:about")).`when`(request).url + + val response = engineSession.webView.webViewClient.shouldInterceptRequest( + engineSession.webView, + request, + ) + + assertNull(response) + } + + @Test + fun onLoadRequestWithInterceptorThatDoesNotIntercept() { + var interceptorCalledWithUri: String? = null + + val interceptor = object : RequestInterceptor { + override fun onLoadRequest( + engineSession: EngineSession, + uri: String, + lastUri: String?, + hasUserGesture: Boolean, + isSameDomain: Boolean, + isRedirect: Boolean, + isDirectNavigation: Boolean, + isSubframeRequest: Boolean, + ): RequestInterceptor.InterceptionResponse? { + interceptorCalledWithUri = uri + return null + } + } + + val defaultSettings = DefaultSettings(requestInterceptor = interceptor) + + val engineSession = SystemEngineSession(testContext, defaultSettings) + engineSession.webView = spy(engineSession.webView) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val request: WebResourceRequest = mock() + doReturn(Uri.parse("sample:about")).`when`(request).url + + val response = engineSession.webView.webViewClient.shouldInterceptRequest( + engineSession.webView, + request, + ) + + assertEquals("sample:about", interceptorCalledWithUri) + assertNull(response) + } + + @Test + fun webViewErrorMappingToErrorType() { + assertEquals( + ErrorType.ERROR_UNKNOWN_HOST, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_HOST_LOOKUP), + ) + assertEquals( + ErrorType.ERROR_CONNECTION_REFUSED, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_CONNECT), + ) + assertEquals( + ErrorType.ERROR_CONNECTION_REFUSED, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_IO), + ) + assertEquals( + ErrorType.ERROR_NET_TIMEOUT, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_TIMEOUT), + ) + assertEquals( + ErrorType.ERROR_REDIRECT_LOOP, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_REDIRECT_LOOP), + ) + assertEquals( + ErrorType.ERROR_UNKNOWN_PROTOCOL, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_UNSUPPORTED_SCHEME), + ) + assertEquals( + ErrorType.ERROR_SECURITY_SSL, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE), + ) + assertEquals( + ErrorType.ERROR_MALFORMED_URI, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_BAD_URL), + ) + assertEquals( + ErrorType.UNKNOWN, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_TOO_MANY_REQUESTS), + ) + assertEquals( + ErrorType.ERROR_FILE_NOT_FOUND, + SystemEngineSession.webViewErrorToErrorType(WebViewClient.ERROR_FILE_NOT_FOUND), + ) + assertEquals( + ErrorType.UNKNOWN, + SystemEngineSession.webViewErrorToErrorType(-500), + ) + } + + @Test + fun desktopMode() { + val userAgentMobile = "Mozilla/5.0 (Linux; Android 9) AppleWebKit/537.36 Mobile Safari/537.36" + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val webViewSettings = mock<WebSettings>() + whenever(webView.settings).thenReturn(webViewSettings) + + var desktopMode = false + engineSession.register( + object : EngineSession.Observer { + override fun onDesktopModeChange(enabled: Boolean) { + desktopMode = enabled + } + }, + ) + + engineSession.webView = webView + whenever(webView.settings).thenReturn(webViewSettings) + whenever(webViewSettings.userAgentString).thenReturn(userAgentMobile) + + engineSession.toggleDesktopMode(true) + verify(webViewSettings).useWideViewPort = true + verify(engineSession).toggleDesktopUA(userAgentMobile, true) + assertTrue(desktopMode) + + engineSession.toggleDesktopMode(true) + verify(webView, never()).reload() + + engineSession.toggleDesktopMode(true, true) + verify(webView).reload() + } + + @Test + fun desktopModeWithProvidedTrueWideViewPort() { + val userAgentMobile = "Mozilla/5.0 (Linux; Android 9) AppleWebKit/537.36 Mobile Safari/537.36" + val defaultSettings = DefaultSettings(useWideViewPort = true) + val engineSession = spy(SystemEngineSession(testContext, defaultSettings)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + val webViewSettings = mock<WebSettings>() + var desktopMode = false + + engineSession.register( + object : EngineSession.Observer { + override fun onDesktopModeChange(enabled: Boolean) { + desktopMode = enabled + } + }, + ) + + engineSession.webView = webView + whenever(webView.settings).thenReturn(webViewSettings) + whenever(webViewSettings.userAgentString).thenReturn(userAgentMobile) + + engineSession.toggleDesktopMode(true) + verify(webViewSettings).useWideViewPort = true + verify(engineSession).toggleDesktopUA(userAgentMobile, true) + assertTrue(desktopMode) + } + + @Test + fun desktopModeWithProvidedFalseWideViewPort() { + val userAgentMobile = "Mozilla/5.0 (Linux; Android 9) AppleWebKit/537.36 Mobile Safari/537.36" + val defaultSettings = DefaultSettings(useWideViewPort = false) + val engineSession = spy(SystemEngineSession(testContext, defaultSettings)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + val webViewSettings = mock<WebSettings>() + var desktopMode = false + + engineSession.register( + object : EngineSession.Observer { + override fun onDesktopModeChange(enabled: Boolean) { + desktopMode = enabled + } + }, + ) + + engineSession.webView = webView + whenever(webView.settings).thenReturn(webViewSettings) + whenever(webViewSettings.userAgentString).thenReturn(userAgentMobile) + + engineSession.toggleDesktopMode(true) + verify(webViewSettings).useWideViewPort = true + verify(engineSession).toggleDesktopUA(userAgentMobile, true) + assertTrue(desktopMode) + + engineSession.toggleDesktopMode(false) + verify(webViewSettings).useWideViewPort = false + verify(engineSession).toggleDesktopUA(userAgentMobile, false) + assertFalse(desktopMode) + } + + @Test + fun desktopModeToggleTrueWithNoProvidedDefault() { + val userAgentMobile = "Mozilla/5.0 (Linux; Android 9) AppleWebKit/537.36 Mobile Safari/537.36" + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + + val webViewSettings = mock<WebSettings>() + whenever(webView.settings).thenReturn(webViewSettings) + whenever(webViewSettings.userAgentString).thenReturn(userAgentMobile) + + var desktopMode = false + engineSession.register( + object : EngineSession.Observer { + override fun onDesktopModeChange(enabled: Boolean) { + desktopMode = enabled + } + }, + ) + + engineSession.webView = webView + whenever(webView.settings).thenReturn(webViewSettings) + whenever(webViewSettings.userAgentString).thenReturn(userAgentMobile) + + engineSession.toggleDesktopMode(true) + verify(webViewSettings).useWideViewPort = true + verify(engineSession).toggleDesktopUA(userAgentMobile, true) + assertTrue(desktopMode) + } + + @Test + fun desktopModeToggleFalseWithNoProvidedDefault() { + val userAgentMobile = "Mozilla/5.0 (Linux; Android 9) AppleWebKit/537.36 Mobile Safari/537.36" + val engineSession = spy(SystemEngineSession(testContext)) + + val webView = mock<WebView>() + + val webViewSettings = mock<WebSettings>() + whenever(webView.settings).thenReturn(webViewSettings) + whenever(webViewSettings.userAgentString).thenReturn(userAgentMobile) + + var desktopMode = false + engineSession.register( + object : EngineSession.Observer { + override fun onDesktopModeChange(enabled: Boolean) { + desktopMode = enabled + } + }, + ) + + engineSession.webView = webView + whenever(webView.settings).thenReturn(webViewSettings) + whenever(webViewSettings.userAgentString).thenReturn(userAgentMobile) + + engineSession.toggleDesktopMode(false) + verify(webViewSettings).useWideViewPort = false + verify(engineSession).toggleDesktopUA(userAgentMobile, false) + assertFalse(desktopMode) + } + + @Test + fun desktopModeUA() { + val userAgentMobile = "Mozilla/5.0 (Linux; Android 9) AppleWebKit/537.36 Mobile Safari/537.36" + val userAgentDesktop = "Mozilla/5.0 (Linux; diordnA 9) AppleWebKit/537.36 eliboM Safari/537.36" + val engineSession = spy(SystemEngineSession(testContext)) + + assertEquals(engineSession.toggleDesktopUA(userAgentMobile, false), userAgentMobile) + assertEquals(engineSession.toggleDesktopUA(userAgentMobile, true), userAgentDesktop) + } + + @Test + fun findAll() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + engineSession.findAll("mozilla") + verify(webView, never()).findAllAsync(anyString()) + + engineSession.webView = webView + var findObserved: String? = null + engineSession.register( + object : EngineSession.Observer { + override fun onFind(text: String) { + findObserved = text + } + }, + ) + engineSession.findAll("mozilla") + verify(webView).findAllAsync("mozilla") + assertEquals("mozilla", findObserved) + } + + @Test + fun findNext() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.findNext(true) + verify(webView, never()).findNext(mockitoAny(Boolean::class.java)) + + engineSession.webView = webView + engineSession.findNext(true) + verify(webView).findNext(true) + } + + @Test + fun clearFindMatches() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.clearFindMatches() + verify(webView, never()).clearMatches() + + engineSession.webView = webView + engineSession.clearFindMatches() + verify(webView).clearMatches() + } + + @Test + fun clearDataMakingExpectedCalls() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + val webStorage: WebStorage = mock() + val webViewDatabase: WebViewDatabase = mock() + val context: Context = testContext + + doReturn(webStorage).`when`(engineSession).webStorage() + doReturn(webViewDatabase).`when`(engineSession).webViewDatabase(context) + whenever(webView.context).thenReturn(context) + engineSession.webView = webView + + // clear all data by default + engineSession.clearData() + verify(webView).clearFormData() + verify(webView).clearHistory() + verify(webView).clearMatches() + verify(webView).clearSslPreferences() + verify(webView).clearCache(true) + verify(webStorage).deleteAllData() + verify(webViewDatabase).clearHttpAuthUsernamePassword() + + // clear storages + engineSession.clearData(BrowsingData.select(BrowsingData.DOM_STORAGES)) + verify(webStorage, times(2)).deleteAllData() + verify(webView, times(1)).clearCache(true) + verify(webView, times(1)).clearFormData() + verify(webView, times(1)).clearMatches() + verify(webView, times(1)).clearHistory() + verify(webView, times(1)).clearSslPreferences() + verify(webViewDatabase, times(1)).clearHttpAuthUsernamePassword() + + // clear auth info + engineSession.clearData(BrowsingData.select(BrowsingData.AUTH_SESSIONS)) + verify(webViewDatabase, times(2)).clearHttpAuthUsernamePassword() + verify(webStorage, times(2)).deleteAllData() + verify(webView, times(1)).clearCache(true) + verify(webView, times(1)).clearFormData() + verify(webView, times(1)).clearMatches() + verify(webView, times(1)).clearHistory() + verify(webView, times(1)).clearSslPreferences() + + // clear cookies + engineSession.clearData(BrowsingData.select(BrowsingData.COOKIES)) + verify(webViewDatabase, times(2)).clearHttpAuthUsernamePassword() + verify(webStorage, times(2)).deleteAllData() + verify(webView, times(1)).clearCache(true) + verify(webView, times(1)).clearFormData() + verify(webView, times(1)).clearMatches() + verify(webView, times(1)).clearHistory() + verify(webView, times(1)).clearSslPreferences() + + // clear image cache + engineSession.clearData(BrowsingData.select(BrowsingData.IMAGE_CACHE)) + verify(webView, times(2)).clearCache(true) + verify(webViewDatabase, times(2)).clearHttpAuthUsernamePassword() + verify(webStorage, times(2)).deleteAllData() + verify(webView, times(1)).clearFormData() + verify(webView, times(1)).clearMatches() + verify(webView, times(1)).clearHistory() + verify(webView, times(1)).clearSslPreferences() + + // clear network cache + engineSession.clearData(BrowsingData.select(BrowsingData.NETWORK_CACHE)) + verify(webView, times(3)).clearCache(true) + verify(webViewDatabase, times(2)).clearHttpAuthUsernamePassword() + verify(webStorage, times(2)).deleteAllData() + verify(webView, times(1)).clearFormData() + verify(webView, times(1)).clearMatches() + verify(webView, times(1)).clearHistory() + verify(webView, times(1)).clearSslPreferences() + + // clear all caches + engineSession.clearData(BrowsingData.allCaches()) + verify(webView, times(4)).clearCache(true) + verify(webViewDatabase, times(2)).clearHttpAuthUsernamePassword() + verify(webStorage, times(2)).deleteAllData() + verify(webView, times(1)).clearFormData() + verify(webView, times(1)).clearMatches() + verify(webView, times(1)).clearHistory() + verify(webView, times(1)).clearSslPreferences() + } + + @Test + fun clearDataInvokesSuccessCallback() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + val webStorage: WebStorage = mock() + val webViewDatabase: WebViewDatabase = mock() + val context: Context = testContext + var onSuccessCalled = false + + doReturn(webStorage).`when`(engineSession).webStorage() + doReturn(webViewDatabase).`when`(engineSession).webViewDatabase(context) + whenever(webView.context).thenReturn(context) + engineSession.webView = webView + + engineSession.clearData(onSuccess = { onSuccessCalled = true }) + assertTrue(onSuccessCalled) + } + + @Test + fun clearDataInvokesErrorCallback() { + val engineSession = spy(SystemEngineSession(testContext)) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + val webViewDatabase: WebViewDatabase = mock() + val context: Context = testContext + var onErrorCalled = false + + val exception = RuntimeException() + doThrow(exception).`when`(engineSession).webStorage() + doReturn(webViewDatabase).`when`(engineSession).webViewDatabase(context) + whenever(webView.context).thenReturn(context) + engineSession.webView = webView + + engineSession.clearData( + onError = { + onErrorCalled = true + assertSame(it, exception) + }, + ) + assertTrue(onErrorCalled) + } + + @Test + fun testExitFullscreenModeWithWebViewAndCallBack() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + val customViewCallback = mock<WebChromeClient.CustomViewCallback>() + + engineView.render(engineSession) + engineSession.exitFullScreenMode() + verify(customViewCallback, never()).onCustomViewHidden() + + engineSession.fullScreenCallback = customViewCallback + engineSession.exitFullScreenMode() + verify(customViewCallback).onCustomViewHidden() + } + + @Test + fun closeDestroysWebView() { + val engineSession = SystemEngineSession(testContext) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + engineSession.webView = webView + + engineSession.close() + verify(webView).destroy() + } + + @Test + fun `purgeHistory delegates to clearHistory`() { + val engineSession = SystemEngineSession(testContext) + + val webView: WebView = mock() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.webView = webView + + engineSession.purgeHistory() + verify(webView).clearHistory() + } + + @Test + fun `GIVEN webView_canGoBack() true WHEN goBack() is called THEN verify EngineObserver onNavigateBack() is triggered`() { + var observedOnNavigateBack = false + + val engineSession = SystemEngineSession(testContext) + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.webView = webView + Mockito.`when`(webView.canGoBack()).thenReturn(true) + engineSession.register( + object : EngineSession.Observer { + override fun onNavigateBack() { + observedOnNavigateBack = true + } + }, + ) + + engineSession.goBack() + assertTrue(observedOnNavigateBack) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineTest.kt new file mode 100644 index 0000000000..596157111b --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineTest.kt @@ -0,0 +1,105 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.engine.DefaultSettings +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.UnsupportedSettingException +import mozilla.components.support.test.robolectric.testContext +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class SystemEngineTest { + + @Before + fun setup() { + // This is setting a internal field just for testing purposes as + // WebSettings.getDefaultUserAgent isn't mocked by Roboelectric + SystemEngine.defaultUserAgent = "test-ua-string" + } + + @Test + fun createView() { + val engine = SystemEngine(testContext) + assertTrue(engine.createView(testContext) is SystemEngineView) + } + + @Test + fun createSession() { + val engine = SystemEngine(testContext) + assertTrue(engine.createSession() is SystemEngineSession) + + try { + engine.createSession(true) + // Private browsing not yet supported + fail("Expected UnsupportedOperationException") + } catch (e: UnsupportedOperationException) { } + + try { + engine.createSession(false, "1") + // Contextual identities not yet supported + fail("Expected UnsupportedOperationException") + } catch (e: UnsupportedOperationException) { } + } + + @Test + fun name() { + val engine = SystemEngine(testContext) + assertEquals("System", engine.name()) + } + + @Test + fun settings() { + val engine = SystemEngine( + testContext, + DefaultSettings( + remoteDebuggingEnabled = true, + trackingProtectionPolicy = EngineSession.TrackingProtectionPolicy.strict(), + ), + ) + + assertTrue(engine.settings.remoteDebuggingEnabled) + engine.settings.remoteDebuggingEnabled = false + assertFalse(engine.settings.remoteDebuggingEnabled) + + assertEquals( + engine.settings.trackingProtectionPolicy, + EngineSession.TrackingProtectionPolicy.strict(), + ) + engine.settings.trackingProtectionPolicy = EngineSession.TrackingProtectionPolicy.none() + assertEquals(engine.settings.trackingProtectionPolicy, EngineSession.TrackingProtectionPolicy.none()) + + // Specifying no ua-string default should result in WebView default + // It should be possible to read and set a new default + assertEquals("test-ua-string", engine.settings.userAgentString) + engine.settings.userAgentString = engine.settings.userAgentString + "-test" + assertEquals("test-ua-string-test", engine.settings.userAgentString) + + // It should be possible to specify a custom ua-string default + assertEquals("foo", SystemEngine(testContext, DefaultSettings(userAgentString = "foo")).settings.userAgentString) + } + + // This feature will be covered on this issue + // https://github.com/mozilla-mobile/android-components/issues/4206 + @Test(expected = UnsupportedSettingException::class) + fun safeBrowsingIsNotSupportedYet() { + val engine = SystemEngine( + testContext, + DefaultSettings( + remoteDebuggingEnabled = true, + trackingProtectionPolicy = EngineSession.TrackingProtectionPolicy.strict(), + ), + ) + + engine.settings.safeBrowsingPolicy + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineViewTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineViewTest.kt new file mode 100644 index 0000000000..95cfbd2253 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/SystemEngineViewTest.kt @@ -0,0 +1,1654 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system + +import android.app.Activity +import android.graphics.Bitmap +import android.net.Uri +import android.net.http.SslCertificate +import android.net.http.SslError +import android.os.Build +import android.os.Bundle +import android.os.Message +import android.view.PixelCopy +import android.view.View +import android.webkit.HttpAuthHandler +import android.webkit.JsPromptResult +import android.webkit.JsResult +import android.webkit.SslErrorHandler +import android.webkit.ValueCallback +import android.webkit.WebChromeClient +import android.webkit.WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE +import android.webkit.WebResourceError +import android.webkit.WebResourceRequest +import android.webkit.WebSettings +import android.webkit.WebView +import android.webkit.WebView.HitTestResult +import android.webkit.WebViewClient +import android.webkit.WebViewDatabase +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import mozilla.components.browser.engine.system.matcher.UrlMatcher +import mozilla.components.browser.errorpages.ErrorType +import mozilla.components.concept.engine.EngineSession +import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy +import mozilla.components.concept.engine.EngineSession.TrackingProtectionPolicy.TrackingCategory +import mozilla.components.concept.engine.HitResult +import mozilla.components.concept.engine.InputResultDetail +import mozilla.components.concept.engine.content.blocking.Tracker +import mozilla.components.concept.engine.history.HistoryTrackingDelegate +import mozilla.components.concept.engine.permission.PermissionRequest +import mozilla.components.concept.engine.prompt.PromptRequest +import mozilla.components.concept.engine.request.RequestInterceptor +import mozilla.components.concept.engine.window.WindowRequest +import mozilla.components.concept.fetch.Response +import mozilla.components.concept.storage.PageVisit +import mozilla.components.concept.storage.VisitType +import mozilla.components.support.test.any +import mozilla.components.support.test.argumentCaptor +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.shadow.PixelCopyShadow +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.test.whenever +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertNotEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mockito.doNothing +import org.mockito.Mockito.doReturn +import org.mockito.Mockito.never +import org.mockito.Mockito.reset +import org.mockito.Mockito.spy +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoInteractions +import org.robolectric.Robolectric +import org.robolectric.annotation.Config +import java.io.StringReader + +@ExperimentalCoroutinesApi // for runTest +@RunWith(AndroidJUnit4::class) +class SystemEngineViewTest { + + @Test + fun `EngineView initialization`() { + val engineView = SystemEngineView(testContext) + val webView = WebView(testContext) + + engineView.initWebView(webView) + assertNotNull(webView.webChromeClient) + assertNotNull(webView.webViewClient) + } + + @Test + fun `EngineView renders WebView`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + + engineView.render(engineSession) + assertEquals(engineSession.webView, engineView.getChildAt(0)) + } + + @Test + fun `WebViewClient notifies observers`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + var observedUrl = "" + var observedUserGesture = true + var observedLoadingState = false + var observedSecurityChange: Triple<Boolean, String?, String?> = Triple(false, null, null) + engineSession.register( + object : EngineSession.Observer { + override fun onLoadingStateChange(loading: Boolean) { observedLoadingState = loading } + override fun onLocationChange(url: String, hasUserGesture: Boolean) { + observedUrl = url + observedUserGesture = hasUserGesture + } + override fun onSecurityChange(secure: Boolean, host: String?, issuer: String?) { + observedSecurityChange = Triple(secure, host, issuer) + } + }, + ) + + engineSession.webView.webViewClient.onPageStarted(mock(), "https://wiki.mozilla.org/", null) + assertEquals(true, observedLoadingState) + assertEquals(observedUrl, "https://wiki.mozilla.org/") + + observedLoadingState = true + engineSession.webView.webViewClient.onPageFinished(null, "http://mozilla.org") + assertEquals("http://mozilla.org", observedUrl) + assertEquals(false, observedUserGesture) + assertFalse(observedLoadingState) + assertEquals(Triple(false, null, null), observedSecurityChange) + + val view = mock<WebView>() + engineSession.webView.webViewClient.onPageFinished(view, "http://mozilla.org") + assertEquals(Triple(false, null, null), observedSecurityChange) + + val certificate = mock<SslCertificate>() + val dName = mock<SslCertificate.DName>() + doReturn("testCA").`when`(dName).oName + doReturn(certificate).`when`(view).certificate + engineSession.webView.webViewClient.onPageFinished(view, "http://mozilla.org") + + doReturn("testCA").`when`(dName).oName + doReturn(dName).`when`(certificate).issuedBy + doReturn(certificate).`when`(view).certificate + engineSession.webView.webViewClient.onPageFinished(view, "http://mozilla.org") + assertEquals(Triple(true, "mozilla.org", "testCA"), observedSecurityChange) + } + + @Test + fun `HitResult type handling`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var hitTestResult: HitResult = HitResult.UNKNOWN("") + engineView.render(engineSession) + engineSession.register( + object : EngineSession.Observer { + override fun onLongPress(hitResult: HitResult) { + hitTestResult = hitResult + } + }, + ) + + engineView.handleLongClick(HitTestResult.EMAIL_TYPE, "mailto:asa@mozilla.com") + assertTrue(hitTestResult is HitResult.EMAIL) + assertEquals("mailto:asa@mozilla.com", hitTestResult.src) + + engineView.handleLongClick(HitTestResult.GEO_TYPE, "geo:1,-1") + assertTrue(hitTestResult is HitResult.GEO) + assertEquals("geo:1,-1", hitTestResult.src) + + engineView.handleLongClick(HitTestResult.PHONE_TYPE, "tel:+123456789") + assertTrue(hitTestResult is HitResult.PHONE) + assertEquals("tel:+123456789", hitTestResult.src) + + engineView.handleLongClick(HitTestResult.IMAGE_TYPE, "image.png") + assertTrue(hitTestResult is HitResult.IMAGE) + assertEquals("image.png", hitTestResult.src) + + engineView.handleLongClick(HitTestResult.SRC_ANCHOR_TYPE, "https://mozilla.org") + assertTrue(hitTestResult is HitResult.UNKNOWN) + assertEquals("https://mozilla.org", hitTestResult.src) + + var result = engineView.handleLongClick(HitTestResult.SRC_IMAGE_ANCHOR_TYPE, "image.png") + assertFalse(result) // Intentional for image links; see ImageHandler tests. + + result = engineView.handleLongClick(HitTestResult.EDIT_TEXT_TYPE, "https://mozilla.org") + assertFalse(result) + } + + @Test + fun `ImageHandler notifies observers`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + val handler = SystemEngineView.ImageHandler(engineSession) + val message = mock<Message>() + val bundle = mock<Bundle>() + var observerNotified = false + + whenever(message.data).thenReturn(bundle) + whenever(message.data.getString("url")).thenReturn("https://mozilla.org") + whenever(message.data.getString("src")).thenReturn("file.png") + + engineView.render(engineSession) + engineSession.register( + object : EngineSession.Observer { + override fun onLongPress(hitResult: HitResult) { + observerNotified = true + } + }, + ) + + handler.handleMessage(message) + assertTrue(observerNotified) + + observerNotified = false + val nullHandler = SystemEngineView.ImageHandler(null) + nullHandler.handleMessage(message) + assertFalse(observerNotified) + } + + @Test(expected = IllegalStateException::class) + fun `null image src`() { + val engineSession = SystemEngineSession(testContext) + val handler = SystemEngineView.ImageHandler(engineSession) + val message = mock<Message>() + val bundle = mock<Bundle>() + + whenever(message.data).thenReturn(bundle) + whenever(message.data.getString("url")).thenReturn("https://mozilla.org") + + handler.handleMessage(message) + } + + @Test(expected = IllegalStateException::class) + fun `null image url`() { + val engineSession = SystemEngineSession(testContext) + val handler = SystemEngineView.ImageHandler(engineSession) + val message = mock<Message>() + val bundle = mock<Bundle>() + + whenever(message.data).thenReturn(bundle) + whenever(message.data.getString("src")).thenReturn("file.png") + + handler.handleMessage(message) + } + + @Test + fun `WebChromeClient notifies observers`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + var observedProgress = 0 + engineSession.register( + object : EngineSession.Observer { + override fun onProgress(progress: Int) { observedProgress = progress } + }, + ) + + engineSession.webView.webChromeClient!!.onProgressChanged(null, 100) + assertEquals(100, observedProgress) + } + + @Test + fun `SystemEngineView updates current session url via onPageStart events`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + assertEquals("", engineSession.currentUrl) + engineSession.webView.webViewClient.onPageStarted(engineSession.webView, "https://www.mozilla.org/", null) + assertEquals("https://www.mozilla.org/", engineSession.currentUrl) + + engineSession.webView.webViewClient.onPageStarted(engineSession.webView, "https://www.firefox.com/", null) + assertEquals("https://www.firefox.com/", engineSession.currentUrl) + } + + @Test + fun `WebView client notifies navigation state changes`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + + val observer: EngineSession.Observer = mock() + val webView: WebView = mock() + whenever(webView.canGoBack()).thenReturn(true) + whenever(webView.canGoForward()).thenReturn(true) + + engineSession.register(observer) + verify(observer, never()).onNavigationStateChange(true, true) + + engineView.render(engineSession) + engineSession.webView.webViewClient.onPageStarted(webView, "https://www.mozilla.org/", null) + verify(observer).onNavigationStateChange(true, true) + } + + @Test + fun `WebView client notifies configured history delegate of url visits`() = runTest { + val engineSession = SystemEngineSession(testContext) + + val engineView = SystemEngineView(testContext) + val webView: WebView = mock() + val historyDelegate: HistoryTrackingDelegate = mock() + + engineView.render(engineSession) + + // Nothing breaks if delegate isn't set. + engineSession.webView.webViewClient.doUpdateVisitedHistory(webView, "https://www.mozilla.com", false) + + engineSession.settings.historyTrackingDelegate = historyDelegate + whenever(historyDelegate.shouldStoreUri(any())).thenReturn(true) + + engineSession.webView.webViewClient.doUpdateVisitedHistory(webView, "https://www.mozilla.com", false) + verify(historyDelegate).onVisited(eq("https://www.mozilla.com"), eq(PageVisit(VisitType.LINK))) + + engineSession.webView.webViewClient.doUpdateVisitedHistory(webView, "https://www.mozilla.com", true) + verify(historyDelegate).onVisited(eq("https://www.mozilla.com"), eq(PageVisit(VisitType.RELOAD))) + } + + @Test + fun `WebView client checks with the delegate if the URI visit should be recorded`() = runTest { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + val webView: WebView = mock() + engineView.render(engineSession) + + val historyDelegate: HistoryTrackingDelegate = mock() + engineSession.settings.historyTrackingDelegate = historyDelegate + + whenever(historyDelegate.shouldStoreUri("https://www.mozilla.com")).thenReturn(true) + + // Verify that engine session asked delegate if uri should be stored. + engineSession.webView.webViewClient.doUpdateVisitedHistory(webView, "https://www.mozilla.com", false) + verify(historyDelegate).onVisited(eq("https://www.mozilla.com"), eq(PageVisit(VisitType.LINK))) + verify(historyDelegate).shouldStoreUri("https://www.mozilla.com") + + // Verify that engine won't try to store a uri that delegate doesn't want. + engineSession.webView.webViewClient.doUpdateVisitedHistory(webView, "https://www.mozilla.com/not-allowed", false) + verify(historyDelegate, never()).onVisited(eq("https://www.mozilla.com/not-allowed"), any()) + verify(historyDelegate).shouldStoreUri("https://www.mozilla.com/not-allowed") + Unit + } + + @Test + fun `WebView client requests history from configured history delegate`() = runTest { + val engineSession = SystemEngineSession(testContext) + + val engineView = SystemEngineView(testContext) + val historyDelegate = object : HistoryTrackingDelegate { + override suspend fun onVisited(uri: String, visit: PageVisit) { + fail() + } + + override fun shouldStoreUri(uri: String): Boolean { + return true + } + + override suspend fun onTitleChanged(uri: String, title: String) { + fail() + } + + override suspend fun onPreviewImageChange(uri: String, previewImageUrl: String) { + fail() + } + + override suspend fun getVisited(uris: List<String>): List<Boolean> { + fail() + return emptyList() + } + + override suspend fun getVisited(): List<String> { + return listOf("https://www.mozilla.com") + } + } + + engineView.render(engineSession) + + // Nothing breaks if delegate isn't set. + engineSession.webView.webChromeClient!!.getVisitedHistory(mock()) + + engineSession.settings.historyTrackingDelegate = historyDelegate + + val historyValueCallback: ValueCallback<Array<String>> = mock() + engineSession.webView.webChromeClient!!.getVisitedHistory(historyValueCallback) + verify(historyValueCallback).onReceiveValue(arrayOf("https://www.mozilla.com")) + } + + @Test + fun `WebView client notifies configured history delegate of title changes`() = runTest { + val engineSession = SystemEngineSession(testContext) + + val engineView = SystemEngineView(testContext) + val webView: WebView = mock() + val historyDelegate: HistoryTrackingDelegate = mock() + + engineView.render(engineSession) + + // Nothing breaks if delegate isn't set. + engineSession.webView.webChromeClient!!.onReceivedTitle(webView, "New title!") + + // We can now set the delegate. Were it set before the render call, + // it'll get overwritten during settings initialization. + engineSession.settings.historyTrackingDelegate = historyDelegate + + // Delegate not notified if, somehow, there's no currentUrl present in the view. + engineSession.webView.webChromeClient!!.onReceivedTitle(webView, "New title!") + verify(historyDelegate, never()).onTitleChanged(eq(""), eq("New title!")) + + // This sets the currentUrl. + engineSession.webView.webViewClient.onPageStarted(webView, "https://www.mozilla.org/", null) + + engineSession.webView.webChromeClient!!.onReceivedTitle(webView, "New title!") + verify(historyDelegate).onTitleChanged(eq("https://www.mozilla.org/"), eq("New title!")) + + reset(historyDelegate) + + // Empty title when none provided + engineSession.webView.webChromeClient!!.onReceivedTitle(webView, null) + verify(historyDelegate).onTitleChanged(eq("https://www.mozilla.org/"), eq("")) + } + + @Test + fun `WebView client notifies observers about title changes`() { + val engineSession = SystemEngineSession(testContext) + + val engineView = SystemEngineView(testContext) + val observer: EngineSession.Observer = mock() + val webView: WebView = mock() + whenever(webView.canGoBack()).thenReturn(true) + whenever(webView.canGoForward()).thenReturn(true) + + engineSession.register(observer) + engineView.render(engineSession) + engineSession.webView.webChromeClient!!.onReceivedTitle(webView, "Hello World!") + verify(observer).onTitleChange(eq("Hello World!")) + verify(observer).onNavigationStateChange(true, true) + + reset(observer) + + // Empty title when none provided. + engineSession.webView.webChromeClient!!.onReceivedTitle(webView, null) + verify(observer).onTitleChange(eq("")) + verify(observer).onNavigationStateChange(true, true) + } + + @Test + fun `download listener notifies observers`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + var observerNotified = false + + engineSession.register( + object : EngineSession.Observer { + override fun onExternalResource( + url: String, + fileName: String?, + contentLength: Long?, + contentType: String?, + cookie: String?, + userAgent: String?, + isPrivate: Boolean, + skipConfirmation: Boolean, + openInApp: Boolean, + response: Response?, + ) { + assertEquals("https://download.mozilla.org", url) + assertEquals("image.png", fileName) + assertEquals(1337L, contentLength) + assertNull(cookie) + assertEquals("Components/1.0", userAgent) + + observerNotified = true + } + }, + ) + + val listener = engineView.createDownloadListener() + listener.onDownloadStart( + "https://download.mozilla.org", + "Components/1.0", + "attachment; filename=\"image.png\"", + "image/png", + 1337, + ) + + assertTrue(observerNotified) + } + + @Test + fun `WebView client tracking protection`() { + SystemEngineView.URL_MATCHER = UrlMatcher(arrayOf("blocked.random")) + + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val webViewClient = engineSession.webView.webViewClient + val invalidRequest = mock<WebResourceRequest>() + whenever(invalidRequest.isForMainFrame).thenReturn(false) + whenever(invalidRequest.url).thenReturn(Uri.parse("market://foo.bar/")) + + var response = webViewClient.shouldInterceptRequest(engineSession.webView, invalidRequest) + assertNull(response) + + engineSession.trackingProtectionPolicy = TrackingProtectionPolicy.strict() + response = webViewClient.shouldInterceptRequest(engineSession.webView, invalidRequest) + assertNotNull(response) + assertNull(response!!.data) + assertNull(response.encoding) + assertNull(response.mimeType) + + val faviconRequest = mock<WebResourceRequest>() + whenever(faviconRequest.isForMainFrame).thenReturn(false) + whenever(faviconRequest.url).thenReturn(Uri.parse("http://foo/favicon.ico")) + response = webViewClient.shouldInterceptRequest(engineSession.webView, faviconRequest) + assertNotNull(response) + assertNull(response!!.data) + assertNull(response.encoding) + assertNull(response.mimeType) + + val blockedRequest = mock<WebResourceRequest>() + whenever(blockedRequest.isForMainFrame).thenReturn(false) + whenever(blockedRequest.url).thenReturn(Uri.parse("http://blocked.random")) + + var trackerBlocked: Tracker? = null + engineSession.register( + object : EngineSession.Observer { + override fun onTrackerBlocked(tracker: Tracker) { + trackerBlocked = tracker + } + }, + ) + + response = webViewClient.shouldInterceptRequest(engineSession.webView, blockedRequest) + assertNotNull(response) + assertNull(response!!.data) + assertNull(response.encoding) + assertNull(response.mimeType) + assertTrue(trackerBlocked!!.trackingCategories.isEmpty()) + } + + @Test + fun `blocked trackers are reported with correct categories`() { + val BLOCK_LIST = """{ + "license": "test-license", + "categories": { + "Advertising": [ + { + "AdTest1": { + "http://www.adtest1.com/": [ + "adtest1.com" + ] + } + } + ], + "Analytics": [ + { + "AnalyticsTest": { + "http://analyticsTest1.com/": [ + "analyticsTest1.com" + ] + } + } + ], + "Content": [ + { + "ContentTest1": { + "http://contenttest1.com/": [ + "contenttest1.com" + ] + } + } + ], + "Social": [ + { + "SocialTest1": { + "http://www.socialtest1.com/": [ + "socialtest1.com" + ] + } + } + ] + } + } + """ + SystemEngineView.URL_MATCHER = UrlMatcher.createMatcher( + StringReader(BLOCK_LIST), + StringReader("{}"), + ) + + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var trackerBlocked: Tracker? = null + + engineView.render(engineSession) + val webViewClient = engineSession.webView.webViewClient + + engineSession.trackingProtectionPolicy = TrackingProtectionPolicy.strict() + + engineSession.register( + object : EngineSession.Observer { + override fun onTrackerBlocked(tracker: Tracker) { + trackerBlocked = tracker + } + }, + ) + + val blockedRequest = mock<WebResourceRequest>() + whenever(blockedRequest.isForMainFrame).thenReturn(false) + + whenever(blockedRequest.url).thenReturn(Uri.parse("http://www.adtest1.com/")) + webViewClient.shouldInterceptRequest(engineSession.webView, blockedRequest) + + assertTrue(trackerBlocked!!.trackingCategories.first() == TrackingCategory.AD) + + whenever(blockedRequest.url).thenReturn(Uri.parse("http://analyticsTest1.com/")) + webViewClient.shouldInterceptRequest(engineSession.webView, blockedRequest) + + assertTrue(trackerBlocked!!.trackingCategories.first() == TrackingCategory.ANALYTICS) + + whenever(blockedRequest.url).thenReturn(Uri.parse("http://www.socialtest1.com/")) + webViewClient.shouldInterceptRequest(engineSession.webView, blockedRequest) + + assertTrue(trackerBlocked!!.trackingCategories.first() == TrackingCategory.SOCIAL) + + SystemEngineView.URL_MATCHER = null + } + + @Test + @Suppress("Deprecation") + fun `WebViewClient calls interceptor from deprecated onReceivedError API`() { + val engineSession = spy(SystemEngineSession(testContext)) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + doNothing().`when`(engineSession).initSettings() + + val requestInterceptor: RequestInterceptor = mock() + val webViewClient = engineSession.webView.webViewClient + + // No session or interceptor attached. + webViewClient.onReceivedError( + engineSession.webView, + WebViewClient.ERROR_UNKNOWN, + null, + "http://failed.random", + ) + verifyNoInteractions(requestInterceptor) + + // Session attached, but not interceptor. + engineView.render(engineSession) + webViewClient.onReceivedError( + engineSession.webView, + WebViewClient.ERROR_UNKNOWN, + null, + "http://failed.random", + ) + verifyNoInteractions(requestInterceptor) + + // Session and interceptor. + engineSession.settings.requestInterceptor = requestInterceptor + webViewClient.onReceivedError( + engineSession.webView, + WebViewClient.ERROR_UNKNOWN, + null, + "http://failed.random", + ) + verify(requestInterceptor).onErrorRequest(engineSession, ErrorType.UNKNOWN, "http://failed.random") + + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.webView = webView + val errorResponse = RequestInterceptor.ErrorResponse("about:fail") + webViewClient.onReceivedError( + engineSession.webView, + WebViewClient.ERROR_UNKNOWN, + null, + "http://failed.random", + ) + verify(webView, never()).loadUrl(ArgumentMatchers.anyString()) + + whenever(requestInterceptor.onErrorRequest(engineSession, ErrorType.UNKNOWN, "http://failed.random")) + .thenReturn(errorResponse) + webViewClient.onReceivedError( + engineSession.webView, + WebViewClient.ERROR_UNKNOWN, + null, + "http://failed.random", + ) + verify(webView).loadUrl("about:fail") + + val errorResponse2 = RequestInterceptor.ErrorResponse("about:fail2") + webViewClient.onReceivedError( + engineSession.webView, + WebViewClient.ERROR_UNKNOWN, + null, + "http://failed.random", + ) + verify(webView, never()).loadUrl("about:fail2") + + whenever(requestInterceptor.onErrorRequest(engineSession, ErrorType.UNKNOWN, "http://failed.random")) + .thenReturn(errorResponse2) + webViewClient.onReceivedError( + engineSession.webView, + WebViewClient.ERROR_UNKNOWN, + null, + "http://failed.random", + ) + verify(webView).loadUrl("about:fail2") + } + + @Test + fun `WebViewClient calls interceptor from new onReceivedError API`() { + val engineSession = spy(SystemEngineSession(testContext)) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + doNothing().`when`(engineSession).initSettings() + + val requestInterceptor: RequestInterceptor = mock() + val webViewClient = engineSession.webView.webViewClient + val webRequest: WebResourceRequest = mock() + val webError: WebResourceError = mock() + val url: Uri = mock() + + webViewClient.onReceivedError(engineSession.webView, webRequest, webError) + verifyNoInteractions(requestInterceptor) + + engineView.render(engineSession) + webViewClient.onReceivedError(engineSession.webView, webRequest, webError) + verifyNoInteractions(requestInterceptor) + + whenever(webError.errorCode).thenReturn(WebViewClient.ERROR_UNKNOWN) + whenever(webRequest.url).thenReturn(url) + whenever(url.toString()).thenReturn("http://failed.random") + engineSession.settings.requestInterceptor = requestInterceptor + webViewClient.onReceivedError(engineSession.webView, webRequest, webError) + verify(requestInterceptor, never()).onErrorRequest(engineSession, ErrorType.UNKNOWN, "http://failed.random") + + whenever(webRequest.isForMainFrame).thenReturn(true) + webViewClient.onReceivedError(engineSession.webView, webRequest, webError) + verify(requestInterceptor).onErrorRequest(engineSession, ErrorType.UNKNOWN, "http://failed.random") + + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.webView = webView + val errorResponse = RequestInterceptor.ErrorResponse("about:fail") + webViewClient.onReceivedError(engineSession.webView, webRequest, webError) + verify(webView, never()).loadUrl(ArgumentMatchers.anyString()) + + whenever(requestInterceptor.onErrorRequest(engineSession, ErrorType.UNKNOWN, "http://failed.random")) + .thenReturn(errorResponse) + webViewClient.onReceivedError(engineSession.webView, webRequest, webError) + verify(webView).loadUrl("about:fail") + + val errorResponse2 = RequestInterceptor.ErrorResponse("about:fail2") + webViewClient.onReceivedError(engineSession.webView, webRequest, webError) + verify(webView, never()).loadUrl("about:fail2") + + whenever(requestInterceptor.onErrorRequest(engineSession, ErrorType.UNKNOWN, "http://failed.random")) + .thenReturn(errorResponse2) + webViewClient.onReceivedError(engineSession.webView, webRequest, webError) + verify(webView).loadUrl("about:fail2") + } + + @Test + fun `WebViewClient calls interceptor when onReceivedSslError`() { + val engineSession = spy(SystemEngineSession(testContext)) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + doNothing().`when`(engineSession).initSettings() + + val requestInterceptor: RequestInterceptor = mock() + val webViewClient = engineSession.webView.webViewClient + val handler: SslErrorHandler = mock() + val error: SslError = mock() + + webViewClient.onReceivedSslError(engineSession.webView, handler, error) + verifyNoInteractions(requestInterceptor) + + engineView.render(engineSession) + webViewClient.onReceivedSslError(engineSession.webView, handler, error) + verifyNoInteractions(requestInterceptor) + + whenever(error.primaryError).thenReturn(SslError.SSL_EXPIRED) + whenever(error.url).thenReturn("http://failed.random") + engineSession.settings.requestInterceptor = requestInterceptor + webViewClient.onReceivedSslError(engineSession.webView, handler, error) + verify(requestInterceptor).onErrorRequest(engineSession, ErrorType.ERROR_SECURITY_SSL, "http://failed.random") + verify(handler, times(3)).cancel() + + val webView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(webView.settings).thenReturn(settings) + + engineSession.webView = webView + val errorResponse = RequestInterceptor.ErrorResponse("about:fail") + webViewClient.onReceivedSslError(engineSession.webView, handler, error) + verify(webView, never()).loadUrl(ArgumentMatchers.anyString()) + + whenever( + requestInterceptor.onErrorRequest( + engineSession, + ErrorType.ERROR_SECURITY_SSL, + "http://failed.random", + ), + ).thenReturn(errorResponse) + webViewClient.onReceivedSslError(engineSession.webView, handler, error) + verify(webView).loadUrl("about:fail") + + val errorResponse2 = RequestInterceptor.ErrorResponse("about:fail2") + webViewClient.onReceivedSslError(engineSession.webView, handler, error) + verify(webView, never()).loadUrl("about:fail2") + + whenever(requestInterceptor.onErrorRequest(engineSession, ErrorType.ERROR_SECURITY_SSL, "http://failed.random")) + .thenReturn(errorResponse2) + webViewClient.onReceivedSslError(engineSession.webView, handler, error) + verify(webView).loadUrl("about:fail2") + + whenever(requestInterceptor.onErrorRequest(engineSession, ErrorType.ERROR_SECURITY_SSL, "http://failed.random")) + .thenReturn(RequestInterceptor.ErrorResponse("http://failed.random")) + webViewClient.onReceivedSslError(engineSession.webView, handler, error) + verify(webView).loadUrl("http://failed.random") + } + + @Test + fun `WebViewClient blocks WebFonts`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val webViewClient = engineSession.webView.webViewClient + val webFontRequest = mock<WebResourceRequest>() + whenever(webFontRequest.url).thenReturn(Uri.parse("/fonts/test.woff")) + assertNull(webViewClient.shouldInterceptRequest(engineSession.webView, webFontRequest)) + + engineView.render(engineSession) + assertNull(webViewClient.shouldInterceptRequest(engineSession.webView, webFontRequest)) + + engineSession.settings.webFontsEnabled = false + + val request = mock<WebResourceRequest>() + whenever(request.url).thenReturn(Uri.parse("http://mozilla.org")) + assertNull(webViewClient.shouldInterceptRequest(engineSession.webView, request)) + + val response = webViewClient.shouldInterceptRequest(engineSession.webView, webFontRequest) + assertNotNull(response) + assertNull(response!!.data) + assertNull(response.encoding) + assertNull(response.mimeType) + } + + @Test + fun `FindListener notifies observers`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + var observerNotified = false + + engineSession.register( + object : EngineSession.Observer { + override fun onFindResult(activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Boolean) { + assertEquals(0, activeMatchOrdinal) + assertEquals(1, numberOfMatches) + assertTrue(isDoneCounting) + observerNotified = true + } + }, + ) + + val listener = engineView.createFindListener() + listener.onFindResultReceived(0, 1, true) + assertTrue(observerNotified) + } + + @Test + fun `lifecycle methods are invoked`() { + val mockWebView = mock<WebView>() + val settings = mock<WebSettings>() + whenever(mockWebView.settings).thenReturn(settings) + + val engineSession1 = SystemEngineSession(testContext) + val engineSession2 = SystemEngineSession(testContext) + + val engineView = SystemEngineView(testContext) + engineView.onPause() + engineView.onResume() + engineView.onDestroy() + + engineSession1.webView = mockWebView + engineView.render(engineSession1) + engineView.onDestroy() + + engineView.render(engineSession2) + assertNotNull(engineSession2.webView.parent) + + engineView.onDestroy() + assertNull(engineSession2.webView.parent) + + engineView.render(engineSession1) + engineView.onPause() + verify(mockWebView, times(1)).onPause() + verify(mockWebView, times(1)).pauseTimers() + + engineView.onResume() + verify(mockWebView, times(1)).onResume() + verify(mockWebView, times(1)).resumeTimers() + + engineView.onDestroy() + } + + @Test + fun `showCustomView notifies fullscreen mode observers and execs callback`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val observer: EngineSession.Observer = mock() + engineSession.register(observer) + + val view = mock<View>() + val customViewCallback = mock<WebChromeClient.CustomViewCallback>() + engineSession.webView.webChromeClient!!.onShowCustomView(view, customViewCallback) + + verify(observer).onFullScreenChange(true) + } + + @Test + fun `addFullScreenView execs callback and removeView`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val view = View(ApplicationProvider.getApplicationContext()) + val customViewCallback = mock<WebChromeClient.CustomViewCallback>() + + assertNull(engineSession.fullScreenCallback) + + engineSession.webView.webChromeClient!!.onShowCustomView(view, customViewCallback) + + assertNotNull(engineSession.fullScreenCallback) + assertEquals(customViewCallback, engineSession.fullScreenCallback) + assertEquals("mozac_system_engine_fullscreen", view.tag) + + engineSession.webView.webChromeClient!!.onHideCustomView() + assertEquals(View.VISIBLE, engineSession.webView.visibility) + } + + @Test + fun `addFullScreenView with no matching webView`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val view = View(ApplicationProvider.getApplicationContext()) + val customViewCallback = mock<WebChromeClient.CustomViewCallback>() + + engineSession.webView.tag = "not_webview" + engineSession.webView.webChromeClient!!.onShowCustomView(view, customViewCallback) + + assertNotEquals(View.INVISIBLE, engineSession.webView.visibility) + } + + @Test + fun `removeFullScreenView with no matching views`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + val view = View(ApplicationProvider.getApplicationContext()) + val customViewCallback = mock<WebChromeClient.CustomViewCallback>() + + // When the fullscreen view isn't available + engineSession.webView.webChromeClient!!.onShowCustomView(view, customViewCallback) + engineView.findViewWithTag<View>("mozac_system_engine_fullscreen").tag = "not_fullscreen" + + engineSession.webView.webChromeClient!!.onHideCustomView() + + assertNotNull(engineSession.fullScreenCallback) + verify(engineSession.fullScreenCallback, never())?.onCustomViewHidden() + assertEquals(View.INVISIBLE, engineSession.webView.visibility) + + // When fullscreen view is available, but WebView isn't. + engineView.findViewWithTag<View>("not_fullscreen").tag = "mozac_system_engine_fullscreen" + engineSession.webView.tag = "not_webView" + + engineSession.webView.webChromeClient!!.onHideCustomView() + + assertEquals(View.INVISIBLE, engineSession.webView.visibility) + } + + @Test + fun `fullscreenCallback is null`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + engineSession.webView.webChromeClient!!.onHideCustomView() + assertNull(engineSession.fullScreenCallback) + } + + @Test + fun `onPageFinished handles invalid URL`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + var observedUrl = "" + var observedLoadingState = true + var observedSecurityChange: Triple<Boolean, String?, String?> = Triple(false, null, null) + engineSession.register( + object : EngineSession.Observer { + override fun onLoadingStateChange(loading: Boolean) { observedLoadingState = loading } + override fun onLocationChange(url: String, hasUserGesture: Boolean) { observedUrl = url } + override fun onSecurityChange(secure: Boolean, host: String?, issuer: String?) { + observedSecurityChange = Triple(secure, host, issuer) + } + }, + ) + + // We need a certificate to trigger parsing the potentially invalid URL for + // the host parameter in onSecurityChange + val view = mock<WebView>() + val certificate = mock<SslCertificate>() + val dName = mock<SslCertificate.DName>() + doReturn("testCA").`when`(dName).oName + doReturn(dName).`when`(certificate).issuedBy + doReturn(certificate).`when`(view).certificate + + engineSession.webView.webViewClient.onPageFinished(view, "invalid:") + assertEquals("invalid:", observedUrl) + assertFalse(observedLoadingState) + assertEquals(Triple(true, null, "testCA"), observedSecurityChange) + } + + @Test + fun `URL matcher categories can be changed`() { + SystemEngineView.URL_MATCHER = null + val resources = testContext.resources + + var urlMatcher = SystemEngineView.getOrCreateUrlMatcher( + resources, + TrackingProtectionPolicy.select( + arrayOf( + TrackingCategory.AD, + TrackingCategory.ANALYTICS, + ), + ), + ) + assertEquals(setOf(UrlMatcher.ADVERTISING, UrlMatcher.ANALYTICS), urlMatcher.enabledCategories) + + urlMatcher = SystemEngineView.getOrCreateUrlMatcher( + resources, + TrackingProtectionPolicy.select( + arrayOf( + TrackingCategory.AD, + TrackingCategory.SOCIAL, + ), + ), + ) + assertEquals(setOf(UrlMatcher.ADVERTISING, UrlMatcher.SOCIAL), urlMatcher.enabledCategories) + } + + @Test + fun `URL matcher supports compounded categories`() { + val recommendedPolicy = TrackingProtectionPolicy.recommended() + val strictPolicy = TrackingProtectionPolicy.strict() + val resources = testContext.resources + val recommendedCategories = setOf( + UrlMatcher.ADVERTISING, + UrlMatcher.ANALYTICS, + UrlMatcher.SOCIAL, + UrlMatcher.FINGERPRINTING, + UrlMatcher.CRYPTOMINING, + ) + val strictCategories = setOf( + UrlMatcher.ADVERTISING, + UrlMatcher.ANALYTICS, + UrlMatcher.SOCIAL, + UrlMatcher.FINGERPRINTING, + UrlMatcher.CRYPTOMINING, + ) + + var urlMatcher = SystemEngineView.getOrCreateUrlMatcher(resources, recommendedPolicy) + + assertEquals(recommendedCategories, urlMatcher.enabledCategories) + + urlMatcher = SystemEngineView.getOrCreateUrlMatcher(resources, strictPolicy) + + assertEquals(strictCategories, urlMatcher.enabledCategories) + } + + @Test + fun `permission requests are forwarded to observers`() { + val permissionRequest: android.webkit.PermissionRequest = mock() + whenever(permissionRequest.resources).thenReturn(emptyArray()) + whenever(permissionRequest.origin).thenReturn(Uri.parse("https://mozilla.org")) + + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + var observedPermissionRequest: PermissionRequest? = null + var cancelledPermissionRequest: PermissionRequest? = null + engineSession.register( + object : EngineSession.Observer { + override fun onContentPermissionRequest(permissionRequest: PermissionRequest) { + observedPermissionRequest = permissionRequest + } + + override fun onCancelContentPermissionRequest(permissionRequest: PermissionRequest) { + cancelledPermissionRequest = permissionRequest + } + }, + ) + + engineSession.webView.webChromeClient!!.onPermissionRequest(permissionRequest) + assertNotNull(observedPermissionRequest) + + engineSession.webView.webChromeClient!!.onPermissionRequestCanceled(permissionRequest) + assertNotNull(cancelledPermissionRequest) + } + + @Test + fun `window requests are forwarded to observers`() { + val permissionRequest: android.webkit.PermissionRequest = mock() + whenever(permissionRequest.resources).thenReturn(emptyArray()) + whenever(permissionRequest.origin).thenReturn(Uri.parse("https://mozilla.org")) + + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + engineView.render(engineSession) + + var createWindowRequest: WindowRequest? = null + var closeWindowRequest: WindowRequest? = null + engineSession.register( + object : EngineSession.Observer { + override fun onWindowRequest(windowRequest: WindowRequest) { + if (windowRequest.type == WindowRequest.Type.OPEN) { + createWindowRequest = windowRequest + } else { + closeWindowRequest = windowRequest + } + } + }, + ) + + engineSession.webView.webChromeClient!!.onCreateWindow(mock(), false, false, null) + assertNotNull(createWindowRequest) + assertNull(closeWindowRequest) + + engineSession.webView.webChromeClient!!.onCloseWindow(mock()) + assertNotNull(closeWindowRequest) + } + + @Test + fun `Calling onShowFileChooser must provide a FilePicker PromptRequest`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var onSingleFileSelectedWasCalled = false + var onMultipleFilesSelectedWasCalled = false + var onDismissWasCalled = false + var request: PromptRequest? = null + + val callback = ValueCallback<Array<Uri>> { + if (it == null) { + onDismissWasCalled = true + } else { + if (it.size == 1) { + onSingleFileSelectedWasCalled = true + } else { + onMultipleFilesSelectedWasCalled = true + } + } + } + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + + engineView.render(engineSession) + + val mockFileChooserParams = mock<WebChromeClient.FileChooserParams>() + + doReturn(MODE_OPEN_MULTIPLE).`when`(mockFileChooserParams).mode + + engineSession.webView.webChromeClient!!.onShowFileChooser(null, callback, mockFileChooserParams) + + val filePickerRequest = request as PromptRequest.File + assertTrue(request is PromptRequest.File) + + filePickerRequest.onSingleFileSelected(mock(), mock()) + assertTrue(onSingleFileSelectedWasCalled) + + filePickerRequest.onMultipleFilesSelected(mock(), arrayOf(mock(), mock())) + assertTrue(onMultipleFilesSelectedWasCalled) + + filePickerRequest.onDismiss() + assertTrue(onDismissWasCalled) + + assertTrue(filePickerRequest.mimeTypes.isEmpty()) + assertTrue(filePickerRequest.isMultipleFilesSelection) + + doReturn(arrayOf("")).`when`(mockFileChooserParams).acceptTypes + engineSession.webView.webChromeClient!!.onShowFileChooser(null, callback, mockFileChooserParams) + assertTrue(filePickerRequest.mimeTypes.isEmpty()) + } + + @Test + fun `canScrollVerticallyDown can be called without session`() { + val engineView = SystemEngineView(testContext) + assertFalse(engineView.canScrollVerticallyDown()) + + engineView.render(SystemEngineSession(testContext)) + assertFalse(engineView.canScrollVerticallyDown()) + } + + @Test + fun `onLongClick can be called without session`() { + val engineView = SystemEngineView(testContext) + assertFalse(engineView.onLongClick(null)) + + engineView.render(SystemEngineSession(testContext)) + assertFalse(engineView.onLongClick(null)) + } + + @Test + fun `Calling onJsAlert must provide an Alert PromptRequest`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + + engineView.render(engineSession) + + val mockJSResult = mock<JsResult>() + + engineSession.webView.webChromeClient!!.onJsAlert(mock(), "http://www.mozilla.org", "message", mockJSResult) + + val alertRequest = request as PromptRequest.Alert + assertTrue(request is PromptRequest.Alert) + + assertTrue(alertRequest.title.contains("mozilla.org")) + assertEquals(alertRequest.message, "message") + + alertRequest.onConfirm(true) + verify(mockJSResult).confirm() + + alertRequest.onDismiss() + verify(mockJSResult).cancel() + } + + @Test + fun `calling onJsPrompt must provide a TextPrompt PromptRequest`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + + engineView.render(engineSession) + + val mockJSPromptResult = mock<JsPromptResult>() + + engineSession.webView.webChromeClient!!.onJsPrompt( + mock(), + "http://www.mozilla.org", + "message", + "defaultValue", + mockJSPromptResult, + ) + + val textPromptRequest = request as PromptRequest.TextPrompt + assertTrue(request is PromptRequest.TextPrompt) + + assertTrue(textPromptRequest.title.contains("mozilla.org")) + assertEquals(textPromptRequest.hasShownManyDialogs, false) + assertEquals(textPromptRequest.inputLabel, "message") + assertEquals(textPromptRequest.inputValue, "defaultValue") + + textPromptRequest.onConfirm(true, "value") + verify(mockJSPromptResult).confirm("value") + + textPromptRequest.onDismiss() + verify(mockJSPromptResult).cancel() + + textPromptRequest.onConfirm(true, "value") + } + + @Test + fun `calling onJsPrompt with a null session must not provide a TextPrompt PromptRequest`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + + engineView.render(engineSession) + + val mockJSPromptResult = mock<JsPromptResult>() + engineView.session = null + + val wasTheDialogHandled = engineSession.webView.webChromeClient!!.onJsPrompt( + mock(), + "http://www.mozilla.org", + "message", + "defaultValue", + mockJSPromptResult, + ) + + assertTrue(wasTheDialogHandled) + assertNull(request) + verify(mockJSPromptResult).cancel() + } + + @Test + fun `calling onJsConfirm must provide a Confirm PromptRequest`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + + engineView.render(engineSession) + + val mockJSPromptResult = mock<JsResult>() + + engineSession.webView.webChromeClient!!.onJsConfirm( + mock(), + "http://www.mozilla.org", + "message", + mockJSPromptResult, + ) + + val confirmPromptRequest = request as PromptRequest.Confirm + assertTrue(request is PromptRequest.Confirm) + + assertTrue(confirmPromptRequest.title.contains("mozilla.org")) + assertEquals(confirmPromptRequest.hasShownManyDialogs, false) + assertEquals(confirmPromptRequest.message, "message") + + confirmPromptRequest.onConfirmPositiveButton(true) + verify(mockJSPromptResult).confirm() + + confirmPromptRequest.onDismiss() + verify(mockJSPromptResult).cancel() + + confirmPromptRequest.onConfirmNegativeButton(true) + verify(mockJSPromptResult, times(2)).cancel() + } + + @Test + @Config(sdk = [Build.VERSION_CODES.N]) + fun captureThumbnailOnPreO() { + val activity = Robolectric.buildActivity(Activity::class.java).setup().get() + val engineView = SystemEngineView(activity) + val webView = mock<WebView>() + + whenever(webView.width).thenReturn(100) + whenever(webView.height).thenReturn(200) + + engineView.session = mock() + + whenever(engineView.session!!.webView).thenReturn(webView) + + var thumbnail: Bitmap? = null + + engineView.captureThumbnail { + thumbnail = it + } + verify(webView).draw(any()) + assertNotNull(thumbnail) + + engineView.session = null + engineView.captureThumbnail { + thumbnail = it + } + + assertNull(thumbnail) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.O], shadows = [PixelCopyShadow::class]) + fun captureThumbnailOnPostO() { + val activity = Robolectric.buildActivity(Activity::class.java).setup().get() + val engineView = SystemEngineView(activity) + val webView = mock<WebView>() + whenever(webView.width).thenReturn(100) + whenever(webView.height).thenReturn(200) + + var thumbnail: Bitmap? = null + + engineView.session = null + engineView.captureThumbnail { + thumbnail = it + } + assertNull(thumbnail) + + engineView.session = mock() + whenever(engineView.session!!.webView).thenReturn(webView) + + PixelCopyShadow.copyResult = PixelCopy.ERROR_UNKNOWN + engineView.captureThumbnail { + thumbnail = it + } + assertNull(thumbnail) + + PixelCopyShadow.copyResult = PixelCopy.SUCCESS + engineView.captureThumbnail { + thumbnail = it + } + assertNotNull(thumbnail) + } + + @Test + fun `calling onReceivedHttpAuthRequest must provide an Authentication PromptRequest`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + engineView.render(engineSession) + + val authHandler = mock<HttpAuthHandler>() + val host = "mozilla.org" + val realm = "realm" + + engineSession.webView.webViewClient.onReceivedHttpAuthRequest(engineSession.webView, authHandler, host, realm) + + val authRequest = request as PromptRequest.Authentication + assertTrue(request is PromptRequest.Authentication) + + assertEquals(authRequest.title, "") + + authRequest.onConfirm("u", "p") + verify(authHandler).proceed("u", "p") + + authRequest.onDismiss() + verify(authHandler).cancel() + } + + @Test + fun `calling onReceivedHttpAuthRequest with a null session must not provide an Authentication PromptRequest`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + engineView.render(engineSession) + + val authHandler = mock<HttpAuthHandler>() + engineView.session = null + + engineSession.webView.webViewClient.onReceivedHttpAuthRequest(mock(), authHandler, "mozilla.org", "realm") + + assertNull(request) + verify(authHandler).cancel() + } + + @Test + fun `onReceivedHttpAuthRequest correctly handles realm`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + engineView.render(engineSession) + + val webView = engineSession.webView + val authHandler = mock<HttpAuthHandler>() + val host = "mozilla.org" + + val longRealm = "Login with a user name of httpwatch and a different password each time" + webView.webViewClient.onReceivedHttpAuthRequest(webView, authHandler, host, longRealm) + assertTrue((request as PromptRequest.Authentication).message.endsWith("differen…”")) + + val emptyRealm = "" + webView.webViewClient.onReceivedHttpAuthRequest(webView, authHandler, host, emptyRealm) + val noRealmMessageTail = testContext.getString(R.string.mozac_browser_engine_system_auth_no_realm_message).let { + it.substring(it.length - 10) + } + assertTrue((request as PromptRequest.Authentication).message.endsWith(noRealmMessageTail)) + } + + @Test + @Config(sdk = [Build.VERSION_CODES.N]) + @Suppress("Deprecation") + fun `onReceivedHttpAuthRequest takes credentials from WebView`() { + val engineSession = SystemEngineSession(testContext) + val engineView = SystemEngineView(testContext) + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + + engineSession.webView = spy(engineSession.webView) + engineView.render(engineSession) + + // use captor as getWebViewClient() is available only from Oreo + // and this test runs on N to not use WebViewDatabase + val captor = argumentCaptor<WebViewClient>() + verify(engineSession.webView).webViewClient = captor.capture() + val webViewClient = captor.value + + val host = "mozilla.org" + val realm = "realm" + val userName = "user123" + val password = "pass@123" + + val validCredentials = arrayOf(userName, password) + whenever(engineSession.webView.getHttpAuthUsernamePassword(host, realm)).thenReturn(validCredentials) + webViewClient.onReceivedHttpAuthRequest(engineSession.webView, mock(), host, realm) + assertEquals((request as PromptRequest.Authentication).userName, userName) + assertEquals((request as PromptRequest.Authentication).password, password) + + val nullCredentials = null + whenever(engineSession.webView.getHttpAuthUsernamePassword(host, realm)).thenReturn(nullCredentials) + webViewClient.onReceivedHttpAuthRequest(engineSession.webView, mock(), host, realm) + assertEquals((request as PromptRequest.Authentication).userName, "") + assertEquals((request as PromptRequest.Authentication).password, "") + + val credentialsWithNulls = arrayOf<String?>(null, null) + whenever(engineSession.webView.getHttpAuthUsernamePassword(host, realm)).thenReturn(credentialsWithNulls) + webViewClient.onReceivedHttpAuthRequest(engineSession.webView, mock(), host, realm) + assertEquals((request as PromptRequest.Authentication).userName, "") + assertEquals((request as PromptRequest.Authentication).password, "") + } + + @Test + @Config(sdk = [Build.VERSION_CODES.O]) + fun `onReceivedHttpAuthRequest uses WebViewDatabase on Oreo+`() { + val engineSession = spy(SystemEngineSession(testContext)) + val engineView = SystemEngineView(testContext) + var request: PromptRequest? = null + + engineSession.register( + object : EngineSession.Observer { + override fun onPromptRequest(promptRequest: PromptRequest) { + request = promptRequest + } + }, + ) + engineView.render(engineSession) + + val host = "mozilla.org" + val realm = "realm" + val userName = "userFromDB" + val password = "pass@123FromDB" + val webViewDatabase = mock<WebViewDatabase>() + whenever(webViewDatabase.getHttpAuthUsernamePassword(host, realm)).thenReturn(arrayOf(userName, password)) + whenever(engineSession.webViewDatabase(testContext)).thenReturn(webViewDatabase) + + engineSession.webView.webViewClient.onReceivedHttpAuthRequest(engineSession.webView, mock(), host, realm) + + val authRequest = request as PromptRequest.Authentication + assertEquals(authRequest.userName, userName) + assertEquals(authRequest.password, password) + } + + @Test + fun `GIVEN SystemEngineView WHEN getInputResultDetail is called THEN it returns the instance from webView`() { + val engineView = SystemEngineView(testContext) + val engineSession = SystemEngineSession(testContext) + val webView = spy(NestedWebView(testContext)) + engineSession.webView = webView + engineView.render(engineSession) + val inputResult = InputResultDetail.newInstance() + doReturn(inputResult).`when`(webView).inputResultDetail + + assertSame(inputResult, engineView.getInputResultDetail()) + } + + @Test + fun `GIVEN SystemEngineView WHEN getInputResultDetail is called THEN it returns a new default instance if not available from webView`() { + val engineView = spy(SystemEngineView(testContext)) + + val result = engineView.getInputResultDetail() + + assertNotNull(result) + assertTrue(result.isTouchHandlingUnknown()) + assertFalse(result.canScrollToLeft()) + assertFalse(result.canScrollToTop()) + assertFalse(result.canScrollToRight()) + assertFalse(result.canScrollToBottom()) + assertFalse(result.canOverscrollLeft()) + assertFalse(result.canOverscrollTop()) + assertFalse(result.canOverscrollRight()) + assertFalse(result.canOverscrollBottom()) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/ReversibleStringTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/ReversibleStringTest.kt new file mode 100644 index 0000000000..e7c417be45 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/ReversibleStringTest.kt @@ -0,0 +1,122 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.matcher + +import org.junit.Assert.assertEquals +import org.junit.Test + +class ReversibleStringTest { + + @Test(expected = StringIndexOutOfBoundsException::class) + @Throws(StringIndexOutOfBoundsException::class) + fun outOfBounds() { + val fullStringRaw = "a" + val fullString = ReversibleString.create(fullStringRaw) + fullString.charAt(1) + } + + @Test(expected = StringIndexOutOfBoundsException::class) + @Throws(StringIndexOutOfBoundsException::class) + fun outOfBoundsAfterSubstring() { + val fullStringRaw = "abcd" + val fullString = ReversibleString.create(fullStringRaw) + + val substring = fullString.substring(3) + substring.charAt(1) + } + + @Test(expected = StringIndexOutOfBoundsException::class) + @Throws(StringIndexOutOfBoundsException::class) + fun outOfBoundsSubstring() { + val fullStringRaw = "abcd" + val fullString = ReversibleString.create(fullStringRaw) + fullString.substring(5) + } + + @Test(expected = StringIndexOutOfBoundsException::class) + @Throws(StringIndexOutOfBoundsException::class) + fun outOfBoundsSubstringNegative() { + val fullStringRaw = "abcd" + val fullString = ReversibleString.create(fullStringRaw) + fullString.substring(-1) + } + + @Test(expected = StringIndexOutOfBoundsException::class) + @Throws(StringIndexOutOfBoundsException::class) + fun outOfBoundsAfterSubstringEmpty() { + val fullStringRaw = "abcd" + val fullString = ReversibleString.create(fullStringRaw) + + val substring = fullString.substring(4) + substring.charAt(0) + } + + @Test + fun substringLength() { + val fullStringRaw = "a" + val fullString = ReversibleString.create(fullStringRaw) + + assertEquals("Length must match input string length", fullStringRaw.length, fullString.length()) + + val sameString = fullString.substring(0) + assertEquals("substring(0) should equal input String", fullStringRaw.length, sameString.length()) + assertEquals("substring(0) should equal input String", fullStringRaw[0], sameString.charAt(0)) + + val emptyString = fullString.substring(1) + assertEquals("Empty substring should be empty", 0, emptyString.length()) + } + + @Test + fun forwardString() { + val fullStringRaw = "abcd" + val fullString = ReversibleString.create(fullStringRaw) + + assertEquals("Length must match input string length", fullStringRaw.length, fullString.length()) + + for (i in 0 until fullStringRaw.length) { + assertEquals("Character doesn't match input string character", fullStringRaw[i], fullString.charAt(i)) + } + + val substringRaw = fullStringRaw.substring(2) + val substring = fullString.substring(2) + + for (i in 0 until substringRaw.length) { + assertEquals("Character doesn't match input string character", substringRaw[i], substring.charAt(i)) + } + } + + @Test + fun reverseString() { + val fullUnreversedStringRaw = "abcd" + val fullStringRaw = StringBuffer(fullUnreversedStringRaw).reverse().toString() + val fullString = ReversibleString.create(fullUnreversedStringRaw).reverse() + + assertEquals("Length must match input string length", fullStringRaw.length, fullString.length()) + + for (i in 0 until fullStringRaw.length) { + assertEquals("Character doesn't match input string character", fullStringRaw[i], fullString.charAt(i)) + } + + val substringRaw = fullStringRaw.substring(2) + val substring = fullString.substring(2) + + for (i in 0 until substringRaw.length) { + assertEquals("Character doesn't match input string character", substringRaw[i], substring.charAt(i)) + } + } + + @Test + fun reverseReversedString() { + val fullUnreversedStringRaw = "abcd" + val fullStringRaw = StringBuffer(fullUnreversedStringRaw).toString() + val fullString = ReversibleString.create(fullUnreversedStringRaw).reverse().reverse() + + assertEquals("Length must match input string length", fullStringRaw.length, fullString.length()) + + for (i in 0 until fullStringRaw.length) { + assertEquals("Character doesn't match input string character", fullStringRaw[i], fullString.charAt(i)) + } + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/SafelistTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/SafelistTest.kt new file mode 100644 index 0000000000..0fdba4f16f --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/SafelistTest.kt @@ -0,0 +1,126 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.matcher + +import android.util.JsonReader +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Assert.fail +import org.junit.Test +import org.junit.runner.RunWith +import java.io.StringReader + +@RunWith(AndroidJUnit4::class) +class SafelistTest { + + /** + * Test setup: + * mozilla.org: allow foo.com + * foo.mozilla.org: additionally allow bar.com + * + * Test: + * mozilla.org can only use foo.com, but foo.mozilla.org can use both foo.com and bar.com + */ + @Test + fun safelist() { + val mozillaOrg = "mozilla.org" + val fooMozillaOrg = "foo.mozilla.org" + val fooCom = "foo.com" + val barCom = "bar.com" + + val fooComTrie = Trie.createRootNode() + fooComTrie.put(fooCom.reverse()) + + val barComTrie = Trie.createRootNode() + barComTrie.put(barCom.reverse()) + + val safelist = Safelist() + safelist.put(mozillaOrg.reverse(), fooComTrie) + safelist.put(fooMozillaOrg.reverse(), barComTrie) + + assertTrue(safelist.contains("http://$mozillaOrg", "http://$fooCom")) + assertFalse(safelist.contains("http://$mozillaOrg", "http://$barCom")) + assertTrue(safelist.contains("http://hello.$mozillaOrg", "http://$fooCom")) + assertFalse(safelist.contains("http://hello.$mozillaOrg", "http://$barCom")) + assertTrue(safelist.contains("http://$mozillaOrg/somewhere", "http://$fooCom/somewhereElse/bla/bla")) + assertFalse(safelist.contains("http://$mozillaOrg/another/page.html?u=a", "http://$barCom/hello")) + + assertTrue(safelist.contains("http://$fooMozillaOrg", "http://$fooCom")) + assertTrue(safelist.contains("http://$fooMozillaOrg", "http://$barCom")) + assertTrue(safelist.contains("http://hello.$fooMozillaOrg", "http://$fooCom")) + assertTrue(safelist.contains("http://hello.$fooMozillaOrg", "http://$barCom")) + assertTrue(safelist.contains("http://$fooMozillaOrg/somewhere", "http://$fooCom/somewhereElse/bla/bla")) + assertTrue(safelist.contains("http://$fooMozillaOrg/another/page.html?u=a", "http://$barCom/hello")) + + // Test some invalid inputs + assertFalse(safelist.contains("http://$barCom", "http://$barCom")) + assertFalse(safelist.contains("http://$barCom", "http://$mozillaOrg")) + + // Check we don't safelist resources for data: + assertFalse(safelist.contains("data:text/html;stuff", "http://$fooCom/somewhereElse/bla/bla")) + } + + @Test + fun safelistTrie() { + val safelist = Trie.createRootNode() + safelist.put("abc") + + val trie = SafelistTrie.createRootNode() + trie.putSafelist("def", safelist) + Assert.assertNull(trie.findNode("abc")) + + val foundSafelist = trie.findNode("def") as SafelistTrie + Assert.assertNotNull(foundSafelist) + Assert.assertNotNull(foundSafelist.safelist?.findNode("abc")) + + try { + trie.putSafelist("def", safelist) + fail("Expected IllegalStateException") + } catch (e: IllegalStateException) { } + } + + val SAFE_LIST_JSON = """{ + "Host1": { + "properties": [ + "host1.com", + "host1.de" + ], + "resources": [ + "host1ads.com", + "host1ads.de" + ] + }, + "Host2": { + "properties": [ + "host2.com", + "host2.de" + ], + "resources": [ + "host2ads.com", + "host2ads.de" + ] + } + }""" + + @Test + fun fromJson() { + val safelist = Safelist.fromJson(JsonReader(StringReader(SAFE_LIST_JSON))) + + assertTrue(safelist.contains("http://host1.com", "http://host1ads.com")) + assertTrue(safelist.contains("https://host1.com", "https://host1ads.de")) + assertTrue(safelist.contains("javascript://host1.de", "javascript://host1ads.com")) + assertTrue(safelist.contains("file://host1.de", "file://host1ads.de")) + + assertTrue(safelist.contains("http://host2.com", "http://host2ads.com")) + assertTrue(safelist.contains("about://host2.com", "about://host2ads.de")) + assertTrue(safelist.contains("http://host2.de", "http://host2ads.com")) + assertTrue(safelist.contains("http://host2.de", "http://host2ads.de")) + + assertFalse(safelist.contains("data://host2.de", "data://host2ads.de")) + assertFalse(safelist.contains("foo://host2.de", "foo://host2ads.de")) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/TrieTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/TrieTest.kt new file mode 100644 index 0000000000..38b47761a5 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/TrieTest.kt @@ -0,0 +1,46 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.matcher + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class TrieTest { + + @Test + fun findNode() { + val trie = Trie.createRootNode() + + assertNull(trie.findNode("hello")) + val putNode = trie.put("hello") + val foundNode = trie.findNode("hello") + assertNotNull(putNode) + assertNotNull(foundNode) + assertEquals(putNode, foundNode) + + // Substring matching doesn't happen (except for subdomains) + assertNull(trie.findNode("hell")) + assertNull(trie.findNode("hellop")) + + // Ensure both old and new overlapping strings can still be found + trie.put("hellohello") + assertNotNull(trie.findNode("hello")) + assertNotNull(trie.findNode("hellohello")) + assertNull(trie.findNode("hell")) + assertNull(trie.findNode("hellop")) + + // Domain and subdomain can be found + trie.put("foo.com".reverse()) + assertNotNull(trie.findNode("foo.com".reverse())) + assertNotNull(trie.findNode("bar.foo.com".reverse())) + assertNull(trie.findNode("bar-foo.com".reverse())) + assertNull(trie.findNode("oo.com".reverse())) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/UrlMatcherTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/UrlMatcherTest.kt new file mode 100644 index 0000000000..e01572e130 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/matcher/UrlMatcherTest.kt @@ -0,0 +1,296 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.matcher + +import android.net.Uri +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.engine.system.matcher.UrlMatcher.Companion.ADVERTISING +import mozilla.components.browser.engine.system.matcher.UrlMatcher.Companion.ANALYTICS +import mozilla.components.browser.engine.system.matcher.UrlMatcher.Companion.CONTENT +import mozilla.components.browser.engine.system.matcher.UrlMatcher.Companion.SOCIAL +import mozilla.components.support.test.any +import mozilla.components.support.test.mock +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import java.io.StringReader +import java.util.HashMap + +@RunWith(AndroidJUnit4::class) +class UrlMatcherTest { + + @Test + fun basicMatching() { + val matcher = UrlMatcher(arrayOf("bcd.random")) + + assertTrue(matcher.matches("http://bcd.random/something", "http://mozilla.org").first) + assertTrue(matcher.matches("http://bcd.random", "http://mozilla.org").first) + assertTrue(matcher.matches("http://www.bcd.random", "http://mozilla.org").first) + assertTrue(matcher.matches("http://www.bcd.random/something", "http://mozilla.org").first) + assertTrue(matcher.matches("http://foobar.bcd.random", "http://mozilla.org").first) + assertTrue(matcher.matches("http://foobar.bcd.random/something", "http://mozilla.org").first) + + assertFalse(matcher.matches("http://other.random", "http://mozilla.org").first) + assertFalse(matcher.matches("http://other.random/something", "http://mozilla.org").first) + assertFalse(matcher.matches("http://www.other.random", "http://mozilla.org").first) + assertFalse(matcher.matches("http://www.other.random/something", "http://mozilla.org").first) + assertFalse(matcher.matches("http://bcd.specific", "http://mozilla.org").first) + assertFalse(matcher.matches("http://bcd.specific/something", "http://mozilla.org").first) + assertFalse(matcher.matches("http://www.bcd.specific", "http://mozilla.org").first) + assertFalse(matcher.matches("http://www.bcd.specific/something", "http://mozilla.org").first) + + assertFalse(matcher.matches("http://mozilla.org/resource", "data:text/html;stuff here").first) + assertTrue(matcher.matches("http://bcd.random/resource", "data:text/html;stuff here").first) + } + + /** + * Tests that category enabling/disabling works correctly. We test this by creating + * 4 categories, each with only one domain. We then iterate over all permutations of categories, + * and test that only the expected domains are actually blocked. + */ + @Test + fun enableDisableCategories() { + val categories = HashMap<String, Trie>() + val suppportedCategories = mutableSetOf<String>() + val enabledCategories = mutableSetOf<String>() + val categoryCount = 4 + + for (i in 0 until categoryCount) { + val trie = Trie.createRootNode() + trie.put("category$i.com".reverse()) + + val categoryName = "category$i" + categories[categoryName] = trie + enabledCategories.add(categoryName) + suppportedCategories.add(categoryName) + } + + val matcher = UrlMatcher(suppportedCategories, enabledCategories, categories) + + // We can test every permutation by iterating over every value of a 4-bit integer (each bit + // indicates whether a given category is enabled or disabled). + // N categories -> N bits == (2^N - 1) == '1111...' + // 4 categories -> 4 bits == 15 == 2^N-1 = '1111' + val allEnabledPattern = (1 shl categoryCount) - 1 + for (categoryPattern in 0..allEnabledPattern) { + // Ensure all the correct categories enabled + for (currentCategory in 0 until categoryCount) { + val currentBit = 1 shl currentCategory + val enabled = currentBit and categoryPattern == currentBit + matcher.setCategoryEnabled("category$currentCategory", enabled) + + // Make sure our category enabling code actually sets the correct + // values for a few known combinations (i.e. we're doing a test within the test) + if (categoryPattern == 0) { + assertFalse("All categories should be disabled for categorypattern==0", enabled) + } else if (categoryPattern == allEnabledPattern) { + assertTrue("All categories should be enabled for categorypattern=='111....'", enabled) + } else if (categoryPattern == Integer.parseInt("1100", 2)) { + if (currentCategory < 2) { + assertFalse("Categories 0/1 expected to be disabled", enabled) + } else { + assertTrue("Categories >= 2 expected to be enabled", enabled) + } + } + } + + for (currentCategory in 0 until categoryCount) { + val currentBit = 1 shl currentCategory + val enabled = currentBit and categoryPattern == currentBit + val url = "http://category$currentCategory.com" + assertEquals( + "Incorrect category matched for combo=$categoryPattern url=$url", + enabled, + matcher.matches(url, "http://www.mozilla.org").first, + ) + } + } + } + + val BLOCK_LIST = """{ + "license": "test-license", + "categories": { + "Advertising": [ + { + "AdTest1": { + "http://www.adtest1.com/": [ + "adtest1.com", + "adtest1.de" + ] + } + }, + { + "AdTest2": { + "http://www.adtest2.com/": [ + "adtest2.com" + ] + } + } + ], + "Analytics": [ + { + "AnalyticsTest": { + "http://analyticsTest1.com/": [ + "analyticsTest1.com", + "analyticsTest1.de" + ] + } + } + ], + "Content": [ + { + "ContentTest1": { + "http://contenttest1.com/": [ + "contenttest1.com" + ] + } + } + ], + "Social": [ + { + "SocialTest1": { + "http://www.socialtest1.com/": [ + "socialtest1.com", + "socialtest1.de" + ] + } + } + ], + "Legacy Disconnect": [ + { + "Ignored1": { + "http://www.ignored1.com/": [ + "ignored.de" + ] + } + } + ], + "Legacy Content": [ + { + "Ignored2": { + "http://www.ignored2.com/": [ + "ignored.de" + ] + } + } + ] + } + } + """ + + val SAFE_LIST = """{ + "SocialTest1": { + "properties": [ + "www.socialtest1.com" + ], + "resources": [ + "socialtest1.de" + ] + } + }""" + + @Test + fun createMatcher() { + val matcher = UrlMatcher.createMatcher( + StringReader(BLOCK_LIST), + StringReader(SAFE_LIST), + ) + + // Check returns correct category + val (matchesAds, categoryAds) = matcher.matches("http://adtest1.com", "http://www.adtest1.com") + + assertTrue(matchesAds) + assertEquals(categoryAds, ADVERTISING) + + val (matchesAds2, categoryAd2) = matcher.matches("http://adtest1.de", "http://www.adtest1.com") + + assertTrue(matchesAds2) + assertEquals(categoryAd2, ADVERTISING) + + val (matchesSocial, categorySocial) = matcher.matches( + "http://socialtest1.com/", + "http://www.socialtest1.com/", + ) + + assertTrue(matchesSocial) + assertEquals(categorySocial, SOCIAL) + + val (matchesContent, categoryContent) = matcher.matches( + "http://contenttest1.com/", + "http://www.contenttest1.com/", + ) + + assertTrue(matchesContent) + assertEquals(categoryContent, CONTENT) + + val (matchesAnalytics, categoryAnalytics) = matcher.matches( + "http://analyticsTest1.com/", + "http://www.analyticsTest1.com/", + ) + + assertTrue(matchesAnalytics) + assertEquals(categoryAnalytics, ANALYTICS) + + // Check that safe list worked + assertTrue(matcher.matches("http://socialtest1.com", "http://www.socialtest1.com").first) + assertFalse(matcher.matches("http://socialtest1.de", "http://www.socialtest1.com").first) + + // Check ignored categories + assertFalse(matcher.matches("http://ignored1.de", "http://www.ignored1.com").first) + assertFalse(matcher.matches("http://ignored2.de", "http://www.ignored2.com").first) + } + + @Test + fun isWebFont() { + assertFalse(UrlMatcher.isWebFont(mock())) + assertFalse(UrlMatcher.isWebFont(Uri.parse("mozilla.org"))) + assertTrue(UrlMatcher.isWebFont(Uri.parse("/fonts/test.woff2"))) + assertTrue(UrlMatcher.isWebFont(Uri.parse("/fonts/test.woff"))) + assertTrue(UrlMatcher.isWebFont(Uri.parse("/fonts/test.eot"))) + assertTrue(UrlMatcher.isWebFont(Uri.parse("/fonts/test.ttf"))) + assertTrue(UrlMatcher.isWebFont(Uri.parse("/fonts/test.otf"))) + } + + @Test + fun setCategoriesEnabled() { + val matcher = spy( + UrlMatcher.createMatcher( + StringReader(BLOCK_LIST), + StringReader(SAFE_LIST), + setOf("Advertising", "Analytics"), + ), + ) + + matcher.setCategoriesEnabled(setOf("Advertising", "Analytics")) + verify(matcher, never()).setCategoryEnabled(any(), anyBoolean()) + + matcher.setCategoriesEnabled(setOf("Advertising", "Analytics", "Content")) + verify(matcher).setCategoryEnabled("Advertising", true) + verify(matcher).setCategoryEnabled("Analytics", true) + verify(matcher).setCategoryEnabled("Content", true) + } + + @Test + fun webFontsNotBlockedByDefault() { + val matcher = UrlMatcher.createMatcher( + StringReader(BLOCK_LIST), + StringReader(SAFE_LIST), + setOf(UrlMatcher.ADVERTISING, UrlMatcher.ANALYTICS, UrlMatcher.SOCIAL, UrlMatcher.CONTENT), + ) + + assertFalse( + matcher.matches( + "http://mozilla.org/fonts/test.woff2", + "http://mozilla.org", + ).first, + ) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/permission/SystemPermissionRequestTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/permission/SystemPermissionRequestTest.kt new file mode 100644 index 0000000000..bed30887bb --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/permission/SystemPermissionRequestTest.kt @@ -0,0 +1,99 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.permission + +import android.net.Uri +import android.webkit.PermissionRequest +import android.webkit.PermissionRequest.RESOURCE_AUDIO_CAPTURE +import android.webkit.PermissionRequest.RESOURCE_PROTECTED_MEDIA_ID +import android.webkit.PermissionRequest.RESOURCE_VIDEO_CAPTURE +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.concept.engine.permission.Permission +import mozilla.components.support.test.eq +import mozilla.components.support.test.mock +import mozilla.components.support.test.whenever +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class SystemPermissionRequestTest { + + @Test + fun `uri is equal to native request origin`() { + val nativeRequest: PermissionRequest = mock() + whenever(nativeRequest.origin).thenReturn(Uri.parse("https://mozilla.org")) + whenever(nativeRequest.resources).thenReturn(emptyArray()) + val request = SystemPermissionRequest(nativeRequest) + assertEquals(request.uri, "https://mozilla.org") + } + + @Test + fun `resources are correctly mapped to permissions`() { + val nativeRequest: PermissionRequest = mock() + whenever(nativeRequest.origin).thenReturn(Uri.parse("https://mozilla.org")) + whenever(nativeRequest.resources).thenReturn( + arrayOf( + RESOURCE_AUDIO_CAPTURE, + RESOURCE_VIDEO_CAPTURE, + RESOURCE_PROTECTED_MEDIA_ID, + ), + ) + + val expected = listOf( + Permission.ContentAudioCapture(RESOURCE_AUDIO_CAPTURE), + Permission.ContentVideoCapture(RESOURCE_VIDEO_CAPTURE), + Permission.ContentProtectedMediaId(RESOURCE_PROTECTED_MEDIA_ID), + ) + val request = SystemPermissionRequest(nativeRequest) + assertEquals(expected, request.permissions) + } + + @Test + fun `reject denies native request`() { + val nativeRequest: PermissionRequest = mock() + whenever(nativeRequest.origin).thenReturn(Uri.parse("https://mozilla.org")) + whenever(nativeRequest.resources).thenReturn(emptyArray()) + + val request = SystemPermissionRequest(nativeRequest) + request.reject() + verify(nativeRequest).deny() + } + + @Test + fun `grant permission to all native request resources`() { + val resources = arrayOf( + RESOURCE_AUDIO_CAPTURE, + RESOURCE_VIDEO_CAPTURE, + RESOURCE_PROTECTED_MEDIA_ID, + ) + + val nativeRequest: PermissionRequest = mock() + whenever(nativeRequest.origin).thenReturn(Uri.parse("https://mozilla.org")) + whenever(nativeRequest.resources).thenReturn(resources) + + val request = SystemPermissionRequest(nativeRequest) + request.grant() + verify(nativeRequest).grant(eq(resources)) + } + + @Test + fun `grant permission to selected native request resources`() { + val resources = arrayOf( + RESOURCE_AUDIO_CAPTURE, + RESOURCE_VIDEO_CAPTURE, + RESOURCE_PROTECTED_MEDIA_ID, + ) + + val nativeRequest: PermissionRequest = mock() + whenever(nativeRequest.origin).thenReturn(Uri.parse("https://mozilla.org")) + whenever(nativeRequest.resources).thenReturn(resources) + + val request = SystemPermissionRequest(nativeRequest) + request.grant(listOf(Permission.ContentAudioCapture(RESOURCE_AUDIO_CAPTURE))) + verify(nativeRequest).grant(eq(arrayOf(RESOURCE_AUDIO_CAPTURE))) + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/window/SystemWindowRequestTest.kt b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/window/SystemWindowRequestTest.kt new file mode 100644 index 0000000000..37efa78ace --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/java/mozilla/components/browser/engine/system/window/SystemWindowRequestTest.kt @@ -0,0 +1,76 @@ +/* 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/. */ + +package mozilla.components.browser.engine.system.window + +import android.os.Message +import android.webkit.WebSettings +import android.webkit.WebView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import mozilla.components.browser.engine.system.SystemEngineSession +import mozilla.components.support.test.mock +import mozilla.components.support.test.robolectric.testContext +import mozilla.components.support.test.whenever +import org.junit.Assert.assertEquals +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@RunWith(AndroidJUnit4::class) +class SystemWindowRequestTest { + + @Test + fun `init request`() { + val curWebView = mock<WebView>() + val newWebView = mock<WebView>() + val newEngineSession = mock<SystemEngineSession>() + val request = SystemWindowRequest(curWebView, newEngineSession, newWebView, true, true) + + assertTrue(request.openAsDialog) + assertTrue(request.triggeredByUser) + assertEquals("", request.url) + } + + @Test + fun `prepare sets webview on engine session`() { + val curWebView = mock<WebView>() + val newWebView = mock<WebView>() + val settings = mock<WebSettings>() + + whenever(curWebView.settings).thenReturn(settings) + whenever(newWebView.settings).thenReturn(settings) + + val newEngineSession = SystemEngineSession(testContext) + val request = SystemWindowRequest(curWebView, newEngineSession, newWebView) + + val engineSession = request.prepare() as SystemEngineSession + assertSame(newWebView, engineSession.webView) + } + + @Test + fun `start sends message to target`() { + val curWebView = mock<WebView>() + val newWebView = mock<WebView>() + val resultMsg = mock<Message>() + val newEngineSession = mock<SystemEngineSession>() + + SystemWindowRequest(curWebView, newEngineSession, newWebView, false, false).start() + verify(resultMsg, never()).sendToTarget() + + SystemWindowRequest(curWebView, newEngineSession, newWebView, false, false, resultMsg).start() + verify(resultMsg, never()).sendToTarget() + + resultMsg.obj = "" + SystemWindowRequest(curWebView, newEngineSession, newWebView, false, false, resultMsg).start() + verify(resultMsg, never()).sendToTarget() + + resultMsg.obj = mock<WebView.WebViewTransport>() + SystemWindowRequest(curWebView, newEngineSession, newWebView, false, false, resultMsg).start() + verify(resultMsg, times(1)).sendToTarget() + } +} diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/mobile/android/android-components/components/browser/engine-system/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker new file mode 100644 index 0000000000..cf1c399ea8 --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker @@ -0,0 +1,2 @@ +mock-maker-inline +// This allows mocking final classes (classes are final by default in Kotlin) diff --git a/mobile/android/android-components/components/browser/engine-system/src/test/resources/robolectric.properties b/mobile/android/android-components/components/browser/engine-system/src/test/resources/robolectric.properties new file mode 100644 index 0000000000..932b01b9eb --- /dev/null +++ b/mobile/android/android-components/components/browser/engine-system/src/test/resources/robolectric.properties @@ -0,0 +1 @@ +sdk=28 |