diff --git a/DEPS b/DEPS index b57ba6f..9b41aae 100644 --- a/DEPS +++ b/DEPS
@@ -96,7 +96,7 @@ # Three lines of non-changing comments so that # the commit queue can handle CLs rolling catapult # and whatever else without interference from each other. - 'catapult_revision': 'f49ec2dcbd4701deac2247e4aa59e9cbf281b4dc', + 'catapult_revision': 'fe5e396b07f8f81d7cd56ad930fdae77533fcc6b', # Three lines of non-changing comments so that # the commit queue can handle CLs rolling libFuzzer # and whatever else without interference from each other.
diff --git a/android_webview/native/aw_autofill_client.cc b/android_webview/native/aw_autofill_client.cc index 1c9f37c..26f2b94 100644 --- a/android_webview/native/aw_autofill_client.cc +++ b/android_webview/native/aw_autofill_client.cc
@@ -87,6 +87,11 @@ return nullptr; } +autofill::SaveCardBubbleController* +AwAutofillClient::GetSaveCardBubbleController() { + return nullptr; +} + autofill::PersonalDataManager* AwAutofillClient::GetPersonalDataManager() { return nullptr; } @@ -251,6 +256,7 @@ void AwAutofillClient::ConfirmSaveCreditCardToCloud( const autofill::CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) { NOTIMPLEMENTED(); }
diff --git a/android_webview/native/aw_autofill_client.h b/android_webview/native/aw_autofill_client.h index 04c343d7..0316127 100644 --- a/android_webview/native/aw_autofill_client.h +++ b/android_webview/native/aw_autofill_client.h
@@ -25,6 +25,7 @@ class CreditCard; class FormStructure; class PersonalDataManager; +class SaveCardBubbleController; } namespace content { @@ -67,6 +68,7 @@ IdentityProvider* GetIdentityProvider() override; rappor::RapporServiceImpl* GetRapporServiceImpl() override; ukm::UkmService* GetUkmService() override; + autofill::SaveCardBubbleController* GetSaveCardBubbleController() override; void ShowAutofillSettings() override; void ShowUnmaskPrompt( const autofill::CreditCard& card, @@ -78,6 +80,7 @@ void ConfirmSaveCreditCardToCloud( const autofill::CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) override; void ConfirmCreditCardFillAssist(const autofill::CreditCard& card, const base::Closure& callback) override;
diff --git a/ash/display/resolution_notification_controller.cc b/ash/display/resolution_notification_controller.cc index b6db541..8d472b6 100644 --- a/ash/display/resolution_notification_controller.cc +++ b/ash/display/resolution_notification_controller.cc
@@ -162,7 +162,7 @@ display::Screen::GetScreen()->RemoveObserver(this); } -void ResolutionNotificationController::PrepareNotification( +bool ResolutionNotificationController::PrepareNotificationAndSetDisplayMode( int64_t display_id, const scoped_refptr<display::ManagedDisplayMode>& old_resolution, const scoped_refptr<display::ManagedDisplayMode>& new_resolution, @@ -170,7 +170,14 @@ DCHECK(old_resolution); DCHECK(new_resolution); - DCHECK(!display::Display::IsInternalDisplayId(display_id)); + display::DisplayManager* const display_manager = + Shell::Get()->display_manager(); + if (display::Display::IsInternalDisplayId(display_id)) { + // We don't show notifications to confirm/revert the resolution change in + // the case of an internal display. + return display_manager->SetDisplayMode(display_id, new_resolution); + } + // If multiple resolution changes are invoked for the same display, // the original resolution for the first resolution change has to be used // instead of the specified |old_resolution|. @@ -192,6 +199,15 @@ new_resolution, accept_callback)); if (original_resolution && !original_resolution->size().IsEmpty()) change_info_->old_resolution = original_resolution; + + if (!display_manager->SetDisplayMode(display_id, new_resolution)) { + // Discard the prepared notification data since we failed to set the new + // resolution. + change_info_.reset(); + return false; + } + + return true; } bool ResolutionNotificationController::DoesNotificationTimeout() {
diff --git a/ash/display/resolution_notification_controller.h b/ash/display/resolution_notification_controller.h index e12e1ba..c00150b 100644 --- a/ash/display/resolution_notification_controller.h +++ b/ash/display/resolution_notification_controller.h
@@ -31,23 +31,34 @@ ResolutionNotificationController(); ~ResolutionNotificationController() override; - // Prepare a resolution change notification for |display_id| from - // |old_resolution| to |new_resolution|, which offers a button to revert the - // change in case something goes wrong. The notification times out if there's - // only one display connected and the user is trying to modify its resolution. - // In that case, the timeout has to be set since the user cannot make any - // changes if something goes wrong. + // If |display_id| is not the internal display, Prepare a resolution change + // notification for |display_id| from |old_resolution| to |new_resolution|, + // which offers a button to revert the change in case something goes wrong. + // The notification times out if there's only one display connected and the + // user is trying to modify its resolution. In that case, the timeout has to + // be set since the user cannot make any changes if something goes wrong. + // + // Then call DisplayManager::SetDisplayMode() to apply the resolution change, + // and return the result; true if success, false otherwise. + // In case SetDisplayMode() fails, the prepared notification will be + // discarded. + // + // If |display_id| is the internal display, the resolution change is applied + // directly without preparing the confirm/revert notification (this kind of + // notification is only useful for external displays). // // This method does not create a notification itself. The notification will be // created the next OnDisplayConfigurationChanged(), which will be called - // asynchronously after the resolution change is requested. So typically this - // method will be combined with resolution change methods like - // DisplayManager::SetDisplayMode(). - void PrepareNotification( + // asynchronously after the resolution change is requested by this method. + // + // |accept_callback| will be called when the user accepts the resoltion change + // by closing the notification bubble or clicking on the accept button (if + // any). + bool PrepareNotificationAndSetDisplayMode( int64_t display_id, const scoped_refptr<display::ManagedDisplayMode>& old_resolution, const scoped_refptr<display::ManagedDisplayMode>& new_resolution, - const base::Closure& accept_callback); + const base::Closure& accept_callback) WARN_UNUSED_RESULT; // Returns true if the notification is visible or scheduled to be visible and // the notification times out.
diff --git a/ash/display/resolution_notification_controller_unittest.cc b/ash/display/resolution_notification_controller_unittest.cc index 977eacc..b1064f98 100644 --- a/ash/display/resolution_notification_controller_unittest.cc +++ b/ash/display/resolution_notification_controller_unittest.cc
@@ -66,12 +66,10 @@ old_mode->native(), old_mode->ui_scale(), old_mode->device_scale_factor())); - if (display_manager()->SetDisplayMode(display.id(), new_mode)) { - controller()->PrepareNotification( - display.id(), old_mode, new_mode, - base::Bind(&ResolutionNotificationControllerTest::OnAccepted, - base::Unretained(this))); - } + EXPECT_TRUE(controller()->PrepareNotificationAndSetDisplayMode( + display.id(), old_mode, new_mode, + base::Bind(&ResolutionNotificationControllerTest::OnAccepted, + base::Unretained(this)))); // OnConfigurationChanged event won't be emitted in the test environment, // so invoke UpdateDisplay() to emit that event explicitly.
diff --git a/base/process/process_metrics_unittest.cc b/base/process/process_metrics_unittest.cc index ed98020..8abdb264 100644 --- a/base/process/process_metrics_unittest.cc +++ b/base/process/process_metrics_unittest.cc
@@ -418,7 +418,8 @@ #endif // defined(OS_LINUX) || defined(OS_ANDROID) // All the values should be less than the total amount of memory. -#if !defined(OS_WIN) +#if !defined(OS_WIN) && !defined(OS_IOS) + // TODO(crbug.com/711450): re-enable the following assertion on iOS. EXPECT_LT(info.free, info.total); #endif #if defined(OS_LINUX) || defined(OS_ANDROID)
diff --git a/cc/BUILD.gn b/cc/BUILD.gn index c62ac3a..f7918544 100644 --- a/cc/BUILD.gn +++ b/cc/BUILD.gn
@@ -647,8 +647,6 @@ "test/test_occlusion_tracker.h", "test/test_shared_bitmap_manager.cc", "test/test_shared_bitmap_manager.h", - "test/test_skcanvas.cc", - "test/test_skcanvas.h", "test/test_task_graph_runner.cc", "test/test_task_graph_runner.h", "test/test_texture.cc", @@ -766,7 +764,6 @@ "output/texture_mailbox_deleter_unittest.cc", "paint/discardable_image_map_unittest.cc", "paint/display_item_list_unittest.cc", - "paint/paint_op_buffer_unittest.cc", "quads/draw_polygon_unittest.cc", "quads/draw_quad_unittest.cc", "quads/nine_patch_generator_unittest.cc", @@ -937,7 +934,6 @@ "//cc/ipc", "//cc/ipc:interfaces", "//cc/paint", - "//cc/paint", "//cc/surfaces", "//cc/surfaces:surface_id", "//gpu",
diff --git a/cc/output/dc_layer_overlay.cc b/cc/output/dc_layer_overlay.cc index 9a02f93..eda508e 100644 --- a/cc/output/dc_layer_overlay.cc +++ b/cc/output/dc_layer_overlay.cc
@@ -30,6 +30,16 @@ return DCLayerOverlayProcessor::DC_LAYER_SUCCESS; } +// This returns the smallest rectangle in target space that contains the quad. +gfx::RectF ClippedQuadRectangle(const DrawQuad* quad) { + gfx::RectF quad_rect = MathUtil::MapClippedRect( + quad->shared_quad_state->quad_to_target_transform, + gfx::RectF(quad->rect)); + if (quad->shared_quad_state->is_clipped) + quad_rect.Intersect(gfx::RectF(quad->shared_quad_state->clip_rect)); + return quad_rect; +} + // Find a rectangle containing all the quads in a list that occlude the area // in target_quad. gfx::RectF GetOcclusionBounds(const gfx::RectF& target_quad, @@ -38,13 +48,11 @@ gfx::RectF occlusion_bounding_box; for (auto overlap_iter = quad_list_begin; overlap_iter != quad_list_end; ++overlap_iter) { - gfx::RectF overlap_rect = MathUtil::MapClippedRect( - overlap_iter->shared_quad_state->quad_to_target_transform, - gfx::RectF(overlap_iter->rect)); float opacity = overlap_iter->shared_quad_state->opacity; if (opacity < std::numeric_limits<float>::epsilon()) continue; const DrawQuad* quad = *overlap_iter; + gfx::RectF overlap_rect = ClippedQuadRectangle(quad); if (quad->material == DrawQuad::SOLID_COLOR) { SkColor color = SolidColorDrawQuad::MaterialCast(quad)->color; float alpha = (SkColorGetA(color) * (1.0f / 255.0f)) * opacity; @@ -130,8 +138,7 @@ continue; } - gfx::Rect quad_rectangle = MathUtil::MapEnclosingClippedRect( - it->shared_quad_state->quad_to_target_transform, it->rect); + gfx::Rect quad_rectangle = gfx::ToEnclosingRect(ClippedQuadRectangle(*it)); gfx::RectF occlusion_bounding_box = GetOcclusionBounds(gfx::RectF(quad_rectangle), quad_list->begin(), it); if (occlusion_bounding_box.IsEmpty()) { @@ -139,7 +146,7 @@ // underneath it. if (it->shared_quad_state->quad_to_target_transform .Preserves2dAxisAlignment() && - !display_rect_changed) { + !display_rect_changed && !it->ShouldDrawWithBlending()) { damage_rect->Subtract(quad_rectangle); } quad_list->EraseAndInvalidateAllPointers(it);
diff --git a/cc/output/overlay_unittest.cc b/cc/output/overlay_unittest.cc index 2abe158..c989fbd 100644 --- a/cc/output/overlay_unittest.cc +++ b/cc/output/overlay_unittest.cc
@@ -1873,6 +1873,80 @@ } } +TEST_F(DCLayerOverlayTest, ClipRect) { + base::test::ScopedFeatureList feature_list; + feature_list.InitAndEnableFeature(features::kDirectCompositionUnderlays); + + // Process twice. The second time through the overlay list shouldn't change, + // which will allow the damage rect to reflect just the changes in that + // frame. + for (size_t i = 0; i < 2; ++i) { + std::unique_ptr<RenderPass> pass = CreateRenderPass(); + CreateOpaqueQuadAt(resource_provider_.get(), + pass->shared_quad_state_list.back(), pass.get(), + gfx::Rect(0, 2, 100, 100), SK_ColorWHITE); + pass->shared_quad_state_list.back()->is_clipped = true; + pass->shared_quad_state_list.back()->clip_rect = gfx::Rect(0, 3, 100, 100); + SharedQuadState* shared_state = pass->CreateAndAppendSharedQuadState(); + shared_state->opacity = 1.f; + CreateFullscreenCandidateYUVVideoQuad(resource_provider_.get(), + shared_state, pass.get()); + shared_state->is_clipped = true; + // Clipped rect shouldn't be overlapped by clipped opaque quad rect. + shared_state->clip_rect = gfx::Rect(0, 0, 100, 3); + + DCLayerOverlayList dc_layer_list; + OverlayCandidateList overlay_list; + RenderPassFilterList render_pass_filters; + RenderPassFilterList render_pass_background_filters; + damage_rect_ = gfx::Rect(1, 1, 10, 10); + overlay_processor_->ProcessForOverlays( + resource_provider_.get(), pass.get(), render_pass_filters, + render_pass_background_filters, &overlay_list, nullptr, &dc_layer_list, + &damage_rect_, &content_bounds_); + EXPECT_EQ(0U, overlay_list.size()); + EXPECT_EQ(1U, dc_layer_list.size()); + // Because of clip rects the overlay isn't occluded and shouldn't be an + // underlay. + EXPECT_EQ(1, dc_layer_list.back().shared_state->z_order); + if (i == 1) { + // The damage rect should only contain contents that aren't in the + // clipped overlay rect. + EXPECT_EQ(gfx::Rect(1, 3, 10, 8), damage_rect_); + } + } +} + +TEST_F(DCLayerOverlayTest, TransparentOnTop) { + base::test::ScopedFeatureList feature_list; + + // Process twice. The second time through the overlay list shouldn't change, + // which will allow the damage rect to reflect just the changes in that + // frame. + for (size_t i = 0; i < 2; ++i) { + std::unique_ptr<RenderPass> pass = CreateRenderPass(); + CreateFullscreenCandidateYUVVideoQuad(resource_provider_.get(), + pass->shared_quad_state_list.back(), + pass.get()); + pass->shared_quad_state_list.back()->opacity = 0.5f; + + DCLayerOverlayList dc_layer_list; + OverlayCandidateList overlay_list; + RenderPassFilterList render_pass_filters; + RenderPassFilterList render_pass_background_filters; + damage_rect_ = gfx::Rect(1, 1, 10, 10); + overlay_processor_->ProcessForOverlays( + resource_provider_.get(), pass.get(), render_pass_filters, + render_pass_background_filters, &overlay_list, nullptr, &dc_layer_list, + &damage_rect_, &content_bounds_); + EXPECT_EQ(0U, overlay_list.size()); + EXPECT_EQ(1U, dc_layer_list.size()); + EXPECT_EQ(1, dc_layer_list.back().shared_state->z_order); + // Quad isn't opaque, so underlying damage must remain the same. + EXPECT_EQ(gfx::Rect(1, 1, 10, 10), damage_rect_); + } +} + class OverlayInfoRendererGL : public GLRenderer { public: OverlayInfoRendererGL(const RendererSettings* settings,
diff --git a/cc/paint/BUILD.gn b/cc/paint/BUILD.gn index 23b0f8b1..c442f04 100644 --- a/cc/paint/BUILD.gn +++ b/cc/paint/BUILD.gn
@@ -32,17 +32,11 @@ "paint_canvas.cc", "paint_canvas.h", "paint_export.h", - "paint_flags.cc", "paint_flags.h", - "paint_op_buffer.cc", - "paint_op_buffer.h", - "paint_record.cc", "paint_record.h", "paint_recorder.cc", "paint_recorder.h", "paint_shader.h", - "record_paint_canvas.cc", - "record_paint_canvas.h", "skia_paint_canvas.cc", "skia_paint_canvas.h", "transform_display_item.cc",
diff --git a/cc/paint/display_item_list.cc b/cc/paint/display_item_list.cc index c1b069548..8359948 100644 --- a/cc/paint/display_item_list.cc +++ b/cc/paint/display_item_list.cc
@@ -100,13 +100,9 @@ if (canvas->quickReject(item.picture->cullRect())) break; - // TODO(enne): Maybe the PaintRecord itself could know whether this - // was needed? It's not clear whether these save/restore semantics - // that SkPicture handles during playback are things that should be - // kept around. - canvas->save(); + // SkPicture always does a wrapping save/restore on the canvas, so it is + // not necessary here. item.picture->playback(canvas, callback); - canvas->restore(); break; } case DisplayItem::FLOAT_CLIP: { @@ -180,33 +176,6 @@ canvas->restore(); } -// Atttempts to merge a CompositingDisplayItem and DrawingDisplayItem -// into a single "draw with alpha". This function returns true if -// it was successful. If false, then the caller is responsible for -// drawing these items. This is a DisplayItemList version of the -// SkRecord optimization SkRecordNoopSaveLayerDrawRestores. -static bool MergeAndDrawIfPossible(const CompositingDisplayItem& save_item, - const DrawingDisplayItem& draw_item, - SkCanvas* canvas) { - if (save_item.color_filter) - return false; - if (save_item.xfermode != SkBlendMode::kSrcOver) - return false; - // TODO(enne): I believe that lcd_text_requires_opaque_layer is not - // relevant here and that lcd text is preserved post merge, but I haven't - // tested that. - const PaintRecord* record = draw_item.picture.get(); - if (record->approximateOpCount() != 1) - return false; - - const PaintOp* op = record->GetFirstOp(); - if (!op->IsDrawOp()) - return false; - - op->RasterWithAlpha(canvas, save_item.alpha); - return true; -} - void DisplayItemList::Raster(SkCanvas* canvas, SkPicture::AbortCallback* callback) const { gfx::Rect canvas_playback_rect; @@ -215,33 +184,14 @@ std::vector<size_t> indices; rtree_.Search(canvas_playback_rect, &indices); - for (size_t i = 0; i < indices.size(); ++i) { + for (size_t index : indices) { + RasterItem(items_[index], canvas, callback); + // We use a callback during solid color analysis on the compositor thread to // break out early. Since we're handling a sequence of pictures via rtree // query results ourselves, we have to respect the callback and early out. if (callback && callback->abort()) break; - - const DisplayItem& item = items_[indices[i]]; - // Optimize empty begin/end compositing and merge begin/draw/end compositing - // where possible. - // TODO(enne): remove empty clips here too? - // TODO(enne): does this happen recursively? Or is this good enough? - if (i < indices.size() - 2 && item.type == DisplayItem::COMPOSITING) { - const DisplayItem& second = items_[indices[i + 1]]; - const DisplayItem& third = items_[indices[i + 2]]; - if (second.type == DisplayItem::DRAWING && - third.type == DisplayItem::END_COMPOSITING) { - if (MergeAndDrawIfPossible( - static_cast<const CompositingDisplayItem&>(item), - static_cast<const DrawingDisplayItem&>(second), canvas)) { - i += 2; - continue; - } - } - } - - RasterItem(item, canvas, callback); } }
diff --git a/cc/paint/display_item_list_unittest.cc b/cc/paint/display_item_list_unittest.cc index b166229c6..f1b9e75 100644 --- a/cc/paint/display_item_list_unittest.cc +++ b/cc/paint/display_item_list_unittest.cc
@@ -17,17 +17,16 @@ #include "cc/paint/compositing_display_item.h" #include "cc/paint/drawing_display_item.h" #include "cc/paint/filter_display_item.h" + #include "cc/paint/float_clip_display_item.h" #include "cc/paint/paint_canvas.h" #include "cc/paint/paint_flags.h" #include "cc/paint/paint_record.h" #include "cc/paint/paint_recorder.h" -#include "cc/paint/skia_paint_canvas.h" #include "cc/paint/transform_display_item.h" #include "cc/test/geometry_test_utils.h" #include "cc/test/pixel_test_utils.h" #include "cc/test/skia_common.h" -#include "cc/test/test_skcanvas.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -81,19 +80,6 @@ return recorder.finishRecordingAsPicture(); } -sk_sp<const PaintRecord> CreateRectPictureWithAlpha(const gfx::Rect& bounds, - uint8_t alpha) { - PaintRecorder recorder; - PaintCanvas* canvas = - recorder.beginRecording(bounds.width(), bounds.height()); - PaintFlags flags; - flags.setAlpha(alpha); - canvas->drawRect( - SkRect::MakeXYWH(bounds.x(), bounds.y(), bounds.width(), bounds.height()), - flags); - return recorder.finishRecordingAsPicture(); -} - void AppendFirstSerializationTestPicture(scoped_refptr<DisplayItemList> list, const gfx::Size& layer_size) { gfx::PointF offset(2.f, 3.f); @@ -718,110 +704,4 @@ EXPECT_RECT_EQ(filter_bounds, list->VisualRectForTesting(3)); } -// Verify that raster time optimizations for compositing item / draw single op / -// end compositing item can be collapsed together into a single draw op -// with the opacity from the compositing item folded in. -TEST(DisplayItemListTest, SaveDrawRestore) { - auto list = make_scoped_refptr(new DisplayItemList); - - list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( - 80, SkBlendMode::kSrcOver, nullptr, nullptr, false); - list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - kVisualRect, CreateRectPictureWithAlpha(kVisualRect, 40)); - list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); - list->Finalize(); - - SaveCountingCanvas canvas; - list->Raster(&canvas, nullptr); - - EXPECT_EQ(0, canvas.save_count_); - EXPECT_EQ(0, canvas.restore_count_); - EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_); - - float expected_alpha = 80 * 40 / 255.f; - EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f); -} - -// Verify that compositing item / end compositing item is a noop. -// Here we're testing that Skia does an optimization that skips -// save/restore with nothing in between. If skia stops doing this -// then we should reimplement this optimization in display list raster. -TEST(DisplayItemListTest, SaveRestoreNoops) { - auto list = make_scoped_refptr(new DisplayItemList); - - list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( - 80, SkBlendMode::kSrcOver, nullptr, nullptr, false); - list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); - list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( - 255, SkBlendMode::kSrcOver, nullptr, nullptr, false); - list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); - list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( - 255, SkBlendMode::kSrc, nullptr, nullptr, false); - list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); - list->Finalize(); - - SaveCountingCanvas canvas; - list->Raster(&canvas, nullptr); - - EXPECT_EQ(0, canvas.save_count_); - EXPECT_EQ(0, canvas.restore_count_); -} - -// The same as SaveDrawRestore, but with save flags that prevent the -// optimization. -TEST(DisplayItemListTest, SaveDrawRestoreFail_BadSaveFlags) { - auto list = make_scoped_refptr(new DisplayItemList); - - // Use a blend mode that's not compatible with the SaveDrawRestore - // optimization. - list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( - 80, SkBlendMode::kSrc, nullptr, nullptr, false); - list->CreateAndAppendDrawingItem<DrawingDisplayItem>( - kVisualRect, CreateRectPictureWithAlpha(kVisualRect, 40)); - list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); - list->Finalize(); - - SaveCountingCanvas canvas; - list->Raster(&canvas, nullptr); - - EXPECT_EQ(1, canvas.save_count_); - EXPECT_EQ(1, canvas.restore_count_); - EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_); - EXPECT_LE(40, canvas.paint_.getAlpha()); -} - -// The same as SaveDrawRestore, but with too many ops in the PaintRecord. -TEST(DisplayItemListTest, SaveDrawRestoreFail_TooManyOps) { - sk_sp<const PaintRecord> record; - { - PaintRecorder recorder; - PaintCanvas* canvas = - recorder.beginRecording(kVisualRect.width(), kVisualRect.height()); - PaintFlags flags; - flags.setAlpha(40); - canvas->drawRect(gfx::RectToSkRect(kVisualRect), flags); - // Add an extra op here. - canvas->drawRect(gfx::RectToSkRect(kVisualRect), flags); - record = recorder.finishRecordingAsPicture(); - } - EXPECT_GT(record->approximateOpCount(), 1); - - auto list = make_scoped_refptr(new DisplayItemList); - - list->CreateAndAppendPairedBeginItem<CompositingDisplayItem>( - 80, SkBlendMode::kSrcOver, nullptr, nullptr, false); - list->CreateAndAppendDrawingItem<DrawingDisplayItem>(kVisualRect, - std::move(record)); - list->CreateAndAppendPairedEndItem<EndCompositingDisplayItem>(); - list->Finalize(); - - SaveCountingCanvas canvas; - list->Raster(&canvas, nullptr); - - EXPECT_EQ(1, canvas.save_count_); - EXPECT_EQ(1, canvas.restore_count_); - EXPECT_EQ(gfx::RectToSkRect(kVisualRect), canvas.draw_rect_); - EXPECT_LE(40, canvas.paint_.getAlpha()); -} - } // namespace cc
diff --git a/cc/paint/paint_canvas.h b/cc/paint/paint_canvas.h index 199d9d9..daacc301 100644 --- a/cc/paint/paint_canvas.h +++ b/cc/paint/paint_canvas.h
@@ -10,24 +10,19 @@ #include "base/memory/ref_counted.h" #include "build/build_config.h" #include "cc/paint/paint_export.h" +#include "cc/paint/paint_record.h" #include "third_party/skia/include/core/SkCanvas.h" namespace cc { class DisplayItemList; class PaintFlags; -class PaintOpBuffer; - -using PaintRecord = PaintOpBuffer; class CC_PAINT_EXPORT PaintCanvas { public: virtual ~PaintCanvas() {} virtual SkMetaData& getMetaData() = 0; - - // TODO(enne): this only appears to mostly be used to determine if this is - // recording or not, so could be simplified or removed. virtual SkImageInfo imageInfo() const = 0; // TODO(enne): It would be nice to get rid of flush() entirely, as it @@ -46,7 +41,7 @@ int y) = 0; virtual int save() = 0; virtual int saveLayer(const SkRect* bounds, const PaintFlags* flags) = 0; - virtual int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) = 0; + virtual int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) = 0; virtual void restore() = 0; virtual int getSaveCount() const = 0; @@ -97,8 +92,6 @@ virtual bool getDeviceClipBounds(SkIRect* bounds) const = 0; virtual void drawColor(SkColor color, SkBlendMode mode) = 0; void drawColor(SkColor color) { drawColor(color, SkBlendMode::kSrcOver); } - - // TODO(enne): This is a synonym for drawColor with kSrc. Remove it. virtual void clear(SkColor color) = 0; virtual void drawLine(SkScalar x0,
diff --git a/cc/paint/paint_flags.cc b/cc/paint/paint_flags.cc deleted file mode 100644 index e16a8bb..0000000 --- a/cc/paint/paint_flags.cc +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/paint/paint_flags.h" - -namespace cc { - -bool PaintFlags::IsSimpleOpacity() const { - uint32_t color = getColor(); - if (SK_ColorTRANSPARENT != SkColorSetA(color, SK_AlphaTRANSPARENT)) - return false; - if (!isSrcOver()) - return false; - if (getLooper()) - return false; - if (getPathEffect()) - return false; - if (getShader()) - return false; - if (getMaskFilter()) - return false; - if (getColorFilter()) - return false; - if (getImageFilter()) - return false; - return true; -} - -bool PaintFlags::SupportsFoldingAlpha() const { - if (!isSrcOver()) - return false; - if (getColorFilter()) - return false; - if (getImageFilter()) - return false; - if (getLooper()) - return false; - return true; -} - -} // namespace cc
diff --git a/cc/paint/paint_flags.h b/cc/paint/paint_flags.h index 37b460d6..b7e96c6 100644 --- a/cc/paint/paint_flags.h +++ b/cc/paint/paint_flags.h
@@ -7,6 +7,7 @@ #include "base/compiler_specific.h" #include "cc/paint/paint_export.h" +#include "cc/paint/paint_shader.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkColorFilter.h" #include "third_party/skia/include/core/SkDrawLooper.h" @@ -18,8 +19,6 @@ namespace cc { -using PaintShader = SkShader; - class CC_PAINT_EXPORT PaintFlags { public: enum Style { @@ -199,14 +198,6 @@ return paint_.computeFastBounds(orig, storage); } - bool operator==(const PaintFlags& flags) { return flags.paint_ == paint_; } - bool operator!=(const PaintFlags& flags) { return flags.paint_ != paint_; } - - // Returns true if this just represents an opacity blend when - // used as saveLayer flags. - bool IsSimpleOpacity() const; - bool SupportsFoldingAlpha() const; - private: friend const SkPaint& ToSkPaint(const PaintFlags& flags); friend const SkPaint* ToSkPaint(const PaintFlags* flags);
diff --git a/cc/paint/paint_op_buffer.cc b/cc/paint/paint_op_buffer.cc deleted file mode 100644 index a0170bd..0000000 --- a/cc/paint/paint_op_buffer.cc +++ /dev/null
@@ -1,555 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/paint/paint_op_buffer.h" - -#include "cc/paint/display_item_list.h" -#include "cc/paint/paint_record.h" -#include "third_party/skia/include/core/SkAnnotation.h" - -namespace cc { - -#define TYPES(M) \ - M(AnnotateOp) \ - M(ClipPathOp) \ - M(ClipRectOp) \ - M(ClipRRectOp) \ - M(ConcatOp) \ - M(DrawArcOp) \ - M(DrawCircleOp) \ - M(DrawColorOp) \ - M(DrawDisplayItemListOp) \ - M(DrawDRRectOp) \ - M(DrawImageOp) \ - M(DrawImageRectOp) \ - M(DrawIRectOp) \ - M(DrawLineOp) \ - M(DrawOvalOp) \ - M(DrawPathOp) \ - M(DrawPosTextOp) \ - M(DrawRecordOp) \ - M(DrawRectOp) \ - M(DrawRRectOp) \ - M(DrawTextOp) \ - M(DrawTextBlobOp) \ - M(NoopOp) \ - M(RestoreOp) \ - M(RotateOp) \ - M(SaveOp) \ - M(SaveLayerOp) \ - M(SaveLayerAlphaOp) \ - M(ScaleOp) \ - M(SetMatrixOp) \ - M(TranslateOp) - -// Helper template to share common code for RasterWithAlpha when paint ops -// have or don't have PaintFlags. -template <typename T, bool HasFlags> -struct Rasterizer { - static void Raster(const T* op, - SkCanvas* canvas, - const SkMatrix& original_ctm) { - // Paint ops with kHasPaintFlags need to declare RasterWithPaintFlags - // otherwise, the paint op needs its own Raster function. Without its - // own, this becomes an infinite loop as PaintOp::Raster calls itself. - static_assert( - !std::is_same<decltype(&PaintOp::Raster), decltype(&T::Raster)>::value, - "No Raster function"); - - op->Raster(canvas); - } - static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) { - DCHECK(T::kIsDrawOp); - // TODO(enne): is it ok to just drop the bounds here? - canvas->saveLayerAlpha(nullptr, alpha); - op->Raster(canvas); - canvas->restore(); - } -}; - -template <typename T> -struct Rasterizer<T, true> { - static void Raster(const T* op, - SkCanvas* canvas, - const SkMatrix& original_ctm) { - op->RasterWithFlags(canvas, op->flags); - } - static void RasterWithAlpha(const T* op, SkCanvas* canvas, uint8_t alpha) { - DCHECK(T::kIsDrawOp); - SkMatrix unused_matrix; - if (alpha == 255) { - Raster(op, canvas, unused_matrix); - } else if (op->flags.SupportsFoldingAlpha()) { - PaintFlags flags = op->flags; - flags.setAlpha(SkMulDiv255Round(flags.getAlpha(), alpha)); - op->RasterWithFlags(canvas, flags); - } else { - canvas->saveLayerAlpha(nullptr, alpha); - op->RasterWithFlags(canvas, op->flags); - canvas->restore(); - } - } -}; - -template <> -struct Rasterizer<SetMatrixOp, false> { - static void Raster(const SetMatrixOp* op, - SkCanvas* canvas, - const SkMatrix& original_ctm) { - op->Raster(canvas, original_ctm); - } - static void RasterWithAlpha(const SetMatrixOp* op, - SkCanvas* canvas, - uint8_t alpha) { - NOTREACHED(); - } -}; - -template <> -struct Rasterizer<DrawRecordOp, false> { - static void Raster(const DrawRecordOp* op, - SkCanvas* canvas, - const SkMatrix& original_ctm) { - op->Raster(canvas); - } - static void RasterWithAlpha(const DrawRecordOp* op, - SkCanvas* canvas, - uint8_t alpha) { - // This "looking into records" optimization is done here instead of - // in the PaintOpBuffer::Raster function as DisplayItemList calls - // into RasterWithAlpha directly. - if (op->record->approximateOpCount() == 1) { - op->record->GetFirstOp()->RasterWithAlpha(canvas, alpha); - return; - } - - canvas->saveLayerAlpha(nullptr, alpha); - op->Raster(canvas); - canvas->restore(); - } -}; - -// TODO(enne): partially specialize RasterWithAlpha for draw color? - -static constexpr size_t kNumOpTypes = - static_cast<size_t>(PaintOpType::LastPaintOpType) + 1; - -// Verify that every op is in the TYPES macro. -#define M(T) +1 -static_assert(kNumOpTypes == TYPES(M), "Missing op in list"); -#undef M - -using RasterFunction = void (*)(const PaintOp* op, - SkCanvas* canvas, - const SkMatrix& original_ctm); -#define M(T) \ - [](const PaintOp* op, SkCanvas* canvas, const SkMatrix& original_ctm) { \ - Rasterizer<T, T::kHasPaintFlags>::Raster(static_cast<const T*>(op), \ - canvas, original_ctm); \ - }, -static const RasterFunction g_raster_functions[kNumOpTypes] = {TYPES(M)}; -#undef M - -using RasterAlphaFunction = void (*)(const PaintOp* op, - SkCanvas* canvas, - uint8_t alpha); -#define M(T) \ - T::kIsDrawOp ? \ - [](const PaintOp* op, SkCanvas* canvas, uint8_t alpha) { \ - Rasterizer<T, T::kHasPaintFlags>::RasterWithAlpha( \ - static_cast<const T*>(op), canvas, alpha); \ - } : static_cast<RasterAlphaFunction>(nullptr), -static const RasterAlphaFunction g_raster_alpha_functions[kNumOpTypes] = { - TYPES(M)}; -#undef M - -// Most state ops (matrix, clip, save, restore) have a trivial destructor. -// TODO(enne): evaluate if we need the nullptr optimization or if -// we even need to differentiate trivial destructors here. -using VoidFunction = void (*)(PaintOp* op); -#define M(T) \ - !std::is_trivially_destructible<T>::value \ - ? [](PaintOp* op) { static_cast<T*>(op)->~T(); } \ - : static_cast<VoidFunction>(nullptr), -static const VoidFunction g_destructor_functions[kNumOpTypes] = {TYPES(M)}; -#undef M - -#define M(T) T::kIsDrawOp, -static bool g_is_draw_op[kNumOpTypes] = {TYPES(M)}; -#undef M - -#define M(T) \ - static_assert(sizeof(T) <= sizeof(LargestPaintOp), \ - #T " must be no bigger than LargestPaintOp"); -TYPES(M); -#undef M - -#undef TYPES - -SkRect PaintOp::kUnsetRect = {SK_ScalarInfinity, 0, 0, 0}; - -void AnnotateOp::Raster(SkCanvas* canvas) const { - switch (annotation_type) { - case PaintCanvas::AnnotationType::URL: - SkAnnotateRectWithURL(canvas, rect, data.get()); - break; - case PaintCanvas::AnnotationType::LINK_TO_DESTINATION: - SkAnnotateLinkToDestination(canvas, rect, data.get()); - break; - case PaintCanvas::AnnotationType::NAMED_DESTINATION: { - SkPoint point = SkPoint::Make(rect.x(), rect.y()); - SkAnnotateNamedDestination(canvas, point, data.get()); - break; - } - } -} - -void ClipPathOp::Raster(SkCanvas* canvas) const { - canvas->clipPath(path, op, antialias); -} - -void ClipRectOp::Raster(SkCanvas* canvas) const { - canvas->clipRect(rect, op, antialias); -} - -void ClipRRectOp::Raster(SkCanvas* canvas) const { - canvas->clipRRect(rrect, op, antialias); -} - -void ConcatOp::Raster(SkCanvas* canvas) const { - canvas->concat(matrix); -} - -void DrawArcOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawArc(oval, start_angle, sweep_angle, use_center, ToSkPaint(flags)); -} - -void DrawCircleOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawCircle(cx, cy, radius, ToSkPaint(flags)); -} - -void DrawColorOp::Raster(SkCanvas* canvas) const { - canvas->drawColor(color, mode); -} - -void DrawDisplayItemListOp::Raster(SkCanvas* canvas) const { - list->Raster(canvas, nullptr); -} - -void DrawDRRectOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawDRRect(outer, inner, ToSkPaint(flags)); -} - -void DrawImageOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawImage(image.get(), left, top, ToSkPaint(&flags)); -} - -void DrawImageRectOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - // TODO(enne): Probably PaintCanvas should just use the skia enum directly. - SkCanvas::SrcRectConstraint skconstraint = - static_cast<SkCanvas::SrcRectConstraint>(constraint); - canvas->drawImageRect(image.get(), src, dst, ToSkPaint(&flags), skconstraint); -} - -void DrawIRectOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawIRect(rect, ToSkPaint(flags)); -} - -void DrawLineOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawLine(x0, y0, x1, y1, ToSkPaint(flags)); -} - -void DrawOvalOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawOval(oval, ToSkPaint(flags)); -} - -void DrawPathOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawPath(path, ToSkPaint(flags)); -} - -void DrawPosTextOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawPosText(paint_op_data(this), bytes, paint_op_array<SkPoint>(this), - ToSkPaint(flags)); -} - -void DrawRecordOp::Raster(SkCanvas* canvas) const { - record->playback(canvas); -} - -void DrawRectOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawRect(rect, ToSkPaint(flags)); -} - -void DrawRRectOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawRRect(rrect, ToSkPaint(flags)); -} - -void DrawTextOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawText(paint_op_data(this), bytes, x, y, ToSkPaint(flags)); -} - -void DrawTextBlobOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - canvas->drawTextBlob(blob.get(), x, y, ToSkPaint(flags)); -} - -void RestoreOp::Raster(SkCanvas* canvas) const { - canvas->restore(); -} - -void RotateOp::Raster(SkCanvas* canvas) const { - canvas->rotate(degrees); -} - -void SaveOp::Raster(SkCanvas* canvas) const { - canvas->save(); -} - -void SaveLayerOp::RasterWithFlags(SkCanvas* canvas, - const PaintFlags& flags) const { - // See PaintOp::kUnsetRect - bool unset = bounds.left() == SK_ScalarInfinity; - - canvas->saveLayer(unset ? nullptr : &bounds, ToSkPaint(&flags)); -} - -void SaveLayerAlphaOp::Raster(SkCanvas* canvas) const { - // See PaintOp::kUnsetRect - bool unset = bounds.left() == SK_ScalarInfinity; - canvas->saveLayerAlpha(unset ? nullptr : &bounds, alpha); -} - -void ScaleOp::Raster(SkCanvas* canvas) const { - canvas->scale(sx, sy); -} - -void SetMatrixOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const { - canvas->setMatrix(SkMatrix::Concat(original_ctm, matrix)); -} - -void TranslateOp::Raster(SkCanvas* canvas) const { - canvas->translate(dx, dy); -} - -bool PaintOp::IsDrawOp() const { - return g_is_draw_op[type]; -} - -void PaintOp::Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const { - g_raster_functions[type](this, canvas, original_ctm); -} - -void PaintOp::RasterWithAlpha(SkCanvas* canvas, uint8_t alpha) const { - g_raster_alpha_functions[type](this, canvas, alpha); -} - -DrawDisplayItemListOp::DrawDisplayItemListOp( - scoped_refptr<DisplayItemList> list) - : list(list) {} - -size_t DrawDisplayItemListOp::AdditionalBytesUsed() const { - return list->ApproximateMemoryUsage(); -} - -int ClipPathOp::CountSlowPaths() const { - return antialias && !path.isConvex() ? 1 : 0; -} - -int DrawLineOp::CountSlowPaths() const { - if (const SkPathEffect* effect = flags.getPathEffect()) { - SkPathEffect::DashInfo info; - SkPathEffect::DashType dashType = effect->asADash(&info); - if (flags.getStrokeCap() != PaintFlags::kRound_Cap && - dashType == SkPathEffect::kDash_DashType && info.fCount == 2) { - // The PaintFlags will count this as 1, so uncount that here as - // this kind of line is special cased and not slow. - return -1; - } - } - return 0; -} - -int DrawPathOp::CountSlowPaths() const { - // This logic is copied from SkPathCounter instead of attempting to expose - // that from Skia. - if (!flags.isAntiAlias() || path.isConvex()) - return 0; - - PaintFlags::Style paintStyle = flags.getStyle(); - const SkRect& pathBounds = path.getBounds(); - if (paintStyle == PaintFlags::kStroke_Style && flags.getStrokeWidth() == 0) { - // AA hairline concave path is not slow. - return 0; - } else if (paintStyle == PaintFlags::kFill_Style && - pathBounds.width() < 64.f && pathBounds.height() < 64.f && - !path.isVolatile()) { - // AADF eligible concave path is not slow. - return 0; - } else { - return 1; - } -} - -AnnotateOp::AnnotateOp(PaintCanvas::AnnotationType annotation_type, - const SkRect& rect, - sk_sp<SkData> data) - : annotation_type(annotation_type), rect(rect), data(std::move(data)) {} - -AnnotateOp::~AnnotateOp() = default; - -DrawDisplayItemListOp::~DrawDisplayItemListOp() = default; - -DrawImageOp::DrawImageOp(sk_sp<const SkImage> image, - SkScalar left, - SkScalar top, - const PaintFlags* flags) - : image(std::move(image)), - left(left), - top(top), - flags(flags ? *flags : PaintFlags()) {} - -DrawImageOp::~DrawImageOp() = default; - -DrawImageRectOp::DrawImageRectOp(sk_sp<const SkImage> image, - const SkRect& src, - const SkRect& dst, - const PaintFlags* flags, - PaintCanvas::SrcRectConstraint constraint) - : image(std::move(image)), - flags(flags ? *flags : PaintFlags()), - src(src), - dst(dst), - constraint(constraint) {} - -DrawImageRectOp::~DrawImageRectOp() = default; - -DrawPosTextOp::DrawPosTextOp(size_t bytes, - size_t count, - const PaintFlags& flags) - : PaintOpWithDataArray(bytes, count), flags(flags) {} - -DrawPosTextOp::~DrawPosTextOp() = default; - -DrawRecordOp::DrawRecordOp(sk_sp<const PaintRecord> record) - : record(std::move(record)) {} - -DrawRecordOp::~DrawRecordOp() = default; - -size_t DrawRecordOp::AdditionalBytesUsed() const { - return record->approximateBytesUsed(); -} - -DrawTextBlobOp::DrawTextBlobOp(sk_sp<SkTextBlob> blob, - SkScalar x, - SkScalar y, - const PaintFlags& flags) - : blob(std::move(blob)), x(x), y(y), flags(flags) {} - -DrawTextBlobOp::~DrawTextBlobOp() = default; - -PaintOpBuffer::PaintOpBuffer() : cull_rect_(SkRect::MakeEmpty()) {} - -PaintOpBuffer::PaintOpBuffer(const SkRect& cull_rect) : cull_rect_(cull_rect) {} - -PaintOpBuffer::~PaintOpBuffer() { - Reset(); -} - -void PaintOpBuffer::Reset() { - for (auto* op : Iterator(this)) { - auto func = g_destructor_functions[op->type]; - if (func) - func(op); - } - - // Leave data_ allocated, reserved_ unchanged. - used_ = 0; - op_count_ = 0; - num_slow_paths_ = 0; -} - -void PaintOpBuffer::playback(SkCanvas* canvas) const { - // TODO(enne): a PaintRecord that contains a SetMatrix assumes that the - // SetMatrix is local to that PaintRecord itself. Said differently, if you - // translate(x, y), then draw a paint record with a SetMatrix(identity), - // the translation should be preserved instead of clobbering the top level - // transform. This could probably be done more efficiently. - SkMatrix original = canvas->getTotalMatrix(); - - for (Iterator iter(this); iter; ++iter) { - // Optimize out save/restores or save/draw/restore that can be a single - // draw. See also: similar code in SkRecordOpts and cc's DisplayItemList. - // TODO(enne): consider making this recursive? - const PaintOp* op = *iter; - if (op->GetType() == PaintOpType::SaveLayerAlpha) { - const PaintOp* second = iter.peek1(); - if (second) { - if (second->GetType() == PaintOpType::Restore) { - ++iter; - continue; - } - if (second->IsDrawOp()) { - const PaintOp* third = iter.peek2(); - if (third && third->GetType() == PaintOpType::Restore) { - const SaveLayerAlphaOp* save_op = - static_cast<const SaveLayerAlphaOp*>(op); - second->RasterWithAlpha(canvas, save_op->alpha); - ++iter; - ++iter; - continue; - } - } - } - } - // TODO(enne): skip SaveLayer followed by restore with nothing in - // between, however SaveLayer with image filters on it (or maybe - // other PaintFlags options) are not a noop. Figure out what these - // are so we can skip them correctly. - - op->Raster(canvas, original); - } -} - -void PaintOpBuffer::playback(SkCanvas* canvas, - SkPicture::AbortCallback* callback) const { - // The abort callback is only used for analysis, in general, so - // this playback code can be more straightforward and not do the - // optimizations in the other function. - if (!callback) { - playback(canvas); - return; - } - - SkMatrix original = canvas->getTotalMatrix(); - - // TODO(enne): ideally callers would just iterate themselves and we - // can remove the entire notion of an abort callback. - for (auto* op : Iterator(this)) { - op->Raster(canvas, original); - if (callback && callback->abort()) - return; - } -} - -void PaintOpBuffer::ShrinkToFit() { - if (!used_ || used_ == reserved_) - return; - data_.realloc(used_); - reserved_ = used_; -} - -} // namespace cc
diff --git a/cc/paint/paint_op_buffer.h b/cc/paint/paint_op_buffer.h deleted file mode 100644 index 03a4e6b..0000000 --- a/cc/paint/paint_op_buffer.h +++ /dev/null
@@ -1,782 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_PAINT_PAINT_OP_BUFFER_H_ -#define CC_PAINT_PAINT_OP_BUFFER_H_ - -#include <stdint.h> - -#include "base/logging.h" -#include "cc/paint/paint_canvas.h" -#include "cc/paint/paint_export.h" -#include "cc/paint/paint_flags.h" -#include "third_party/skia/include/core/SkPicture.h" -#include "third_party/skia/include/core/SkRect.h" -#include "third_party/skia/include/core/SkTextBlob.h" - -// PaintOpBuffer is a reimplementation of SkLiteDL. -// See: third_party/skia/src/core/SkLiteDL.h. - -namespace cc { - -class DisplayItemList; - -class ThreadsafeMatrix : public SkMatrix { - public: - explicit ThreadsafeMatrix(const SkMatrix& matrix) : SkMatrix(matrix) { - (void)getType(); - } -}; - -class ThreadsafePath : public SkPath { - public: - explicit ThreadsafePath(const SkPath& path) : SkPath(path) { - updateBoundsCache(); - } -}; - -enum class PaintOpType : uint8_t { - Annotate, - ClipPath, - ClipRect, - ClipRRect, - Concat, - DrawArc, - DrawCircle, - DrawColor, - DrawDisplayItemList, - DrawDRRect, - DrawImage, - DrawImageRect, - DrawIRect, - DrawLine, - DrawOval, - DrawPath, - DrawPosText, - DrawRecord, - DrawRect, - DrawRRect, - DrawText, - DrawTextBlob, - Noop, - Restore, - Rotate, - Save, - SaveLayer, - SaveLayerAlpha, - Scale, - SetMatrix, - Translate, - LastPaintOpType = Translate, -}; - -struct CC_PAINT_EXPORT PaintOp { - uint32_t type : 8; - uint32_t skip : 24; - - PaintOpType GetType() const { return static_cast<PaintOpType>(type); } - - void Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const; - bool IsDrawOp() const; - - // Only valid for draw ops. - void RasterWithAlpha(SkCanvas* canvas, uint8_t alpha) const; - - int CountSlowPaths() const { return 0; } - - // Returns the number of bytes used by this op in referenced sub records - // and display lists. This doesn't count other objects like paths or blobs. - size_t AdditionalBytesUsed() const { return 0; } - - static constexpr bool kIsDrawOp = false; - // If an op has |kHasPaintFlags| set to true, it must: - // (1) Provide a PaintFlags member called |flags| - // (2) Provide a RasterWithFlags function instead of a Raster function. - static constexpr bool kHasPaintFlags = false; - static SkRect kUnsetRect; -}; - -struct PaintOpWithData : PaintOp { - // Having data is just a helper for ops that have a varying amount of data and - // want a way to store that inline. This is for ops that pass in a - // void* and a length. - explicit PaintOpWithData(size_t bytes) : bytes(bytes) {} - - // Get data out by calling paint_op_data. This can't be part of the class - // because it needs to know the size of the derived type. - size_t bytes; -}; - -template <typename T> -const void* paint_op_data(const T* op) { - static_assert(std::is_convertible<T, PaintOpWithData>::value, - "T is not a PaintOpWithData"); - // Arbitrary data for a PaintOp is stored after the PaintOp itself - // in the PaintOpBuffer. Therefore, to access this data, it's - // pointer math to increment past the size of T. Accessing the - // next op in the buffer is ((char*)op) + op->skip, with the data - // fitting between. - return op + 1; -} - -template <typename T> -void* paint_op_data(T* op) { - static_assert(std::is_convertible<T, PaintOpWithData>::value, - "T is not a PaintOpWithData"); - return op + 1; -} - -struct PaintOpWithDataArrayBase : PaintOpWithData { - // Helper class for static asserts in push functions. - using PaintOpWithData::PaintOpWithData; -}; - -template <typename T> -struct PaintOpWithDataArray : PaintOpWithDataArrayBase { - // Paint op that has a T[count] and a char[bytes]. - PaintOpWithDataArray(size_t bytes, size_t count) - : PaintOpWithDataArrayBase(bytes), count(count) {} - // Use paint_op_array to get array data. - - size_t count; -}; - -template <typename M, typename T> -const M* paint_op_array(const T* op) { - static_assert(std::is_convertible<T, PaintOpWithDataArrayBase>::value, - "T is not a PaintOpWithDataArray"); - // See comment in paint_op_data. Array data is stored after - // any void* data. Memory layout here is: |op|data|array data|next op| - return SkTAddOffset<const M>(op + 1, op->bytes); -} -template <typename M, typename T> -M* paint_op_array(T* op) { - static_assert(std::is_convertible<T, PaintOpWithDataArrayBase>::value, - "T is not a PaintOpWithDataArray"); - return SkTAddOffset<M>(op + 1, op->bytes); -} - -struct AnnotateOp final : PaintOp { - enum class AnnotationType { - URL, - LinkToDestination, - NamedDestination, - }; - - static constexpr PaintOpType kType = PaintOpType::Annotate; - AnnotateOp(PaintCanvas::AnnotationType annotation_type, - const SkRect& rect, - sk_sp<SkData> data); - ~AnnotateOp(); - void Raster(SkCanvas* canvas) const; - - PaintCanvas::AnnotationType annotation_type; - SkRect rect; - sk_sp<SkData> data; -}; - -struct ClipPathOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::ClipPath; - ClipPathOp(SkPath path, SkClipOp op, bool antialias) - : path(path), op(op), antialias(antialias) {} - void Raster(SkCanvas* canvas) const; - int CountSlowPaths() const; - - ThreadsafePath path; - SkClipOp op; - bool antialias; -}; - -struct ClipRectOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::ClipRect; - ClipRectOp(const SkRect& rect, SkClipOp op, bool antialias) - : rect(rect), op(op), antialias(antialias) {} - void Raster(SkCanvas* canvas) const; - - SkRect rect; - SkClipOp op; - bool antialias; -}; - -struct ClipRRectOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::ClipRRect; - ClipRRectOp(const SkRRect& rrect, SkClipOp op, bool antialias) - : rrect(rrect), op(op), antialias(antialias) {} - void Raster(SkCanvas* canvas) const; - - SkRRect rrect; - SkClipOp op; - bool antialias; -}; - -struct ConcatOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::Concat; - explicit ConcatOp(const SkMatrix& matrix) : matrix(matrix) {} - void Raster(SkCanvas* canvas) const; - - ThreadsafeMatrix matrix; -}; - -struct DrawArcOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawArc; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawArcOp(const SkRect& oval, - SkScalar start_angle, - SkScalar sweep_angle, - bool use_center, - const PaintFlags& flags) - : oval(oval), - start_angle(start_angle), - sweep_angle(sweep_angle), - use_center(use_center), - flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkRect oval; - SkScalar start_angle; - SkScalar sweep_angle; - bool use_center; - PaintFlags flags; -}; - -struct DrawCircleOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawCircle; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawCircleOp(SkScalar cx, - SkScalar cy, - SkScalar radius, - const PaintFlags& flags) - : cx(cx), cy(cy), radius(radius), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkScalar cx; - SkScalar cy; - SkScalar radius; - PaintFlags flags; -}; - -struct DrawColorOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawColor; - static constexpr bool kIsDrawOp = true; - DrawColorOp(SkColor color, SkBlendMode mode) : color(color), mode(mode) {} - void Raster(SkCanvas* canvas) const; - - SkColor color; - SkBlendMode mode; -}; - -struct DrawDisplayItemListOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawDisplayItemList; - static constexpr bool kIsDrawOp = true; - explicit DrawDisplayItemListOp(scoped_refptr<DisplayItemList> list); - ~DrawDisplayItemListOp(); - void Raster(SkCanvas* canvas) const; - size_t AdditionalBytesUsed() const; - // TODO(enne): DisplayItemList should know number of slow paths. - - scoped_refptr<DisplayItemList> list; -}; - -struct DrawDRRectOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawDRRect; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawDRRectOp(const SkRRect& outer, - const SkRRect& inner, - const PaintFlags& flags) - : outer(outer), inner(inner), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkRRect outer; - SkRRect inner; - PaintFlags flags; -}; - -struct DrawImageOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawImage; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawImageOp(sk_sp<const SkImage> image, - SkScalar left, - SkScalar top, - const PaintFlags* flags); - ~DrawImageOp(); - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - sk_sp<const SkImage> image; - SkScalar left; - SkScalar top; - PaintFlags flags; -}; - -struct DrawImageRectOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawImageRect; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawImageRectOp(sk_sp<const SkImage> image, - const SkRect& src, - const SkRect& dst, - const PaintFlags* flags, - PaintCanvas::SrcRectConstraint constraint); - ~DrawImageRectOp(); - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - sk_sp<const SkImage> image; - PaintFlags flags; - SkRect src; - SkRect dst; - PaintCanvas::SrcRectConstraint constraint; -}; - -struct DrawIRectOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawIRect; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawIRectOp(const SkIRect& rect, const PaintFlags& flags) - : rect(rect), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkIRect rect; - PaintFlags flags; -}; - -struct DrawLineOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawLine; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawLineOp(SkScalar x0, - SkScalar y0, - SkScalar x1, - SkScalar y1, - const PaintFlags& flags) - : x0(x0), y0(y0), x1(x1), y1(y1), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - int CountSlowPaths() const; - - SkScalar x0; - SkScalar y0; - SkScalar x1; - SkScalar y1; - PaintFlags flags; -}; - -struct DrawOvalOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawOval; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawOvalOp(const SkRect& oval, const PaintFlags& flags) - : oval(oval), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkRect oval; - PaintFlags flags; -}; - -struct DrawPathOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawPath; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawPathOp(const SkPath& path, const PaintFlags& flags) - : path(path), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - int CountSlowPaths() const; - - ThreadsafePath path; - PaintFlags flags; -}; - -struct DrawPosTextOp final : PaintOpWithDataArray<SkPoint> { - static constexpr PaintOpType kType = PaintOpType::DrawPosText; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawPosTextOp(size_t bytes, size_t count, const PaintFlags& flags); - ~DrawPosTextOp(); - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - PaintFlags flags; -}; - -struct DrawRecordOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawRecord; - static constexpr bool kIsDrawOp = true; - explicit DrawRecordOp(sk_sp<const PaintRecord> record); - ~DrawRecordOp(); - void Raster(SkCanvas* canvas) const; - size_t AdditionalBytesUsed() const; - - sk_sp<const PaintRecord> record; -}; - -struct DrawRectOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawRect; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawRectOp(const SkRect& rect, const PaintFlags& flags) - : rect(rect), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkRect rect; - PaintFlags flags; -}; - -struct DrawRRectOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawRRect; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawRRectOp(const SkRRect& rrect, const PaintFlags& flags) - : rrect(rrect), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkRRect rrect; - PaintFlags flags; -}; - -struct DrawTextOp final : PaintOpWithData { - static constexpr PaintOpType kType = PaintOpType::DrawText; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawTextOp(size_t bytes, SkScalar x, SkScalar y, const PaintFlags& flags) - : PaintOpWithData(bytes), x(x), y(y), flags(flags) {} - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkScalar x; - SkScalar y; - PaintFlags flags; -}; - -struct DrawTextBlobOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::DrawTextBlob; - static constexpr bool kIsDrawOp = true; - static constexpr bool kHasPaintFlags = true; - DrawTextBlobOp(sk_sp<SkTextBlob> blob, - SkScalar x, - SkScalar y, - const PaintFlags& flags); - ~DrawTextBlobOp(); - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - sk_sp<SkTextBlob> blob; - SkScalar x; - SkScalar y; - PaintFlags flags; -}; - -struct NoopOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::Noop; - void Raster(SkCanvas* canvas) const {} -}; - -struct RestoreOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::Restore; - void Raster(SkCanvas* canvas) const; -}; - -struct RotateOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::Rotate; - explicit RotateOp(SkScalar degrees) : degrees(degrees) {} - void Raster(SkCanvas* canvas) const; - - SkScalar degrees; -}; - -struct SaveOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::Save; - void Raster(SkCanvas* canvas) const; -}; - -struct SaveLayerOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::SaveLayer; - static constexpr bool kHasPaintFlags = true; - SaveLayerOp(const SkRect* bounds, const PaintFlags* flags) - : bounds(bounds ? *bounds : kUnsetRect) { - if (flags) - this->flags = *flags; - } - void RasterWithFlags(SkCanvas* canvas, const PaintFlags& flags) const; - - SkRect bounds; - PaintFlags flags; -}; - -struct SaveLayerAlphaOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::SaveLayerAlpha; - SaveLayerAlphaOp(const SkRect* bounds, uint8_t alpha) - : bounds(bounds ? *bounds : kUnsetRect), alpha(alpha) {} - void Raster(SkCanvas* canvas) const; - - SkRect bounds; - uint8_t alpha; -}; - -struct ScaleOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::Scale; - ScaleOp(SkScalar sx, SkScalar sy) : sx(sx), sy(sy) {} - void Raster(SkCanvas* canvas) const; - - SkScalar sx; - SkScalar sy; -}; - -struct SetMatrixOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::SetMatrix; - explicit SetMatrixOp(const SkMatrix& matrix) : matrix(matrix) {} - // This is the only op that needs the original ctm of the SkCanvas - // used for raster (since SetMatrix is relative to the recording origin and - // shouldn't clobber the SkCanvas raster origin). - // - // TODO(enne): Find some cleaner way to do this, possibly by making - // all SetMatrix calls Concat?? - void Raster(SkCanvas* canvas, const SkMatrix& original_ctm) const; - - ThreadsafeMatrix matrix; -}; - -struct TranslateOp final : PaintOp { - static constexpr PaintOpType kType = PaintOpType::Translate; - TranslateOp(SkScalar dx, SkScalar dy) : dx(dx), dy(dy) {} - void Raster(SkCanvas* canvas) const; - - SkScalar dx; - SkScalar dy; -}; - -using LargestPaintOp = DrawDRRectOp; - -class CC_PAINT_EXPORT PaintOpBuffer : public SkRefCnt { - public: - enum { kPageSize = 4096 }; - - PaintOpBuffer(); - explicit PaintOpBuffer(const SkRect& cull_rect); - ~PaintOpBuffer() override; - - void Reset(); - - void playback(SkCanvas* canvas) const; - void playback(SkCanvas* canvas, SkPicture::AbortCallback* callback) const; - - // TODO(enne): These are no longer approximate. Rename these. - int approximateOpCount() const { return op_count_; } - size_t approximateBytesUsed() const { - return sizeof(*this) + reserved_ + subrecord_bytes_used_; - } - int numSlowPaths() const { return num_slow_paths_; } - - // Resize the PaintOpBuffer to exactly fit the current amount of used space. - void ShrinkToFit(); - - const SkRect& cullRect() const { return cull_rect_; } - - PaintOp* GetFirstOp() const { - return reinterpret_cast<PaintOp*>(const_cast<char*>(&first_op_[0])); - } - - template <typename T, typename... Args> - void push(Args&&... args) { - static_assert(std::is_convertible<T, PaintOp>::value, "T not a PaintOp."); - static_assert(!std::is_convertible<T, PaintOpWithData>::value, - "Type needs to use push_with_data"); - push_internal<T>(0, std::forward<Args>(args)...); - } - - template <typename T, typename... Args> - void push_with_data(const void* data, size_t bytes, Args&&... args) { - static_assert(std::is_convertible<T, PaintOpWithData>::value, - "T is not a PaintOpWithData"); -#if !defined(OS_CHROMEOS) - // TODO(enne): non-linux chromeos builds think that DrawTextOp - // can be converted to a PaintOpWithDataArrayBase. OOPS. - static_assert(!std::is_convertible<T, PaintOpWithDataArrayBase>::value, - "Type needs to use push_with_data_array"); -#endif - DCHECK_GE(bytes, 0u); - T* op = push_internal<T>(bytes, bytes, std::forward<Args>(args)...); - memcpy(paint_op_data(op), data, bytes); - -#if DCHECK_IS_ON() - // Double check the data fits between op and next op and doesn't clobber. - char* op_start = reinterpret_cast<char*>(op); - char* op_end = op_start + sizeof(T); - char* next_op = op_start + op->skip; - char* data_start = reinterpret_cast<char*>(paint_op_data(op)); - char* data_end = data_start + bytes; - DCHECK_GE(data_start, op_end); - DCHECK_LT(data_start, next_op); - DCHECK_LE(data_end, next_op); -#endif - } - - template <typename T, typename M, typename... Args> - void push_with_data_array(const void* data, - size_t bytes, - const M* array, - size_t count, - Args&&... args) { - static_assert(std::is_convertible<T, PaintOpWithDataArray<M>>::value, - "T is not a PaintOpWithDataArray"); - DCHECK_GE(bytes, 0u); - DCHECK_GE(count, 0u); - size_t array_size = sizeof(M) * count; - size_t total_size = bytes + array_size; - T* op = - push_internal<T>(total_size, bytes, count, std::forward<Args>(args)...); - memcpy(paint_op_data(op), data, bytes); - memcpy(paint_op_array<M>(op), array, array_size); - -#if DCHECK_IS_ON() - // Double check data and array don't clobber op, next op, or each other - char* op_start = reinterpret_cast<char*>(op); - char* op_end = op_start + sizeof(T); - char* next_op = op_start + op->skip; - char* data_start = reinterpret_cast<char*>(paint_op_data(op)); - char* data_end = data_start + bytes; - char* array_start = reinterpret_cast<char*>(paint_op_array<M>(op)); - char* array_end = array_start + array_size; - DCHECK_GE(data_start, op_end); - DCHECK_LE(data_start, array_start); - DCHECK_GE(array_start, data_end); - DCHECK_LE(array_end, next_op); -#endif - } - - class Iterator { - public: - explicit Iterator(const PaintOpBuffer* buffer) - : buffer_(buffer), ptr_(buffer_->data_.get()) {} - - PaintOp* operator->() const { - return op_idx_ ? reinterpret_cast<PaintOp*>(ptr_) : buffer_->GetFirstOp(); - } - PaintOp* operator*() const { return operator->(); } - Iterator begin() { return Iterator(buffer_, buffer_->data_.get(), 0); } - Iterator end() { - return Iterator(buffer_, buffer_->data_.get() + buffer_->used_, - buffer_->approximateOpCount()); - } - bool operator!=(const Iterator& other) { - // Not valid to compare iterators on different buffers. - DCHECK_EQ(other.buffer_, buffer_); - return other.op_idx_ != op_idx_; - } - Iterator& operator++() { - if (!op_idx_++) - return *this; - PaintOp* op = **this; - uint32_t type = op->type; - CHECK_LE(type, static_cast<uint32_t>(PaintOpType::LastPaintOpType)); - ptr_ += op->skip; - return *this; - } - operator bool() const { return op_idx_ < buffer_->approximateOpCount(); } - - int op_idx() const { return op_idx_; } - - // Return the next op without advancing the iterator, or nullptr if none. - PaintOp* peek1() const { - if (op_idx_ + 1 >= buffer_->approximateOpCount()) - return nullptr; - if (!op_idx_) - return reinterpret_cast<PaintOp*>(ptr_); - return reinterpret_cast<PaintOp*>(ptr_ + (*this)->skip); - } - - // Return the op two ops ahead without advancing the iterator, or nullptr if - // none. - PaintOp* peek2() const { - if (op_idx_ + 2 >= buffer_->approximateOpCount()) - return nullptr; - char* next = ptr_ + reinterpret_cast<PaintOp*>(ptr_)->skip; - PaintOp* next_op = reinterpret_cast<PaintOp*>(next); - if (!op_idx_) - return next_op; - return reinterpret_cast<PaintOp*>(next + next_op->skip); - } - - private: - Iterator(const PaintOpBuffer* buffer, char* ptr, int op_idx) - : buffer_(buffer), ptr_(ptr), op_idx_(op_idx) {} - - const PaintOpBuffer* buffer_ = nullptr; - char* ptr_ = nullptr; - int op_idx_ = 0; - }; - - private: - template <typename T, bool HasFlags> - struct CountSlowPathsFromFlags { - static int Count(const T* op) { return 0; } - }; - - template <typename T> - struct CountSlowPathsFromFlags<T, true> { - static int Count(const T* op) { return op->flags.getPathEffect() ? 1 : 0; } - }; - - template <typename T, typename... Args> - T* push_internal(size_t bytes, Args&&... args) { - size_t skip = SkAlignPtr(sizeof(T) + bytes); - DCHECK_LT(skip, static_cast<size_t>(1) << 24); - if (used_ + skip > reserved_ || !op_count_) { - if (!op_count_) { - if (bytes) { - // Internal first_op buffer doesn't have room for extra data. - // If the op wants extra bytes, then we'll just store a Noop - // in the first_op and proceed from there. This seems unlikely - // to be a common case. - push<NoopOp>(); - } else { - T* op = reinterpret_cast<T*>(&first_op_[0]); - new (op) T{std::forward<Args>(args)...}; - op->type = static_cast<uint32_t>(T::kType); - op->skip = 0; - op_count_++; - return op; - } - } - - static_assert(SkIsPow2(kPageSize), - "This math needs updating for non-pow2."); - // Next greater multiple of kPageSize. - reserved_ = (used_ + skip + kPageSize) & ~(kPageSize - 1); - data_.realloc(reserved_); - } - DCHECK_LE(used_ + skip, reserved_); - - T* op = reinterpret_cast<T*>(data_.get() + used_); - used_ += skip; - new (op) T(std::forward<Args>(args)...); - op->type = static_cast<uint32_t>(T::kType); - op->skip = skip; - op_count_++; - - num_slow_paths_ += CountSlowPathsFromFlags<T, T::kHasPaintFlags>::Count(op); - num_slow_paths_ += op->CountSlowPaths(); - - subrecord_bytes_used_ += op->AdditionalBytesUsed(); - - return op; - } - - // As a performance optimization because n=1 is an extremely common case just - // store the first op in the PaintOpBuffer itself to avoid an extra alloc. - char first_op_[sizeof(LargestPaintOp)]; - SkAutoTMalloc<char> data_; - size_t used_ = 0; - size_t reserved_ = 0; - int op_count_ = 0; - - // Record paths for veto-to-msaa for gpu raster. - int num_slow_paths_ = 0; - // Record additional bytes used by referenced sub-records and display lists. - size_t subrecord_bytes_used_ = 0; - SkRect cull_rect_; - - DISALLOW_COPY_AND_ASSIGN(PaintOpBuffer); -}; - -} // namespace cc - -#endif // CC_PAINT_PAINT_OP_BUFFER_H_
diff --git a/cc/paint/paint_op_buffer_unittest.cc b/cc/paint/paint_op_buffer_unittest.cc deleted file mode 100644 index f3b22da..0000000 --- a/cc/paint/paint_op_buffer_unittest.cc +++ /dev/null
@@ -1,247 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/paint/paint_op_buffer.h" -#include "cc/test/test_skcanvas.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace cc { - -TEST(PaintOpBufferTest, Empty) { - PaintOpBuffer buffer; - EXPECT_EQ(buffer.approximateOpCount(), 0); - EXPECT_EQ(buffer.approximateBytesUsed(), sizeof(PaintOpBuffer)); - EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); - - buffer.Reset(); - EXPECT_EQ(buffer.approximateOpCount(), 0); - EXPECT_EQ(buffer.approximateBytesUsed(), sizeof(PaintOpBuffer)); - EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); -} - -TEST(PaintOpBufferTest, SimpleAppend) { - SkRect rect = SkRect::MakeXYWH(2, 3, 4, 5); - PaintFlags flags; - flags.setColor(SK_ColorMAGENTA); - flags.setAlpha(100); - SkColor draw_color = SK_ColorRED; - SkBlendMode blend = SkBlendMode::kSrc; - - PaintOpBuffer buffer; - buffer.push<SaveLayerOp>(&rect, &flags); - buffer.push<SaveOp>(); - buffer.push<DrawColorOp>(draw_color, blend); - buffer.push<RestoreOp>(); - - EXPECT_EQ(buffer.approximateOpCount(), 4); - - PaintOpBuffer::Iterator iter(&buffer); - ASSERT_EQ(iter->GetType(), PaintOpType::SaveLayer); - SaveLayerOp* save_op = static_cast<SaveLayerOp*>(*iter); - EXPECT_EQ(save_op->bounds, rect); - EXPECT_TRUE(save_op->flags == flags); - ++iter; - - ASSERT_EQ(iter->GetType(), PaintOpType::Save); - ++iter; - - ASSERT_EQ(iter->GetType(), PaintOpType::DrawColor); - DrawColorOp* op = static_cast<DrawColorOp*>(*iter); - EXPECT_EQ(op->color, draw_color); - EXPECT_EQ(op->mode, blend); - ++iter; - - ASSERT_EQ(iter->GetType(), PaintOpType::Restore); - ++iter; - - EXPECT_FALSE(iter); -} - -// PaintOpBuffer has a special case for first ops stored locally, so -// make sure that appending different kind of ops as a first op works -// properly, as well as resetting and reusing the first local op. -TEST(PaintOpBufferTest, FirstOpWithAndWithoutData) { - PaintOpBuffer buffer; - char text[] = "asdf"; - - // Use a color filter and its ref count to verify that the destructor - // is called on ops after reset. - PaintFlags flags; - sk_sp<SkColorFilter> filter = - SkColorFilter::MakeModeFilter(SK_ColorMAGENTA, SkBlendMode::kSrcOver); - flags.setColorFilter(filter); - EXPECT_EQ(filter->getRefCnt(), 2); - - buffer.push_with_data<DrawTextOp>(text, arraysize(text), 0.f, 0.f, flags); - EXPECT_EQ(filter->getRefCnt(), 3); - - // Verify that when the first op has data, which may not fit in the - // PaintRecord internal buffer, that it adds a noop as the first op - // and then appends the "op with data" into the heap buffer. - ASSERT_EQ(buffer.approximateOpCount(), 2); - EXPECT_EQ(buffer.GetFirstOp()->GetType(), PaintOpType::Noop); - - // Verify iteration behavior and brief smoke test of op state. - { - PaintOpBuffer::Iterator iter(&buffer); - PaintOp* noop = *iter; - EXPECT_EQ(buffer.GetFirstOp(), noop); - ++iter; - - PaintOp* op = *iter; - ASSERT_EQ(op->GetType(), PaintOpType::DrawText); - DrawTextOp* draw_text_op = static_cast<DrawTextOp*>(op); - EXPECT_EQ(draw_text_op->bytes, arraysize(text)); - - void* data = paint_op_data(draw_text_op); - EXPECT_EQ(memcmp(data, text, arraysize(text)), 0); - - ++iter; - EXPECT_FALSE(iter); - } - - // Reset, verify state, and append an op that will fit in the first slot. - buffer.Reset(); - EXPECT_EQ(filter->getRefCnt(), 2); - - ASSERT_EQ(buffer.approximateOpCount(), 0); - EXPECT_EQ(PaintOpBuffer::Iterator(&buffer), false); - - SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); - buffer.push<DrawRectOp>(rect, flags); - EXPECT_EQ(filter->getRefCnt(), 3); - - ASSERT_EQ(buffer.approximateOpCount(), 1); - EXPECT_EQ(buffer.GetFirstOp()->GetType(), PaintOpType::DrawRect); - - PaintOpBuffer::Iterator iter(&buffer); - ASSERT_EQ(iter->GetType(), PaintOpType::DrawRect); - DrawRectOp* draw_rect_op = static_cast<DrawRectOp*>(*iter); - EXPECT_EQ(draw_rect_op->rect, rect); - - ++iter; - EXPECT_FALSE(iter); - - buffer.Reset(); - ASSERT_EQ(buffer.approximateOpCount(), 0); - EXPECT_EQ(filter->getRefCnt(), 2); -} - -TEST(PaintOpBufferTest, Peek) { - PaintOpBuffer buffer; - - uint8_t alpha = 100; - buffer.push<SaveLayerAlphaOp>(nullptr, alpha); - PaintFlags draw_flags; - buffer.push<DrawRectOp>(SkRect::MakeXYWH(1, 2, 3, 4), draw_flags); - buffer.push<RestoreOp>(); - buffer.push<SaveOp>(); - buffer.push<NoopOp>(); - buffer.push<RestoreOp>(); - - PaintOpBuffer::Iterator init_iter(&buffer); - PaintOp* peek[2] = {*init_iter, init_iter.peek1()}; - - // Expect that while iterating that next = current.peek1() and that - // next.peek1() == current.peek2(). - for (PaintOpBuffer::Iterator iter(&buffer); iter; ++iter) { - EXPECT_EQ(*iter, peek[0]) << iter.op_idx(); - EXPECT_EQ(iter.peek1(), peek[1]) << iter.op_idx(); - - peek[0] = iter.peek1(); - peek[1] = iter.peek2(); - } -} - -TEST(PaintOpBufferTest, PeekEmpty) { - PaintOpBuffer empty; - PaintOpBuffer::Iterator empty_iter(&empty); - EXPECT_EQ(nullptr, empty_iter.peek1()); - EXPECT_EQ(nullptr, empty_iter.peek2()); -} - -// Verify that a SaveLayerAlpha / Draw / Restore can be optimized to just -// a draw with opacity. -TEST(PaintOpBufferTest, SaveDrawRestore) { - PaintOpBuffer buffer; - - uint8_t alpha = 100; - buffer.push<SaveLayerAlphaOp>(nullptr, alpha); - - PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); - draw_flags.setAlpha(50); - EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); - SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); - buffer.push<DrawRectOp>(rect, draw_flags); - buffer.push<RestoreOp>(); - - SaveCountingCanvas canvas; - buffer.playback(&canvas); - - EXPECT_EQ(0, canvas.save_count_); - EXPECT_EQ(0, canvas.restore_count_); - EXPECT_EQ(rect, canvas.draw_rect_); - - // Expect the alpha from the draw and the save layer to be folded together. - // Since alpha is stored in a uint8_t and gets rounded, so use tolerance. - float expected_alpha = alpha * 50 / 255.f; - EXPECT_LE(std::abs(expected_alpha - canvas.paint_.getAlpha()), 1.f); -} - -// The same as SaveDrawRestore, but test that the optimization doesn't apply -// when the drawing op's flags are not compatible with being folded into the -// save layer with opacity. -TEST(PaintOpBufferTest, SaveDrawRestoreFail_BadFlags) { - PaintOpBuffer buffer; - - uint8_t alpha = 100; - buffer.push<SaveLayerAlphaOp>(nullptr, alpha); - - PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); - draw_flags.setAlpha(50); - draw_flags.setBlendMode(SkBlendMode::kSrc); - EXPECT_FALSE(draw_flags.SupportsFoldingAlpha()); - SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); - buffer.push<DrawRectOp>(rect, draw_flags); - buffer.push<RestoreOp>(); - - SaveCountingCanvas canvas; - buffer.playback(&canvas); - - EXPECT_EQ(1, canvas.save_count_); - EXPECT_EQ(1, canvas.restore_count_); - EXPECT_EQ(rect, canvas.draw_rect_); - EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha()); -} - -// The same as SaveDrawRestore, but test that the optimization doesn't apply -// when there are more than one ops between the save and restore. -TEST(PaintOpBufferTest, SaveDrawRestoreFail_TooManyOps) { - PaintOpBuffer buffer; - - uint8_t alpha = 100; - buffer.push<SaveLayerAlphaOp>(nullptr, alpha); - - PaintFlags draw_flags; - draw_flags.setColor(SK_ColorMAGENTA); - draw_flags.setAlpha(50); - draw_flags.setBlendMode(SkBlendMode::kSrcOver); - EXPECT_TRUE(draw_flags.SupportsFoldingAlpha()); - SkRect rect = SkRect::MakeXYWH(1, 2, 3, 4); - buffer.push<DrawRectOp>(rect, draw_flags); - buffer.push<NoopOp>(); - buffer.push<RestoreOp>(); - - SaveCountingCanvas canvas; - buffer.playback(&canvas); - - EXPECT_EQ(1, canvas.save_count_); - EXPECT_EQ(1, canvas.restore_count_); - EXPECT_EQ(rect, canvas.draw_rect_); - EXPECT_EQ(draw_flags.getAlpha(), canvas.paint_.getAlpha()); -} - -} // namespace cc
diff --git a/cc/paint/paint_record.cc b/cc/paint/paint_record.cc deleted file mode 100644 index 52cb2524..0000000 --- a/cc/paint/paint_record.cc +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/paint/paint_record.h" - -#include "cc/paint/paint_op_buffer.h" -#include "third_party/skia/include/core/SkPictureRecorder.h" - -namespace cc { - -sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record) { - SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(record->cullRect()); - record->playback(canvas); - return recorder.finishRecordingAsPicture(); -} - -sk_sp<const SkPicture> ToSkPicture(sk_sp<const PaintRecord> record) { - SkPictureRecorder recorder; - SkCanvas* canvas = recorder.beginRecording(record->cullRect()); - record->playback(canvas); - return recorder.finishRecordingAsPicture(); -} - -} // namespace cc
diff --git a/cc/paint/paint_record.h b/cc/paint/paint_record.h index daeee004..8506606b 100644 --- a/cc/paint/paint_record.h +++ b/cc/paint/paint_record.h
@@ -5,22 +5,19 @@ #ifndef CC_PAINT_PAINT_RECORD_H_ #define CC_PAINT_PAINT_RECORD_H_ -#include "cc/paint/paint_export.h" -#include "cc/paint/paint_op_buffer.h" #include "third_party/skia/include/core/SkPicture.h" namespace cc { -// TODO(enne): Don't want to rename the world for this. Using these as the -// same types for now prevents an extra allocation. Probably PaintRecord -// will become an interface in the future. -using PaintRecord = PaintOpBuffer; +using PaintRecord = SkPicture; -// TODO(enne): Remove these if possible, they are really expensive. -CC_PAINT_EXPORT sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record); +inline sk_sp<SkPicture> ToSkPicture(sk_sp<PaintRecord> record) { + return record; +} -CC_PAINT_EXPORT sk_sp<const SkPicture> ToSkPicture( - sk_sp<const PaintRecord> record); +inline sk_sp<const SkPicture> ToSkPicture(sk_sp<const PaintRecord> record) { + return record; +} } // namespace cc
diff --git a/cc/paint/paint_recorder.cc b/cc/paint/paint_recorder.cc index c43f965..672f0712 100644 --- a/cc/paint/paint_recorder.cc +++ b/cc/paint/paint_recorder.cc
@@ -4,36 +4,9 @@ #include "cc/paint/paint_recorder.h" -#include "cc/paint/paint_op_buffer.h" - namespace cc { PaintRecorder::PaintRecorder() = default; - PaintRecorder::~PaintRecorder() = default; -PaintCanvas* PaintRecorder::beginRecording(const SkRect& bounds) { - buffer_.reset(new PaintOpBuffer(bounds)); - canvas_.emplace(buffer_.get(), bounds); - return getRecordingCanvas(); -} - -sk_sp<PaintRecord> PaintRecorder::finishRecordingAsPicture() { - // SkPictureRecorder users expect that their saves are automatically - // closed for them. - // - // NOTE: Blink paint in general doesn't appear to need this, but the - // RecordingImageBufferSurface::fallBackToRasterCanvas finishing off the - // current frame depends on this. Maybe we could remove this assumption and - // just have callers do it. - canvas_->restoreToCount(1); - - // Some users (e.g. printing) use the existence of the recording canvas - // to know if recording is finished, so reset it here. - canvas_.reset(); - - buffer_->ShrinkToFit(); - return std::move(buffer_); -} - } // namespace cc
diff --git a/cc/paint/paint_recorder.h b/cc/paint/paint_recorder.h index 7f582b85..2bbea83b 100644 --- a/cc/paint/paint_recorder.h +++ b/cc/paint/paint_recorder.h
@@ -9,36 +9,47 @@ #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/optional.h" +#include "cc/paint/paint_canvas.h" #include "cc/paint/paint_record.h" -#include "cc/paint/record_paint_canvas.h" +#include "cc/paint/skia_paint_canvas.h" +#include "third_party/skia/include/core/SkPictureRecorder.h" namespace cc { -class PaintOpBuffer; - class CC_PAINT_EXPORT PaintRecorder { public: PaintRecorder(); ~PaintRecorder(); - PaintCanvas* beginRecording(const SkRect& bounds); + ALWAYS_INLINE PaintCanvas* beginRecording(const SkRect& bounds) { + uint32_t record_flags = 0; + canvas_.emplace(recorder_.beginRecording(bounds, nullptr, record_flags)); + return getRecordingCanvas(); + } - // TODO(enne): should make everything go through the non-rect version. - // See comments in RecordPaintCanvas ctor for why. - PaintCanvas* beginRecording(SkScalar width, SkScalar height) { - return beginRecording(SkRect::MakeWH(width, height)); + ALWAYS_INLINE PaintCanvas* beginRecording(SkScalar width, SkScalar height) { + uint32_t record_flags = 0; + canvas_.emplace( + recorder_.beginRecording(width, height, nullptr, record_flags)); + return getRecordingCanvas(); } // Only valid between between and finish recording. - ALWAYS_INLINE RecordPaintCanvas* getRecordingCanvas() { + ALWAYS_INLINE PaintCanvas* getRecordingCanvas() { return canvas_.has_value() ? &canvas_.value() : nullptr; } - sk_sp<PaintRecord> finishRecordingAsPicture(); + ALWAYS_INLINE sk_sp<PaintRecord> finishRecordingAsPicture() { + sk_sp<SkPicture> picture = recorder_.finishRecordingAsPicture(); + // Some users (e.g. printing) use the existence of the recording canvas + // to know if recording is finished, so reset it here. + canvas_.reset(); + return sk_ref_sp(static_cast<PaintRecord*>(picture.get())); + } private: - sk_sp<PaintOpBuffer> buffer_; - base::Optional<RecordPaintCanvas> canvas_; + SkPictureRecorder recorder_; + base::Optional<SkiaPaintCanvas> canvas_; DISALLOW_COPY_AND_ASSIGN(PaintRecorder); };
diff --git a/cc/paint/record_paint_canvas.cc b/cc/paint/record_paint_canvas.cc deleted file mode 100644 index 63e134f..0000000 --- a/cc/paint/record_paint_canvas.cc +++ /dev/null
@@ -1,381 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/paint/record_paint_canvas.h" - -#include "base/memory/ptr_util.h" -#include "cc/paint/display_item_list.h" -#include "cc/paint/paint_op_buffer.h" -#include "cc/paint/paint_record.h" -#include "cc/paint/paint_recorder.h" -#include "third_party/skia/include/core/SkAnnotation.h" -#include "third_party/skia/include/core/SkMetaData.h" -#include "third_party/skia/include/utils/SkNWayCanvas.h" - -namespace cc { - -RecordPaintCanvas::RecordPaintCanvas(PaintOpBuffer* buffer, - const SkRect& cull_rect) - : buffer_(buffer), - canvas_(static_cast<int>(std::ceil(cull_rect.right())), - static_cast<int>(std::ceil(cull_rect.bottom()))) { - DCHECK(buffer_); - - // This is part of the "recording canvases have a size, but why" dance. - // By creating a canvas of size (right x bottom) and then clipping it, - // It makes getDeviceClipBounds return the original cull rect, which code - // in GraphicsContextCanvas on Mac expects. (Just creating an SkNoDrawCanvas - // with the cull_rect makes a canvas of size (width x height) instead - // which is incorrect. SkRecorder cheats with private resetForNextCanvas. - canvas_.clipRect(SkRect::Make(cull_rect.roundOut()), SkClipOp::kIntersect, - false); -} - -RecordPaintCanvas::~RecordPaintCanvas() = default; - -SkMetaData& RecordPaintCanvas::getMetaData() { - // This could just be SkMetaData owned by RecordPaintCanvas, but since - // SkCanvas already has one, we might as well use it directly. - return canvas_.getMetaData(); -} - -SkImageInfo RecordPaintCanvas::imageInfo() const { - return canvas_.imageInfo(); -} - -void RecordPaintCanvas::flush() { - // This is a noop when recording. -} - -SkISize RecordPaintCanvas::getBaseLayerSize() const { - return canvas_.getBaseLayerSize(); -} - -bool RecordPaintCanvas::writePixels(const SkImageInfo& info, - const void* pixels, - size_t row_bytes, - int x, - int y) { - NOTREACHED(); - return false; -} - -int RecordPaintCanvas::save() { - buffer_->push<SaveOp>(); - return canvas_.save(); -} - -int RecordPaintCanvas::saveLayer(const SkRect* bounds, - const PaintFlags* flags) { - if (flags) { - if (flags->IsSimpleOpacity()) { - // TODO(enne): maybe more callers should know this and call - // saveLayerAlpha instead of needing to check here. - uint8_t alpha = SkColorGetA(flags->getColor()); - return saveLayerAlpha(bounds, alpha); - } - - // TODO(enne): it appears that image filters affect matrices and color - // matrices affect transparent flags on SkCanvas layers, but it's not clear - // whether those are actually needed and we could just skip ToSkPaint here. - buffer_->push<SaveLayerOp>(bounds, flags); - const SkPaint& paint = ToSkPaint(*flags); - return canvas_.saveLayer(bounds, &paint); - } - buffer_->push<SaveLayerOp>(bounds, flags); - return canvas_.saveLayer(bounds, nullptr); -} - -int RecordPaintCanvas::saveLayerAlpha(const SkRect* bounds, uint8_t alpha) { - buffer_->push<SaveLayerAlphaOp>(bounds, alpha); - return canvas_.saveLayerAlpha(bounds, alpha); -} - -void RecordPaintCanvas::restore() { - buffer_->push<RestoreOp>(); - canvas_.restore(); -} - -int RecordPaintCanvas::getSaveCount() const { - return canvas_.getSaveCount(); -} - -void RecordPaintCanvas::restoreToCount(int save_count) { - DCHECK_GE(save_count, 1); - int diff = canvas_.getSaveCount() - save_count; - DCHECK_GE(diff, 0); - for (int i = 0; i < diff; ++i) - restore(); -} - -void RecordPaintCanvas::translate(SkScalar dx, SkScalar dy) { - buffer_->push<TranslateOp>(dx, dy); - canvas_.translate(dx, dy); -} - -void RecordPaintCanvas::scale(SkScalar sx, SkScalar sy) { - buffer_->push<ScaleOp>(sx, sy); - canvas_.scale(sx, sy); -} - -void RecordPaintCanvas::rotate(SkScalar degrees) { - buffer_->push<RotateOp>(degrees); - canvas_.rotate(degrees); -} - -void RecordPaintCanvas::concat(const SkMatrix& matrix) { - buffer_->push<ConcatOp>(matrix); - canvas_.concat(matrix); -} - -void RecordPaintCanvas::setMatrix(const SkMatrix& matrix) { - buffer_->push<SetMatrixOp>(matrix); - canvas_.setMatrix(matrix); -} - -void RecordPaintCanvas::clipRect(const SkRect& rect, - SkClipOp op, - bool antialias) { - buffer_->push<ClipRectOp>(rect, op, antialias); - canvas_.clipRect(rect, op, antialias); -} - -void RecordPaintCanvas::clipRRect(const SkRRect& rrect, - SkClipOp op, - bool antialias) { - // TODO(enne): does this happen? Should the caller know this? - if (rrect.isRect()) { - clipRect(rrect.getBounds(), op, antialias); - return; - } - buffer_->push<ClipRRectOp>(rrect, op, antialias); - canvas_.clipRRect(rrect, op, antialias); -} - -void RecordPaintCanvas::clipPath(const SkPath& path, - SkClipOp op, - bool antialias) { - if (!path.isInverseFillType() && canvas_.getTotalMatrix().rectStaysRect()) { - // TODO(enne): do these cases happen? should the caller know that this isn't - // a path? - SkRect rect; - if (path.isRect(&rect)) { - clipRect(rect, op, antialias); - return; - } - SkRRect rrect; - if (path.isOval(&rect)) { - rrect.setOval(rect); - clipRRect(rrect, op, antialias); - return; - } - if (path.isRRect(&rrect)) { - clipRRect(rrect, op, antialias); - return; - } - } - - buffer_->push<ClipPathOp>(path, op, antialias); - canvas_.clipPath(path, op, antialias); - return; -} - -bool RecordPaintCanvas::quickReject(const SkRect& rect) const { - return canvas_.quickReject(rect); -} - -bool RecordPaintCanvas::quickReject(const SkPath& path) const { - return canvas_.quickReject(path); -} - -SkRect RecordPaintCanvas::getLocalClipBounds() const { - return canvas_.getLocalClipBounds(); -} - -bool RecordPaintCanvas::getLocalClipBounds(SkRect* bounds) const { - return canvas_.getLocalClipBounds(bounds); -} - -SkIRect RecordPaintCanvas::getDeviceClipBounds() const { - return canvas_.getDeviceClipBounds(); -} - -bool RecordPaintCanvas::getDeviceClipBounds(SkIRect* bounds) const { - return canvas_.getDeviceClipBounds(bounds); -} - -void RecordPaintCanvas::drawColor(SkColor color, SkBlendMode mode) { - buffer_->push<DrawColorOp>(color, mode); -} - -void RecordPaintCanvas::clear(SkColor color) { - buffer_->push<DrawColorOp>(color, SkBlendMode::kSrc); -} - -void RecordPaintCanvas::drawLine(SkScalar x0, - SkScalar y0, - SkScalar x1, - SkScalar y1, - const PaintFlags& flags) { - buffer_->push<DrawLineOp>(x0, y0, x1, y1, flags); -} - -void RecordPaintCanvas::drawRect(const SkRect& rect, const PaintFlags& flags) { - buffer_->push<DrawRectOp>(rect, flags); -} - -void RecordPaintCanvas::drawIRect(const SkIRect& rect, - const PaintFlags& flags) { - buffer_->push<DrawIRectOp>(rect, flags); -} - -void RecordPaintCanvas::drawOval(const SkRect& oval, const PaintFlags& flags) { - buffer_->push<DrawOvalOp>(oval, flags); -} - -void RecordPaintCanvas::drawRRect(const SkRRect& rrect, - const PaintFlags& flags) { - buffer_->push<DrawRRectOp>(rrect, flags); -} - -void RecordPaintCanvas::drawDRRect(const SkRRect& outer, - const SkRRect& inner, - const PaintFlags& flags) { - if (outer.isEmpty()) - return; - if (inner.isEmpty()) { - drawRRect(outer, flags); - return; - } - buffer_->push<DrawDRRectOp>(outer, inner, flags); -} - -void RecordPaintCanvas::drawCircle(SkScalar cx, - SkScalar cy, - SkScalar radius, - const PaintFlags& flags) { - buffer_->push<DrawCircleOp>(cx, cy, radius, flags); -} - -void RecordPaintCanvas::drawArc(const SkRect& oval, - SkScalar start_angle, - SkScalar sweep_angle, - bool use_center, - const PaintFlags& flags) { - buffer_->push<DrawArcOp>(oval, start_angle, sweep_angle, use_center, flags); -} - -void RecordPaintCanvas::drawRoundRect(const SkRect& rect, - SkScalar rx, - SkScalar ry, - const PaintFlags& flags) { - // TODO(enne): move this into base class? - if (rx > 0 && ry > 0) { - SkRRect rrect; - rrect.setRectXY(rect, rx, ry); - drawRRect(rrect, flags); - } else { - drawRect(rect, flags); - } -} - -void RecordPaintCanvas::drawPath(const SkPath& path, const PaintFlags& flags) { - buffer_->push<DrawPathOp>(path, flags); -} - -void RecordPaintCanvas::drawImage(sk_sp<const SkImage> image, - SkScalar left, - SkScalar top, - const PaintFlags* flags) { - buffer_->push<DrawImageOp>(image, left, top, flags); -} - -void RecordPaintCanvas::drawImageRect(sk_sp<const SkImage> image, - const SkRect& src, - const SkRect& dst, - const PaintFlags* flags, - SrcRectConstraint constraint) { - buffer_->push<DrawImageRectOp>(image, src, dst, flags, constraint); -} - -void RecordPaintCanvas::drawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const PaintFlags* flags) { - // TODO(enne): Move into base class? - if (bitmap.drawsNothing()) - return; - drawImage(SkImage::MakeFromBitmap(bitmap), left, top, flags); -} - -void RecordPaintCanvas::drawText(const void* text, - size_t byte_length, - SkScalar x, - SkScalar y, - const PaintFlags& flags) { - buffer_->push_with_data<DrawTextOp>(text, byte_length, x, y, flags); -} - -void RecordPaintCanvas::drawPosText(const void* text, - size_t byte_length, - const SkPoint pos[], - const PaintFlags& flags) { - size_t count = ToSkPaint(flags).countText(text, byte_length); - buffer_->push_with_data_array<DrawPosTextOp>(text, byte_length, pos, count, - flags); -} - -void RecordPaintCanvas::drawTextBlob(sk_sp<SkTextBlob> blob, - SkScalar x, - SkScalar y, - const PaintFlags& flags) { - buffer_->push<DrawTextBlobOp>(blob, x, y, flags); -} - -void RecordPaintCanvas::drawDisplayItemList( - scoped_refptr<DisplayItemList> list) { - buffer_->push<DrawDisplayItemListOp>(list); -} - -void RecordPaintCanvas::drawPicture(sk_sp<const PaintRecord> record) { - // TODO(enne): If this is small, maybe flatten it? - buffer_->push<DrawRecordOp>(record); -} - -bool RecordPaintCanvas::isClipEmpty() const { - return canvas_.isClipEmpty(); -} - -bool RecordPaintCanvas::isClipRect() const { - return canvas_.isClipRect(); -} - -const SkMatrix& RecordPaintCanvas::getTotalMatrix() const { - return canvas_.getTotalMatrix(); -} - -void RecordPaintCanvas::temporary_internal_describeTopLayer( - SkMatrix* matrix, - SkIRect* clip_bounds) { - return canvas_.temporary_internal_describeTopLayer(matrix, clip_bounds); -} - -bool RecordPaintCanvas::ToPixmap(SkPixmap* output) { - // TODO(enne): It'd be nice to make this NOTREACHED() or remove this from - // RecordPaintCanvas, but this is used by GraphicsContextCanvas for knowing - // whether or not it can raster directly into pixels with Cg. - return false; -} - -void RecordPaintCanvas::Annotate(AnnotationType type, - const SkRect& rect, - sk_sp<SkData> data) { - buffer_->push<AnnotateOp>(type, rect, data); -} - -void RecordPaintCanvas::PlaybackPaintRecord(sk_sp<const PaintRecord> record) { - drawPicture(record); -} - -} // namespace cc
diff --git a/cc/paint/record_paint_canvas.h b/cc/paint/record_paint_canvas.h deleted file mode 100644 index e39697c..0000000 --- a/cc/paint/record_paint_canvas.h +++ /dev/null
@@ -1,160 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_PAINT_RECORD_PAINT_CANVAS_H_ -#define CC_PAINT_RECORD_PAINT_CANVAS_H_ - -#include <memory> - -#include "base/compiler_specific.h" -#include "base/logging.h" -#include "base/macros.h" -#include "build/build_config.h" -#include "cc/paint/paint_canvas.h" -#include "cc/paint/paint_flags.h" -#include "cc/paint/paint_record.h" -#include "third_party/skia/include/utils/SkNoDrawCanvas.h" - -namespace cc { - -class PaintOpBuffer; -class PaintFlags; - -class CC_PAINT_EXPORT RecordPaintCanvas final : public PaintCanvas { - public: - explicit RecordPaintCanvas(PaintOpBuffer* buffer, const SkRect& cull_rect); - ~RecordPaintCanvas() override; - - SkMetaData& getMetaData() override; - SkImageInfo imageInfo() const override; - - void flush() override; - - SkISize getBaseLayerSize() const override; - bool writePixels(const SkImageInfo& info, - const void* pixels, - size_t row_bytes, - int x, - int y) override; - int save() override; - int saveLayer(const SkRect* bounds, const PaintFlags* flags) override; - int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) override; - - void restore() override; - int getSaveCount() const override; - void restoreToCount(int save_count) override; - void translate(SkScalar dx, SkScalar dy) override; - void scale(SkScalar sx, SkScalar sy) override; - void rotate(SkScalar degrees) override; - void concat(const SkMatrix& matrix) override; - void setMatrix(const SkMatrix& matrix) override; - - void clipRect(const SkRect& rect, SkClipOp op, bool antialias) override; - void clipRRect(const SkRRect& rrect, SkClipOp op, bool antialias) override; - void clipPath(const SkPath& path, SkClipOp op, bool antialias) override; - bool quickReject(const SkRect& rect) const override; - bool quickReject(const SkPath& path) const override; - SkRect getLocalClipBounds() const override; - bool getLocalClipBounds(SkRect* bounds) const override; - SkIRect getDeviceClipBounds() const override; - bool getDeviceClipBounds(SkIRect* bounds) const override; - void drawColor(SkColor color, SkBlendMode mode) override; - void clear(SkColor color) override; - - void drawLine(SkScalar x0, - SkScalar y0, - SkScalar x1, - SkScalar y1, - const PaintFlags& flags) override; - void drawRect(const SkRect& rect, const PaintFlags& flags) override; - void drawIRect(const SkIRect& rect, const PaintFlags& flags) override; - void drawOval(const SkRect& oval, const PaintFlags& flags) override; - void drawRRect(const SkRRect& rrect, const PaintFlags& flags) override; - void drawDRRect(const SkRRect& outer, - const SkRRect& inner, - const PaintFlags& flags) override; - void drawCircle(SkScalar cx, - SkScalar cy, - SkScalar radius, - const PaintFlags& flags) override; - void drawArc(const SkRect& oval, - SkScalar start_angle, - SkScalar sweep_angle, - bool use_center, - const PaintFlags& flags) override; - void drawRoundRect(const SkRect& rect, - SkScalar rx, - SkScalar ry, - const PaintFlags& flags) override; - void drawPath(const SkPath& path, const PaintFlags& flags) override; - void drawImage(sk_sp<const SkImage> image, - SkScalar left, - SkScalar top, - const PaintFlags* flags) override; - void drawImageRect(sk_sp<const SkImage> image, - const SkRect& src, - const SkRect& dst, - const PaintFlags* flags, - SrcRectConstraint constraint) override; - void drawBitmap(const SkBitmap& bitmap, - SkScalar left, - SkScalar top, - const PaintFlags* flags) override; - - void drawText(const void* text, - size_t byte_length, - SkScalar x, - SkScalar y, - const PaintFlags& flags) override; - void drawPosText(const void* text, - size_t byte_length, - const SkPoint pos[], - const PaintFlags& flags) override; - void drawTextBlob(sk_sp<SkTextBlob> blob, - SkScalar x, - SkScalar y, - const PaintFlags& flags) override; - - void drawDisplayItemList( - scoped_refptr<DisplayItemList> display_item_list) override; - - void drawPicture(sk_sp<const PaintRecord> record) override; - - bool isClipEmpty() const override; - bool isClipRect() const override; - const SkMatrix& getTotalMatrix() const override; - - void temporary_internal_describeTopLayer(SkMatrix* matrix, - SkIRect* clip_bounds) override; - - bool ToPixmap(SkPixmap* output) override; - void Annotate(AnnotationType type, - const SkRect& rect, - sk_sp<SkData> data) override; - - void PlaybackPaintRecord(sk_sp<const PaintRecord> record) override; - - // Don't shadow non-virtual helper functions. - using PaintCanvas::clipRect; - using PaintCanvas::clipRRect; - using PaintCanvas::clipPath; - using PaintCanvas::drawBitmap; - using PaintCanvas::drawColor; - using PaintCanvas::drawImage; - using PaintCanvas::drawPicture; - - private: - PaintOpBuffer* buffer_; - - // TODO(enne): Although RecordPaintCanvas is mostly a write-only interface - // where paint commands are stored, occasionally users of PaintCanvas want - // to ask stateful questions mid-stream of clip and transform state. - // To avoid duplicating all this code (for now?), just forward to an SkCanvas - // that's not backed by anything but can answer these questions. - SkNoDrawCanvas canvas_; -}; - -} // namespace cc - -#endif // CC_PAINT_RECORD_PAINT_CANVAS_H_
diff --git a/cc/paint/skia_paint_canvas.cc b/cc/paint/skia_paint_canvas.cc index 4000942d..94b0cfb7 100644 --- a/cc/paint/skia_paint_canvas.cc +++ b/cc/paint/skia_paint_canvas.cc
@@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "cc/paint/skia_paint_canvas.h" +#include "cc/paint/paint_canvas.h" #include "base/memory/ptr_util.h" #include "cc/paint/display_item_list.h" @@ -58,7 +58,7 @@ return canvas_->saveLayer(bounds, ToSkPaint(flags)); } -int SkiaPaintCanvas::saveLayerAlpha(const SkRect* bounds, uint8_t alpha) { +int SkiaPaintCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) { return canvas_->saveLayerAlpha(bounds, alpha); }
diff --git a/cc/paint/skia_paint_canvas.h b/cc/paint/skia_paint_canvas.h index aaed2b4..669ca9b 100644 --- a/cc/paint/skia_paint_canvas.h +++ b/cc/paint/skia_paint_canvas.h
@@ -46,7 +46,7 @@ int y) override; int save() override; int saveLayer(const SkRect* bounds, const PaintFlags* flags) override; - int saveLayerAlpha(const SkRect* bounds, uint8_t alpha) override; + int saveLayerAlpha(const SkRect* bounds, U8CPU alpha) override; void restore() override; int getSaveCount() const override;
diff --git a/cc/test/test_skcanvas.cc b/cc/test/test_skcanvas.cc deleted file mode 100644 index e45f42de..0000000 --- a/cc/test/test_skcanvas.cc +++ /dev/null
@@ -1,26 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "cc/test/test_skcanvas.h" - -namespace cc { - -SaveCountingCanvas::SaveCountingCanvas() : SkNoDrawCanvas(100, 100) {} - -SkCanvas::SaveLayerStrategy SaveCountingCanvas::getSaveLayerStrategy( - const SaveLayerRec& rec) { - save_count_++; - return SkNoDrawCanvas::getSaveLayerStrategy(rec); -} - -void SaveCountingCanvas::willRestore() { - restore_count_++; -} - -void SaveCountingCanvas::onDrawRect(const SkRect& rect, const SkPaint& paint) { - draw_rect_ = rect; - paint_ = paint; -} - -} // namespace cc
diff --git a/cc/test/test_skcanvas.h b/cc/test/test_skcanvas.h deleted file mode 100644 index 2b130a4..0000000 --- a/cc/test/test_skcanvas.h +++ /dev/null
@@ -1,31 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CC_TEST_TEST_SKCANVAS_H_ -#define CC_TEST_TEST_SKCANVAS_H_ - -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/utils/SkNoDrawCanvas.h" - -namespace cc { - -class SaveCountingCanvas : public SkNoDrawCanvas { - public: - SaveCountingCanvas(); - - // Note: getSaveLayerStrategy is used as "willSave", as willSave - // is not always called. - SaveLayerStrategy getSaveLayerStrategy(const SaveLayerRec& rec) override; - void willRestore() override; - void onDrawRect(const SkRect& rect, const SkPaint& paint) override; - - int save_count_ = 0; - int restore_count_ = 0; - SkRect draw_rect_; - SkPaint paint_; -}; - -} // namespace cc - -#endif // CC_TEST_TEST_SKCANVAS_H_
diff --git a/cc/tiles/gpu_image_decode_cache.cc b/cc/tiles/gpu_image_decode_cache.cc index 0f152a44..39a8226 100644 --- a/cc/tiles/gpu_image_decode_cache.cc +++ b/cc/tiles/gpu_image_decode_cache.cc
@@ -1130,9 +1130,6 @@ } } - // TODO(ccameron,msarett): Convert image to target color space. - // http://crbug.com/706613 - if (image_data->decode.data()) { // An at-raster task decoded this before us. Ingore our decode. return; @@ -1196,6 +1193,14 @@ image_data->decode.mark_used(); DCHECK(uploaded_image); + if (draw_image.target_color_space().IsValid()) { + TRACE_EVENT0("cc", "GpuImageDecodeCache::UploadImage - color conversion"); + uploaded_image = uploaded_image->makeColorSpace( + draw_image.target_color_space().ToSkColorSpace(), + SkTransferFunctionBehavior::kIgnore); + } + DCHECK(uploaded_image); + // At-raster may have decoded this while we were unlocked. If so, ignore our // result. if (!image_data->upload.image()) @@ -1214,8 +1219,7 @@ draw_image.matrix(), CalculateUploadScaleFilterQuality(draw_image), upload_scale_mip_level); size_t data_size = draw_image.image()->getDeferredTextureImageData( - *context_threadsafe_proxy_.get(), ¶ms, 1, nullptr, - draw_image.target_color_space().ToSkColorSpace().get()); + *context_threadsafe_proxy_.get(), ¶ms, 1, nullptr, nullptr); if (data_size == 0) { // Can't upload image, too large or other failure. Try to use SW fallback.
diff --git a/cc/tiles/software_image_decode_cache.cc b/cc/tiles/software_image_decode_cache.cc index d7c1059..716d1f4 100644 --- a/cc/tiles/software_image_decode_cache.cc +++ b/cc/tiles/software_image_decode_cache.cc
@@ -446,7 +446,7 @@ case kLow_SkFilterQuality: if (key.should_use_subrect()) return GetSubrectImageDecode(key, std::move(image)); - return GetOriginalImageDecode(std::move(image)); + return GetOriginalSizeImageDecode(key, std::move(image)); case kMedium_SkFilterQuality: case kHigh_SkFilterQuality: return GetScaledImageDecode(key, std::move(image)); @@ -563,22 +563,35 @@ } std::unique_ptr<SoftwareImageDecodeCache::DecodedImage> -SoftwareImageDecodeCache::GetOriginalImageDecode(sk_sp<const SkImage> image) { +SoftwareImageDecodeCache::GetOriginalSizeImageDecode( + const ImageKey& key, + sk_sp<const SkImage> image) { SkImageInfo decoded_info = CreateImageInfo(image->width(), image->height(), format_); + sk_sp<SkColorSpace> target_color_space = + key.target_color_space().ToSkColorSpace(); + std::unique_ptr<base::DiscardableMemory> decoded_pixels; { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "SoftwareImageDecodeCache::GetOriginalImageDecode - " + "SoftwareImageDecodeCache::GetOriginalSizeImageDecode - " "allocate decoded pixels"); decoded_pixels = base::DiscardableMemoryAllocator::GetInstance() ->AllocateLockedDiscardableMemory(decoded_info.minRowBytes() * decoded_info.height()); } + if (target_color_space) { + TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), + "SoftwareImageDecodeCache::GetOriginalSizeImageDecode - " + "color conversion"); + image = image->makeColorSpace(target_color_space, + SkTransferFunctionBehavior::kIgnore); + DCHECK(image); + } { TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("cc.debug"), - "SoftwareImageDecodeCache::GetOriginalImageDecode - " + "SoftwareImageDecodeCache::GetOriginalSizeImageDecode - " "read pixels"); bool result = image->readPixels(decoded_info, decoded_pixels->data(), decoded_info.minRowBytes(), 0, 0, @@ -590,12 +603,10 @@ } } - // TODO(ccameron,msarett): Convert image to target color space. - // http://crbug.com/706613 - - return base::MakeUnique<DecodedImage>(decoded_info, std::move(decoded_pixels), - SkSize::Make(0, 0), - next_tracing_id_.GetNext()); + return base::MakeUnique<DecodedImage>( + decoded_info.makeColorSpace(target_color_space), + std::move(decoded_pixels), SkSize::Make(0, 0), + next_tracing_id_.GetNext()); } std::unique_ptr<SoftwareImageDecodeCache::DecodedImage> @@ -609,6 +620,9 @@ kNone_SkFilterQuality, SkMatrix::I(), key.target_color_space()); ImageKey original_size_key = ImageKey::FromDrawImage(original_size_draw_image); + sk_sp<SkColorSpace> target_color_space = + key.target_color_space().ToSkColorSpace(); + // Sanity checks. DCHECK(original_size_key.can_use_original_size_decode()) << original_size_key.ToString(); @@ -621,6 +635,8 @@ if (!decoded_draw_image.image()) return nullptr; + DCHECK(SkColorSpace::Equals(decoded_draw_image.image()->colorSpace(), + target_color_space.get())); SkImageInfo subrect_info = CreateImageInfo( key.target_size().width(), key.target_size().height(), format_); std::unique_ptr<base::DiscardableMemory> subrect_pixels; @@ -649,11 +665,9 @@ DCHECK(result); } - // TODO(ccameron,msarett): Convert image to target color space. - // http://crbug.com/706613 - return base::WrapUnique( - new DecodedImage(subrect_info, std::move(subrect_pixels), + new DecodedImage(subrect_info.makeColorSpace(target_color_space), + std::move(subrect_pixels), SkSize::Make(-key.src_rect().x(), -key.src_rect().y()), next_tracing_id_.GetNext())); } @@ -669,6 +683,9 @@ kNone_SkFilterQuality, SkMatrix::I(), key.target_color_space()); ImageKey original_size_key = ImageKey::FromDrawImage(original_size_draw_image); + sk_sp<SkColorSpace> target_color_space = + key.target_color_space().ToSkColorSpace(); + // Sanity checks. DCHECK(original_size_key.can_use_original_size_decode()) << original_size_key.ToString(); @@ -691,6 +708,8 @@ } DCHECK(!key.target_size().IsEmpty()); + DCHECK(SkColorSpace::Equals(decoded_draw_image.image()->colorSpace(), + target_color_space.get())); SkImageInfo scaled_info = CreateImageInfo( key.target_size().width(), key.target_size().height(), format_); std::unique_ptr<base::DiscardableMemory> scaled_pixels; @@ -714,11 +733,9 @@ DCHECK(result) << key.ToString(); } - // TODO(ccameron,msarett): Convert image to target color space. - // http://crbug.com/706613 - return base::MakeUnique<DecodedImage>( - scaled_info, std::move(scaled_pixels), + scaled_info.makeColorSpace(decoded_draw_image.image()->refColorSpace()), + std::move(scaled_pixels), SkSize::Make(-key.src_rect().x(), -key.src_rect().y()), next_tracing_id_.GetNext()); }
diff --git a/cc/tiles/software_image_decode_cache.h b/cc/tiles/software_image_decode_cache.h index 86b9d7f..1d3e2ac 100644 --- a/cc/tiles/software_image_decode_cache.h +++ b/cc/tiles/software_image_decode_cache.h
@@ -249,18 +249,20 @@ DecodedDrawImage GetDecodedImageForDrawInternal(const ImageKey& key, const DrawImage& draw_image); - // GetOriginalImageDecode is called by DecodeImageInternal when the quality - // does not scale the image. Like DecodeImageInternal, it should be called - // with no lock acquired and it returns nullptr if the decoding failed. - std::unique_ptr<DecodedImage> GetOriginalImageDecode( + // GetOriginalSizeImageDecode is called by DecodeImageInternal when the + // quality does not scale the image. Like DecodeImageInternal, it should be + // called with no lock acquired and it returns nullptr if the decoding failed. + std::unique_ptr<DecodedImage> GetOriginalSizeImageDecode( + const ImageKey& key, sk_sp<const SkImage> image); - // GetSubrectImageDecode is similar to GetOriginalImageDecode in that no scale - // is performed on the image. However, we extract a subrect (copy it out) and - // only return this subrect in order to cache a smaller amount of memory. Note - // that this uses GetOriginalImageDecode to get the initial data, which - // ensures that we cache an unlocked version of the original image in case we - // need to extract multiple subrects (as would be the case in an atlas). + // GetSubrectImageDecode is similar to GetOriginalSizeImageDecode in that no + // scale is performed on the image. However, we extract a subrect (copy it + // out) and only return this subrect in order to cache a smaller amount of + // memory. Note that this uses GetOriginalSizeImageDecode to get the initial + // data, which ensures that we cache an unlocked version of the original image + // in case we need to extract multiple subrects (as would be the case in an + // atlas). std::unique_ptr<DecodedImage> GetSubrectImageDecode( const ImageKey& key, sk_sp<const SkImage> image);
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn index 57b41d5d..2d73b88 100644 --- a/chrome/android/BUILD.gn +++ b/chrome/android/BUILD.gn
@@ -360,7 +360,10 @@ "//components/web_restrictions:web_restrictions_java", "//content/public/android:content_java", "//device/geolocation:geolocation_java", + "//mojo/public/java:bindings_java", + "//mojo/public/java:system_java", "//net/android:net_java", + "//third_party/WebKit/public:android_mojo_bindings_java", "//third_party/WebKit/public:blink_headers_java", "//third_party/WebKit/public:mojo_bindings_java", "//third_party/android_tools:android_support_annotations_java", @@ -370,6 +373,7 @@ "//third_party/cacheinvalidation:cacheinvalidation_javalib", "//third_party/hamcrest:hamcrest_java", "//ui/android:ui_java", + "//url/mojo:url_mojom_gurl_java", google_play_services_library, ] srcjar_deps = [ "//base:base_build_config_gen" ]
diff --git a/chrome/android/java/AndroidManifest.xml b/chrome/android/java/AndroidManifest.xml index a528f07..46c4a51 100644 --- a/chrome/android/java/AndroidManifest.xml +++ b/chrome/android/java/AndroidManifest.xml
@@ -36,6 +36,7 @@ {% endif %} <uses-permission-sdk-m android:name="android.permission.BLUETOOTH"/> <uses-permission-sdk-m android:name="android.permission.BLUETOOTH_ADMIN"/> + <uses-permission-sdk-m android:name="android.permission.REORDER_TASKS"/> <uses-permission-sdk-m android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> <uses-permission android:name="android.permission.CAMERA" /> @@ -365,41 +366,7 @@ {% block supports_video_persistence %} {% endblock %} > - <!-- - See the VRChromeTabbedActivity alias below for an explanation of this dummy intent - filter. We need to add these filters here as well, or non-presenting webVR pages will - trigger a daydream incompatible app message. - --> - <intent-filter> - <action android:name="org.chromium.chrome.browser.dummy.action" /> - <category android:name="com.google.intent.category.DAYDREAM" /> - <category android:name="com.google.intent.category.CARDBOARD" /> - </intent-filter> </activity> - - {% if enable_vr == "true" %} - <!-- - TODO(mthiesse): Temporarily skip ChromeLauncherActivity when returning from Daydream - DON flow to avoid polluting metrics. - --> - <activity-alias android:name="org.chromium.chrome.browser.VRChromeTabbedActivity" - android:targetActivity="org.chromium.chrome.browser.ChromeTabbedActivity" - android:enableVrMode="@string/gvr_vr_mode_component"> - <!-- - Daydream api categorizes an activity to three categories: Cardboard only, hybrid - or Daydream. It does so by testing if intents can be resolved by the activity - that requests it. - In Chrome, CTA is the activity that uses Daydream api and we want to be in hybrid - category. So add an intent filter that could pass Daydream tests here. - --> - <intent-filter> - <action android:name="org.chromium.chrome.browser.dummy.action" /> - <category android:name="com.google.intent.category.DAYDREAM" /> - <category android:name="com.google.intent.category.CARDBOARD" /> - </intent-filter> - </activity-alias> - {% endif %} - <activity android:name="org.chromium.chrome.browser.ChromeTabbedActivity2" android:theme="@style/TabbedModeTheme" android:exported="false" @@ -731,8 +698,7 @@ {% if channel in ['canary', 'default'] %} <!-- Search widget --> - <receiver android:name="org.chromium.chrome.browser.searchwidget.SearchWidgetProvider" - android:process=":search_widget"> + <receiver android:name="org.chromium.chrome.browser.searchwidget.SearchWidgetProvider"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> @@ -748,7 +714,7 @@ <!-- Search Activity --> <activity android:name="org.chromium.chrome.browser.searchwidget.SearchActivity" - android:theme="@style/MainTheme" + android:theme="@style/SearchActivityTheme" android:label="Search" android:exported="false" android:launchMode="singleTask"
diff --git a/chrome/android/java/res/layout/search_activity.xml b/chrome/android/java/res/layout/search_activity.xml index 739da14..b468890f 100644 --- a/chrome/android/java/res/layout/search_activity.xml +++ b/chrome/android/java/res/layout/search_activity.xml
@@ -6,8 +6,7 @@ <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/control_container" android:layout_width="match_parent" - android:layout_height="match_parent" - android:background="@color/google_grey_500" > + android:layout_height="match_parent" > <!-- This view is unnecessary visually, but required for the LocationBarLayout. --> <org.chromium.chrome.browser.searchwidget.SearchActivityFadingBackgroundView
diff --git a/chrome/android/java/res/values-v17/styles.xml b/chrome/android/java/res/values-v17/styles.xml index beace155..a88aab4c8 100644 --- a/chrome/android/java/res/values-v17/styles.xml +++ b/chrome/android/java/res/values-v17/styles.xml
@@ -42,6 +42,10 @@ <item name="android:windowSharedElementExitTransition" tools:targetApi="21">@transition/move_image</item> </style> + <style name="SearchActivityTheme" parent="MainTheme"> + <item name="android:windowBackground">@color/google_grey_500</item> + </style> + <style name="TabbedModeTheme" parent="MainTheme"> <item name="android:windowBackground">@drawable/window_background</item> </style>
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/AppIndexingUtil.java b/chrome/android/java/src/org/chromium/chrome/browser/AppIndexingUtil.java index 4e4aca0..8b54604ab 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/AppIndexingUtil.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/AppIndexingUtil.java
@@ -4,9 +4,12 @@ package org.chromium.chrome.browser; +import android.os.SystemClock; +import android.util.LruCache; import android.webkit.URLUtil; import org.chromium.base.SysUtils; +import org.chromium.base.VisibleForTesting; import org.chromium.blink.mojom.document_metadata.CopylessPaste; import org.chromium.blink.mojom.document_metadata.WebPage; import org.chromium.chrome.browser.historyreport.AppIndexingReporter; @@ -19,31 +22,124 @@ * This is the top-level CopylessPaste metadata extraction for AppIndexing. */ public class AppIndexingUtil { - public static void extractCopylessPasteMetadata(final Tab tab) { - String url = tab.getUrl(); + private static final int CACHE_SIZE = 100; + private static final int CACHE_VISIT_CUTOFF_MS = 60 * 60 * 1000; // 1 hour + // Cache of recently seen urls. If a url is among the CACHE_SIZE most recent pages visited, and + // the parse was in the last CACHE_VISIT_CUTOFF_MS milliseconds, then we don't parse the page, + // and instead just report the view (not the content) to App Indexing. + private LruCache<String, CacheEntry> mPageCache; + + /** + * Extracts entities from document metadata and reports it to on-device App Indexing. + * This call can cache entities from recently parsed webpages, in which case, only the url and + * title of the page is reported to App Indexing. + */ + public void extractCopylessPasteMetadata(final Tab tab) { + final String url = tab.getUrl(); boolean isHttpOrHttps = URLUtil.isHttpsUrl(url) || URLUtil.isHttpUrl(url); - if (SysUtils.isLowEndDevice() || tab.isIncognito() - || !ChromeFeatureList.isEnabled(ChromeFeatureList.COPYLESS_PASTE) - || !isHttpOrHttps) { + if (!isEnabledForDevice() || tab.isIncognito() || !isHttpOrHttps) { return; } + // There are three conditions that can occur with respect to the cache. + // 1. Cache hit, and an entity was found previously. Report only the page view to App + // Indexing. + // 2. Cache hit, but no entity was found. Ignore. + // 3. Cache miss, we need to parse the page. + if (wasPageVisitedRecently(url)) { + if (lastPageVisitContainedEntity(url)) { + // Condition 1 + getAppIndexingReporter().reportWebPageView(url, tab.getTitle()); + } + // Condition 2 + } else { + // Condition 3 + CopylessPaste copylessPaste = getCopylessPasteInterface(tab); + if (copylessPaste == null) { + return; + } + copylessPaste.getEntities(new CopylessPaste.GetEntitiesResponse() { + @Override + public void call(WebPage webpage) { + putCacheEntry(url, webpage != null); + if (webpage == null) return; + getAppIndexingReporter().reportWebPage(webpage); + } + }); + } + } + + private boolean wasPageVisitedRecently(String url) { + if (url == null) { + return false; + } + CacheEntry entry = getPageCache().get(url); + if (entry == null || (getElapsedTime() - entry.lastSeenTimeMs > CACHE_VISIT_CUTOFF_MS)) { + return false; + } + return true; + } + + /** + * Returns true if the page is in the cache and it contained an entity the last time it was + * parsed. + */ + private boolean lastPageVisitContainedEntity(String url) { + if (url == null) { + return false; + } + CacheEntry entry = getPageCache().get(url); + if (entry == null || !entry.containedEntity) { + return false; + } + return true; + } + + private void putCacheEntry(String url, boolean containedEntity) { + CacheEntry entry = new CacheEntry(); + entry.lastSeenTimeMs = getElapsedTime(); + entry.containedEntity = containedEntity; + getPageCache().put(url, entry); + } + + @VisibleForTesting + AppIndexingReporter getAppIndexingReporter() { + return AppIndexingReporter.getInstance(); + } + + @VisibleForTesting + CopylessPaste getCopylessPasteInterface(Tab tab) { WebContents webContents = tab.getWebContents(); - if (webContents == null) return; + if (webContents == null) return null; RenderFrameHost mainFrame = webContents.getMainFrame(); - if (mainFrame == null) return; + if (mainFrame == null) return null; InterfaceProvider interfaces = mainFrame.getRemoteInterfaces(); - if (interfaces == null) return; + if (interfaces == null) return null; - CopylessPaste copylesspaste = interfaces.getInterface(CopylessPaste.MANAGER); - copylesspaste.getEntities(new CopylessPaste.GetEntitiesResponse() { - @Override - public void call(WebPage webpage) { - if (webpage == null) return; - AppIndexingReporter.getInstance().reportWebPage(webpage); - } - }); + return interfaces.getInterface(CopylessPaste.MANAGER); + } + + @VisibleForTesting + long getElapsedTime() { + return SystemClock.elapsedRealtime(); + } + + boolean isEnabledForDevice() { + return !SysUtils.isLowEndDevice() + && ChromeFeatureList.isEnabled(ChromeFeatureList.COPYLESS_PASTE); + } + + private LruCache<String, CacheEntry> getPageCache() { + if (mPageCache == null) { + mPageCache = new LruCache<String, CacheEntry>(CACHE_SIZE); + } + return mPageCache; + } + + private static class CacheEntry { + public long lastSeenTimeMs; + public boolean containedEntity; } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java index 3fae79dc..aff7e4b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeActivity.java
@@ -853,7 +853,7 @@ MultiWindowUtils.getInstance().isInMultiWindowMode(this)); VideoPersister.getInstance().cleanup(this); - VrShellDelegate.maybeRegisterVREntryHook(this); + VrShellDelegate.maybeRegisterVrEntryHook(this); } @Override @@ -869,7 +869,7 @@ if (tab != null) getTabContentManager().cacheTabThumbnail(tab); ContentViewCore cvc = getContentViewCore(); if (cvc != null) cvc.onPause(); - VrShellDelegate.maybeUnregisterVREntryHook(this); + VrShellDelegate.maybeUnregisterVrEntryHook(this); markSessionEnd(); super.onPauseWithNative(); } @@ -2131,15 +2131,25 @@ } } + @Override + public boolean onActivityResultWithNative(int requestCode, int resultCode, Intent intent) { + if (super.onActivityResultWithNative(requestCode, resultCode, intent)) return true; + if (requestCode == VrShellDelegate.EXIT_VR_RESULT) { + VrShellDelegate.onExitVrResult(resultCode); + return true; + } + return false; + } + /** * Called when VR mode is entered using this activity. 2D UI components that steal focus or * draw over VR contents should be hidden in this call. */ - public void onEnterVR() {} + public void onEnterVr() {} /** * Called when VR mode using this activity is exited. Any state set for VR should be restored * in this call, including showing 2D UI that was hidden. */ - public void onExitVR() {} + public void onExitVr() {} }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java index 9c8fddfb..f357f06 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeSwitches.java
@@ -182,6 +182,9 @@ public static final String ALWAYS_EXTRACT_WEBAPK_RUNTIME_DEX_ON_STARTUP = "always-extract-webapk-dex-on-startup"; + /** Enable non-'org.chromium.webapk' prefixed package names with proper signature. */ + public static final String ENABLE_ANY_WEBAPK_PACKAGE_NAME = "any-webapk-package-name"; + /** * Forces a check for whether the WebAPK's Web Manifest has changed each time that a WebAPK is * launched.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java index 8e454fe..e7b6ff7 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/ChromeTabbedActivity.java
@@ -231,6 +231,8 @@ private LocaleManager mLocaleManager; + private AppIndexingUtil mAppIndexingUtil; + /** * Keeps track of whether or not a specific tab was created based on the startup intent. */ @@ -267,13 +269,13 @@ @Override public boolean isShowingBrowserControlsEnabled() { - if (VrShellDelegate.isInVR()) return false; + if (VrShellDelegate.isInVr()) return false; return super.isShowingBrowserControlsEnabled(); } @Override public boolean isHidingBrowserControlsEnabled() { - if (VrShellDelegate.isInVR()) return true; + if (VrShellDelegate.isInVr()) return true; return super.isHidingBrowserControlsEnabled(); } } @@ -314,6 +316,7 @@ public ChromeTabbedActivity() { mActivityStopMetrics = new ActivityStopMetrics(); mMainIntentMetrics = new MainIntentBehaviorMetrics(this); + mAppIndexingUtil = new AppIndexingUtil(); } @Override @@ -444,7 +447,7 @@ @SuppressLint("NewApi") private boolean shouldDestroyIncognitoProfile() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return false; - if (VrShellDelegate.isInVR()) return false; // VR uses an incognito profile for rendering. + if (VrShellDelegate.isInVr()) return false; // VR uses an incognito profile for rendering. Context context = ContextUtils.getApplicationContext(); ActivityManager manager = @@ -578,10 +581,7 @@ if (CommandLine.getInstance().hasSwitch(ContentSwitches.ENABLE_TEST_INTENTS)) { handleDebugIntent(intent); } - if (VrShellDelegate.isDaydreamVrIntent(intent)) { - // TODO(mthiesse): Move this into ChromeActivity. crbug.com/688611 - VrShellDelegate.enterVRFromIntent(intent); - } else if (ShortcutHelper.isShowToastIntent(intent)) { + if (ShortcutHelper.isShowToastIntent(intent)) { ShortcutHelper.showAddedToHomescreenToastFromIntent(intent); } } finally { @@ -817,12 +817,7 @@ mIntentWithEffect = false; if ((mIsOnFirstRun || getSavedInstanceState() == null) && intent != null) { - if (VrShellDelegate.isDaydreamVrIntent(intent)) { - // TODO(mthiesse): Improve startup when started from a VR intent. - // crbug.com/668541 - // TODO(mthiesse): Move this into ChromeActivity. crbug.com/688611 - VrShellDelegate.enterVRIfNecessary(); - } else if (!mIntentHandler.shouldIgnoreIntent(intent)) { + if (!mIntentHandler.shouldIgnoreIntent(intent)) { mIntentWithEffect = mIntentHandler.onNewIntent(intent); } @@ -900,10 +895,6 @@ } } return true; - } else if (requestCode == VrShellDelegate.EXIT_VR_RESULT) { - // TODO(mthiesse): Move this into ChromeActivity. crbug.com/688611 - VrShellDelegate.onExitVRResult(resultCode); - return true; } return false; } @@ -1249,7 +1240,7 @@ @Override public void onPageLoadFinished(final Tab tab) { - AppIndexingUtil.extractCopylessPasteMetadata(tab); + mAppIndexingUtil.extractCopylessPasteMetadata(tab); } @Override @@ -1468,7 +1459,7 @@ if (!currentModel.isIncognito()) currentModel.openMostRecentlyClosedTab(); RecordUserAction.record("MobileTabClosedUndoShortCut"); } else if (id == R.id.enter_vr_id) { - VrShellDelegate.enterVRIfNecessary(); + VrShellDelegate.enterVrIfNecessary(); } else { return super.onMenuOrKeyboardAction(id, fromMenu); } @@ -1941,14 +1932,14 @@ } @Override - public void onEnterVR() { - super.onEnterVR(); + public void onEnterVr() { + super.onEnterVr(); mControlContainer.setVisibility(View.INVISIBLE); } @Override - public void onExitVR() { - super.onExitVR(); + public void onExitVr() { + super.onExitVr(); mControlContainer.setVisibility(View.VISIBLE); // We prevent the incognito profile from being destroyed while in VR, so upon exiting VR we
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java index 1894998..80350eb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/compositor/CompositorViewHolder.java
@@ -961,7 +961,7 @@ * {@link CompositorViewHolder} so that VR can take ownership of Chrome's rendering. * @return The detached {@link TabModelSelector}. */ - public TabModelSelector detachForVR() { + public TabModelSelector detachForVr() { if (mTabModelSelector != null) mTabModelSelector.removeObserver(mTabModelSelectorObserver); TabModelSelector selector = mTabModelSelector; mTabModelSelector = null; @@ -976,7 +976,7 @@ * so that it can take back ownership of Chrome's rendering. * @param tabModelSelector */ - public void onExitVR(TabModelSelector tabModelSelector) { + public void onExitVr(TabModelSelector tabModelSelector) { getCompositorView().setVisibility(View.VISIBLE); attachToTabModelSelector(tabModelSelector); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/historyreport/AppIndexingReporter.java b/chrome/android/java/src/org/chromium/chrome/browser/historyreport/AppIndexingReporter.java index 92670c0..a50ca8d4 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/historyreport/AppIndexingReporter.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/historyreport/AppIndexingReporter.java
@@ -29,6 +29,15 @@ } /** + * Reports view of provided url to on-device index. + * Base class does not implement any reporting, and call is a no-op. Child classes should + * implement this functionality. + */ + public void reportWebPageView(String url, String title) { + // Overriden by private class. Base class does nothing. + } + + /** * Clears history of reported entities. * Currently, we do not support clearing only a subset of history. Base class does not implement * any reporting, and call is a no-op. Child classes should implement this functionality.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/invalidation/InvalidationController.java b/chrome/android/java/src/org/chromium/chrome/browser/invalidation/InvalidationController.java index 9a26c64..d767d0b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/invalidation/InvalidationController.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/invalidation/InvalidationController.java
@@ -159,11 +159,6 @@ private int mNumRecentTabPages; /** - * Whether GCM Upstream should be used for sending upstream messages. - */ - private boolean mUseGcmUpstream; - - /** * Whether GCM has been initialized for Invalidations. */ private boolean mGcmInitialized; @@ -209,7 +204,8 @@ new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... arg0) { - AndroidGcmController.get(mContext).initializeGcm(mUseGcmUpstream); + boolean useGcmUpstream = true; + AndroidGcmController.get(mContext).initializeGcm(useGcmUpstream); return null; } }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -308,10 +304,7 @@ boolean canDisableSessionInvalidations = !requireInvalidationsForInstrumentation && !requireInvalidationsForSuggestions; - boolean canUseGcmUpstream = - FieldTrialList.findFullName("InvalidationsGCMUpstream").equals("Enabled"); - sInstance = new InvalidationController( - context, canDisableSessionInvalidations, canUseGcmUpstream); + sInstance = new InvalidationController(context, canDisableSessionInvalidations); } return sInstance; } @@ -343,12 +336,10 @@ * Creates an instance using {@code context} to send intents. */ @VisibleForTesting - InvalidationController( - Context context, boolean canDisableSessionInvalidations, boolean canUseGcmUpstream) { + InvalidationController(Context context, boolean canDisableSessionInvalidations) { Context appContext = context.getApplicationContext(); if (appContext == null) throw new NullPointerException("Unable to get application context"); mContext = appContext; - mUseGcmUpstream = canUseGcmUpstream; mCanDisableSessionInvalidations = canDisableSessionInvalidations; mSessionInvalidationsEnabled = !mCanDisableSessionInvalidations; mEnableSessionInvalidationsTimer = new Timer();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java index 4e295e37c..59bdeeb 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/JourneyLogger.java
@@ -42,7 +42,8 @@ public static final int EVENT_SHOWN = 1 << 0; public static final int EVENT_PAY_CLICKED = 1 << 1; public static final int EVENT_RECEIVED_INSTRUMENT_DETAILS = 1 << 2; - public static final int EVENT_MAX = 8; + public static final int EVENT_SKIPPED_SHOW = 1 << 3; + public static final int EVENT_MAX = 16; // The minimum expected value of CustomCountHistograms is always set to 1. It is still possible // to log the value 0 to that type of histogram.
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java index 843622c..3a39415c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/PaymentRequestImpl.java
@@ -598,8 +598,8 @@ mDidRecordShowEvent = true; mShouldRecordAbortReason = true; - recordSuccessFunnelHistograms("Shown"); - mJourneyLogger.setEventOccurred(JourneyLogger.EVENT_SHOWN); + recordSuccessFunnelHistograms("SkippedShow"); + mJourneyLogger.setEventOccurred(JourneyLogger.EVENT_SKIPPED_SHOW); mJourneyLogger.setShowCalled(); onPayClicked(null /* selectedShippingAddress */, null /* selectedShippingOption */,
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java index 7313f57..665f1a3b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/payments/ui/PaymentRequestSection.java
@@ -458,7 +458,7 @@ mChevronView.setVisibility( mDisplayMode == DISPLAY_MODE_EXPANDABLE ? VISIBLE : GONE); } else { - // Show the edit button and hide the chevron and the summary. + // Show the edit button and hide the chevron. boolean isButtonAllowed = mDisplayMode == DISPLAY_MODE_EXPANDABLE || mDisplayMode == DISPLAY_MODE_NORMAL; mChevronView.setVisibility(GONE); @@ -677,6 +677,13 @@ new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); breakdownParams.gravity = Gravity.END; mainSectionLayout.addView(mBreakdownLayout, breakdownParams); + + // Sets the summary right text view takes the same available space as the summary left + // text view. + LinearLayout.LayoutParams rightTextViewLayoutParams = + (LinearLayout.LayoutParams) getSummaryRightTextView().getLayoutParams(); + rightTextViewLayoutParams.width = 0; + rightTextViewLayoutParams.weight = 1f; } /** @@ -699,6 +706,9 @@ ApiCompatibilityUtils.setTextAlignment(mUpdatedView, TEXT_ALIGNMENT_TEXT_END); mUpdatedView.setTextColor(ApiCompatibilityUtils.getColor( context.getResources(), R.color.google_green_700)); + ApiCompatibilityUtils.setMarginStart(updatedLayoutParams, + context.getResources().getDimensionPixelSize( + R.dimen.payments_section_small_spacing)); ApiCompatibilityUtils.setMarginEnd( updatedLayoutParams, context.getResources().getDimensionPixelSize( R.dimen.payments_section_small_spacing)); @@ -834,6 +844,23 @@ } @Override + public void setDisplayMode(int displayMode) { + // Displays the summary left text view in at most three lines if in focus mode, + // otherwise display it in a single line. + if (displayMode == DISPLAY_MODE_FOCUSED) { + setSummaryProperties(TruncateAt.END, false /* leftIsSingleLine */, + null /* rightTruncate */, false /* rightIsSingleLine */); + getSummaryLeftTextView().setMaxLines(3); + } else { + setSummaryProperties(TruncateAt.END, true /* leftIsSingleLine */, + null /* rightTruncate */, false /* rightIsSingleLine */); + getSummaryLeftTextView().setMaxLines(1); + } + + super.setDisplayMode(displayMode); + } + + @Override protected void updateControlLayout() { if (!mIsLayoutInitialized) return;
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java index 864caba7..609ff7c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/ChromePreferenceManager.java
@@ -45,6 +45,7 @@ private static final String HERB_FLAVOR_KEY = "herb_flavor"; private static final String WEBAPK_COMMAND_LINE_KEY = "webapk.command_line_enabled"; private static final String WEBAPK_RUNTIME_KEY = "webapk.runtime_enabled"; + private static final String WEBAPK_ANY_PACKAGE_KEY = "webapk.any_package_name"; private static final String CHROME_HOME_ENABLED_KEY = "chrome_home_enabled"; private static final String CHROME_DEFAULT_BROWSER = "applink.chrome_default_browser"; @@ -351,6 +352,16 @@ writeBoolean(WEBAPK_RUNTIME_KEY, isEnabled); } + /** Checks the cached value for the webapk any package name feature. */ + public boolean getCachedWebApkAnyPackageName() { + return mSharedPreferences.getBoolean(WEBAPK_ANY_PACKAGE_KEY, false); + } + + /** Writes the cached value for the webapk any package name feature is enabled. */ + public void setCachedWebApkAnyPackageNameEnabled(boolean isEnabled) { + writeBoolean(WEBAPK_ANY_PACKAGE_KEY, isEnabled); + } + public boolean getCachedChromeDefaultBrowser() { return mSharedPreferences.getBoolean(CHROME_DEFAULT_BROWSER, false); }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java index 161a60a..5fe5f3a 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/website/ManageSpaceActivity.java
@@ -38,6 +38,7 @@ import org.chromium.chrome.browser.preferences.Preferences; import org.chromium.chrome.browser.preferences.PreferencesLauncher; import org.chromium.chrome.browser.preferences.website.Website.StoredDataClearedCallback; +import org.chromium.chrome.browser.searchwidget.SearchWidgetProvider; import java.util.Collection; @@ -257,6 +258,8 @@ RecordHistogram.recordEnumeratedHistogram("Android.ManageSpace.ActionTaken", OPTION_CLEAR_APP_DATA, OPTION_MAX); } + + SearchWidgetProvider.reset(); activityManager.clearApplicationUserData(); } });
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java index deeb6a1..84d23c9b 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivity.java
@@ -7,20 +7,17 @@ import android.content.Intent; import android.net.Uri; import android.os.Handler; -import android.support.customtabs.CustomTabsIntent; import android.support.v4.app.ActivityOptionsCompat; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import org.chromium.base.ContextUtils; import org.chromium.chrome.R; import org.chromium.chrome.browser.IntentHandler; import org.chromium.chrome.browser.WebContentsFactory; import org.chromium.chrome.browser.WindowDelegate; import org.chromium.chrome.browser.customtabs.CustomTabsConnection; -import org.chromium.chrome.browser.document.ChromeLauncherActivity; import org.chromium.chrome.browser.init.AsyncInitializationActivity; import org.chromium.chrome.browser.init.ChromeBrowserInitializer; import org.chromium.chrome.browser.omnibox.AutocompleteController; @@ -171,22 +168,12 @@ // resending a queued query after the user deleted it. if (TextUtils.isEmpty(url)) return; - // Fix up the URL and send it to a tab. + // Fix up the URL and send it to the full browser. String fixedUrl = UrlFormatter.fixupUrl(url); Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(fixedUrl)); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NEW_DOCUMENT); - - boolean useHerbTab = ContextUtils.getAppSharedPreferences().getBoolean( - SearchWidgetProvider.PREF_USE_HERB_TAB, false); - if (useHerbTab) { - intent = ChromeLauncherActivity.createCustomTabActivityIntent(this, intent, true); - intent.putExtra(CustomTabsIntent.EXTRA_TITLE_VISIBILITY_STATE, - CustomTabsIntent.SHOW_PAGE_TITLE); - } else { - intent.setPackage(getPackageName()); - IntentHandler.addTrustedIntentExtras(intent); - } - + intent.setPackage(getPackageName()); + IntentHandler.addTrustedIntentExtras(intent); IntentUtils.safeStartActivity(this, intent, ActivityOptionsCompat .makeCustomAnimation(this, android.R.anim.fade_in, android.R.anim.fade_out)
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java index bd189f4b..76487ba 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchActivityLocationBarLayout.java
@@ -75,12 +75,13 @@ /** Called when the SearchActivity has finished initialization. */ void onDeferredStartup(boolean isVoiceSearchIntent) { + SearchWidgetProvider.updateCachedVoiceSearchAvailability(isVoiceSearchEnabled()); if (isVoiceSearchIntent && mUrlBar.isFocused()) onUrlFocusChange(true); } /** Begins a new query. */ void beginQuery(boolean isVoiceSearchIntent) { - if (isVoiceSearchIntent) { + if (isVoiceSearchEnabled() && isVoiceSearchIntent) { startVoiceRecognition(); } else { focusTextBox();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java index 8527aed..e3a01fd 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/searchwidget/SearchWidgetProvider.java
@@ -15,6 +15,7 @@ import android.os.Bundle; import android.support.v4.app.ActivityOptionsCompat; import android.text.TextUtils; +import android.view.View; import android.widget.RemoteViews; import org.chromium.base.ContextUtils; @@ -23,13 +24,13 @@ import org.chromium.base.library_loader.LibraryLoader; import org.chromium.chrome.R; import org.chromium.chrome.browser.IntentHandler; -import org.chromium.chrome.browser.init.AsyncInitializationActivity; import org.chromium.chrome.browser.search_engines.TemplateUrlService; import org.chromium.chrome.browser.search_engines.TemplateUrlService.LoadListener; import org.chromium.chrome.browser.search_engines.TemplateUrlService.TemplateUrlServiceObserver; /** - * Widget that lets the user search using the default search engine in Chrome. + * Widget that lets the user search using their default search engine. + * * Because this is a BroadcastReceiver, it dies immediately after it runs. A new one is created * for each new broadcast. */ @@ -59,20 +60,19 @@ static final String EXTRA_START_VOICE_SEARCH = "org.chromium.chrome.browser.searchwidget.START_VOICE_SEARCH"; + private static final String PREF_IS_VOICE_SEARCH_AVAILABLE = + "org.chromium.chrome.browser.searchwidget.IS_VOICE_SEARCH_AVAILABLE"; private static final String PREF_SEARCH_ENGINE_SHORTNAME = "org.chromium.chrome.browser.searchwidget.SEARCH_ENGINE_SHORTNAME"; - static final String PREF_USE_HERB_TAB = "org.chromium.chrome.browser.searchwidget.USE_HERB_TAB"; private static final String TAG = "searchwidget"; private static final Object OBSERVER_LOCK = new Object(); private static SearchWidgetTemplateUrlServiceObserver sObserver; - private static String sCachedSearchEngineName; /** * Creates the singleton instance of the observer that will monitor for search engine changes. - * The native library and the browser process must have been fully loaded before calling this, - * after {@link AsyncInitializationActivity#finishNativeInitialization}. + * The native library and the browser process must have been fully loaded before calling this. */ public static void initialize() { ThreadUtils.assertOnUiThread(); @@ -84,13 +84,19 @@ sObserver = new SearchWidgetTemplateUrlServiceObserver(); TemplateUrlService service = TemplateUrlService.getInstance(); + service.registerLoadListener(sObserver); service.addObserver(sObserver); - if (!service.isLoaded()) { - service.registerLoadListener(sObserver); - service.load(); - } + if (!service.isLoaded()) service.load(); } - updateCachedEngineName(); + } + + /** Nukes all cached information and forces all widgets to start with a blank slate. */ + public static void reset() { + SharedPreferences.Editor editor = ContextUtils.getAppSharedPreferences().edit(); + editor.remove(PREF_IS_VOICE_SEARCH_AVAILABLE); + editor.remove(PREF_SEARCH_ENGINE_SHORTNAME); + editor.apply(); + updateAllWidgets(); } @Override @@ -98,13 +104,11 @@ if (IntentHandler.isIntentChromeOrFirstParty(intent)) { if (ACTION_START_TEXT_QUERY.equals(intent.getAction())) { startSearchActivity(context, false); - return; } else if (ACTION_START_VOICE_QUERY.equals(intent.getAction())) { startSearchActivity(context, true); - return; + } else if (ACTION_UPDATE_ALL_WIDGETS.equals(intent.getAction())) { + performUpdate(context); } - } else if (ACTION_UPDATE_ALL_WIDGETS.equals(intent.getAction())) { - performUpdate(context); return; } super.onReceive(context, intent); @@ -115,13 +119,12 @@ // Launch the SearchActivity. Intent searchIntent = new Intent(); - searchIntent.setClassName(context, SearchActivity.class.getName()); + searchIntent.setClass(context, SearchActivity.class); searchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); searchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); searchIntent.putExtra(EXTRA_START_VOICE_SEARCH, startVoiceSearch); - Bundle optionsBundle; - optionsBundle = + Bundle optionsBundle = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.activity_open_enter, 0) .toBundle(); context.startActivity(searchIntent, optionsBundle); @@ -130,20 +133,29 @@ @Override public void onUpdate(Context context, AppWidgetManager manager, int[] ids) { performUpdate(context, ids); - super.onUpdate(context, manager, ids); } - private void performUpdate(Context context) { + private static void performUpdate(Context context) { AppWidgetManager manager = AppWidgetManager.getInstance(context); performUpdate(context, getAllSearchWidgetIds(manager)); } - private void performUpdate(Context context, int[] ids) { - for (int id : ids) updateWidget(context, id); + private static void performUpdate(Context context, int[] ids) { + if (ids.length == 0) return; + + AppWidgetManager manager = AppWidgetManager.getInstance(context); + SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); + boolean isVoiceSearchAvailable = prefs.getBoolean(PREF_IS_VOICE_SEARCH_AVAILABLE, true); + String engineName = prefs.getString(PREF_SEARCH_ENGINE_SHORTNAME, null); + + for (int id : ids) { + RemoteViews views = createWidgetViews(context, id, engineName, isVoiceSearchAvailable); + manager.updateAppWidget(id, views); + } } - private void updateWidget(Context context, int id) { - AppWidgetManager manager = AppWidgetManager.getInstance(context); + private static RemoteViews createWidgetViews( + Context context, int id, String engineName, boolean isVoiceSearchAvailable) { RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.search_widget_template); @@ -154,74 +166,72 @@ PendingIntent.getBroadcast( context, 0, textIntent, PendingIntent.FLAG_UPDATE_CURRENT)); - Intent voiceIntent = createStartQueryIntent(context, ACTION_START_VOICE_QUERY, id); - views.setOnClickPendingIntent(R.id.microphone_icon, - PendingIntent.getBroadcast( - context, 0, voiceIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + // If voice search is available, clicking on the microphone triggers a voice query. + if (isVoiceSearchAvailable) { + Intent voiceIntent = createStartQueryIntent(context, ACTION_START_VOICE_QUERY, id); + views.setOnClickPendingIntent(R.id.microphone_icon, + PendingIntent.getBroadcast( + context, 0, voiceIntent, PendingIntent.FLAG_UPDATE_CURRENT)); + views.setViewVisibility(R.id.microphone_icon, View.VISIBLE); + } else { + views.setViewVisibility(R.id.microphone_icon, View.GONE); + } // Update what string is displayed by the widget. - String engineName = getCachedEngineName(); String text = TextUtils.isEmpty(engineName) ? context.getString(R.string.search_widget_default) : context.getString(R.string.search_with_product, engineName); views.setTextViewText(R.id.title, text); - manager.updateAppWidget(id, views); + return views; } - /** Force all widgets to update their state. */ - static void updateAllWidgets() { - Intent intent = new Intent(ACTION_UPDATE_ALL_WIDGETS); - intent.setPackage(ContextUtils.getApplicationContext().getPackageName()); - ContextUtils.getApplicationContext().sendBroadcast(intent); - } - - /** Updates the name of the user's default search engine that is cached in SharedPreferences. */ - static void updateCachedEngineName() { - assert LibraryLoader.isInitialized(); - - // Getting an instance of the TemplateUrlService requires that the native library be - // loaded, but the TemplateUrlService itself needs to be initialized. - TemplateUrlService service = TemplateUrlService.getInstance(); - if (!service.isLoaded()) return; - - String engineName = service.getDefaultSearchEngineTemplateUrl().getShortName(); - if (!TextUtils.equals(sCachedSearchEngineName, engineName)) { - sCachedSearchEngineName = engineName; - - SharedPreferences.Editor editor = ContextUtils.getAppSharedPreferences().edit(); - editor.putString(PREF_SEARCH_ENGINE_SHORTNAME, engineName); - editor.apply(); - + /** Caches whether or not a voice search is possible. */ + static void updateCachedVoiceSearchAvailability(boolean isVoiceSearchAvailable) { + SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); + if (prefs.getBoolean(PREF_IS_VOICE_SEARCH_AVAILABLE, true) != isVoiceSearchAvailable) { + prefs.edit().putBoolean(PREF_IS_VOICE_SEARCH_AVAILABLE, isVoiceSearchAvailable).apply(); updateAllWidgets(); } } /** - * Returns the cached name of the user's default search engine. Caching it in SharedPreferences - * prevents us from having to load the native library and the TemplateUrlService. - * - * @return The cached name of the user's default search engine. + * Updates the name of the user's default search engine that is cached in SharedPreferences. + * Caching it in SharedPreferences prevents us from having to load the native library and the + * TemplateUrlService whenever the widget is updated. */ - private static String getCachedEngineName() { - if (sCachedSearchEngineName != null) return sCachedSearchEngineName; + private static void updateCachedEngineName() { + assert LibraryLoader.isInitialized(); + + // Getting an instance of the TemplateUrlService requires that the native library be + // loaded, but the TemplateUrlService itself needs to be initialized. + TemplateUrlService service = TemplateUrlService.getInstance(); + assert service.isLoaded(); + String engineName = service.getDefaultSearchEngineTemplateUrl().getShortName(); SharedPreferences prefs = ContextUtils.getAppSharedPreferences(); - sCachedSearchEngineName = prefs.getString(PREF_SEARCH_ENGINE_SHORTNAME, null); - return sCachedSearchEngineName; + if (!TextUtils.equals(prefs.getString(PREF_SEARCH_ENGINE_SHORTNAME, null), engineName)) { + prefs.edit().putString(PREF_SEARCH_ENGINE_SHORTNAME, engineName).apply(); + updateAllWidgets(); + } } - /** Get the IDs of all of Chrome's search widgets. */ + /** Get the IDs of all existing search widgets. */ private static int[] getAllSearchWidgetIds(AppWidgetManager manager) { return manager.getAppWidgetIds(new ComponentName( ContextUtils.getApplicationContext(), SearchWidgetProvider.class.getName())); } /** Creates a trusted Intent that lets the user begin performing queries. */ - private Intent createStartQueryIntent(Context context, String action, int widgetId) { + private static Intent createStartQueryIntent(Context context, String action, int widgetId) { Intent intent = new Intent(action, Uri.parse(String.valueOf(widgetId))); - intent.setClass(context, getClass()); + intent.setClass(context, SearchWidgetProvider.class); IntentHandler.addTrustedIntentExtras(intent); return intent; } + + /** Immediately updates all widgets. */ + private static void updateAllWidgets() { + performUpdate(ContextUtils.getApplicationContext()); + } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java index 6bc3a53..73b52ad 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/toolbar/ToolbarPhone.java
@@ -2272,7 +2272,7 @@ TabSwitcherCallout.showIfNecessary(getContext(), mToggleTabStackButton); if (mTabSwitcherCallout == null) return; - mTabSwitcherCallout.setOnDismissListener(new OnDismissListener() { + mTabSwitcherCallout.addOnDismissListener(new OnDismissListener() { @Override public void onDismiss() { if (mControlsVisibilityDelegate != null) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java index 6feb3f6d..57117f0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellDelegate.java
@@ -5,10 +5,12 @@ package org.chromium.chrome.browser.vr_shell; import android.app.Activity; +import android.app.ActivityManager; import android.app.PendingIntent; -import android.content.ComponentName; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.net.Uri; @@ -36,14 +38,16 @@ import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.ChromeTabbedActivity; +import org.chromium.chrome.browser.customtabs.CustomTabActivity; import org.chromium.chrome.browser.infobar.InfoBarIdentifier; import org.chromium.chrome.browser.infobar.SimpleConfirmInfoBarBuilder; -import org.chromium.chrome.browser.multiwindow.MultiWindowUtils; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tabmodel.TabModelSelector; +import org.chromium.chrome.browser.webapps.WebappActivity; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -57,30 +61,32 @@ // Pseudo-random number to avoid request id collisions. public static final int EXIT_VR_RESULT = 721251; - public static final int ENTER_VR_NOT_NECESSARY = 0; - public static final int ENTER_VR_CANCELLED = 1; - public static final int ENTER_VR_REQUESTED = 2; - public static final int ENTER_VR_SUCCEEDED = 3; + private static final int ENTER_VR_NOT_NECESSARY = 0; + private static final int ENTER_VR_CANCELLED = 1; + private static final int ENTER_VR_REQUESTED = 2; + private static final int ENTER_VR_SUCCEEDED = 3; @Retention(RetentionPolicy.SOURCE) @IntDef({ENTER_VR_NOT_NECESSARY, ENTER_VR_CANCELLED, ENTER_VR_REQUESTED, ENTER_VR_SUCCEEDED}) - public @interface EnterVRResult {} + private @interface EnterVRResult {} - public static final int VR_NOT_AVAILABLE = 0; - public static final int VR_CARDBOARD = 1; - public static final int VR_DAYDREAM = 2; // Supports both Cardboard and Daydream viewer. + private static final int VR_NOT_AVAILABLE = 0; + private static final int VR_CARDBOARD = 1; + private static final int VR_DAYDREAM = 2; // Supports both Cardboard and Daydream viewer. @Retention(RetentionPolicy.SOURCE) @IntDef({VR_NOT_AVAILABLE, VR_CARDBOARD, VR_DAYDREAM}) - public @interface VrSupportLevel {} + private @interface VrSupportLevel {} // TODO(bshe): These should be replaced by string provided by NDK. Currently, it only available // in SDK and we don't want add dependency to SDK just to get these strings. private static final String DAYDREAM_CATEGORY = "com.google.intent.category.DAYDREAM"; private static final String CARDBOARD_CATEGORY = "com.google.intent.category.CARDBOARD"; - private static final String VR_ACTIVITY_ALIAS = - "org.chromium.chrome.browser.VRChromeTabbedActivity"; + // Linter and formatter disagree on how the line below should be formatted. + /* package */ + static final String VR_ENTRY_RESULT_ACTION = + "org.chromium.chrome.browser.vr_shell.VrEntryResult"; private static final long REENTER_VR_TIMEOUT_MS = 1000; @@ -93,6 +99,7 @@ "market://details?id=" + VrCoreVersionChecker.VR_CORE_PACKAGE_ID; private static VrShellDelegate sInstance; + private static VrBroadcastReceiver sVrBroadcastReceiver; private ChromeActivity mActivity; @@ -107,16 +114,46 @@ private TabModelSelector mTabModelSelector; private boolean mInVr; - private boolean mEnteringVr; + + // Whether or not the VR Device ON flow succeeded. If this is true it means the user has a VR + // headset on, but we haven't switched into VR mode yet. + // See further documentation here: https://developers.google.com/vr/daydream/guides/vr-entry + private boolean mDonSucceeded; private boolean mPaused; private int mRestoreSystemUiVisibilityFlag = -1; private Integer mRestoreOrientation = null; private long mNativeVrShellDelegate; - private boolean mRequestedWebVR; - private long mLastVRExit; + private boolean mRequestedWebVr; + private long mLastVrExit; private boolean mListeningForWebVrActivate; private boolean mListeningForWebVrActivateBeforePause; + private static class VrBroadcastReceiver extends BroadcastReceiver { + private final WeakReference<ChromeActivity> mTargetActivity; + + public VrBroadcastReceiver(ChromeActivity activity) { + mTargetActivity = new WeakReference<ChromeActivity>(activity); + } + + @Override + public void onReceive(Context context, Intent intent) { + ChromeActivity activity = mTargetActivity.get(); + if (activity == null) return; + getInstance(activity).mDonSucceeded = true; + ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)) + .moveTaskToFront(activity.getTaskId(), 0); + } + + /** + * Unregisters this {@link BroadcastReceiver} from the activity it's registered to. + */ + public void unregister() { + ChromeActivity activity = mTargetActivity.get(); + if (activity == null) return; + activity.unregisterReceiver(VrBroadcastReceiver.this); + } + } + /** * Called when the native library is first available. */ @@ -136,7 +173,7 @@ /** * Whether or not we are currently in VR. */ - public static boolean isInVR() { + public static boolean isInVr() { if (sInstance == null) return false; return sInstance.mInVr; } @@ -153,27 +190,16 @@ /** * Enters VR on the current tab if possible. */ - public static void enterVRIfNecessary() { + public static void enterVrIfNecessary() { boolean created_delegate = sInstance == null; VrShellDelegate instance = getInstance(); if (instance == null) return; - if (instance.enterVRInternal() == ENTER_VR_CANCELLED && created_delegate) { + if (instance.enterVrInternal() == ENTER_VR_CANCELLED && created_delegate) { instance.destroy(); } } /** - * Handles a VR intent, entering VR in the process. - */ - public static void enterVRFromIntent(Intent intent) { - assert isDaydreamVrIntent(intent); - boolean created_delegate = sInstance == null; - VrShellDelegate instance = getInstance(); - if (instance == null) return; - if (!instance.enterVRFromIntent() && created_delegate) instance.destroy(); - } - - /** * Whether or not the intent is a Daydream VR Intent. */ public static boolean isDaydreamVrIntent(Intent intent) { @@ -184,9 +210,9 @@ /** * Handles the result of the exit VR flow (DOFF). */ - public static void onExitVRResult(int resultCode) { + public static void onExitVrResult(int resultCode) { if (sInstance == null) return; - sInstance.onExitVRResult(resultCode == Activity.RESULT_OK); + sInstance.onExitVrResult(resultCode == Activity.RESULT_OK); } /** @@ -208,10 +234,10 @@ * If VR Shell is enabled, and the activity is supported, register with the Daydream * platform that this app would like to be launched in VR when the device enters VR. */ - public static void maybeRegisterVREntryHook(Activity activity) { + public static void maybeRegisterVrEntryHook(ChromeActivity activity) { if (sInstance != null) return; // Will be handled in onResume. - if (!(activity instanceof ChromeTabbedActivity)) return; - VrClassesWrapper wrapper = createVrClassesWrapper(); + if (!activitySupportsVrBrowsing(activity)) return; + VrClassesWrapper wrapper = getVrClassesWrapper(); if (wrapper == null) return; VrDaydreamApi api = wrapper.createVrDaydreamApi(activity); if (api == null) return; @@ -223,10 +249,10 @@ * When the app is pausing we need to unregister with the Daydream platform to prevent this app * from being launched from the background when the device enters VR. */ - public static void maybeUnregisterVREntryHook(Activity activity) { + public static void maybeUnregisterVrEntryHook(ChromeActivity activity) { if (sInstance != null) return; // Will be handled in onPause. - if (!(activity instanceof ChromeTabbedActivity)) return; - VrClassesWrapper wrapper = createVrClassesWrapper(); + if (!activitySupportsVrBrowsing(activity)) return; + VrClassesWrapper wrapper = getVrClassesWrapper(); if (wrapper == null) return; VrDaydreamApi api = wrapper.createVrDaydreamApi(activity); if (api == null) return; @@ -235,21 +261,28 @@ @CalledByNative private static VrShellDelegate getInstance() { - return getInstance(ApplicationStatus.getLastTrackedFocusedActivity()); + Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); + if (!(activity instanceof ChromeActivity)) return null; + return getInstance((ChromeActivity) activity); } - private static VrShellDelegate getInstance(Activity activity) { + private static VrShellDelegate getInstance(ChromeActivity activity) { if (!LibraryLoader.isInitialized()) return null; - if (activity == null || !isSupportedActivity(activity)) return null; + if (activity == null || !activitySupportsPresentation(activity)) return null; if (sInstance != null) return sInstance; VrClassesWrapper wrapper = getVrClassesWrapper(); if (wrapper == null) return null; - sInstance = new VrShellDelegate((ChromeActivity) activity, wrapper); + sInstance = new VrShellDelegate(activity, wrapper); return sInstance; } - private static boolean isSupportedActivity(Activity activity) { + private static boolean activitySupportsPresentation(Activity activity) { + return activity instanceof ChromeTabbedActivity || activity instanceof CustomTabActivity + || activity instanceof WebappActivity; + } + + private static boolean activitySupportsVrBrowsing(Activity activity) { return activity instanceof ChromeTabbedActivity; } @@ -279,25 +312,29 @@ } } - private static PendingIntent getEnterVRPendingIntent( - VrDaydreamApi dayreamApi, Activity activity) { - return PendingIntent.getActivity(activity, 0, - dayreamApi.createVrIntent(new ComponentName(activity, VR_ACTIVITY_ALIAS)), - PendingIntent.FLAG_ONE_SHOT); + private static PendingIntent getEnterVrPendingIntent(ChromeActivity activity) { + Intent vrIntent = new Intent(VR_ENTRY_RESULT_ACTION); + return PendingIntent.getBroadcast(activity, 0, vrIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_ONE_SHOT); } /** * Registers the Intent to fire after phone inserted into a headset. */ - private static void registerDaydreamIntent(VrDaydreamApi dayreamApi, Activity activity) { - dayreamApi.registerDaydreamIntent(getEnterVRPendingIntent(dayreamApi, activity)); + private static void registerDaydreamIntent( + final VrDaydreamApi daydreamApi, final ChromeActivity activity) { + if (sVrBroadcastReceiver != null) sVrBroadcastReceiver.unregister(); + IntentFilter filter = new IntentFilter(VR_ENTRY_RESULT_ACTION); + sVrBroadcastReceiver = new VrBroadcastReceiver(activity); + activity.registerReceiver(sVrBroadcastReceiver, filter); + daydreamApi.registerDaydreamIntent(getEnterVrPendingIntent(activity)); } /** * Unregisters the Intent which registered by this context if any. */ - private static void unregisterDaydreamIntent(VrDaydreamApi dayreamApi) { - dayreamApi.unregisterDaydreamIntent(); + private static void unregisterDaydreamIntent(VrDaydreamApi daydreamApi) { + daydreamApi.unregisterDaydreamIntent(); } /** @@ -337,13 +374,13 @@ if (activity == mActivity) destroy(); break; case ActivityState.PAUSED: - if (activity == mActivity) pauseVR(); + if (activity == mActivity) pauseVr(); break; case ActivityState.RESUMED: assert !mInVr; - if (!isSupportedActivity(activity)) return; + if (!activitySupportsPresentation(activity)) return; mActivity = (ChromeActivity) activity; - resumeVR(); + resumeVr(); break; default: break; @@ -370,46 +407,41 @@ mVrDaydreamApi, mVrCoreVersionChecker, mActivity.getActivityTab()); } - /** - * Handle a VR intent, entering VR in the process unless we're unable to. - */ - private boolean enterVRFromIntent() { - // Vr Intent is only used on Daydream devices. - if (mVrSupportLevel != VR_DAYDREAM) return false; - if (mNativeVrShellDelegate == 0) return false; - if (mListeningForWebVrActivateBeforePause && !mRequestedWebVR) { - nativeDisplayActivate(mNativeVrShellDelegate); - return false; + private void maybeSetPresentResult(boolean result) { + if (mNativeVrShellDelegate != 0 && mRequestedWebVr) { + nativeSetPresentResult(mNativeVrShellDelegate, result); } + mRequestedWebVr = false; + } + + /** + * Handle a successful VR DON flow, entering VR in the process unless we're unable to. + * @return False if VR entry failed. + */ + private boolean enterVrAfterDon() { + if (mNativeVrShellDelegate == 0) return false; + if (mListeningForWebVrActivateBeforePause && !mRequestedWebVr) { + nativeDisplayActivate(mNativeVrShellDelegate); + return true; + } + // Normally, if the active page doesn't have a vrdisplayactivate listener, and WebVR was not - // presenting and VrShell was not enabled, we shouldn't enter VR and Daydream Homescreen - // should show after DON flow. But due to a failure in unregisterDaydreamIntent, we still - // try to enterVR. Here we detect this case and force switch to Daydream Homescreen. - if (!mListeningForWebVrActivateBeforePause && !mRequestedWebVR - && !isVrShellEnabled(mVrSupportLevel)) { - mVrDaydreamApi.launchVrHomescreen(); + // presenting and VrShell was not enabled, the Daydream Homescreen should show after the DON + // flow. However, due to a failure in unregisterDaydreamIntent, we still try to enterVR, so + // detect this case and fail to enter VR. + if (!mListeningForWebVrActivateBeforePause && !mRequestedWebVr + && !canEnterVr(mActivity.getActivityTab())) { return false; } - if (mInVr) { - setEnterVRResult(true); - return false; - } - if (!canEnterVR(mActivity.getActivityTab())) { - setEnterVRResult(false); - return false; - } - if (mPaused) { - // We can't enter VR before the application resumes, or we encounter bizarre crashes - // related to gpu surfaces. Set this flag to enter VR on the next resume. - mEnteringVr = true; - } else { - enterVR(); - } + enterVr(); return true; } - private void enterVR() { + private void enterVr() { + // We can't enter VR before the application resumes, or we encounter bizarre crashes + // related to gpu surfaces. + assert !mPaused; if (mNativeVrShellDelegate == 0) return; if (mInVr) return; if (!isWindowModeCorrectForVr()) { @@ -417,15 +449,14 @@ new Handler().post(new Runnable() { @Override public void run() { - enterVR(); + enterVr(); } }); return; } - mEnteringVr = false; if (!createVrShell()) { - restoreWindowMode(); - setEnterVRResult(false); + maybeSetPresentResult(false); + mVrDaydreamApi.launchVrHomescreen(); return; } mVrClassesWrapper.setVrModeEnabled(mActivity, true); @@ -434,14 +465,14 @@ mActivity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); addVrViews(); - mVrShell.initializeNative(mActivity.getActivityTab(), mRequestedWebVR); - mVrShell.setWebVrModeEnabled(mRequestedWebVR); + mVrShell.initializeNative(mActivity.getActivityTab(), mRequestedWebVr); + mVrShell.setWebVrModeEnabled(mRequestedWebVr); // onResume needs to be called on GvrLayout after initialization to make sure DON flow work // properly. mVrShell.resume(); - setEnterVRResult(true); + maybeSetPresentResult(true); mVrShell.getContainer().setOnSystemUiVisibilityChangeListener(this); } @@ -472,24 +503,14 @@ clearVrModeWindowFlags(); } - private void setEnterVRResult(boolean success) { - if (mRequestedWebVR && mNativeVrShellDelegate != 0) { - nativeSetPresentResult(mNativeVrShellDelegate, success); - } - if (!success && !mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) { - mVrClassesWrapper.setVrModeEnabled(mActivity, false); - } - mRequestedWebVR = false; - } - - /* package */ boolean canEnterVR(Tab tab) { + /* package */ boolean canEnterVr(Tab tab) { if (!LibraryLoader.isInitialized()) { return false; } if (mVrSupportLevel == VR_NOT_AVAILABLE || mNativeVrShellDelegate == 0) return false; // If vr shell is not enabled and this is not a web vr request, then return false. if (!isVrShellEnabled(mVrSupportLevel) - && !(mRequestedWebVR || mListeningForWebVrActivate)) { + && !(mRequestedWebVr || mListeningForWebVrActivate)) { return false; } // TODO(mthiesse): When we have VR UI for opening new tabs, etc., allow VR Shell to be @@ -501,40 +522,24 @@ if (tab.isShowingSadTab()) { return false; } - // crbug.com/667781 - if (MultiWindowUtils.getInstance().isInMultiWindowMode(mActivity)) { - return false; - } return true; } @CalledByNative private void presentRequested() { - // TODO(mthiesse): There's a GVR bug where they're not calling us back with the intent we - // ask them to when we call DaydreamApi#launchInVr. As a temporary hack, remember locally - // that we want to enter webVR. - mRequestedWebVR = true; - switch (enterVRInternal()) { + mRequestedWebVr = true; + switch (enterVrInternal()) { case ENTER_VR_NOT_NECESSARY: mVrShell.setWebVrModeEnabled(true); - if (mNativeVrShellDelegate != 0) { - nativeSetPresentResult(mNativeVrShellDelegate, true); - } - mRequestedWebVR = false; + maybeSetPresentResult(true); break; case ENTER_VR_CANCELLED: - if (mNativeVrShellDelegate != 0) { - nativeSetPresentResult(mNativeVrShellDelegate, false); - } - mRequestedWebVR = false; + maybeSetPresentResult(false); break; case ENTER_VR_REQUESTED: break; case ENTER_VR_SUCCEEDED: - if (mNativeVrShellDelegate != 0) { - nativeSetPresentResult(mNativeVrShellDelegate, true); - } - mRequestedWebVR = false; + maybeSetPresentResult(true); break; default: Log.e(TAG, "Unexpected enum."); @@ -545,23 +550,23 @@ * Enters VR Shell if necessary, displaying browser UI and tab contents in VR. */ @EnterVRResult - private int enterVRInternal() { + private int enterVrInternal() { // Update VR support level as it can change at runtime updateVrSupportLevel(); if (mVrSupportLevel == VR_NOT_AVAILABLE) return ENTER_VR_CANCELLED; if (mInVr) return ENTER_VR_NOT_NECESSARY; - if (!canEnterVR(mActivity.getActivityTab())) return ENTER_VR_CANCELLED; + if (!canEnterVr(mActivity.getActivityTab())) return ENTER_VR_CANCELLED; if (mVrSupportLevel == VR_CARDBOARD || !mVrDaydreamApi.isDaydreamCurrentViewer()) { // Avoid using launchInVr which would trigger DON flow regardless current viewer type // due to the lack of support for unexported activities. - enterVR(); + enterVr(); } else { // LANDSCAPE orientation is needed before we can safely enter VR. DON can make sure that // the device is at LANDSCAPE orientation once it is finished. So here we use SENSOR to // avoid forcing LANDSCAPE orientation in order to have a smoother transition. setWindowModeForVr(ActivityInfo.SCREEN_ORIENTATION_SENSOR); - if (!mVrDaydreamApi.launchInVr(getEnterVRPendingIntent(mVrDaydreamApi, mActivity))) { + if (!mVrDaydreamApi.launchInVr(getEnterVrPendingIntent(mActivity))) { restoreWindowMode(); return ENTER_VR_CANCELLED; } @@ -574,28 +579,17 @@ if (!mInVr) return false; mVrShell.setWebVrModeEnabled(false); if (!isVrShellEnabled(mVrSupportLevel)) { - shutdownVR(false /* isPausing */, true /* showTransition */); + shutdownVr(false /* isPausing */, true /* showTransition */); } return true; } - private void resumeVR() { + private void resumeVr() { mPaused = false; - if (mVrSupportLevel == VR_NOT_AVAILABLE) return; - if (mVrSupportLevel == VR_DAYDREAM - && (isVrShellEnabled(mVrSupportLevel) || mListeningForWebVrActivateBeforePause)) { - registerDaydreamIntent(mVrDaydreamApi, mActivity); - } - if (mEnteringVr) { - enterVR(); - } else if (mRequestedWebVR) { - // If this is still set, it means the user backed out of the DON flow, and we won't be - // receiving an intent from daydream. - if (mNativeVrShellDelegate != 0) nativeSetPresentResult(mNativeVrShellDelegate, false); - restoreWindowMode(); - mRequestedWebVR = false; - } + // TODO(mthiesse): If we ever support staying in VR while paused, make sure to call resume + // on VrShell. + assert !mInVr; StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); try { @@ -604,23 +598,32 @@ StrictMode.setThreadPolicy(oldPolicy); } - if (mInVr) { - setupVrModeWindowFlags(); - oldPolicy = StrictMode.allowThreadDiskWrites(); - try { - mVrShell.resume(); - } catch (IllegalArgumentException e) { - Log.e(TAG, "Unable to resume VrShell", e); - } finally { - StrictMode.setThreadPolicy(oldPolicy); + if (mVrSupportLevel != VR_DAYDREAM) return; + if (mListeningForWebVrActivateBeforePause + || (isVrShellEnabled(mVrSupportLevel) && activitySupportsVrBrowsing(mActivity))) { + registerDaydreamIntent(mVrDaydreamApi, mActivity); + } + + if (mVrDaydreamApi.isDaydreamCurrentViewer() + && mLastVrExit + REENTER_VR_TIMEOUT_MS > SystemClock.uptimeMillis()) { + mDonSucceeded = true; + } + + if (mDonSucceeded) { + mDonSucceeded = false; + // If we fail to enter VR when we should have entered VR, return to the home screen. + if (!enterVrAfterDon()) { + maybeSetPresentResult(false); + mVrDaydreamApi.launchVrHomescreen(); } - } else if (mVrSupportLevel == VR_DAYDREAM && mVrDaydreamApi.isDaydreamCurrentViewer() - && mLastVRExit + REENTER_VR_TIMEOUT_MS > SystemClock.uptimeMillis()) { - enterVRInternal(); + } else if (mRestoreOrientation != null) { + // This means the user backed out of the DON flow, and we won't be entering VR. + maybeSetPresentResult(false); + restoreWindowMode(); } } - private void pauseVR() { + private void pauseVr() { mPaused = true; if (mVrSupportLevel == VR_NOT_AVAILABLE) return; @@ -641,17 +644,17 @@ // TODO(mthiesse): When VR Shell lives in its own activity, and integrates with Daydream // home, pause instead of exiting VR here. For now, because VR Apps shouldn't show up in the // non-VR recents, and we don't want ChromeTabbedActivity disappearing, exit VR. - shutdownVR(true /* isPausing */, false /* showTransition */); + shutdownVr(true /* isPausing */, false /* showTransition */); } private boolean onBackPressedInternal() { if (mVrSupportLevel == VR_NOT_AVAILABLE) return false; if (!mInVr) return false; - shutdownVR(false /* isPausing */, false /* showTransition */); + shutdownVr(false /* isPausing */, false /* showTransition */); return true; } - private void onExitVRResult(boolean success) { + private void onExitVrResult(boolean success) { assert mVrSupportLevel != VR_NOT_AVAILABLE; // For now, we don't handle re-entering VR when exit fails, so keep trying to exit. if (!success && mVrDaydreamApi.exitFromVr(EXIT_VR_RESULT, new Intent())) return; @@ -692,10 +695,10 @@ /** * Exits VR Shell, performing all necessary cleanup. */ - /* package */ void shutdownVR(boolean isPausing, boolean showTransition) { + /* package */ void shutdownVr(boolean isPausing, boolean showTransition) { if (!mInVr) return; mInVr = false; - mRequestedWebVR = false; + mRequestedWebVr = false; // Transition screen is not available for Cardboard only (non-Daydream) devices. // TODO(bshe): Fix this once b/33490788 is fixed. boolean transition = mVrSupportLevel == VR_DAYDREAM && showTransition; @@ -705,7 +708,7 @@ } } else { mVrClassesWrapper.setVrModeEnabled(mActivity, false); - mLastVRExit = SystemClock.uptimeMillis(); + mLastVrExit = SystemClock.uptimeMillis(); } restoreWindowMode(); mVrShell.pause(); @@ -765,7 +768,7 @@ assert mVrShell == null; if (mVrClassesWrapper == null) return false; if (mActivity.getCompositorViewHolder() == null) return false; - mTabModelSelector = mActivity.getCompositorViewHolder().detachForVR(); + mTabModelSelector = mActivity.getCompositorViewHolder().detachForVr(); if (mTabModelSelector == null) return false; StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites(); try { @@ -782,11 +785,11 @@ ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); decor.addView(mVrShell.getContainer(), params); - mActivity.onEnterVR(); + mActivity.onEnterVr(); } private void removeVrViews() { - mActivity.onExitVR(); + mActivity.onExitVr(); FrameLayout decor = (FrameLayout) mActivity.getWindow().getDecorView(); decor.removeView(mVrShell.getContainer()); } @@ -817,7 +820,7 @@ mVrShell.getContainer().setOnSystemUiVisibilityChangeListener(null); mVrShell.teardown(); mVrShell = null; - mActivity.getCompositorViewHolder().onExitVR(mTabModelSelector); + mActivity.getCompositorViewHolder().onExitVr(mTabModelSelector); mTabModelSelector = null; } } @@ -856,7 +859,7 @@ private void destroy() { if (sInstance == null) return; - shutdownVR(true, false); + shutdownVr(true, false); if (mNativeVrShellDelegate != 0) nativeDestroy(mNativeVrShellDelegate); mNativeVrShellDelegate = 0; ApplicationStatus.unregisterActivityStateListener(this);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java index f5650c3f..d5ba53c3 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/vr_shell/VrShellImpl.java
@@ -21,18 +21,14 @@ import com.google.vr.ndk.base.GvrLayout; import org.chromium.base.CommandLine; -import org.chromium.base.Log; import org.chromium.base.ThreadUtils; import org.chromium.base.VisibleForTesting; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.chrome.browser.ChromeActivity; import org.chromium.chrome.browser.ChromeSwitches; -import org.chromium.chrome.browser.ChromeVersionInfo; import org.chromium.chrome.browser.NativePage; import org.chromium.chrome.browser.UrlConstants; -import org.chromium.chrome.browser.WebContentsFactory; -import org.chromium.chrome.browser.omnibox.geo.GeolocationHeader; import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.TabObserver; @@ -44,15 +40,12 @@ import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; import org.chromium.chrome.browser.tabmodel.TabModelSelectorTabObserver; import org.chromium.chrome.browser.tabmodel.TabModelUtils; -import org.chromium.content.browser.ContentView; import org.chromium.content.browser.ContentViewCore; import org.chromium.content.browser.MotionEventSynthesizer; import org.chromium.content.browser.WindowAndroidChangedObserver; import org.chromium.content.browser.WindowAndroidProvider; -import org.chromium.content_public.browser.LoadUrlParams; import org.chromium.content_public.browser.WebContents; import org.chromium.ui.UiUtils; -import org.chromium.ui.base.ViewAndroidDelegate; import org.chromium.ui.base.WindowAndroid; import org.chromium.ui.display.DisplayAndroid; import org.chromium.ui.display.VirtualDisplayAndroid; @@ -84,7 +77,6 @@ private final ChromeActivity mActivity; private final VrShellDelegate mDelegate; private final VirtualDisplayAndroid mContentVirtualDisplay; - private final VirtualDisplayAndroid mUiVirtualDisplay; private final TabRedirectHandler mTabRedirectHandler; private final TabObserver mTabObserver; private final TabModelSelectorObserver mTabModelSelectorObserver; @@ -93,7 +85,6 @@ private long mNativeVrShell; - private FrameLayout mUiCVCContainer; private FrameLayout mRenderToSurfaceLayout; private Surface mSurface; private View mPresentationView; @@ -106,10 +97,6 @@ private WindowAndroid mOriginalWindowAndroid; private VrWindowAndroid mContentVrWindowAndroid; - private WebContents mUiContents; - private ContentViewCore mUiCVC; - private VrWindowAndroid mUiVrWindowAndroid; - private boolean mReprojectedRendering; private TabRedirectHandler mNonVrTabRedirectHandler; @@ -128,13 +115,6 @@ mActivity = activity; mDelegate = delegate; mTabModelSelector = tabModelSelector; - mUiCVCContainer = new FrameLayout(getContext()) { - @Override - public boolean dispatchTouchEvent(MotionEvent event) { - return true; - } - }; - addView(mUiCVCContainer, 0, new FrameLayout.LayoutParams(0, 0)); mReprojectedRendering = setAsyncReprojectionEnabled(true); if (mReprojectedRendering) { @@ -156,15 +136,13 @@ getUiLayout().setCloseButtonListener(new Runnable() { @Override public void run() { - mDelegate.shutdownVR(false /* isPausing */, false /* showTransition */); + mDelegate.shutdownVr(false /* isPausing */, false /* showTransition */); } }); DisplayAndroid primaryDisplay = DisplayAndroid.getNonMultiDisplay(activity); mContentVirtualDisplay = VirtualDisplayAndroid.createVirtualDisplay(); mContentVirtualDisplay.setTo(primaryDisplay); - mUiVirtualDisplay = VirtualDisplayAndroid.createVirtualDisplay(); - mUiVirtualDisplay.setTo(primaryDisplay); mTabRedirectHandler = new TabRedirectHandler(mActivity) { @Override @@ -295,19 +273,10 @@ public void initializeNative(Tab currentTab, boolean forWebVR) { mContentVrWindowAndroid = new VrWindowAndroid(mActivity, mContentVirtualDisplay); - mUiVrWindowAndroid = new VrWindowAndroid(mActivity, mUiVirtualDisplay); - mUiContents = WebContentsFactory.createWebContents(true, false); - mUiCVC = new ContentViewCore(mActivity, ChromeVersionInfo.getProductVersion()); - ContentView uiContentView = ContentView.createContentView(mActivity, mUiCVC); - mUiCVC.initialize(ViewAndroidDelegate.createBasicDelegate(uiContentView), - uiContentView, mUiContents, mUiVrWindowAndroid); - - mNativeVrShell = nativeInit(mUiContents, mContentVrWindowAndroid.getNativePointer(), - mUiVrWindowAndroid.getNativePointer(), forWebVR, mDelegate, + mNativeVrShell = nativeInit(mDelegate, mContentVrWindowAndroid.getNativePointer(), forWebVR, getGvrApi().getNativeGvrContext(), mReprojectedRendering); // Set the UI and content sizes before we load the UI. - setUiCssSize(DEFAULT_UI_WIDTH, DEFAULT_UI_HEIGHT, DEFAULT_DPR); if (forWebVR) { DisplayAndroid primaryDisplay = DisplayAndroid.getNonMultiDisplay(mActivity); setContentCssSize(primaryDisplay.getPhysicalDisplayWidth(), @@ -321,18 +290,7 @@ mActivity.getTabModelSelector().addObserver(mTabModelSelectorObserver); createTabModelSelectorTabObserver(); - nativeLoadUIContent(mNativeVrShell); - mPresentationView.setOnTouchListener(mTouchListener); - - uiContentView.setVisibility(View.VISIBLE); - mUiCVC.onShow(); - mUiCVCContainer.addView(uiContentView, new FrameLayout.LayoutParams( - FrameLayout.LayoutParams.MATCH_PARENT, - FrameLayout.LayoutParams.MATCH_PARENT)); - mUiCVC.setBottomControlsHeight(0); - mUiCVC.setTopControlsHeight(0, false); - mUiVrWindowAndroid.onVisibilityChanged(true); } private void createTabList() { @@ -355,7 +313,7 @@ private void swapToForegroundTab() { Tab tab = mActivity.getActivityTab(); if (tab == mTab) return; - if (!mDelegate.canEnterVR(tab)) { + if (!mDelegate.canEnterVr(tab)) { forceExitVr(); return; } @@ -391,24 +349,7 @@ // Exits VR, telling the user to remove their headset, and returning to Chromium. @CalledByNative public void forceExitVr() { - mDelegate.shutdownVR(false /* isPausing */, true /* showTransition */); - } - - @CalledByNative - public void setUiCssSize(float width, float height, float dpr) { - ThreadUtils.assertOnUiThread(); - if (dpr != DEFAULT_DPR) { - Log.w(TAG, "Changing UI DPR causes the UI to flicker and should generally not be " - + "done."); - } - int surfaceWidth = (int) Math.ceil(width * dpr); - int surfaceHeight = (int) Math.ceil(height * dpr); - - Point size = new Point(surfaceWidth, surfaceHeight); - mUiVirtualDisplay.update(size, size, dpr, null, null, null); - mUiCVC.onSizeChanged(surfaceWidth, surfaceHeight, 0, 0); - mUiCVC.onPhysicalBackingSizeChanged(surfaceWidth, surfaceHeight); - nativeUIPhysicalBoundsChanged(mNativeVrShell, surfaceWidth, surfaceHeight, dpr); + mDelegate.shutdownVr(false /* isPausing */, true /* showTransition */); } @CalledByNative @@ -500,9 +441,7 @@ mTabModelSelectorTabObserver.destroy(); mTab.removeObserver(mTabObserver); restoreTabFromVR(); - mUiContents.destroy(); mContentVirtualDisplay.destroy(); - mUiVirtualDisplay.destroy(); super.shutdown(); } @@ -616,14 +555,6 @@ } @CalledByNative - public void loadURL(String url, int transition) { - LoadUrlParams loadUrlParams = new LoadUrlParams(url); - loadUrlParams.setVerbatimHeaders(GeolocationHeader.getGeoHeader(url, mTab)); - loadUrlParams.setTransitionType(transition); - mTab.loadUrl(loadUrlParams); - } - - @CalledByNative public void reload() { mTab.reload(); } @@ -655,13 +586,11 @@ mOnDispatchTouchEventForTesting = callback; } - private native long nativeInit(WebContents uiWebContents, long nativeContentWindowAndroid, - long nativeUiWindowAndroid, boolean forWebVR, VrShellDelegate delegate, long gvrApi, - boolean reprojectedRendering); + private native long nativeInit(VrShellDelegate delegate, long nativeWindowAndroid, + boolean forWebVR, long gvrApi, boolean reprojectedRendering); private native void nativeSetSurface(long nativeVrShell, Surface surface); private native void nativeSwapContents( long nativeVrShell, WebContents webContents, MotionEventSynthesizer eventSynthesizer); - private native void nativeLoadUIContent(long nativeVrShell); private native void nativeDestroy(long nativeVrShell); private native void nativeOnTriggerEvent(long nativeVrShell); private native void nativeOnPause(long nativeVrShell); @@ -669,8 +598,6 @@ private native void nativeOnLoadProgressChanged(long nativeVrShell, double progress); private native void nativeContentPhysicalBoundsChanged(long nativeVrShell, int width, int height, float dpr); - private native void nativeUIPhysicalBoundsChanged(long nativeVrShell, int width, int height, - float dpr); private native void nativeSetWebVrMode(long nativeVrShell, boolean enabled); private native void nativeOnTabListCreated(long nativeVrShell, Tab[] mainTabs, Tab[] incognitoTabs);
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHost.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHost.java index 77f01b87..d6e36d0 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHost.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHost.java
@@ -6,12 +6,14 @@ import android.os.StrictMode; +import org.chromium.base.CommandLine; import org.chromium.base.ContextUtils; import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.library_loader.LibraryLoader; import org.chromium.chrome.browser.AppHooks; import org.chromium.chrome.browser.ChromeFeatureList; +import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.GooglePlayInstallState; import org.chromium.chrome.browser.externalauth.ExternalAuthUtils; import org.chromium.chrome.browser.externalauth.UserRecoverableErrorHandler; @@ -30,7 +32,8 @@ private static Boolean sEnabledForTesting; public static void init() { - WebApkValidator.initWithBrowserHostSignature(ChromeWebApkHostSignature.EXPECTED_SIGNATURE); + WebApkValidator.init(isAnyPackageNameEnabledInPrefs(), + ChromeWebApkHostSignature.EXPECTED_SIGNATURE, ChromeWebApkHostSignature.PUBLIC_KEY); } public static void initForTesting(boolean enabled) { @@ -98,6 +101,21 @@ } /** + * Check the cached value to figure out if any WebAPK package name may be used. We have to use + * the cached value because native library may not yet been loaded. + * @return Whether the feature is enabled. + */ + private static boolean isAnyPackageNameEnabledInPrefs() { + // Will go away once the feature is enabled for everyone by default. + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + try { + return ChromePreferenceManager.getInstance().getCachedWebApkAnyPackageName(); + } finally { + StrictMode.setThreadPolicy(oldPolicy); + } + } + + /** * Once native is loaded we can consult the command-line (set via about:flags) and also finch * state to see if we should enable WebAPKs. */ @@ -110,6 +128,15 @@ Log.d(TAG, "WebApk setting changed (%s => %s)", wasEnabled, isEnabled); preferenceManager.setCachedWebApkRuntimeEnabled(isEnabled); } + + boolean wasAnyPackageNameEnabled = isAnyPackageNameEnabledInPrefs(); + boolean isAnyPackageNameEnabled = + CommandLine.getInstance().hasSwitch(ChromeSwitches.ENABLE_ANY_WEBAPK_PACKAGE_NAME); + if (wasAnyPackageNameEnabled != isAnyPackageNameEnabled) { + Log.d(TAG, "WebApk Any Package name setting changed (%s => %s)", + wasAnyPackageNameEnabled, isAnyPackageNameEnabled); + preferenceManager.setCachedWebApkAnyPackageNameEnabled(isAnyPackageNameEnabled); + } } private static native boolean nativeCanLaunchRendererInWebApkProcess();
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHostSignature.java b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHostSignature.java index 8ccf454..ae97bd15 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHostSignature.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHostSignature.java
@@ -4,8 +4,8 @@ package org.chromium.chrome.browser.webapps; +/** Public key and signature for WebAPKs */ public class ChromeWebApkHostSignature { - // The public key to verify whether a WebAPK is signed by WebAPK Server. static final byte[] EXPECTED_SIGNATURE = new byte[] {48, -126, 4, 104, 48, -126, 2, -48, -96, 3, 2, 1, 2, 2, 20, 120, 33, -22, -36, -115, 7, 116, 66, 116, 113, -122, -126, -124, 32, 44, @@ -65,4 +65,11 @@ -27, -68, 62, -81, 98, -54, -80, -23, 59, 38, -127, 96, 71, 123, 34, -113, -23, -80, 32, -97, -55, -100, 121, 120, 50, -48, 58, 69, -105, 26, 126, 30, -1, -112, -41, -18, -16, 62, 48, -22, -2, 19, 117, -6, 59, 74, -13, 92, -1}; + + // The public key for comment signed WebAPK's. Elliptic Curve, NIST P-256 in ASN.1 format. + static final byte[] PUBLIC_KEY = new byte[] {48, 89, 48, 19, 6, 7, 42, -122, 72, -50, 61, 2, 1, + 6, 8, 42, -122, 72, -50, 61, 3, 1, 7, 3, 66, 0, 4, -25, 45, 2, 49, 44, -60, 107, -108, + -45, 27, -40, -8, -116, 44, 7, -38, -103, 52, -81, 33, -90, -80, -94, 125, -3, -67, 51, + -125, -63, 6, -127, 89, 32, 53, 83, -120, -106, -113, -121, -39, 115, -50, 15, 117, 66, + 78, -89, -124, -120, 4, -61, 8, -90, -67, -6, 71, -120, -120, 23, 23, 77, 75, 103, -28}; }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/ArrowBubbleDrawable.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/ArrowBubbleDrawable.java index 49f50d0..3bef424 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/ArrowBubbleDrawable.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/ArrowBubbleDrawable.java
@@ -22,7 +22,7 @@ /** * A {@link Drawable} that is a bubble with an arrow pointing out of either the top or bottom. */ -class ArrowBubbleDrawable extends Drawable { +class ArrowBubbleDrawable extends Drawable implements Drawable.Callback { private final Rect mCachedBubblePadding = new Rect(); private final int mRadiusPx; @@ -40,6 +40,9 @@ null, null)); mArrowDrawable = (BitmapDrawable) ApiCompatibilityUtils.getDrawable( context.getResources(), R.drawable.bubble_point_white); + + mBubbleDrawable.setCallback(this); + mArrowDrawable.setCallback(this); } /** @@ -72,6 +75,13 @@ } /** + * @return Whether or not the arrow is currently drawing on top of this {@link Drawable}. + */ + public boolean isArrowOnTop() { + return mArrowOnTop; + } + + /** * @param color The color to make the bubble and arrow. */ public void setBubbleColor(@ColorInt int color) { @@ -80,6 +90,22 @@ invalidateSelf(); } + // Drawable.Callback implementation. + @Override + public void invalidateDrawable(Drawable who) { + invalidateSelf(); + } + + @Override + public void scheduleDrawable(Drawable who, Runnable what, long when) { + scheduleSelf(what, when); + } + + @Override + public void unscheduleDrawable(Drawable who, Runnable what) { + unscheduleSelf(what); + } + // Drawable implementation. @Override public void draw(Canvas canvas) {
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/TextBubble.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/TextBubble.java index d08608c..4fdbc1e 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/TextBubble.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/TextBubble.java
@@ -21,6 +21,7 @@ import android.widget.TextView; import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.base.ObserverList; import org.chromium.chrome.R; import org.chromium.chrome.browser.util.MathUtils; @@ -29,7 +30,7 @@ * calls to {@link #setAnchorRect(Rect)}. This should be called at least once before the * {@link #show()} call. To attach to a {@link View} see {@link ViewAnchoredTextBubble}. */ -public class TextBubble implements OnTouchListener, OnDismissListener { +public class TextBubble implements OnTouchListener { /** * Specifies no limit to the popup duration. * @see #setAutoDismissTimeout(long) @@ -63,6 +64,16 @@ } }; + private final OnDismissListener mDismissListener = new OnDismissListener() { + @Override + public void onDismiss() { + if (mIgnoreDismissal) return; + + mHandler.removeCallbacks(mDismissRunnable); + for (OnDismissListener listener : mDismissListeners) listener.onDismiss(); + } + }; + /** * How long to wait before automatically dismissing the bubble. {@link #NO_TIMEOUT} is the * default and means the bubble will stay visible indefinitely. @@ -72,7 +83,7 @@ // Pass through for the internal PopupWindow. This class needs to intercept these for API // purposes, but they are still useful to callers. - private OnDismissListener mDismissListener; + private ObserverList<OnDismissListener> mDismissListeners = new ObserverList<>(); private OnTouchListener mTouchListener; // Positioning/sizing coordinates for the popup bubble. @@ -81,6 +92,13 @@ private int mWidth; private int mHeight; + /** + * Tracks whether or not we are in the process of updating the bubble, which might include a + * dismiss and show. In that case we don't want to let the world know we're dismissing because + * it's only temporary. + */ + private boolean mIgnoreDismissal; + // Content specific variables. /** The resource id for the string to show in the bubble. */ @StringRes @@ -106,7 +124,7 @@ mPopupWindow.setAnimationStyle(R.style.TextBubbleAnimation); mPopupWindow.setTouchInterceptor(this); - mPopupWindow.setOnDismissListener(this); + mPopupWindow.setOnDismissListener(mDismissListener); mMarginPx = context.getResources().getDimensionPixelSize(R.dimen.text_bubble_margin); @@ -148,8 +166,16 @@ * @param onDismissListener A listener to be called when the bubble is dismissed. * @see PopupWindow#setOnDismissListener(OnDismissListener) */ - public void setOnDismissListener(OnDismissListener onDismissListener) { - mDismissListener = onDismissListener; + public void addOnDismissListener(OnDismissListener onDismissListener) { + mDismissListeners.addObserver(onDismissListener); + } + + /** + * @param onDismissListener The listener to remove and not call when the bubble is dismissed. + * @see PopupWindow#setOnDismissListener(OnDismissListener) + */ + public void removeOnDismissListener(OnDismissListener onDismissListener) { + mDismissListeners.removeObserver(onDismissListener); } /** @@ -196,7 +222,11 @@ */ private void updateBubbleLayout() { // Determine the size of the text bubble. - mPopupWindow.getBackground().getPadding(mCachedPaddingRect); + ArrowBubbleDrawable background = (ArrowBubbleDrawable) mPopupWindow.getBackground(); + boolean currentPositionBelow = background.isArrowOnTop(); + boolean preferCurrentOrientation = mPopupWindow.isShowing(); + + background.getPadding(mCachedPaddingRect); int paddingX = mCachedPaddingRect.left + mCachedPaddingRect.right; int paddingY = mCachedPaddingRect.top + mCachedPaddingRect.bottom; @@ -221,6 +251,13 @@ // available. This does bias the bubbles to show below the anchors if possible. boolean positionBelow = idealHeight <= spaceBelowAnchor || spaceBelowAnchor >= spaceAboveAnchor; + + // Override the ideal bubble orientation if we are trying to maintain the current one. + if (preferCurrentOrientation && currentPositionBelow != positionBelow) { + if (currentPositionBelow && idealHeight <= spaceBelowAnchor) positionBelow = true; + if (!currentPositionBelow && idealHeight <= spaceAboveAnchor) positionBelow = false; + } + int maxContentHeight = positionBelow ? spaceBelowAnchor : spaceAboveAnchor; contentView.measure( widthSpec, MeasureSpec.makeMeasureSpec(maxContentHeight, MeasureSpec.AT_MOST)); @@ -247,7 +284,20 @@ // TODO(dtrainor): Figure out how to move the arrow and bubble to make things look better. mDrawable.setPositionProperties(arrowXOffset, positionBelow); - mPopupWindow.update(mX, mY, mWidth, mHeight); + + if (positionBelow != currentPositionBelow) { + // This is a hack to deal with the case where the arrow flips between top and bottom. + // In this case the padding of the background drawable in the PopupWindow changes. + try { + mIgnoreDismissal = true; + mPopupWindow.dismiss(); + mPopupWindow.showAtLocation(mRootView, Gravity.TOP | Gravity.START, mX, mY); + } finally { + mIgnoreDismissal = false; + } + } else { + mPopupWindow.update(mX, mY, mWidth, mHeight); + } } private void createContentView() { @@ -265,11 +315,4 @@ if (mDismissOnTouchInteraction) dismiss(); return returnValue; } - - // OnDismissListener implementation. - @Override - public void onDismiss() { - mHandler.removeCallbacks(mDismissRunnable); - if (mDismissListener != null) mDismissListener.onDismiss(); - } }
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/ViewAnchoredTextBubble.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/ViewAnchoredTextBubble.java index 3a77d626..816acb3c 100644 --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/ViewAnchoredTextBubble.java +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/textbubble/ViewAnchoredTextBubble.java
@@ -8,20 +8,31 @@ import android.graphics.Rect; import android.support.annotation.StringRes; import android.view.View; -import android.view.View.OnLayoutChangeListener; +import android.view.ViewTreeObserver; +import android.view.ViewTreeObserver.OnPreDrawListener; +import android.widget.PopupWindow.OnDismissListener; import org.chromium.base.ApiCompatibilityUtils; +import org.chromium.content.browser.PositionObserver; +import org.chromium.content.browser.ViewPositionObserver; /** * A helper class that anchors a {@link TextBubble} to a particular {@link View}. The bubble will * listen to layout events on the {@link View} and update accordingly. */ -public class ViewAnchoredTextBubble extends TextBubble implements OnLayoutChangeListener { +public class ViewAnchoredTextBubble extends TextBubble + implements PositionObserver.Listener, ViewTreeObserver.OnGlobalLayoutListener, + View.OnAttachStateChangeListener, OnPreDrawListener, OnDismissListener { private final int[] mCachedScreenCoordinates = new int[2]; private final Rect mAnchorRect = new Rect(); private final Rect mInsetRect = new Rect(); private final View mAnchorView; + private final ViewPositionObserver mViewPositionObserver; + + /** If not {@code null}, the {@link ViewTreeObserver} that we are registered to. */ + private ViewTreeObserver mViewTreeObserver; + /** * Creates an instance of a {@link ViewAnchoredTextBubble}. * @param context Context to draw resources from. @@ -31,6 +42,8 @@ public ViewAnchoredTextBubble(Context context, View anchorView, @StringRes int stringId) { super(context, anchorView.getRootView(), stringId); mAnchorView = anchorView; + + mViewPositionObserver = new ViewPositionObserver(mAnchorView); } /** @@ -45,26 +58,53 @@ // TextBubble implementation. @Override public void show() { - mAnchorView.addOnLayoutChangeListener(this); + mViewPositionObserver.addListener(this); + mAnchorView.addOnAttachStateChangeListener(this); + mViewTreeObserver = mAnchorView.getViewTreeObserver(); + mViewTreeObserver.addOnGlobalLayoutListener(this); + mViewTreeObserver.addOnPreDrawListener(this); + refreshAnchorBounds(); super.show(); } @Override - public void dismiss() { - super.dismiss(); - mAnchorView.removeOnLayoutChangeListener(this); + public void onDismiss() { + mViewPositionObserver.removeListener(this); + mAnchorView.removeOnAttachStateChangeListener(this); + + if (mViewTreeObserver != null && mViewTreeObserver.isAlive()) { + mViewTreeObserver.removeOnGlobalLayoutListener(this); + mViewTreeObserver.removeOnPreDrawListener(this); + } + mViewTreeObserver = null; } - // OnLayoutChangeListener implementation. + // ViewTreeObserver.OnGlobalLayoutListener implementation. @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, - int oldTop, int oldRight, int oldBottom) { - if (!mAnchorView.isShown()) { - dismiss(); - return; - } + public void onGlobalLayout() { + if (!mAnchorView.isShown()) dismiss(); + } + // ViewTreeObserver.OnPreDrawListener implementation. + @Override + public boolean onPreDraw() { + if (!mAnchorView.isShown()) dismiss(); + return true; + } + + // View.OnAttachStateChangedObserver implementation. + @Override + public void onViewAttachedToWindow(View v) {} + + @Override + public void onViewDetachedFromWindow(View v) { + dismiss(); + } + + // PositionObserver.Listener implementation. + @Override + public void onPositionChanged(int positionX, int positionY) { refreshAnchorBounds(); }
diff --git a/chrome/android/java_sources.gni b/chrome/android/java_sources.gni index e174070..f113f6b 100644 --- a/chrome/android/java_sources.gni +++ b/chrome/android/java_sources.gni
@@ -1611,6 +1611,7 @@ ] chrome_junit_test_java_sources = [ + "junit/src/org/chromium/chrome/browser/AppIndexingUtilTest.java", "junit/src/org/chromium/chrome/browser/ChromeBackupAgentTest.java", "junit/src/org/chromium/chrome/browser/ChromeBackgroundServiceWaiterTest.java", "junit/src/org/chromium/chrome/browser/DelayedScreenLockIntentHandlerTest.java",
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java index 5f47ee9..300fad6 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/payments/PaymentRequestMetricsTest.java
@@ -10,8 +10,10 @@ import org.chromium.base.ThreadUtils; import org.chromium.base.metrics.RecordHistogram; +import org.chromium.base.test.util.CommandLineFlags; import org.chromium.base.test.util.Feature; import org.chromium.chrome.R; +import org.chromium.chrome.browser.ChromeFeatureList; import org.chromium.chrome.browser.autofill.AutofillTestHelper; import org.chromium.chrome.browser.autofill.PersonalDataManager.AutofillProfile; import org.chromium.chrome.browser.autofill.PersonalDataManager.CreditCard; @@ -263,6 +265,47 @@ } /** + * Expect that the SkippedShow metric is logged when the UI directly goes + * to the payment app UI during a Payment Request. + */ + @MediumTest + @Feature({"Payments"}) + public void testMetrics_SkippedShow() + throws InterruptedException, ExecutionException, TimeoutException { + // Complete a Payment Request with Android Pay. + installPaymentApp("https://android.com/pay", HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE); + triggerUIAndWait("androidPaySkipUiBuy", mResultReady); + + assertEquals(1, + RecordHistogram.getHistogramValueCountForTesting( + "PaymentRequest.CheckoutFunnel.SkippedShow", 1)); + assertEquals(0, + RecordHistogram.getHistogramValueCountForTesting( + "PaymentRequest.CheckoutFunnel.Shown", 1)); + } + + /** + * Expect that the PaymentRequest UI is shown even if all the requirements are met to skip, if + * the skip feature is disabled. + */ + @MediumTest + @Feature({"Payments"}) + @CommandLineFlags.Add({"disable-features=" + ChromeFeatureList.WEB_PAYMENTS_SINGLE_APP_UI_SKIP}) + public void testMetrics_SkippedShow_Disabled() + throws InterruptedException, ExecutionException, TimeoutException { + // Complete a Payment Request with Android Pay. + installPaymentApp("https://android.com/pay", HAVE_INSTRUMENTS, IMMEDIATE_RESPONSE); + triggerUIAndWait("androidPaySkipUiBuy", mReadyToPay); + + assertEquals(1, + RecordHistogram.getHistogramValueCountForTesting( + "PaymentRequest.CheckoutFunnel.Shown", 1)); + assertEquals(0, + RecordHistogram.getHistogramValueCountForTesting( + "PaymentRequest.CheckoutFunnel.SkippedShow", 1)); + } + + /** * Expect that the "Shown" event is recorded only once. */ @MediumTest
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java index 6c076b9..027b3ef 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/search_engines/TemplateUrlServiceTest.java
@@ -109,13 +109,6 @@ @RetryOnFailure @Restriction(ChromeRestriction.RESTRICTION_TYPE_PHONE) // see crbug.com/581268 public void testLoadUrlService() { - Assert.assertFalse(ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { - @Override - public Boolean call() throws Exception { - return TemplateUrlService.getInstance().isLoaded(); - } - })); - waitForTemplateUrlServiceToLoad(); Assert.assertTrue(ThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { @@ -124,6 +117,29 @@ return TemplateUrlService.getInstance().isLoaded(); } })); + + // Add another load listener and ensure that is notified without needing to call load() + // again. + final AtomicBoolean observerNotified = new AtomicBoolean(false); + ThreadUtils.runOnUiThreadBlocking(new Runnable() { + @Override + public void run() { + TemplateUrlService service = TemplateUrlService.getInstance(); + service.registerLoadListener(new LoadListener() { + @Override + public void onTemplateUrlServiceLoaded() { + observerNotified.set(true); + } + }); + } + }); + CriteriaHelper.pollInstrumentationThread( + new Criteria("Observer wasn't notified of TemplateUrlService load.") { + @Override + public boolean isSatisfied() { + return observerNotified.get(); + } + }); } @Test
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java index 9f881de2..c95b75f 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrShellTest.java
@@ -65,13 +65,13 @@ VrUtils.forceEnterVr(); if (supported) { VrUtils.waitForVrSupported(POLL_TIMEOUT_LONG_MS); - assertTrue(VrShellDelegate.isInVR()); + assertTrue(VrShellDelegate.isInVr()); } else { assertFalse(mockApi.getLaunchInVrCalled()); - assertFalse(VrShellDelegate.isInVR()); + assertFalse(VrShellDelegate.isInVr()); } VrUtils.forceExitVr(mDelegate); - assertFalse(VrShellDelegate.isInVR()); + assertFalse(VrShellDelegate.isInVr()); } private void enterExitVrModeImage(boolean supported) throws IOException { @@ -117,9 +117,9 @@ VrUtils.simNfc(getActivity()); if (supported) { VrUtils.waitForVrSupported(POLL_TIMEOUT_LONG_MS); - assertTrue(VrShellDelegate.isInVR()); + assertTrue(VrShellDelegate.isInVr()); } else { - assertFalse(VrShellDelegate.isInVR()); + assertFalse(VrShellDelegate.isInVr()); } VrUtils.forceExitVr(mDelegate); // TODO(bsheedy): Figure out why NFC tests cause the next test to fail @@ -210,7 +210,7 @@ public void run() { CompositorViewHolder compositorViewHolder = (CompositorViewHolder) getActivity().findViewById(R.id.compositor_view_holder); - selector.set(compositorViewHolder.detachForVR()); + selector.set(compositorViewHolder.detachForVr()); oldWidth.set(cvc.getViewportWidthPix()); ViewGroup.LayoutParams layoutParams = compositorViewHolder.getLayoutParams(); @@ -236,7 +236,7 @@ public void run() { CompositorViewHolder compositorViewHolder = (CompositorViewHolder) getActivity() .findViewById(R.id.compositor_view_holder); - compositorViewHolder.onExitVR(selector.get()); + compositorViewHolder.onExitVr(selector.get()); } });
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrUtils.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrUtils.java index 323c312f..e16076e 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrUtils.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/VrUtils.java
@@ -54,7 +54,7 @@ ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { - VrShellDelegate.enterVRIfNecessary(); + VrShellDelegate.enterVrIfNecessary(); } }); } @@ -67,7 +67,7 @@ ThreadUtils.runOnUiThreadBlocking(new Runnable() { @Override public void run() { - vrDelegate.shutdownVR(false /* isPausing */, false /* showTransition */); + vrDelegate.shutdownVr(false /* isPausing */, false /* showTransition */); } }); } @@ -121,7 +121,7 @@ CriteriaHelper.pollUiThread(Criteria.equals(true, new Callable<Boolean>() { @Override public Boolean call() { - return VrShellDelegate.isInVR(); + return VrShellDelegate.isInVr(); } }), timeout, POLL_CHECK_INTERVAL_SHORT_MS); }
diff --git a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java index f964109..58444e3 100644 --- a/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java +++ b/chrome/android/javatests/src/org/chromium/chrome/browser/vr_shell/WebVrTest.java
@@ -234,7 +234,7 @@ loadUrl(getHtmlTestFile(testName), PAGE_LOAD_TIMEOUT_S); assertTrue("VRDisplay found", vrDisplayFound(mWebContents)); enterVrTapAndWait(mWebContents); - assertTrue("VrShellDelegate is in VR", VrShellDelegate.isInVR()); + assertTrue("VrShellDelegate is in VR", VrShellDelegate.isInVr()); endTest(mWebContents); }
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/AppIndexingUtilTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/AppIndexingUtilTest.java new file mode 100644 index 0000000..e7a7b00 --- /dev/null +++ b/chrome/android/junit/src/org/chromium/chrome/browser/AppIndexingUtilTest.java
@@ -0,0 +1,132 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.chrome.browser; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.Spy; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.robolectric.annotation.Config; + +import org.chromium.blink.mojom.document_metadata.CopylessPaste; +import org.chromium.blink.mojom.document_metadata.WebPage; +import org.chromium.chrome.browser.historyreport.AppIndexingReporter; +import org.chromium.chrome.browser.tab.Tab; +import org.chromium.testing.local.LocalRobolectricTestRunner; +import org.chromium.url.mojom.Url; + +/** + * Unit tests for {@link org.chromium.chrome.browser.AppIndexingUtil}. + */ +@RunWith(LocalRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class AppIndexingUtilTest { + @Spy + AppIndexingUtil mUtil = new AppIndexingUtil(); + @Mock + private AppIndexingReporter mReporter; + @Mock + private CopylessPasteTestImpl mCopylessPaste; + @Mock + private Tab mTab; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + doReturn(mReporter).when(mUtil).getAppIndexingReporter(); + doReturn(mCopylessPaste).when(mUtil).getCopylessPasteInterface(any(Tab.class)); + doReturn(true).when(mUtil).isEnabledForDevice(); + doReturn(false).when(mTab).isIncognito(); + + doReturn("http://www.test.com").when(mTab).getUrl(); + doReturn("My neat website").when(mTab).getTitle(); + doReturn(0L).when(mUtil).getElapsedTime(); + doAnswer(new Answer<Void>() { + public Void answer(InvocationOnMock invocation) { + CopylessPaste.GetEntitiesResponse callback = + (CopylessPaste.GetEntitiesResponse) invocation.getArguments()[0]; + WebPage webpage = new WebPage(); + webpage.url = createUrl("http://www.test.com"); + webpage.title = "My neat website"; + callback.call(webpage); + return null; + } + }) + .when(mCopylessPaste) + .getEntities(any(CopylessPaste.GetEntitiesResponse.class)); + } + + @Test + public void testNoCacheHit() { + mUtil.extractCopylessPasteMetadata(mTab); + verify(mCopylessPaste).getEntities(any(CopylessPaste.GetEntitiesResponse.class)); + verify(mReporter).reportWebPage(any(WebPage.class)); + } + + @Test + public void testCacheHit() { + mUtil.extractCopylessPasteMetadata(mTab); + verify(mCopylessPaste).getEntities(any(CopylessPaste.GetEntitiesResponse.class)); + verify(mReporter).reportWebPage(any(WebPage.class)); + verify(mReporter, never()).reportWebPageView(any(String.class), any(String.class)); + + doReturn(1L).when(mUtil).getElapsedTime(); + mUtil.extractCopylessPasteMetadata(mTab); + verify(mReporter).reportWebPageView(eq("http://www.test.com"), eq("My neat website")); + verifyNoMoreInteractions(mCopylessPaste); + verifyNoMoreInteractions(mReporter); + } + + @Test + public void testCacheHit_expired() { + mUtil.extractCopylessPasteMetadata(mTab); + + doReturn(60 * 60 * 1000L + 1).when(mUtil).getElapsedTime(); + mUtil.extractCopylessPasteMetadata(mTab); + verify(mCopylessPaste, times(2)).getEntities(any(CopylessPaste.GetEntitiesResponse.class)); + } + + @Test + public void testCacheHit_noEntity() { + doAnswer(new Answer<Void>() { + public Void answer(InvocationOnMock invocation) { + CopylessPaste.GetEntitiesResponse callback = + (CopylessPaste.GetEntitiesResponse) invocation.getArguments()[0]; + callback.call(null); + return null; + } + }) + .when(mCopylessPaste) + .getEntities(any(CopylessPaste.GetEntitiesResponse.class)); + mUtil.extractCopylessPasteMetadata(mTab); + + doReturn(1L).when(mUtil).getElapsedTime(); + mUtil.extractCopylessPasteMetadata(mTab); + verify(mCopylessPaste, times(1)).getEntities(any(CopylessPaste.GetEntitiesResponse.class)); + verifyNoMoreInteractions(mReporter); + } + + private Url createUrl(String s) { + Url url = new Url(); + url.url = s; + return url; + } + + abstract static class CopylessPasteTestImpl implements CopylessPaste {} +}
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java index 88534f0..cb79d3e 100644 --- a/chrome/android/junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java +++ b/chrome/android/junit/src/org/chromium/chrome/browser/invalidation/InvalidationControllerTest.java
@@ -145,7 +145,7 @@ @Test @Feature({"Sync"}) public void testStop() throws Exception { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.stop(); Intent intent = getOnlyIntent(); validateIntentComponent(intent); @@ -160,7 +160,7 @@ @Test @Feature({"Sync"}) public void testEnsureStartedAndUpdateRegisteredTypes() { - InvalidationController controller = new InvalidationController(mContext, false, false); + InvalidationController controller = new InvalidationController(mContext, false); controller.ensureStartedAndUpdateRegisteredTypes(); Intent intent = getOnlyIntent(); @@ -190,7 +190,7 @@ public void testPauseAndResumeMainActivityWithSyncDisabled() throws Exception { AndroidSyncSettings.disableChromeSync(mContext); - InvalidationController controller = new InvalidationController(mContext, false, false); + InvalidationController controller = new InvalidationController(mContext, false); controller.onApplicationStateChange(ApplicationState.HAS_PAUSED_ACTIVITIES); controller.onApplicationStateChange(ApplicationState.HAS_RUNNING_ACTIVITIES); assertNoNewIntents(); @@ -205,7 +205,7 @@ public void testNullProfileSyncService() throws Exception { ProfileSyncService.overrideForTests(null); - InvalidationController controller = new InvalidationController(mContext, false, false); + InvalidationController controller = new InvalidationController(mContext, false); controller.ensureStartedAndUpdateRegisteredTypes(); assertNoNewIntents(); } @@ -219,7 +219,7 @@ final AtomicBoolean listenerCallbackCalled = new AtomicBoolean(); // Create instance. - new InvalidationController(mContext, true, false) { + new InvalidationController(mContext, true) { @Override public void onApplicationStateChange(int newState) { listenerCallbackCalled.set(true); @@ -241,7 +241,7 @@ @Test @Feature({"Sync"}) public void testCannotToggleSessionInvalidations() { - InvalidationController controller = new InvalidationController(mContext, false, false); + InvalidationController controller = new InvalidationController(mContext, false); controller.ensureStartedAndUpdateRegisteredTypes(); Assert.assertEquals(mAllTypes, getRegisterIntentRegisterTypes(getOnlyIntent())); @@ -265,7 +265,7 @@ @Test @Feature({"Sync"}) public void testRecentTabsPageShown() { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.ensureStartedAndUpdateRegisteredTypes(); Assert.assertEquals(mNonSessionTypes, getRegisterIntentRegisterTypes(getOnlyIntent())); @@ -286,7 +286,7 @@ @Test @Feature({"Sync"}) public void testStartWhileRecentTabsPageShown() { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.onRecentTabsPageOpened(); ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); assertNoNewIntents(); @@ -307,7 +307,7 @@ @Test @Feature({"Sync"}) public void testMultipleRecentTabsPages() { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.ensureStartedAndUpdateRegisteredTypes(); Assert.assertEquals(mNonSessionTypes, getRegisterIntentRegisterTypes(getOnlyIntent())); @@ -334,7 +334,7 @@ @Test @Feature({"Sync"}) public void testOpenCloseRecentTabsPageQuickly() { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.ensureStartedAndUpdateRegisteredTypes(); Assert.assertEquals(mNonSessionTypes, getRegisterIntentRegisterTypes(getOnlyIntent())); @@ -364,7 +364,7 @@ @Test @Feature({"Sync"}) public void testDisableSessionInvalidationsOnStart() { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.ensureStartedAndUpdateRegisteredTypes(); Assert.assertEquals(mNonSessionTypes, getRegisterIntentRegisterTypes(getOnlyIntent())); controller.onRecentTabsPageOpened(); @@ -390,7 +390,7 @@ @Test @Feature({"Sync"}) public void testDisableSessionInvalidationsOnResume() { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.ensureStartedAndUpdateRegisteredTypes(); Assert.assertEquals(mNonSessionTypes, getRegisterIntentRegisterTypes(getOnlyIntent())); controller.onRecentTabsPageOpened(); @@ -415,7 +415,7 @@ @Test @Feature({"Sync"}) public void testPauseAndResumeMainActivity() throws Exception { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.ensureStartedAndUpdateRegisteredTypes(); Assert.assertEquals(mNonSessionTypes, getRegisterIntentRegisterTypes(getOnlyIntent())); controller.onRecentTabsPageOpened(); @@ -439,7 +439,7 @@ @Test @Feature({"Sync"}) public void testPauseAndResumeMainActivityAfterStop() throws Exception { - InvalidationController controller = new InvalidationController(mContext, true, false); + InvalidationController controller = new InvalidationController(mContext, true); controller.ensureStartedAndUpdateRegisteredTypes(); Assert.assertEquals(mNonSessionTypes, getRegisterIntentRegisterTypes(getOnlyIntent()));
diff --git a/chrome/android/webapk/libs/client/BUILD.gn b/chrome/android/webapk/libs/client/BUILD.gn index 4d85d23..bd96ffa 100644 --- a/chrome/android/webapk/libs/client/BUILD.gn +++ b/chrome/android/webapk/libs/client/BUILD.gn
@@ -12,11 +12,13 @@ "src/org/chromium/webapk/lib/client/WebApkNavigationClient.java", "src/org/chromium/webapk/lib/client/WebApkServiceConnectionManager.java", "src/org/chromium/webapk/lib/client/WebApkValidator.java", + "src/org/chromium/webapk/lib/client/WebApkVerifySignature.java", ] deps = [ "//base:base_java", "//chrome/android/webapk/libs/common:common_java", "//chrome/android/webapk/libs/runtime_library:webapk_service_aidl_java", + "//third_party/android_tools:android_support_annotations_java", ] srcjar_deps = [ ":runtime_library_version_java" ] } @@ -36,9 +38,15 @@ java_files = [ "junit/src/org/chromium/webapk/lib/client/WebApkServiceConnectionManagerTest.java", "junit/src/org/chromium/webapk/lib/client/WebApkValidatorTest.java", + "junit/src/org/chromium/webapk/lib/client/WebApkVerifySignatureTest.java", + ] + data = [ + "//chrome/test/data/webapks/", ] deps = [ ":client_java", + "//chrome/android/webapk/libs/common:common_java", "//chrome/android/webapk/libs/runtime_library:webapk_service_aidl_java", + "//testing/android/junit:junit_test_support", ] }
diff --git a/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkValidatorTest.java b/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkValidatorTest.java index dc7ca80..d5eac28 100644 --- a/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkValidatorTest.java +++ b/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkValidatorTest.java
@@ -5,16 +5,21 @@ package org.chromium.webapk.lib.client; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import static org.chromium.webapk.lib.common.WebApkMetaDataKeys.START_URL; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.Signature; +import android.os.Bundle; -import org.chromium.testing.local.LocalRobolectricTestRunner; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -23,13 +28,14 @@ import org.robolectric.annotation.Config; import org.robolectric.res.builder.RobolectricPackageManager; +import org.chromium.testing.local.LocalRobolectricTestRunner; +import org.chromium.testing.local.TestDir; + import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; -/** - * Unit tests for {@link org.chromium.webapk.lib.client.WebApkValidator}. - */ +/** Unit tests for {@link org.chromium.webapk.lib.client.WebApkValidator}. */ @RunWith(LocalRobolectricTestRunner.class) @Config(manifest = Config.NONE) public class WebApkValidatorTest { @@ -37,29 +43,38 @@ private static final String INVALID_WEBAPK_PACKAGE_NAME = "invalid.org.chromium.webapk.foo"; private static final String URL_OF_WEBAPK = "https://www.foo.com"; private static final String URL_WITHOUT_WEBAPK = "https://www.other.com"; + private static final String TEST_DATA_DIR = "webapks/"; - private static final byte[] EXPECTED_SIGNATURE = new byte[] { - 48, -126, 3, -121, 48, -126, 2, 111, -96, 3, 2, 1, 2, 2, 4, 20, -104, -66, -126, 48, 13, - 6, 9, 42, -122, 72, -122, -9, 13, 1, 1, 11, 5, 0, 48, 116, 49, 11, 48, 9, 6, 3, 85, 4, - 6, 19, 2, 67, 65, 49, 16, 48, 14, 6, 3, 85, 4, 8, 19, 7, 79, 110, 116, 97, 114, 105, - 111, 49, 17, 48, 15, 6, 3, 85, 4, 7, 19, 8, 87, 97, 116, 101, 114, 108, 111, 111, 49, - 17, 48, 15, 6, 3, 85, 4, 10, 19, 8, 67, 104, 114, 111, 109, 105, 117, 109, 49, 17, 48}; + private static final byte[] EXPECTED_SIGNATURE = new byte[] {48, -126, 3, -121, 48, -126, 2, + 111, -96, 3, 2, 1, 2, 2, 4, 20, -104, -66, -126, 48, 13, 6, 9, 42, -122, 72, -122, -9, + 13, 1, 1, 11, 5, 0, 48, 116, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 67, 65, 49, 16, 48, + 14, 6, 3, 85, 4, 8, 19, 7, 79, 110, 116, 97, 114, 105, 111, 49, 17, 48, 15, 6, 3, 85, 4, + 7, 19, 8, 87, 97, 116, 101, 114, 108, 111, 111, 49, 17, 48, 15, 6, 3, 85, 4, 10, 19, 8, + 67, 104, 114, 111, 109, 105, 117, 109, 49, 17, 48}; - private static final byte[] SIGNATURE_1 = new byte[] { - 13, 52, 51, 48, 51, 48, 51, 49, 53, 49, 54, 52, 52, 90, 48, 116, 49, 11, 48, 9, 6, 3, - 85, 4, 6, 19, 2, 67, 65, 49, 16, 48, 14, 6, 3, 85, 4, 8, 19, 7, 79, 110, 116, 97, 114}; + private static final byte[] SIGNATURE_1 = new byte[] {13, 52, 51, 48, 51, 48, 51, 49, 53, 49, + 54, 52, 52, 90, 48, 116, 49, 11, 48, 9, 6, 3, 85, 4, 6, 19, 2, 67, 65, 49, 16, 48, 14, + 6, 3, 85, 4, 8, 19, 7, 79, 110, 116, 97, 114}; - private static final byte[] SIGNATURE_2 = new byte[] { - 49, 17, 48, 15, 6, 3, 85, 4, 10, 19, 8, 67, 104, 114, 111, 109, 105, 117, 109, 49, 17, - 48, 15, 6, 3, 85, 4, 11, 19, 8, 67, 104, 114, 111, 109, 105, 117, 109, 49, 26, 48, 24}; + private static final byte[] SIGNATURE_2 = new byte[] {49, 17, 48, 15, 6, 3, 85, 4, 10, 19, 8, + 67, 104, 114, 111, 109, 105, 117, 109, 49, 17, 48, 15, 6, 3, 85, 4, 11, 19, 8, 67, 104, + 114, 111, 109, 105, 117, 109, 49, 26, 48, 24}; + + // This is the public key used for the test files (chrome/test/data/webapks/public.der) + private static final byte[] PUBLIC_KEY = new byte[] {48, 89, 48, 19, 6, 7, 42, -122, 72, -50, + 61, 2, 1, 6, 8, 42, -122, 72, -50, 61, 3, 1, 7, 3, 66, 0, 4, -67, 14, 37, -20, 103, 121, + 124, -60, -21, 83, -114, -120, -87, -38, 26, 78, 82, 55, 44, -23, -2, 104, 115, 82, -55, + -104, 105, -19, -48, 89, -65, 12, -31, 16, -35, 4, -121, -70, -89, 23, 56, 115, 112, 78, + -65, 114, -103, 120, -88, -112, -102, -61, 72, -16, 74, 53, 50, 49, -56, -48, -90, 5, + -116, 78}; private RobolectricPackageManager mPackageManager; @Before public void setUp() { - mPackageManager = (RobolectricPackageManager) RuntimeEnvironment - .application.getPackageManager(); - WebApkValidator.initWithBrowserHostSignature(EXPECTED_SIGNATURE); + mPackageManager = + (RobolectricPackageManager) RuntimeEnvironment.application.getPackageManager(); + WebApkValidator.init(true, EXPECTED_SIGNATURE, PUBLIC_KEY); } /** @@ -76,8 +91,9 @@ mPackageManager.addPackage(newPackageInfoWithBrowserSignature( WEBAPK_PACKAGE_NAME, new Signature(EXPECTED_SIGNATURE))); - assertEquals(WEBAPK_PACKAGE_NAME, WebApkValidator.queryWebApkPackage( - RuntimeEnvironment.application, URL_OF_WEBAPK)); + assertEquals(WEBAPK_PACKAGE_NAME, + WebApkValidator.queryWebApkPackage( + RuntimeEnvironment.application, URL_OF_WEBAPK)); } catch (URISyntaxException e) { Assert.fail("URI is invalid."); } @@ -103,8 +119,8 @@ } /** - * Tests {@link WebApkValidator.queryWebApkPackage()} returns null if no WebAPK handles - * the given URL. + * Tests {@link WebApkValidator.queryWebApkPackage()} returns null if no WebAPK handles the + * given URL. */ @Test public void testQueryWebApkPackageReturnsNullWhenNoWebApkHandlesTheURL() { @@ -139,8 +155,8 @@ } /** - * Tests {@link WebApkValidator.findWebApkPackage} returns null if none of the packages for the - * ResolveInfos start with {@link WebApkConstants.WEBAPK_PACKAGE_PREFIX}. + * Tests {@link WebApkValidator.findWebApkPackage} returns null if null if the package + * name is invalid. */ @Test public void testFindWebApkPackageReturnsNullForInvalidPackageName() { @@ -163,7 +179,7 @@ infos.add(newResolveInfo(WEBAPK_PACKAGE_NAME)); Signature[] signatures = new Signature[] {new Signature(SIGNATURE_1), new Signature(EXPECTED_SIGNATURE), new Signature(SIGNATURE_2)}; - mPackageManager.addPackage(newPackageInfo(WEBAPK_PACKAGE_NAME, signatures)); + mPackageManager.addPackage(newPackageInfo(WEBAPK_PACKAGE_NAME, signatures, null)); assertNull(WebApkValidator.findWebApkPackage(RuntimeEnvironment.application, infos)); } @@ -179,11 +195,58 @@ infos.add(newResolveInfo(WEBAPK_PACKAGE_NAME)); Signature signatures[] = new Signature[] {new Signature(SIGNATURE_1), new Signature(SIGNATURE_2)}; - mPackageManager.addPackage(newPackageInfo(WEBAPK_PACKAGE_NAME, signatures)); + mPackageManager.addPackage(newPackageInfo(WEBAPK_PACKAGE_NAME, signatures, null)); assertNull(WebApkValidator.findWebApkPackage(RuntimeEnvironment.application, infos)); } + /** + * Tests {@link WebApkValidator#isValidWebApk()} for valid comment signed webapks. + */ + @Test + public void testIsValidWebApkCommentSigned() { + String[] filenames = {"example.apk", "java-example.apk"}; + String packageName = "com.webapk.a9c419502bb98fcb7"; + Signature[] signature = new Signature[] {new Signature(SIGNATURE_1)}; + + for (String filename : filenames) { + mPackageManager.removePackage(packageName); + mPackageManager.addPackage( + newPackageInfo(packageName, signature, testFilePath(filename))); + assertTrue(filename + " did not verify", + WebApkValidator.isValidWebApk(RuntimeEnvironment.application, packageName)); + } + } + + /** + * Tests {@link WebApkValidator#isValidWebApk()} for failing comment signed webapks. + * These WebAPKs were modified to fail in specific ways. + */ + @Test + public void testIsValidWebApkCommentSignedFailures() { + String[] filenames = { + "bad-sig.apk", "bad-utf8-fname.apk", "empty.apk", "extra-len-too-large.apk", + "fcomment-too-large.apk", "no-cd.apk", "no-comment.apk", "no-eocd.apk", + "no-lfh.apk", "not-an.apk", "too-many-metainf.apk", "truncated.apk", "zeros.apk", + "zeros-at-end.apk", + }; + String packageName = "com.webapk.a9c419502bb98fcb7"; + Signature[] signature = new Signature[] {new Signature(SIGNATURE_1)}; + + for (String filename : filenames) { + mPackageManager.removePackage(packageName); + mPackageManager.addPackage( + newPackageInfo(packageName, signature, testFilePath(filename))); + assertFalse(filename, + WebApkValidator.isValidWebApk(RuntimeEnvironment.application, packageName)); + } + } + + // Get the full test file path. + private static String testFilePath(String fileName) { + return TestDir.getTestFilePath(TEST_DATA_DIR + fileName); + } + private static ResolveInfo newResolveInfo(String packageName) { ActivityInfo activityInfo = new ActivityInfo(); activityInfo.packageName = packageName; @@ -192,10 +255,15 @@ return resolveInfo; } - private static PackageInfo newPackageInfo(String packageName, Signature[] signatures) { + private static PackageInfo newPackageInfo( + String packageName, Signature[] signatures, String sourceDir) { PackageInfo packageInfo = new PackageInfo(); packageInfo.packageName = packageName; packageInfo.signatures = signatures; + packageInfo.applicationInfo = new ApplicationInfo(); + packageInfo.applicationInfo.metaData = new Bundle(); + packageInfo.applicationInfo.metaData.putString(START_URL, "https://non-empty.com/starturl"); + packageInfo.applicationInfo.sourceDir = sourceDir; return packageInfo; } @@ -203,6 +271,6 @@ // additional ones after the second) are ignored. private static PackageInfo newPackageInfoWithBrowserSignature( String packageName, Signature signature) { - return newPackageInfo(packageName, new Signature[] {new Signature(""), signature}); + return newPackageInfo(packageName, new Signature[] {new Signature(""), signature}, null); } }
diff --git a/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkVerifySignatureTest.java b/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkVerifySignatureTest.java new file mode 100644 index 0000000..4a032a6 --- /dev/null +++ b/chrome/android/webapk/libs/client/junit/src/org/chromium/webapk/lib/client/WebApkVerifySignatureTest.java
@@ -0,0 +1,53 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.webapk.lib.client; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import org.chromium.testing.local.LocalRobolectricTestRunner; + +/** Unit tests for WebApkVerifySignature for Android. */ +@RunWith(LocalRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class WebApkVerifySignatureTest { + /** Elliptical Curves, Digital Signature Algorithm */ + private static final String KEY_FACTORY = "EC"; + + private static final String TEST_DATA_DIR = "/webapks/"; + + @Test + public void testHexToBytes() throws Exception { + byte[] empty = {}; + assertArrayEquals(empty, WebApkVerifySignature.hexToBytes("")); + byte[] test = {(byte) 0xFF, (byte) 0xFE, 0x00, 0x01}; + assertArrayEquals(test, WebApkVerifySignature.hexToBytes("fffe0001")); + assertArrayEquals(test, WebApkVerifySignature.hexToBytes("FFFE0001")); + assertEquals(null, WebApkVerifySignature.hexToBytes("f")); // Odd number of nibbles. + } + + @Test + public void testCommentHash() throws Exception { + byte[] bytes = {(byte) 0xde, (byte) 0xca, (byte) 0xfb, (byte) 0xad}; + assertEquals(null, WebApkVerifySignature.parseCommentSignature("weapk:decafbad")); + assertEquals(null, WebApkVerifySignature.parseCommentSignature("webapk:")); + assertEquals(null, WebApkVerifySignature.parseCommentSignature("webapk:decafbad")); + assertArrayEquals( + bytes, WebApkVerifySignature.parseCommentSignature("webapk:12345:decafbad")); + assertArrayEquals( + bytes, WebApkVerifySignature.parseCommentSignature("XXXwebapk:0000:decafbadXXX")); + assertArrayEquals( + bytes, WebApkVerifySignature.parseCommentSignature("\n\nwebapk:0000:decafbad\n\n")); + assertArrayEquals(bytes, + WebApkVerifySignature.parseCommentSignature("chrome-webapk:000:decafbad\n\n")); + assertArrayEquals(bytes, + WebApkVerifySignature.parseCommentSignature( + "prefixed: chrome-webapk:000:decafbad :suffixed")); + } +}
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java index 36840fc0..5077fd8 100644 --- a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java +++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkValidator.java
@@ -5,6 +5,7 @@ package org.chromium.webapk.lib.client; import static org.chromium.webapk.lib.common.WebApkConstants.WEBAPK_PACKAGE_PREFIX; +import static org.chromium.webapk.lib.common.WebApkMetaDataKeys.START_URL; import android.content.Context; import android.content.Intent; @@ -13,10 +14,19 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.Signature; +import android.os.StrictMode; +import android.text.TextUtils; import android.util.Log; import org.chromium.base.annotations.SuppressFBWarnings; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; +import java.security.KeyFactory; +import java.security.PublicKey; +import java.security.spec.X509EncodedKeySpec; import java.util.Arrays; import java.util.LinkedList; import java.util.List; @@ -26,16 +36,20 @@ * Server. */ public class WebApkValidator { - private static final String TAG = "WebApkValidator"; + private static final String KEY_FACTORY = "EC"; // aka "ECDSA" + + private static boolean sAllWebApkPackageNames; private static byte[] sExpectedSignature; + private static byte[] sCommentSignedPublicKeyBytes; + private static PublicKey sCommentSignedPublicKey; /** - * Queries the PackageManager to determine whether a WebAPK can handle the URL. Ignores - * whether the user has selected a default handler for the URL and whether the default - * handler is the WebAPK. + * Queries the PackageManager to determine whether a WebAPK can handle the URL. Ignores whether + * the user has selected a default handler for the URL and whether the default handler is the + * WebAPK. * - * NOTE(yfriedman): This can fail if multiple WebAPKs can match the supplied url. + * <p>NOTE(yfriedman): This can fail if multiple WebAPKs can match the supplied url. * * @param context The application context. * @param url The url to check. @@ -47,16 +61,16 @@ } /** - * Queries the PackageManager to determine whether a WebAPK can handle the URL. Ignores - * whether the user has selected a default handler for the URL and whether the default - * handler is the WebAPK. + * Queries the PackageManager to determine whether a WebAPK can handle the URL. Ignores whether + * the user has selected a default handler for the URL and whether the default handler is the + * WebAPK. * - * NOTE: This can fail if multiple WebAPKs can match the supplied url. + * <p>NOTE: This can fail if multiple WebAPKs can match the supplied url. * * @param context The application context. * @param url The url to check. * @return Resolve Info of a WebAPK which can handle the URL. Null if the url should not be - * handled by a WebAPK. + * handled by a WebAPK. */ public static ResolveInfo queryResolveInfo(Context context, String url) { return findResolveInfo(context, resolveInfosForUrl(context, url)); @@ -92,7 +106,7 @@ * @param context The context to use to check whether WebAPK is valid. * @param infos The ResolveInfos to search. * @return Package name of the ResolveInfo which corresponds to a WebAPK. Null if none of the - * ResolveInfos corresponds to a WebAPK. + * ResolveInfos corresponds to a WebAPK. */ public static String findWebApkPackage(Context context, List<ResolveInfo> infos) { ResolveInfo resolveInfo = findResolveInfo(context, infos); @@ -125,47 +139,151 @@ * @return true iff the WebAPK is installed and passes security checks */ public static boolean isValidWebApk(Context context, String webappPackageName) { - if (sExpectedSignature == null) { - Log.wtf(TAG, "WebApk validation failure - expected signature not set." - + "missing call to WebApkValidator.initWithBrowserHostSignature"); - } - if (!webappPackageName.startsWith(WEBAPK_PACKAGE_PREFIX)) { + if (sExpectedSignature == null || sCommentSignedPublicKeyBytes == null) { + Log.wtf(TAG, + "WebApk validation failure - expected signature not set." + + "missing call to WebApkValidator.initWithBrowserHostSignature"); return false; } - // check signature PackageInfo packageInfo = null; try { packageInfo = context.getPackageManager().getPackageInfo(webappPackageName, - PackageManager.GET_SIGNATURES); + PackageManager.GET_SIGNATURES | PackageManager.GET_META_DATA); } catch (NameNotFoundException e) { e.printStackTrace(); Log.d(TAG, "WebApk not found"); return false; } + if (isNotWebApkQuick(packageInfo)) { + return false; + } + if (verifyV1WebApk(packageInfo, webappPackageName)) { + return true; + } - final Signature[] arrSignatures = packageInfo.signatures; - if (arrSignatures != null && arrSignatures.length == 2) { - for (Signature signature : arrSignatures) { - if (Arrays.equals(sExpectedSignature, signature.toByteArray())) { - Log.d(TAG, "WebApk valid - signature match!"); - return true; - } + return verifyCommentSignedWebApk(packageInfo, webappPackageName); + } + + /** Determine quickly whether this is definitely not a WebAPK */ + private static boolean isNotWebApkQuick(PackageInfo packageInfo) { + if (packageInfo.applicationInfo == null || packageInfo.applicationInfo.metaData == null) { + Log.e(TAG, "no application info, or metaData retrieved."); + return true; + } + // Having the startURL in AndroidManifest.xml is a strong signal. + String startUrl = packageInfo.applicationInfo.metaData.getString(START_URL); + return TextUtils.isEmpty(startUrl); + } + + private static boolean verifyV1WebApk(PackageInfo packageInfo, String webappPackageName) { + if (packageInfo.signatures == null || packageInfo.signatures.length != 2 + || !webappPackageName.startsWith(WEBAPK_PACKAGE_PREFIX)) { + return false; + } + for (Signature signature : packageInfo.signatures) { + if (Arrays.equals(sExpectedSignature, signature.toByteArray())) { + Log.d(TAG, "WebApk valid - signature match!"); + return true; } } - Log.d(TAG, "WebApk invalid"); return false; } + /** Verify that the comment signed webapk matches the public key. */ + private static boolean verifyCommentSignedWebApk( + PackageInfo packageInfo, String webappPackageName) { + if (!sAllWebApkPackageNames && !webappPackageName.startsWith(WEBAPK_PACKAGE_PREFIX)) { + return false; + } + + PublicKey commentSignedPublicKey; + try { + commentSignedPublicKey = getCommentSignedPublicKey(); + } catch (Exception e) { + Log.e(TAG, "WebApk failed to get Public Key", e); + return false; + } + if (commentSignedPublicKey == null) { + Log.e(TAG, "WebApk validation failure - unable to decode public key"); + return false; + } + if (packageInfo.applicationInfo == null || packageInfo.applicationInfo.sourceDir == null) { + Log.e(TAG, "WebApk validation failure - missing applicationInfo sourcedir"); + return false; + } + + String packageFilename = packageInfo.applicationInfo.sourceDir; + RandomAccessFile file = null; + FileChannel inChannel = null; + StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads(); + + try { + file = new RandomAccessFile(packageFilename, "r"); + inChannel = file.getChannel(); + + MappedByteBuffer buf = + inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); + buf.load(); + + WebApkVerifySignature v = new WebApkVerifySignature(buf); + int result = v.read(); + if (result != WebApkVerifySignature.ERROR_OK) { + Log.e(TAG, String.format("Failure reading %s: %s", packageFilename, result)); + return false; + } + result = v.verifySignature(commentSignedPublicKey); + + // TODO(scottkirkwood): remove this log once well tested. + Log.d(TAG, "File " + packageFilename + ": " + result); + return result == WebApkVerifySignature.ERROR_OK; + } catch (Exception e) { + Log.e(TAG, "WebApk file error for file " + packageFilename, e); + return false; + } finally { + StrictMode.setThreadPolicy(oldPolicy); + if (inChannel != null) { + try { + inChannel.close(); + } catch (IOException e) { + } + } + if (file != null) { + try { + file.close(); + } catch (IOException e) { + } + } + } + } + /** - * Initializes the WebApkValidator with the expected signature that WebAPKs must be signed - * with for the current host. - * @param expectedSignature + * Initializes the WebApkValidator. + * @param allWebApkPackageNames Whether we permit any package names for comment signed WebAPKs. + * @param expectedSignature V1 WebAPK RSA signature. + * @param v2PublicKeyBytes New comment signed public key bytes as x509 encoded public key. */ @SuppressFBWarnings("EI_EXPOSE_STATIC_REP2") - public static void initWithBrowserHostSignature(byte[] expectedSignature) { - if (sExpectedSignature != null) { - return; + public static void init( + boolean allWebApkPackageNames, byte[] expectedSignature, byte[] v2PublicKeyBytes) { + sAllWebApkPackageNames = allWebApkPackageNames; + if (sExpectedSignature == null) { + sExpectedSignature = expectedSignature; } - sExpectedSignature = expectedSignature; + if (sCommentSignedPublicKeyBytes == null) { + sCommentSignedPublicKeyBytes = v2PublicKeyBytes; + } + } + + /** + * Lazy evaluate the creation of the Public Key as the KeyFactories may not yet be initialized. + * @return The decoded PublicKey or null + */ + private static PublicKey getCommentSignedPublicKey() throws Exception { + if (sCommentSignedPublicKey == null) { + sCommentSignedPublicKey = + KeyFactory.getInstance(KEY_FACTORY) + .generatePublic(new X509EncodedKeySpec(sCommentSignedPublicKeyBytes)); + } + return sCommentSignedPublicKey; } }
diff --git a/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVerifySignature.java b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVerifySignature.java new file mode 100644 index 0000000..1c3fb5fa --- /dev/null +++ b/chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkVerifySignature.java
@@ -0,0 +1,404 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package org.chromium.webapk.lib.client; + +import static java.nio.ByteOrder.LITTLE_ENDIAN; + +import android.support.annotation.IntDef; +import android.util.Log; + +import java.nio.ByteBuffer; +import java.security.PublicKey; +import java.security.Signature; +import java.util.ArrayList; +import java.util.Collections; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * WebApkVerifySignature reads in the APK file and verifies the WebApk signature. It reads the + * signature from the zip comment and verifies that it was signed by the public key passed. + */ +public class WebApkVerifySignature { + /** Errors codes. */ + @IntDef({ + ERROR_OK, ERROR_BAD_APK, ERROR_EXTRA_FIELD_TOO_LARGE, ERROR_COMMENT_TOO_LARGE, + ERROR_INCORRECT_SIGNATURE, ERROR_SIGNATURE_NOT_FOUND, ERROR_TOO_MANY_META_INF_FILES, + }) + public @interface Error {} + public static final int ERROR_OK = 0; + public static final int ERROR_BAD_APK = 1; + public static final int ERROR_EXTRA_FIELD_TOO_LARGE = 2; + public static final int ERROR_COMMENT_TOO_LARGE = 3; + public static final int ERROR_INCORRECT_SIGNATURE = 4; + public static final int ERROR_SIGNATURE_NOT_FOUND = 5; + public static final int ERROR_TOO_MANY_META_INF_FILES = 6; + + private static final String TAG = "WebApkVerifySignature"; + + /** End Of Central Directory Signature. */ + private static final long EOCD_SIG = 0x06054b50; + + /** Central Directory Signature. */ + private static final long CD_SIG = 0x02014b50; + + /** Local File Header Signature. */ + private static final long LFH_SIG = 0x04034b50; + + /** Minimum end-of-central-directory size in bytes, including variable length file comment. */ + private static final int MIN_EOCD_SIZE = 22; + + /** Max end-of-central-directory size in bytes permitted. */ + private static final int MAX_EOCD_SIZE = 64 * 1024; + + /** Maximum number of META-INF/ files (allowing for dual signing). */ + private static final int MAX_META_INF_FILES = 5; + + /** The signature algorithm used (must also match with HASH). */ + private static final String SIGNING_ALGORITHM = "SHA256withECDSA"; + + /** + * The pattern we look for in the APK/zip comment for signing key. + * An example is "webapk:0000:<hexvalues>". This pattern can appear anywhere + * in the comment but must be separated from any other parts with a + * separator that doesn't look like a hex character. + */ + private static final Pattern WEBAPK_COMMENT_PATTERN = + Pattern.compile("webapk:\\d+:([a-fA-F0-9]+)"); + + /** Maximum comment length permitted. */ + private static final int MAX_COMMENT_LENGTH = 0; + + /** Maximum extra field length permitted. */ + private static final int MAX_EXTRA_LENGTH = 8; + + /** The memory buffer we are going to read the zip from. */ + private final ByteBuffer mBuffer; + + /** Number of total central directory (zip entry) records. */ + private int mRecordCount; + + /** Byte offset from the start where the central directory is found. */ + private int mCentralDirOffset; + + /** The zip archive comment as a UTF-8 string. */ + private String mComment; + + /** + * Sorted list of 'blocks' of memory we will cryptographically hash. We sort the blocks by + * filename to ensure a repeatable order. + */ + private ArrayList<Block> mBlocks; + + /** Block contains metadata about a zip entry. */ + private static class Block implements Comparable<Block> { + String mFilename; + int mPosition; + int mHeaderSize; + int mCompressedSize; + + Block(String filename, int position, int compressedSize) { + mFilename = filename; + mPosition = position; + mHeaderSize = 0; + mCompressedSize = compressedSize; + } + + /** Added for Comparable, sort lexicographically. */ + @Override + public int compareTo(Block o) { + return mFilename.compareTo(o.mFilename); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Block)) return false; + return mFilename.equals(((Block) o).mFilename); + } + + @Override + public int hashCode() { + return mFilename.hashCode(); + } + } + + /** Constructor simply 'connects' to buffer passed. */ + public WebApkVerifySignature(ByteBuffer buffer) { + mBuffer = buffer; + mBuffer.order(LITTLE_ENDIAN); + } + + /** + * Read in the comment and directory. If there is no parseable comment we won't read the + * directory as there is no point (for speed). On success, all of our private variables will be + * set. + * @return OK on success. + */ + public int read() { + try { + int err = readEOCD(); + if (err != ERROR_OK) { + return err; + } + // Short circuit if no comment found. + if (parseCommentSignature(mComment) == null) { + return ERROR_SIGNATURE_NOT_FOUND; + } + err = readDirectory(); + if (err != ERROR_OK) { + return err; + } + } catch (Exception e) { + return ERROR_BAD_APK; + } + return ERROR_OK; + } + + /** + * verifySignature hashes all the files and then verifies the signature. + * @param pub The public key that it should be verified against. + * @return ERROR_OK if the public key signature verifies. + */ + public int verifySignature(PublicKey pub) { + byte[] sig = parseCommentSignature(mComment); + if (sig == null || sig.length == 0) { + return ERROR_SIGNATURE_NOT_FOUND; + } + try { + Signature signature = Signature.getInstance(SIGNING_ALGORITHM); + signature.initVerify(pub); + int err = calculateHash(signature); + if (err != ERROR_OK) { + return err; + } + return signature.verify(sig) ? ERROR_OK : ERROR_INCORRECT_SIGNATURE; + } catch (Exception e) { + Log.e(TAG, "Exception calculating signature", e); + return ERROR_INCORRECT_SIGNATURE; + } + } + + /** + * calculateHash goes through each file listed in blocks and calculates the SHA-256 + * cryptographic hash. + * @param sig Signature object you can call update on. + */ + public int calculateHash(Signature sig) throws Exception { + Collections.sort(mBlocks); + int metaInfCount = 0; + for (Block block : mBlocks) { + if (block.mFilename.indexOf("META-INF/") == 0) { + metaInfCount++; + if (metaInfCount > MAX_META_INF_FILES) { + // TODO(scottkirkwood): Add whitelist of files. + return ERROR_TOO_MANY_META_INF_FILES; + } + + // Files that begin with META-INF/ are not part of the hash. + // This is because these signatures are added after we comment signed the rest of + // the APK. + continue; + } + + // Hash the filename length and filename to prevent Horton principle violation. + byte[] filename = block.mFilename.getBytes(); + sig.update(intToLittleEndian(filename.length)); + sig.update(filename); + + // Also hash the block length for the same reason. + sig.update(intToLittleEndian(block.mCompressedSize)); + + seek(block.mPosition + block.mHeaderSize); + ByteBuffer slice = mBuffer.slice(); + slice.limit(block.mCompressedSize); + sig.update(slice); + } + return ERROR_OK; + } + + /** + * intToLittleEndian converts an integer to a little endian array of bytes. + * @param value Integer value to convert. + * @return Array of bytes. + */ + private byte[] intToLittleEndian(int value) { + ByteBuffer buffer = ByteBuffer.allocate(4); + buffer.order(LITTLE_ENDIAN); + buffer.putInt(value); + return buffer.array(); + } + + /** + * Extract the bytes of the signature from the comment. We expect + * "webapk:0000:<hexvalues>" comment followed by hex values. Currently we ignore the + * "key id" which is always "0000". + * @return the bytes of the signature. + */ + static byte[] parseCommentSignature(String comment) { + Matcher m = WEBAPK_COMMENT_PATTERN.matcher(comment); + if (!m.find()) { + return null; + } + String s = m.group(1); + return hexToBytes(s); + } + + /** + * Reads the End of Central Directory Record. + * @return ERROR_OK on success. + */ + private int readEOCD() { + int start = findEOCDStart(); + if (start < 0) { + return ERROR_BAD_APK; + } + // Signature(4), Disk Number(2), Start disk number(2), Records on this disk (2) + seek(start + 10); + mRecordCount = read2(); // Number of Central Directory records + seekDelta(4); // Size of central directory + mCentralDirOffset = read4(); // as bytes from start of file. + int commentLength = read2(); + mComment = readString(commentLength); + return ERROR_OK; + } + + /** + * Reads the central directory and populates {@link mBlocks} with data about each entry. + * @return ERROR_OK on success. + */ + @Error + int readDirectory() { + mBlocks = new ArrayList<>(mRecordCount); + seek(mCentralDirOffset); + for (int i = 0; i < mRecordCount; i++) { + int signature = read4(); + if (signature != CD_SIG) { + Log.d(TAG, "Missing Central Directory Signature"); + return ERROR_BAD_APK; + } + // CreatorVersion(2), ReaderVersion(2), Flags(2), CompressionMethod(2) + // ModifiedTime(2), ModifiedDate(2), CRC32(4) = 16 bytes + seekDelta(16); + int compressedSize = read4(); + seekDelta(4); // uncompressed size + int fileNameLength = read2(); + int extraLen = read2(); + int fileCommentLength = read2(); + seekDelta(8); // DiskNumberStart(2), Internal Attrs(2), External Attrs(4) + int offset = read4(); + String filename = readString(fileNameLength); + seekDelta(extraLen + fileCommentLength); + if (extraLen > MAX_EXTRA_LENGTH) { + return ERROR_EXTRA_FIELD_TOO_LARGE; + } + if (fileCommentLength > MAX_COMMENT_LENGTH) { + return ERROR_COMMENT_TOO_LARGE; + } + mBlocks.add(new Block(filename, offset, compressedSize)); + } + + // Read the 'local file header' block to the size of the header in bytes. + for (Block block : mBlocks) { + seek(block.mPosition); + int signature = read4(); + if (signature != LFH_SIG) { + Log.d(TAG, "LFH Signature missing"); + return ERROR_BAD_APK; + } + // ReaderVersion(2), Flags(2), CompressionMethod(2), + // ModifiedTime (2), ModifiedDate(2), CRC32(4), CompressedSize(4), + // UncompressedSize(4) = 22 bytes + seekDelta(22); + int fileNameLength = read2(); + int extraFieldLength = read2(); + if (extraFieldLength > MAX_EXTRA_LENGTH) { + return ERROR_EXTRA_FIELD_TOO_LARGE; + } + + block.mHeaderSize = + (mBuffer.position() - block.mPosition) + fileNameLength + extraFieldLength; + } + return ERROR_OK; + } + + /** + * We search buffer for EOCD_SIG and return the location where we found it. If the file has no + * comment it should seek only once. + * TODO(scottkirkwood): Use a Boyer-Moore search algorithm. + * @return Offset from start of buffer or -1 if not found. + */ + private int findEOCDStart() { + int offset = mBuffer.limit() - MIN_EOCD_SIZE; + int minSearchOffset = Math.max(0, offset - MAX_EOCD_SIZE); + for (; offset >= minSearchOffset; offset--) { + seek(offset); + if (read4() == EOCD_SIG) { + // found! + return offset; + } + } + return -1; + } + + /** + * Seek to this position. + * @param offset offset from start of file. + */ + private void seek(int offset) { + mBuffer.position(offset); + } + + /** + * Skip forward this number of bytes. + * @param delta number of bytes to seek forward. + */ + private void seekDelta(int delta) { + mBuffer.position(mBuffer.position() + delta); + } + + /** + * Reads two bytes in little endian format. + * @return short value read (as an int). + */ + private int read2() { + return mBuffer.getShort(); + } + + /** + * Reads four bytes in little endian format. + * @return value read. + */ + private int read4() { + return mBuffer.getInt(); + } + + /** Read {@link length} many bytes into a string. */ + private String readString(int length) { + if (length <= 0) { + return ""; + } + byte[] bytes = new byte[length]; + mBuffer.get(bytes); + return new String(bytes); + } + + /** + * Convert a hex string into bytes. We store hex in the signature as zip + * tools often don't like binary strings. + */ + static byte[] hexToBytes(String s) { + int len = s.length(); + if (len % 2 != 0) { + // Odd number of nibbles. + return null; + } + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } +}
diff --git a/chrome/app/chromeos_strings.grdp b/chrome/app/chromeos_strings.grdp index c26d2e2..88298674 100644 --- a/chrome/app/chromeos_strings.grdp +++ b/chrome/app/chromeos_strings.grdp
@@ -5627,6 +5627,71 @@ This device was locked by the owner. </message> + <!-- Encryption migration dialog --> + <message name="IDS_ENCRYPTION_MIGRATION_READY_TITLE" desc="Title shown in encryption migration screen, which asks the user to install a critical update."> + Install critical update + </message> + <message name="IDS_ENCRYPTION_MIGRATION_READY_DESCRIPTION" desc="Description shown in encryption migration screen, which asks the user to install a critical update."> + To download and use Android apps, first you need to install an update. While your device is updating, you can’t use it. Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> will restart after installation completes. + </message> + <message name="IDS_ENCRYPTION_MIGRATION_MIGRATING_TITLE" desc="Title shown in encryption migration screen when the migration is ongoing."> + Installing critical update + </message> + <message name="IDS_ENCRYPTION_MIGRATION_MIGRATING_DESCRIPTION" desc="Description shown in encryption migration screen when the migration is ongoing."> + Don’t turn off or close your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> until the update finishes. Your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> will restart after installation completes. + </message> + <message name="IDS_ENCRYPTION_MIGRATION_PROGRESS_LABEL" desc="Label to show the progress of the migration."> + <ph name="PROGRESS_PERCENT">$1<ex>90</ex></ph>% done + </message> + <message name="IDS_ENCRYPTION_MIGRATION_BATTERY_WARNING_LABEL" desc="Label to tell the user that the battery level is too low to start the migration."> + Battery too low for update (<ph name="PROGRESS_PERCENT">$1<ex>10</ex></ph>%) + </message> + <message name="IDS_ENCRYPTION_MIGRATION_ASK_CHARGE_MESSAGE" desc="Label to ask the user to charge the device."> + Please plug your <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> into a power source. + </message> + <message name="IDS_ENCRYPTION_MIGRATION_NECESSARY_BATTERY_LEVEL_MESSAGE" desc="Explanation about how to start migration when the battery level is low."> + Update will begin when battery reaches <ph name="BATTERY_LEVEL">$1<ex>30</ex></ph>%. + </message> + <message name="IDS_ENCRYPTION_MIGRATION_CHARGING_LABEL" desc="Label which is shown when the device is charging."> + Charging. + </message> + <message name="IDS_ENCRYPTION_MIGRATION_FAILED_TITLE" desc="Title shown in encryption migration screen when the migration fails."> + Something went wrong + </message> + <message name="IDS_ENCRYPTION_MIGRATION_FAILED_SUBTITLE" desc="In encryption migration screen, explanation about the failure of migration."> + Sorry, some files were damaged and the update wasn’t successful. Your synced files are safe. + </message> + <message name="IDS_ENCRYPTION_MIGRATION_FAILED_MESSAGE" desc="In encryption migration screen, label to ask user to create the account again."> + Unfortunately, you'll need to add your account to this <ph name="DEVICE_TYPE">$1<ex>Chromebook</ex></ph> again. + </message> + <message name="IDS_ENCRYPTION_MIGRATION_NOSPACE_WARNING_LABEL" desc="Label to tell the user that migration can not start due to low storage space."> + Not enough storage for update + </message> + <message name="IDS_ENCRYPTION_MIGRATION_ASK_FREE_SPACE_MESSAGE" desc="In encryption migration screen, message to ask the user to free some space on the device."> + Please free up some space on your device. + </message> + <message name="IDS_ENCRYPTION_MIGRATION_AVAILABLE_SPACE_LABEL" desc="In encryption migration screen, label to tell the user how much space is available now."> + Available: <ph name="AVAILABLE_SPACE">$1<ex>5 MB</ex></ph> + </message> + <message name="IDS_ENCRYPTION_MIGRATION_NECESSARY_SPACE_LABEL" desc="In encryption migration screen, label to tell the user how much space is necessary to start migration."> + Needed to update: <ph name="NECESSARY_SPACE">$1<ex>10 MB</ex></ph> + </message> + <message name="IDS_ENCRYPTION_MIGRATION_BUTTON_UPDATE" desc="In encryption migration screen, button label to start migration (i.e. update the encryption of user data)."> + Update + </message> + <message name="IDS_ENCRYPTION_MIGRATION_BUTTON_SKIP" desc="In encryption migration screen, button label to skip the migration."> + Skip + </message> + <message name="IDS_ENCRYPTION_MIGRATION_BUTTON_RESTART" desc="In encryption migration screen, button label to restart the device."> + Restart + </message> + <message name="IDS_ENCRYPTION_MIGRATION_BUTTON_CONTINUE" desc="In encryption migration screen, button label to continue to sign in to the user's session."> + Continue + </message> + <message name="IDS_ENCRYPTION_MIGRATION_BUTTON_SIGNIN" desc="In encryption migration screen, button label to sign in to the user's session."> + Sign in + </message> + <!-- Idle warning dialog --> <message name="IDS_IDLE_WARNING_TITLE" desc="Title of the warning dialog shown when the user becomes idle and is about to get logged out."> Are you still there? @@ -6591,9 +6656,6 @@ </message> <!-- Print Job Notification --> - <message name="IDS_PRINT_JOB_WAITING_NOTIFICATION_MESSAGE" desc="Message of the waiting-for-printing notification."> - Waiting to print <ph name="PAGE_NUMBER">$1<ex>5</ex></ph> pages to <ph name="PRINTER_NAME">$2<ex>printer</ex></ph> - </message> <message name="IDS_PRINT_JOB_PRINTING_NOTIFICATION_MESSAGE" desc="Message of the printing-in-progress notification."> Now printing <ph name="PAGE_NUMBER">$1<ex>5</ex></ph> pages to <ph name="PRINTER_NAME">$2<ex>printer</ex></ph> </message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index cbccc4b..6ccbdb1 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc
@@ -2484,6 +2484,11 @@ autofill::kAutofillCreditCardLastUsedDateDisplay, kAutofillCreditCardLastUsedDateFeatureVariations, "AutofillCreditCardLastUsedDate")}, + {"enable-autofill-credit-card-upload-cvc-prompt", + flag_descriptions::kEnableAutofillCreditCardUploadCvcPrompt, + flag_descriptions::kEnableAutofillCreditCardUploadCvcPromptDescription, + kOsDesktop, + FEATURE_VALUE_TYPE(autofill::kAutofillUpstreamRequestCvcIfMissing)}, #if defined(OS_WIN) {"windows10-custom-titlebar", flag_descriptions::kWindows10CustomTitlebarName,
diff --git a/chrome/browser/android/vr_shell/BUILD.gn b/chrome/browser/android/vr_shell/BUILD.gn index d47cd44..e4369569 100644 --- a/chrome/browser/android/vr_shell/BUILD.gn +++ b/chrome/browser/android/vr_shell/BUILD.gn
@@ -57,8 +57,6 @@ "vr_gl_util.h", "vr_input_manager.cc", "vr_input_manager.h", - "vr_omnibox.cc", - "vr_omnibox.h", "vr_shell.cc", "vr_shell.h", "vr_shell_delegate.cc", @@ -73,10 +71,6 @@ "vr_web_contents_observer.h", ] - if (enable_vr_shell_ui_dev) { - defines += [ "ENABLE_VR_SHELL_UI_DEV" ] - } - deps = [ ":vr_shell_jni_headers", "//base",
diff --git a/chrome/browser/android/vr_shell/animation.h b/chrome/browser/android/vr_shell/animation.h index 084f4582..db062bbc 100644 --- a/chrome/browser/android/vr_shell/animation.h +++ b/chrome/browser/android/vr_shell/animation.h
@@ -23,8 +23,7 @@ class Animation { public: enum Property { - COPYRECT = 0, - SIZE, + SIZE = 0, TRANSLATION, SCALE, ROTATION,
diff --git a/chrome/browser/android/vr_shell/ui_element.cc b/chrome/browser/android/vr_shell/ui_element.cc index f38fb7a..8b8c25a3 100644 --- a/chrome/browser/android/vr_shell/ui_element.cc +++ b/chrome/browser/android/vr_shell/ui_element.cc
@@ -114,12 +114,6 @@ // If |from| is not specified, start at the current values. if (animation.from.size() == 0) { switch (animation.property) { - case Animation::COPYRECT: - animation.from.push_back(copy_rect.x()); - animation.from.push_back(copy_rect.y()); - animation.from.push_back(copy_rect.width()); - animation.from.push_back(copy_rect.height()); - break; case Animation::SIZE: animation.from.push_back(size.x()); animation.from.push_back(size.y()); @@ -161,10 +155,6 @@ animation.from[i] + (value * (animation.to[i] - animation.from[i])); } switch (animation.property) { - case Animation::COPYRECT: - CHECK_EQ(animation.from.size(), 4u); - copy_rect.SetRect(values[0], values[1], values[2], values[3]); - break; case Animation::SIZE: CHECK_EQ(animation.from.size(), 2u); size.set_x(values[0]);
diff --git a/chrome/browser/android/vr_shell/ui_element.h b/chrome/browser/android/vr_shell/ui_element.h index 796c1f9..3f40974 100644 --- a/chrome/browser/android/vr_shell/ui_element.h +++ b/chrome/browser/android/vr_shell/ui_element.h
@@ -34,9 +34,7 @@ enum Fill { NONE = 0, - // The element is filled with part of the HTML UI as specified by the copy - // rect. - SPRITE = 1, + SKIA = 1, // The element is filled with a radial gradient as specified by the edge and // center color. OPAQUE_GRADIENT = 2, @@ -118,9 +116,6 @@ // rather than the world. bool lock_to_fov = false; - // Specifies the region (in pixels) of a texture to render. - gfx::Rect copy_rect = {0, 0, 0, 0}; - // The size of the object. This does not affect children. gfx::Vector3dF size = {1.0f, 1.0f, 1.0f};
diff --git a/chrome/browser/android/vr_shell/ui_element_unittest.cc b/chrome/browser/android/vr_shell/ui_element_unittest.cc index db5cfe5b9..f9eda14 100644 --- a/chrome/browser/android/vr_shell/ui_element_unittest.cc +++ b/chrome/browser/android/vr_shell/ui_element_unittest.cc
@@ -43,20 +43,6 @@ } // namespace -TEST(UiElements, AnimateCopyRect) { - UiElement rect; - rect.copy_rect = {10, 100, 1000, 10000}; - std::unique_ptr<Animation> animation(new Animation( - 0, Animation::Property::COPYRECT, - std::unique_ptr<easing::Easing>(new easing::Linear()), {}, - {20, 200, 2000, 20000}, usToTicks(50000), usToDelta(10000))); - rect.animations.emplace_back(std::move(animation)); - rect.Animate(usToTicks(50000)); - EXPECT_RECTF_EQ(rect.copy_rect, gfx::RectF(10, 100, 1000, 10000)); - rect.Animate(usToTicks(60000)); - EXPECT_RECTF_EQ(rect.copy_rect, gfx::RectF(20, 200, 2000, 20000)); -} - TEST(UiElements, AnimateSize) { UiElement rect; rect.size = {10, 100, 1};
diff --git a/chrome/browser/android/vr_shell/ui_interface.cc b/chrome/browser/android/vr_shell/ui_interface.cc index 3eee6f2..fed51cac 100644 --- a/chrome/browser/android/vr_shell/ui_interface.cc +++ b/chrome/browser/android/vr_shell/ui_interface.cc
@@ -4,146 +4,47 @@ #include "chrome/browser/android/vr_shell/ui_interface.h" -#include <memory> -#include <utility> - -#include "base/memory/ptr_util.h" -#include "chrome/browser/android/vr_shell/vr_omnibox.h" -#include "chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h" #include "url/gurl.h" namespace vr_shell { -UiInterface::UiInterface(Mode initial_mode) - : omnibox_(base::MakeUnique<VrOmnibox>(this)) { - SetMode(initial_mode); -} - -UiInterface::~UiInterface() {} +UiInterface::UiInterface(Mode initial_mode) : mode_(initial_mode) {} void UiInterface::SetMode(Mode mode) { mode_ = mode; - FlushModeState(); } void UiInterface::SetFullscreen(bool enabled) { fullscreen_ = enabled; - FlushModeState(); } -void UiInterface::HandleOmniboxInput(const base::DictionaryValue& input) { - omnibox_->HandleInput(input); -} +void UiInterface::SetSecurityLevel(int level) {} -void UiInterface::SetOmniboxSuggestions( - std::unique_ptr<base::Value> suggestions) { - updates_.Set("suggestions", std::move(suggestions)); - FlushUpdates(); -} +void UiInterface::SetWebVRSecureOrigin(bool secure) {} -void UiInterface::FlushModeState() { - updates_.SetInteger("mode", static_cast<int>(mode_)); - updates_.SetBoolean("fullscreen", fullscreen_); - FlushUpdates(); -} +void UiInterface::SetLoading(bool loading) {} -void UiInterface::SetSecurityLevel(int level) { - updates_.SetInteger("securityLevel", level); - FlushUpdates(); -} +void UiInterface::SetLoadProgress(double progress) {} -void UiInterface::SetWebVRSecureOrigin(bool secure) { - updates_.SetBoolean("webVRSecureOrigin", secure); - FlushUpdates(); -} - -void UiInterface::SetLoading(bool loading) { - updates_.SetBoolean("loading", loading); - FlushUpdates(); -} - -void UiInterface::SetLoadProgress(double progress) { - updates_.SetDouble("loadProgress", progress); - FlushUpdates(); -} - -void UiInterface::InitTabList() { - tab_list_ = base::MakeUnique<base::ListValue>(); -} +void UiInterface::InitTabList() {} void UiInterface::AppendToTabList(bool incognito, int id, - const base::string16& title) { - auto dict = base::MakeUnique<base::DictionaryValue>(); - dict->SetBoolean("incognito", incognito); - dict->SetInteger("id", id); - dict->SetString("title", title); - tab_list_->Append(std::move(dict)); -} + const base::string16& title) {} -void UiInterface::FlushTabList() { - updates_.Set("setTabs", std::move(tab_list_)); - FlushUpdates(); -} +void UiInterface::UpdateTab(bool incognito, int id, const std::string& title) {} -void UiInterface::UpdateTab(bool incognito, int id, const std::string& title) { - auto details = base::MakeUnique<base::DictionaryValue>(); - details->SetBoolean("incognito", incognito); - details->SetInteger("id", id); - details->SetString("title", title); - updates_.Set("updateTab", std::move(details)); -} +void UiInterface::FlushTabList() {} -void UiInterface::RemoveTab(bool incognito, int id) { - auto details = base::MakeUnique<base::DictionaryValue>(); - details->SetBoolean("incognito", incognito); - details->SetInteger("id", id); - updates_.Set("removeTab", std::move(details)); -} +void UiInterface::RemoveTab(bool incognito, int id) {} -void UiInterface::SetURL(const GURL& url) { - auto details = base::MakeUnique<base::DictionaryValue>(); - details->SetString("host", url.host()); - details->SetString("path", url.path()); +void UiInterface::SetURL(const GURL& url) {} - updates_.Set("url", std::move(details)); - FlushUpdates(); -} +void UiInterface::HandleAppButtonGesturePerformed(Direction direction) {} -void UiInterface::HandleAppButtonGesturePerformed(Direction direction) { - updates_.SetInteger("appButtonGesturePerformed", direction); - FlushUpdates(); -} - -void UiInterface::HandleAppButtonClicked() { - updates_.SetBoolean("appButtonClicked", true); - FlushUpdates(); -} +void UiInterface::HandleAppButtonClicked() {} void UiInterface::SetHistoryButtonsEnabled(bool can_go_back, - bool can_go_forward) { - updates_.SetBoolean("canGoBack", can_go_back); - updates_.SetBoolean("canGoForward", can_go_forward); - FlushUpdates(); -} - -void UiInterface::OnDomContentsLoaded() { - loaded_ = true; -#if defined(ENABLE_VR_SHELL_UI_DEV) - updates_.SetBoolean("enableReloadUi", true); -#endif - FlushUpdates(); -} - -void UiInterface::SetUiCommandHandler(UiCommandHandler* handler) { - handler_ = handler; -} - -void UiInterface::FlushUpdates() { - if (loaded_ && handler_) { - handler_->SendCommandToUi(updates_); - updates_.Clear(); - } -} + bool can_go_forward) {} } // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_interface.h b/chrome/browser/android/vr_shell/ui_interface.h index 3039009a..10e2cc81 100644 --- a/chrome/browser/android/vr_shell/ui_interface.h +++ b/chrome/browser/android/vr_shell/ui_interface.h
@@ -15,13 +15,6 @@ namespace vr_shell { -class VrOmnibox; - -class UiCommandHandler { - public: - virtual void SendCommandToUi(const base::Value& value) = 0; -}; - // This class manages the communication of browser state from VR shell to the // HTML UI. State information is asynchronous and unidirectional. class UiInterface { @@ -40,7 +33,7 @@ }; explicit UiInterface(Mode initial_mode); - virtual ~UiInterface(); + virtual ~UiInterface() = default; // Set HTML UI state or pass events. void SetMode(Mode mode); @@ -55,29 +48,13 @@ void UpdateTab(bool incognito, int id, const std::string& title); void RemoveTab(bool incognito, int id); void SetURL(const GURL& url); - void SetOmniboxSuggestions(std::unique_ptr<base::Value> suggestions); void HandleAppButtonGesturePerformed(Direction direction); void HandleAppButtonClicked(); void SetHistoryButtonsEnabled(bool can_go_back, bool can_go_forward); - // Handlers for HTML UI commands and notifications. - void OnDomContentsLoaded(); - void HandleOmniboxInput(const base::DictionaryValue& input); - - void SetUiCommandHandler(UiCommandHandler* handler); - private: - void FlushUpdates(); - void FlushModeState(); - Mode mode_; bool fullscreen_ = false; - UiCommandHandler* handler_; - bool loaded_ = false; - base::DictionaryValue updates_; - std::unique_ptr<base::ListValue> tab_list_; - - std::unique_ptr<VrOmnibox> omnibox_; DISALLOW_COPY_AND_ASSIGN(UiInterface); };
diff --git a/chrome/browser/android/vr_shell/ui_scene.cc b/chrome/browser/android/vr_shell/ui_scene.cc index d953856..9982c98 100644 --- a/chrome/browser/android/vr_shell/ui_scene.cc +++ b/chrome/browser/android/vr_shell/ui_scene.cc
@@ -19,129 +19,6 @@ namespace { -// Parse an integer to an int or enum value. -template <typename T> -bool ParseInt(const base::DictionaryValue& dict, - const std::string& key, - T* output) { - int value; - if (!dict.GetInteger(key, &value)) { - return false; - } - *output = static_cast<T>(value); - return true; -} - -// Parse a floating point number to a float or double. -template <typename T> -bool ParseFloat(const base::DictionaryValue& dict, - const std::string& key, - T* output) { - double value; - if (!dict.GetDouble(key, &value)) { - return false; - } - *output = value; - return true; -} - -bool ParseColorf(const base::DictionaryValue& dict, - const std::string& key, - vr::Colorf* output) { - const base::DictionaryValue* item_dict; - if (dict.GetDictionary(key, &item_dict)) { - double value; - CHECK(item_dict->GetDouble("r", &value)); - output->r = value; - CHECK(item_dict->GetDouble("g", &value)); - output->g = value; - CHECK(item_dict->GetDouble("b", &value)); - output->b = value; - CHECK(item_dict->GetDouble("a", &value)); - output->a = value; - return true; - } else { - return false; - } -} - -void ParseFloats(const base::DictionaryValue& dict, - const std::vector<std::string>& keys, - std::vector<float>* vec) { - for (const auto& key : keys) { - double value; - CHECK(dict.GetDouble(key, &value)) << "parsing tag " << key; - vec->push_back(value); - } -} - -bool ParseEndpointToFloats(Animation::Property property, - const base::DictionaryValue& dict, - std::vector<float>* vec) { - switch (property) { - case Animation::Property::COPYRECT: - ParseFloats(dict, {"x", "y", "width", "height"}, vec); - return true; - case Animation::Property::SIZE: - ParseFloats(dict, {"x", "y"}, vec); - return true; - case Animation::Property::SCALE: - ParseFloats(dict, {"x", "y", "z"}, vec); - return true; - case Animation::Property::ROTATION: - ParseFloats(dict, {"x", "y", "z", "a"}, vec); - return true; - case Animation::Property::TRANSLATION: - ParseFloats(dict, {"x", "y", "z"}, vec); - return true; - case Animation::Property::OPACITY: - ParseFloats(dict, {"x"}, vec); - return true; - } - return false; -} - -std::unique_ptr<easing::Easing> ParseEasing(const base::DictionaryValue& dict) { - easing::EasingType easingType; - CHECK(ParseInt(dict, "type", &easingType)); - std::unique_ptr<easing::Easing> result; - - switch (easingType) { - case easing::EasingType::LINEAR: { - result = base::MakeUnique<easing::Linear>(); - break; - } - case easing::EasingType::CUBICBEZIER: { - double p1x, p1y, p2x, p2y; - CHECK(dict.GetDouble("p1x", &p1x)); - CHECK(dict.GetDouble("p1y", &p1y)); - CHECK(dict.GetDouble("p2x", &p2x)); - CHECK(dict.GetDouble("p2y", &p2y)); - result = base::MakeUnique<easing::CubicBezier>(p1x, p1y, p2x, p2y); - break; - } - case easing::EasingType::EASEIN: { - double pow; - CHECK(dict.GetDouble("pow", &pow)); - result = base::MakeUnique<easing::EaseIn>(pow); - break; - } - case easing::EasingType::EASEOUT: { - double pow; - CHECK(dict.GetDouble("pow", &pow)); - result = base::MakeUnique<easing::EaseOut>(pow); - break; - } - case easing::EasingType::EASEINOUT: { - double pow; - CHECK(dict.GetDouble("pow", &pow)); - result = base::MakeUnique<easing::EaseInOut>(pow); - break; - } - } - return result; -} - void ApplyAnchoring(const UiElement& parent, XAnchoring x_anchoring, YAnchoring y_anchoring, @@ -188,26 +65,6 @@ ui_elements_.push_back(std::move(element)); } -void UiScene::AddUiElementFromDict(const base::DictionaryValue& dict) { - int id; - CHECK(ParseInt(dict, "id", &id)); - CHECK_EQ(GetUiElementById(id), nullptr); - - auto element = base::MakeUnique<UiElement>(); - element->id = id; - - ApplyDictToElement(dict, element.get()); - ui_elements_.push_back(std::move(element)); -} - -void UiScene::UpdateUiElementFromDict(const base::DictionaryValue& dict) { - int id; - CHECK(ParseInt(dict, "id", &id)); - UiElement* element = GetUiElementById(id); - CHECK_NE(element, nullptr); - ApplyDictToElement(dict, element); -} - void UiScene::RemoveUiElement(int element_id) { for (auto it = ui_elements_.begin(); it != ui_elements_.end(); ++it) { if ((*it)->id == element_id) { @@ -230,50 +87,6 @@ element->animations.emplace_back(std::move(animation)); } -void UiScene::AddAnimationFromDict(const base::DictionaryValue& dict, - const base::TimeTicks& current_time) { - int animation_id; - int element_id; - Animation::Property property; - double start_time_ms; - double duration_ms; - - const base::DictionaryValue* easing_dict = nullptr; - const base::DictionaryValue* from_dict = nullptr; - const base::DictionaryValue* to_dict = nullptr; - std::vector<float> from; - std::vector<float> to; - - CHECK(ParseInt(dict, "id", &animation_id)); - CHECK(ParseInt(dict, "meshId", &element_id)); - CHECK(ParseInt(dict, "property", &property)); - CHECK(ParseFloat(dict, "startInMillis", &start_time_ms)); - CHECK(ParseFloat(dict, "durationMillis", &duration_ms)); - - CHECK(dict.GetDictionary("easing", &easing_dict)); - auto easing = ParseEasing(*easing_dict); - - CHECK(dict.GetDictionary("to", &to_dict)); - ParseEndpointToFloats(property, *to_dict, &to); - - // The start location is optional. If not specified, the animation will - // start from the element's current location. - dict.GetDictionary("from", &from_dict); - if (from_dict != nullptr) { - ParseEndpointToFloats(property, *from_dict, &from); - } - - base::TimeDelta delay = base::TimeDelta::FromMilliseconds(start_time_ms); - base::TimeTicks start = current_time + delay; - base::TimeDelta duration = base::TimeDelta::FromMilliseconds(duration_ms); - - UiElement* element = GetUiElementById(element_id); - CHECK_NE(element, nullptr); - element->animations.emplace_back(base::MakeUnique<Animation>( - animation_id, static_cast<Animation::Property>(property), - std::move(easing), from, to, start, duration)); -} - void UiScene::RemoveAnimation(int element_id, int animation_id) { UiElement* element = GetUiElementById(element_id); CHECK_NE(element, nullptr); @@ -287,49 +100,6 @@ } } -void UiScene::HandleCommands(std::unique_ptr<base::ListValue> commands, - const base::TimeTicks& time) { - for (auto& item : *commands) { - base::DictionaryValue* dict; - CHECK(item.GetAsDictionary(&dict)); - - Command type; - base::DictionaryValue* data; - CHECK(ParseInt(*dict, "type", &type)); - CHECK(dict->GetDictionary("data", &data)); - - switch (type) { - case Command::ADD_ELEMENT: - AddUiElementFromDict(*data); - break; - case Command::UPDATE_ELEMENT: - UpdateUiElementFromDict(*data); - break; - case Command::REMOVE_ELEMENT: { - int element_id; - CHECK(ParseInt(*data, "id", &element_id)); - RemoveUiElement(element_id); - break; - } - case Command::ADD_ANIMATION: - AddAnimationFromDict(*data, time); - break; - case Command::REMOVE_ANIMATION: { - int element_id, animation_id; - CHECK(ParseInt(*data, "id", &animation_id)); - CHECK(ParseInt(*data, "meshId", &element_id)); - RemoveAnimation(element_id, animation_id); - break; - } - case Command::CONFIGURE_SCENE: - ParseColorf(*data, "backgroundColor", &background_color_); - ParseFloat(*data, "backgroundDistance", &background_distance_); - data->GetBoolean("drawWebVr", &webvr_rendering_enabled_); - break; - } - } -} - void UiScene::UpdateTransforms(const base::TimeTicks& time) { for (auto& element : ui_elements_) { // Process all animations before calculating object transforms. @@ -433,92 +203,4 @@ element->dirty = false; } -void UiScene::ApplyDictToElement(const base::DictionaryValue& dict, - UiElement* element) { - int parent_id; - - if (ParseInt(dict, "parentId", &parent_id)) { - CHECK_GE(parent_id, 0); - CHECK_NE(GetUiElementById(parent_id), nullptr); - element->parent_id = parent_id; - } - - dict.GetString("name", &element->name); - dict.GetBoolean("visible", &element->visible); - dict.GetBoolean("hitTestable", &element->hit_testable); - dict.GetBoolean("lockToFov", &element->lock_to_fov); - ParseInt(dict, "drawPhase", &element->draw_phase); - ParseFloat(dict, "opacity", &element->opacity); - - DCHECK(!(element->lock_to_fov && element->parent_id != -1)); - float val; - if (ParseFloat(dict, "sizeX", &val)) - element->size.set_x(val); - if (ParseFloat(dict, "sizeY", &val)) - element->size.set_y(val); - if (ParseFloat(dict, "scaleX", &val)) - element->scale.set_x(val); - if (ParseFloat(dict, "scaleY", &val)) - element->scale.set_y(val); - if (ParseFloat(dict, "scaleZ", &val)) - element->scale.set_z(val); - if (ParseFloat(dict, "translationX", &val)) - element->translation.set_x(val); - if (ParseFloat(dict, "translationY", &val)) - element->translation.set_y(val); - if (ParseFloat(dict, "translationZ", &val)) - element->translation.set_z(val); - ParseFloat(dict, "rotationX", &element->rotation.x); - ParseFloat(dict, "rotationY", &element->rotation.y); - ParseFloat(dict, "rotationZ", &element->rotation.z); - ParseFloat(dict, "rotationAngle", &element->rotation.angle); - - if (ParseInt(dict, "xAnchoring", &element->x_anchoring)) { - CHECK_GE(element->parent_id, 0); - } - if (ParseInt(dict, "yAnchoring", &element->y_anchoring)) { - CHECK_GE(element->parent_id, 0); - } - - // Parse the element fill. - if (ParseInt(dict, "fillType", &element->fill)) { - // If the previous content element has a new filling now make sure this is - // tracked correctly. - if (content_element_ == element && element->fill != Fill::CONTENT) { - content_element_ = nullptr; - } - - int val; - switch (element->fill) { - case Fill::SPRITE: - CHECK(ParseInt(dict, "copyRectX", &val)); - element->copy_rect.set_x(val); - CHECK(ParseInt(dict, "copyRectY", &val)); - element->copy_rect.set_y(val); - CHECK(ParseInt(dict, "copyRectWidth", &val)); - element->copy_rect.set_width(val); - CHECK(ParseInt(dict, "copyRectHeight", &val)); - element->copy_rect.set_height(val); - break; - case Fill::OPAQUE_GRADIENT: - CHECK(ParseColorf(dict, "edgeColor", &element->edge_color)); - CHECK(ParseColorf(dict, "centerColor", &element->center_color)); - break; - case Fill::GRID_GRADIENT: - CHECK(ParseColorf(dict, "edgeColor", &element->edge_color)); - CHECK(ParseColorf(dict, "centerColor", &element->center_color)); - CHECK(ParseInt(dict, "gridlineCount", &element->gridline_count)); - CHECK_GE(element->gridline_count, 0); - break; - case Fill::CONTENT: - CHECK_EQ(content_element_, nullptr); - content_element_ = element; - break; - default: - element->fill = Fill::NONE; - break; - } - } -} - } // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/ui_scene.h b/chrome/browser/android/vr_shell/ui_scene.h index 2308740..9ab39b6 100644 --- a/chrome/browser/android/vr_shell/ui_scene.h +++ b/chrome/browser/android/vr_shell/ui_scene.h
@@ -12,7 +12,6 @@ #include "device/vr/vr_types.h" namespace base { -class DictionaryValue; class ListValue; class TimeTicks; } @@ -38,21 +37,11 @@ void AddUiElement(std::unique_ptr<UiElement> element); - // Add a UI element according to a dictionary passed from the UI HTML. - void AddUiElementFromDict(const base::DictionaryValue& dict); - - // Update an existing element with new properties. - void UpdateUiElementFromDict(const base::DictionaryValue& dict); - void RemoveUiElement(int element_id); // Add an animation to the scene, on element |element_id|. void AddAnimation(int element_id, std::unique_ptr<Animation> animation); - // Add an animation according to a dictionary passed from the UI HTML. - void AddAnimationFromDict(const base::DictionaryValue& dict, - const base::TimeTicks& current_time); - // Remove |animation_id| from element |element_id|. void RemoveAnimation(int element_id, int animation_id); @@ -79,8 +68,6 @@ private: void ApplyRecursiveTransforms(UiElement* element); - void ApplyDictToElement(const base::DictionaryValue& dict, - UiElement* element); std::vector<std::unique_ptr<UiElement>> ui_elements_; UiElement* content_element_ = nullptr;
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.cc b/chrome/browser/android/vr_shell/ui_scene_manager.cc index 915f86b..dfd3fa6e 100644 --- a/chrome/browser/android/vr_shell/ui_scene_manager.cc +++ b/chrome/browser/android/vr_shell/ui_scene_manager.cc
@@ -70,10 +70,6 @@ return weak_ptr_factory_.GetWeakPtr(); } -void UiSceneManager::UpdateScene(std::unique_ptr<base::ListValue> commands) { - scene_->HandleCommands(std::move(commands), base::TimeTicks::Now()); -} - void UiSceneManager::SetWebVRMode(bool web_vr) { web_vr_mode_ = web_vr; ConfigureSecurityWarnings();
diff --git a/chrome/browser/android/vr_shell/ui_scene_manager.h b/chrome/browser/android/vr_shell/ui_scene_manager.h index df979c4..529d047 100644 --- a/chrome/browser/android/vr_shell/ui_scene_manager.h +++ b/chrome/browser/android/vr_shell/ui_scene_manager.h
@@ -22,8 +22,6 @@ base::WeakPtr<UiSceneManager> GetWeakPtr(); - void UpdateScene(std::unique_ptr<base::ListValue> commands); - void SetWebVRSecureOrigin(bool secure); void SetWebVRMode(bool web_vr);
diff --git a/chrome/browser/android/vr_shell/ui_scene_unittest.cc b/chrome/browser/android/vr_shell/ui_scene_unittest.cc index 9498f85..e57e543 100644 --- a/chrome/browser/android/vr_shell/ui_scene_unittest.cc +++ b/chrome/browser/android/vr_shell/ui_scene_unittest.cc
@@ -228,227 +228,4 @@ AnchoringTest, ::testing::ValuesIn(anchoring_test_cases)); -TEST(UiScene, AddUiElementFromDictionary) { - UiScene scene; - addElement(&scene, 11); - - base::DictionaryValue dict; - - dict.SetInteger("id", 10); - dict.SetString("name", "abc"); - dict.SetInteger("parentId", 11); - dict.SetBoolean("visible", false); - dict.SetBoolean("hitTestable", false); - dict.SetInteger("fillType", Fill::SPRITE); - dict.SetInteger("xAnchoring", XAnchoring::XLEFT); - dict.SetInteger("yAnchoring", YAnchoring::YTOP); - dict.SetDouble("opacity", 0.357); - - dict.SetInteger("copyRectX", 100); - dict.SetInteger("copyRectY", 101); - dict.SetInteger("copyRectWidth", 102); - dict.SetInteger("copyRectHeight", 103); - - dict.SetDouble("sizeX", 200); - dict.SetDouble("sizeY", 201); - - dict.SetDouble("scaleX", 300); - dict.SetDouble("scaleY", 301); - dict.SetDouble("scaleZ", 302); - - dict.SetDouble("rotationX", 400); - dict.SetDouble("rotationY", 401); - dict.SetDouble("rotationZ", 402); - dict.SetDouble("rotationAngle", 403); - - dict.SetDouble("translationX", 500); - dict.SetDouble("translationY", 501); - dict.SetDouble("translationZ", 502); - - scene.AddUiElementFromDict(dict); - const auto* element = scene.GetUiElementById(10); - EXPECT_NE(element, nullptr); - - EXPECT_EQ(element->id, 10); - EXPECT_EQ(element->name, "abc"); - EXPECT_EQ(element->parent_id, 11); - EXPECT_EQ(element->visible, false); - EXPECT_EQ(element->hit_testable, false); - EXPECT_EQ(element->fill, Fill::SPRITE); - EXPECT_EQ(element->x_anchoring, XAnchoring::XLEFT); - EXPECT_EQ(element->y_anchoring, YAnchoring::YTOP); - EXPECT_FLOAT_EQ(element->opacity, 0.357); - - EXPECT_EQ(element->copy_rect.x(), 100); - EXPECT_EQ(element->copy_rect.y(), 101); - EXPECT_EQ(element->copy_rect.width(), 102); - EXPECT_EQ(element->copy_rect.height(), 103); - - EXPECT_FLOAT_EQ(element->size.x(), 200); - EXPECT_FLOAT_EQ(element->size.y(), 201); - EXPECT_FLOAT_EQ(element->size.z(), 1); - - EXPECT_FLOAT_EQ(element->scale.x(), 300); - EXPECT_FLOAT_EQ(element->scale.y(), 301); - EXPECT_FLOAT_EQ(element->scale.z(), 302); - - EXPECT_FLOAT_EQ(element->rotation.x, 400); - EXPECT_FLOAT_EQ(element->rotation.y, 401); - EXPECT_FLOAT_EQ(element->rotation.z, 402); - EXPECT_FLOAT_EQ(element->rotation.angle, 403); - - EXPECT_FLOAT_EQ(element->translation.x(), 500); - EXPECT_FLOAT_EQ(element->translation.y(), 501); - EXPECT_FLOAT_EQ(element->translation.z(), 502); - - dict.Clear(); - dict.SetInteger("id", 12); - dict.SetBoolean("lockToFov", true); - - scene.AddUiElementFromDict(dict); - element = scene.GetUiElementById(12); - EXPECT_NE(element, nullptr); - - EXPECT_EQ(element->id, 12); - EXPECT_EQ(element->lock_to_fov, true); -} - -TEST(UiScene, AddUiElementFromDictionary_Fill) { - UiScene scene; - base::DictionaryValue dict; - - dict.SetInteger("copyRectX", 1); - dict.SetInteger("copyRectY", 2); - dict.SetInteger("copyRectWidth", 3); - dict.SetInteger("copyRectHeight", 4); - - base::DictionaryValue edge_color; - edge_color.SetDouble("r", 0.1); - edge_color.SetDouble("g", 0.2); - edge_color.SetDouble("b", 0.3); - edge_color.SetDouble("a", 0.4); - - base::DictionaryValue center_color; - center_color.SetDouble("r", 0.5); - center_color.SetDouble("g", 0.6); - center_color.SetDouble("b", 0.7); - center_color.SetDouble("a", 0.8); - - // Test SPRITE filling. - dict.SetInteger("id", 9); - dict.SetInteger("fillType", Fill::SPRITE); - scene.AddUiElementFromDict(dict); - const auto* element = scene.GetUiElementById(9); - - EXPECT_EQ(element->fill, Fill::SPRITE); - EXPECT_EQ(element->copy_rect.x(), 1); - EXPECT_EQ(element->copy_rect.y(), 2); - EXPECT_EQ(element->copy_rect.width(), 3); - EXPECT_EQ(element->copy_rect.height(), 4); - - // Test OPAQUE_GRADIENT filling. - dict.Clear(); - dict.SetInteger("id", 10); - dict.SetInteger("fillType", Fill::OPAQUE_GRADIENT); - dict.Set("edgeColor", edge_color.DeepCopy()); - dict.Set("centerColor", center_color.DeepCopy()); - scene.AddUiElementFromDict(dict); - element = scene.GetUiElementById(10); - - EXPECT_EQ(element->fill, Fill::OPAQUE_GRADIENT); - EXPECT_FLOAT_EQ(element->edge_color.r, 0.1); - EXPECT_FLOAT_EQ(element->edge_color.g, 0.2); - EXPECT_FLOAT_EQ(element->edge_color.b, 0.3); - EXPECT_FLOAT_EQ(element->edge_color.a, 0.4); - EXPECT_FLOAT_EQ(element->center_color.r, 0.5); - EXPECT_FLOAT_EQ(element->center_color.g, 0.6); - EXPECT_FLOAT_EQ(element->center_color.b, 0.7); - EXPECT_FLOAT_EQ(element->center_color.a, 0.8); - - // Test GRID_GRADIENT filling. - dict.Clear(); - dict.SetInteger("id", 11); - dict.SetInteger("fillType", Fill::GRID_GRADIENT); - dict.Set("edgeColor", edge_color.DeepCopy()); - dict.Set("centerColor", center_color.DeepCopy()); - dict.SetInteger("gridlineCount", 10); - scene.AddUiElementFromDict(dict); - element = scene.GetUiElementById(11); - - EXPECT_EQ(element->fill, Fill::GRID_GRADIENT); - EXPECT_FLOAT_EQ(element->edge_color.r, 0.1); - EXPECT_FLOAT_EQ(element->edge_color.g, 0.2); - EXPECT_FLOAT_EQ(element->edge_color.b, 0.3); - EXPECT_FLOAT_EQ(element->edge_color.a, 0.4); - EXPECT_FLOAT_EQ(element->center_color.r, 0.5); - EXPECT_FLOAT_EQ(element->center_color.g, 0.6); - EXPECT_FLOAT_EQ(element->center_color.b, 0.7); - EXPECT_FLOAT_EQ(element->center_color.a, 0.8); - EXPECT_EQ(element->gridline_count, 10); - - // Test CONTENT filling. - dict.Clear(); - dict.SetInteger("id", 12); - dict.SetInteger("fillType", Fill::CONTENT); - scene.AddUiElementFromDict(dict); - element = scene.GetUiElementById(12); - - EXPECT_EQ(element->fill, Fill::CONTENT); -} - -TEST(UiScene, AddAnimationFromDictionary) { - UiScene scene; - addElement(&scene, 0); - - base::DictionaryValue dict; - - dict.SetInteger("id", 10); - dict.SetInteger("meshId", 0); - dict.SetDouble("startInMillis", 12345); - dict.SetDouble("durationMillis", 54321); - dict.SetInteger("property", Animation::Property::ROTATION); - - auto easing = base::MakeUnique<base::DictionaryValue>(); - easing->SetInteger("type", vr_shell::easing::EasingType::CUBICBEZIER); - easing->SetInteger("p1x", 101); - easing->SetInteger("p1y", 101); - easing->SetInteger("p2x", 101); - easing->SetInteger("p2y", 101); - dict.Set("easing", std::move(easing)); - - auto to = base::MakeUnique<base::DictionaryValue>(); - to->SetInteger("x", 200); - to->SetInteger("y", 201); - to->SetInteger("z", 202); - to->SetInteger("a", 203); - dict.Set("to", std::move(to)); - - auto from = base::MakeUnique<base::DictionaryValue>(); - from->SetInteger("x", 300); - from->SetInteger("y", 301); - from->SetInteger("z", 302); - from->SetInteger("a", 303); - dict.Set("from", std::move(from)); - - scene.AddAnimationFromDict(dict, usToTicks(10000000)); - const auto* element = scene.GetUiElementById(0); - const auto* animation = element->animations[0].get(); - EXPECT_NE(animation, nullptr); - - EXPECT_EQ(animation->id, 10); - - EXPECT_FLOAT_EQ(200, animation->to[0]); - EXPECT_FLOAT_EQ(201, animation->to[1]); - EXPECT_FLOAT_EQ(202, animation->to[2]); - EXPECT_FLOAT_EQ(203, animation->to[3]); - - EXPECT_FLOAT_EQ(300, animation->from[0]); - EXPECT_FLOAT_EQ(301, animation->from[1]); - EXPECT_FLOAT_EQ(302, animation->from[2]); - EXPECT_FLOAT_EQ(303, animation->from[3]); - - EXPECT_EQ(usToTicks(22345000), animation->start); - EXPECT_EQ(usToDelta(54321000), animation->duration); -} - } // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_omnibox.cc b/chrome/browser/android/vr_shell/vr_omnibox.cc deleted file mode 100644 index a76509d753..0000000 --- a/chrome/browser/android/vr_shell/vr_omnibox.cc +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/android/vr_shell/vr_omnibox.h" - -#include <string> -#include <utility> - -#include "base/strings/string16.h" -#include "chrome/browser/android/vr_shell/ui_interface.h" -#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h" -#include "chrome/browser/profiles/profile_manager.h" -#include "components/omnibox/browser/autocomplete_classifier.h" -#include "components/omnibox/browser/autocomplete_controller.h" -#include "components/omnibox/browser/autocomplete_input.h" - -namespace vr_shell { - -VrOmnibox::VrOmnibox(UiInterface* ui) - : ui_(ui), - profile_(ProfileManager::GetLastUsedProfile()), - autocomplete_controller_(base::MakeUnique<AutocompleteController>( - base::MakeUnique<ChromeAutocompleteProviderClient>(profile_), - this, - AutocompleteClassifier::DefaultOmniboxProviders())) {} - -VrOmnibox::~VrOmnibox() = default; - -void VrOmnibox::HandleInput(const base::DictionaryValue& dict) { - base::string16 text; - CHECK(dict.GetString("text", &text)); - - // TODO(crbug.com/683344): Scrub and appropriately tune these parameters. - GURL current_url; - size_t cursor_pos = base::string16::npos; - std::string desired_tld; - metrics::OmniboxEventProto::PageClassification page_classification = - metrics::OmniboxEventProto::OTHER; - bool prevent_inline_autocomplete = false; - bool prefer_keyword = false; - bool allow_exact_keyword_match = false; - bool want_asynchronous_matches = true; - bool from_omnibox_focus = false; - - autocomplete_controller_->Start(AutocompleteInput( - text, cursor_pos, desired_tld, current_url, page_classification, - prevent_inline_autocomplete, prefer_keyword, allow_exact_keyword_match, - want_asynchronous_matches, from_omnibox_focus, - ChromeAutocompleteSchemeClassifier(profile_))); -} - -void VrOmnibox::OnResultChanged(bool default_match_changed) { - const AutocompleteResult& result = autocomplete_controller_->result(); - auto suggestions = base::MakeUnique<base::ListValue>(); - - for (const AutocompleteMatch& match : result) { - auto entry = base::MakeUnique<base::DictionaryValue>(); - entry->SetString("description", match.contents); - entry->SetString("url", match.destination_url.spec()); - suggestions->Append(std::move(entry)); - } - - ui_->SetOmniboxSuggestions(std::move(suggestions)); -} - -} // namespace vr_shell
diff --git a/chrome/browser/android/vr_shell/vr_omnibox.h b/chrome/browser/android/vr_shell/vr_omnibox.h deleted file mode 100644 index d4d058a7..0000000 --- a/chrome/browser/android/vr_shell/vr_omnibox.h +++ /dev/null
@@ -1,41 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_ANDROID_VR_SHELL_VR_OMNIBOX_H_ -#define CHROME_BROWSER_ANDROID_VR_SHELL_VR_OMNIBOX_H_ - -#include <memory> - -#include "base/macros.h" -#include "base/values.h" -#include "components/omnibox/browser/autocomplete_controller_delegate.h" - -class AutocompleteController; -class Profile; - -namespace vr_shell { - -class UiInterface; - -class VrOmnibox : public AutocompleteControllerDelegate { - public: - explicit VrOmnibox(UiInterface* ui); - ~VrOmnibox() override; - - void HandleInput(const base::DictionaryValue& dict); - - private: - void OnResultChanged(bool default_match_changed) override; - - UiInterface* ui_; - - Profile* profile_; - std::unique_ptr<AutocompleteController> autocomplete_controller_; - - DISALLOW_COPY_AND_ASSIGN(VrOmnibox); -}; - -} // namespace vr_shell - -#endif // CHROME_BROWSER_ANDROID_VR_SHELL_VR_OMNIBOX_H_
diff --git a/chrome/browser/android/vr_shell/vr_shell.cc b/chrome/browser/android/vr_shell/vr_shell.cc index 76a7142..224ea19 100644 --- a/chrome/browser/android/vr_shell/vr_shell.cc +++ b/chrome/browser/android/vr_shell/vr_shell.cc
@@ -61,8 +61,6 @@ namespace { vr_shell::VrShell* g_instance; -static const char kVrShellUIURL[] = "chrome://vr-shell-ui"; - void SetIsInVR(content::WebContents* contents, bool is_in_vr) { if (contents && contents->GetRenderWidgetHostView()) contents->GetRenderWidgetHostView()->SetIsInVR(is_in_vr); @@ -83,20 +81,14 @@ VrShell::VrShell(JNIEnv* env, jobject obj, - ui::WindowAndroid* content_window, - content::WebContents* ui_contents, - ui::WindowAndroid* ui_window, + ui::WindowAndroid* window, bool for_web_vr, VrShellDelegate* delegate, gvr_context* gvr_api, bool reprojected_rendering) - : WebContentsObserver(ui_contents), - vr_shell_enabled_(base::FeatureList::IsEnabled(features::kVrShell)), - content_window_(content_window), - content_compositor_( - base::MakeUnique<VrCompositor>(content_window_, false)), - ui_contents_(ui_contents), - ui_compositor_(base::MakeUnique<VrCompositor>(ui_window, true)), + : vr_shell_enabled_(base::FeatureList::IsEnabled(features::kVrShell)), + window_(window), + compositor_(base::MakeUnique<VrCompositor>(window_, false)), delegate_provider_(delegate), main_thread_task_runner_(base::ThreadTaskRunnerHandle::Get()), reprojected_rendering_(reprojected_rendering), @@ -107,9 +99,6 @@ g_instance = this; j_vr_shell_.Reset(env, obj); - ui_input_manager_ = base::MakeUnique<VrInputManager>(ui_contents_); - ui_compositor_->SetLayer(ui_contents_); - gl_thread_ = base::MakeUnique<VrGLThread>(weak_ptr_factory_.GetWeakPtr(), main_thread_task_runner_, gvr_api, for_web_vr, reprojected_rendering_); @@ -118,8 +107,8 @@ options.priority = base::ThreadPriority::DISPLAY; gl_thread_->StartWithOptions(options); - html_interface_ = base::MakeUnique<UiInterface>( - for_web_vr ? UiInterface::Mode::WEB_VR : UiInterface::Mode::STANDARD); + ui_ = base::MakeUnique<UiInterface>(for_web_vr ? UiInterface::Mode::WEB_VR + : UiInterface::Mode::STANDARD); content::BrowserThread::PostTask( content::BrowserThread::FILE, FROM_HERE, @@ -138,57 +127,50 @@ const JavaParamRef<jobject>& touch_event_synthesizer) { content::WebContents* contents = content::WebContents::FromJavaWebContents(web_contents); - if (contents == main_contents_ && + if (contents == web_contents_ && touch_event_synthesizer.obj() == j_motion_event_synthesizer_.obj()) return; - SetIsInVR(main_contents_, false); + SetIsInVR(web_contents_, false); j_motion_event_synthesizer_.Reset(env, touch_event_synthesizer); - main_contents_ = contents; - content_compositor_->SetLayer(main_contents_); - SetIsInVR(main_contents_, true); + web_contents_ = contents; + compositor_->SetLayer(web_contents_); + SetIsInVR(web_contents_, true); ContentFrameWasResized(false /* unused */); SetUiState(); - if (!main_contents_) { + if (!web_contents_) { android_ui_gesture_target_ = base::MakeUnique<AndroidUiGestureTarget>( j_motion_event_synthesizer_.obj(), Java_VrShellImpl_getNativePageScrollRatio(env, j_vr_shell_.obj())); - content_input_manager_ = nullptr; + input_manager_ = nullptr; vr_web_contents_observer_ = nullptr; metrics_helper_ = nullptr; return; } - content_input_manager_ = base::MakeUnique<VrInputManager>(main_contents_); - vr_web_contents_observer_ = base::MakeUnique<VrWebContentsObserver>( - main_contents_, html_interface_.get(), this); + input_manager_ = base::MakeUnique<VrInputManager>(web_contents_); + vr_web_contents_observer_ = + base::MakeUnique<VrWebContentsObserver>(web_contents_, ui_.get(), this); // TODO(billorr): Make VrMetricsHelper tab-aware and able to track multiple // tabs. crbug.com/684661 - metrics_helper_ = base::MakeUnique<VrMetricsHelper>(main_contents_); + metrics_helper_ = base::MakeUnique<VrMetricsHelper>(web_contents_); metrics_helper_->SetVRActive(true); metrics_helper_->SetWebVREnabled(webvr_mode_); } void VrShell::SetUiState() { - if (!main_contents_) { + if (!web_contents_) { // TODO(mthiesse): Properly handle native page URLs. - html_interface_->SetURL(GURL()); - html_interface_->SetLoading(false); - html_interface_->SetFullscreen(false); + ui_->SetURL(GURL()); + ui_->SetLoading(false); + ui_->SetFullscreen(false); } else { - html_interface_->SetURL(main_contents_->GetVisibleURL()); - html_interface_->SetLoading(main_contents_->IsLoading()); - html_interface_->SetFullscreen(main_contents_->IsFullscreen()); + ui_->SetURL(web_contents_->GetVisibleURL()); + ui_->SetLoading(web_contents_->IsLoading()); + ui_->SetFullscreen(web_contents_->IsFullscreen()); } } -void VrShell::LoadUIContent(JNIEnv* env, const JavaParamRef<jobject>& obj) { - GURL url(kVrShellUIURL); - ui_contents_->GetController().LoadURL( - url, content::Referrer(), - ui::PageTransition::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string("")); -} - bool RegisterVrShell(JNIEnv* env) { return RegisterNativesImpl(env); } @@ -256,7 +238,7 @@ // exit vr session if (metrics_helper_) metrics_helper_->SetVRActive(false); - SetIsInVR(main_contents_, false); + SetIsInVR(web_contents_, false); } void VrShell::OnResume(JNIEnv* env, const JavaParamRef<jobject>& obj) { @@ -265,7 +247,7 @@ if (metrics_helper_) metrics_helper_->SetVRActive(true); - SetIsInVR(main_contents_, true); + SetIsInVR(web_contents_, true); } void VrShell::SetSurface(JNIEnv* env, @@ -279,20 +261,6 @@ base::Unretained(window))); } -base::WeakPtr<VrShell> VrShell::GetWeakPtr( - const content::WebContents* web_contents) { - // Ensure that the WebContents requesting the VrShell instance is the one - // we created. - if (g_instance != nullptr && g_instance->ui_contents_ == web_contents) - return g_instance->weak_ptr_factory_.GetWeakPtr(); - return base::WeakPtr<VrShell>(nullptr); -} - -void VrShell::OnDomContentsLoaded() { - SetUiState(); - html_interface_->OnDomContentsLoaded(); -} - void VrShell::SetWebVrMode(JNIEnv* env, const base::android::JavaParamRef<jobject>& obj, bool enabled) { @@ -302,8 +270,8 @@ PostToGlThreadWhenReady(base::Bind(&VrShellGl::SetWebVrMode, gl_thread_->GetVrShellGl(), enabled)); - html_interface_->SetMode(enabled ? UiInterface::Mode::WEB_VR - : UiInterface::Mode::STANDARD); + ui_->SetMode(enabled ? UiInterface::Mode::WEB_VR + : UiInterface::Mode::STANDARD); PostToGlThreadWhenReady(base::Bind(&UiSceneManager::SetWebVRMode, gl_thread_->GetSceneManager(), enabled)); } @@ -311,17 +279,17 @@ void VrShell::OnLoadProgressChanged(JNIEnv* env, const JavaParamRef<jobject>& obj, double progress) { - html_interface_->SetLoadProgress(progress); + ui_->SetLoadProgress(progress); } void VrShell::OnTabListCreated(JNIEnv* env, const JavaParamRef<jobject>& obj, jobjectArray tabs, jobjectArray incognito_tabs) { - html_interface_->InitTabList(); + ui_->InitTabList(); ProcessTabArray(env, tabs, false); ProcessTabArray(env, incognito_tabs, true); - html_interface_->FlushTabList(); + ui_->FlushTabList(); } void VrShell::ProcessTabArray(JNIEnv* env, jobjectArray tabs, bool incognito) { @@ -330,8 +298,7 @@ jobject jtab = env->GetObjectArrayElement(tabs, i); TabAndroid* tab = TabAndroid::GetNativeTab(env, JavaParamRef<jobject>(env, jtab)); - html_interface_->AppendToTabList(incognito, tab->GetAndroidId(), - tab->GetTitle()); + ui_->AppendToTabList(incognito, tab->GetAndroidId(), tab->GetTitle()); } } @@ -342,19 +309,17 @@ jstring jtitle) { std::string title; base::android::ConvertJavaStringToUTF8(env, jtitle, &title); - html_interface_->UpdateTab(incognito, id, title); + ui_->UpdateTab(incognito, id, title); } void VrShell::OnTabRemoved(JNIEnv* env, const JavaParamRef<jobject>& obj, jboolean incognito, jint id) { - html_interface_->RemoveTab(incognito, id); + ui_->RemoveTab(incognito, id); } void VrShell::SetWebVRSecureOrigin(bool secure_origin) { - // TODO(cjgrant): Align this state with the logic that drives the omnibox. - html_interface_->SetWebVRSecureOrigin(secure_origin); PostToGlThreadWhenReady(base::Bind(&UiSceneManager::SetWebVRSecureOrigin, gl_thread_->GetSceneManager(), secure_origin)); @@ -402,7 +367,7 @@ base::android::ScopedJavaGlobalRef<jobject> VrShell::TakeContentSurface( JNIEnv* env, const JavaParamRef<jobject>& obj) { - content_compositor_->SurfaceChanged(nullptr); + compositor_->SurfaceChanged(nullptr); base::android::ScopedJavaGlobalRef<jobject> surface(env, content_surface_); content_surface_ = nullptr; return surface; @@ -418,16 +383,12 @@ const JavaParamRef<jobject>& obj, jboolean can_go_back, jboolean can_go_forward) { - html_interface_->SetHistoryButtonsEnabled(can_go_back, can_go_forward); -} - -void VrShell::UiSurfaceChanged(jobject surface) { - ui_compositor_->SurfaceChanged(surface); + ui_->SetHistoryButtonsEnabled(can_go_back, can_go_forward); } void VrShell::ContentSurfaceChanged(jobject surface) { content_surface_ = surface; - content_compositor_->SurfaceChanged(surface); + compositor_->SurfaceChanged(surface); JNIEnv* env = base::android::AttachCurrentThread(); Java_VrShellImpl_contentSurfaceChanged(env, j_vr_shell_.obj()); } @@ -438,12 +399,12 @@ void VrShell::AppButtonGesturePerformed(UiInterface::Direction direction) { if (vr_shell_enabled_) - html_interface_->HandleAppButtonGesturePerformed(direction); + ui_->HandleAppButtonGesturePerformed(direction); } void VrShell::AppButtonPressed() { if (vr_shell_enabled_) - html_interface_->HandleAppButtonClicked(); + ui_->HandleAppButtonClicked(); } void VrShell::ContentPhysicalBoundsChanged(JNIEnv* env, @@ -455,37 +416,13 @@ PostToGlThreadWhenReady(base::Bind(&VrShellGl::ContentPhysicalBoundsChanged, gl_thread_->GetVrShellGl(), width, height)); - content_compositor_->SetWindowBounds(gfx::Size(width, height)); -} - -void VrShell::UIPhysicalBoundsChanged(JNIEnv* env, - const JavaParamRef<jobject>& object, - jint width, - jint height, - jfloat dpr) { - PostToGlThreadWhenReady(base::Bind(&VrShellGl::UIPhysicalBoundsChanged, - gl_thread_->GetVrShellGl(), width, - height)); - ui_compositor_->SetWindowBounds(gfx::Size(width, height)); -} - -UiInterface* VrShell::GetUiInterface() { - return html_interface_.get(); -} - -void VrShell::UpdateScene(const base::ListValue* args) { - PostToGlThreadWhenReady(base::Bind(&UiSceneManager::UpdateScene, - gl_thread_->GetSceneManager(), - base::Passed(args->CreateDeepCopy()))); + compositor_->SetWindowBounds(gfx::Size(width, height)); } void VrShell::DoUiAction(const UiAction action, const base::DictionaryValue* arguments) { // Actions that can be handled natively. switch (action) { - case OMNIBOX_CONTENT: - html_interface_->HandleOmniboxInput(*arguments); - return; case SET_CONTENT_PAUSED: { bool paused; CHECK(arguments->GetBoolean("paused", &paused)); @@ -493,34 +430,15 @@ return; } case HISTORY_BACK: - if (main_contents_ && main_contents_->IsFullscreen()) { - main_contents_->ExitFullscreen(false); + if (web_contents_ && web_contents_->IsFullscreen()) { + web_contents_->ExitFullscreen(false); return; } // Otherwise handle in java. break; - case ZOOM_OUT: // Not handled yet. - case ZOOM_IN: // Not handled yet. - return; - case KEY_EVENT: { - int char_value; - int modifiers = 0; - arguments->GetInteger("modifiers", &modifiers); - CHECK(arguments->GetInteger("charValue", &char_value)); - ui_input_manager_->GenerateKeyboardEvent(char_value, modifiers); - return; - } case EXIT_PRESENT: delegate_provider_->ExitWebVRPresent(); return; -#if defined(ENABLE_VR_SHELL_UI_DEV) - case RELOAD_UI: - ui_contents_->GetController().Reload(content::ReloadType::NORMAL, false); - html_interface_.reset(new UiInterface(UiInterface::Mode::STANDARD)); - SetUiState(); - vr_web_contents_observer_->SetUiInterface(html_interface_.get()); - return; -#endif default: break; } @@ -548,66 +466,34 @@ case RELOAD: Java_VrShellImpl_reload(env, j_vr_shell_.obj()); break; - case LOAD_URL: { - base::string16 url_string; - CHECK(arguments->GetString("url", &url_string)); - base::android::ScopedJavaLocalRef<jstring> string = - base::android::ConvertUTF16ToJavaString(env, url_string); - Java_VrShellImpl_loadURL(env, j_vr_shell_.obj(), string, - ui::PageTransition::PAGE_TRANSITION_TYPED); - break; - } default: NOTREACHED(); } } -void VrShell::RenderViewHostChanged(content::RenderViewHost* old_host, - content::RenderViewHost* new_host) { - content::RenderWidgetHostView* view = new_host->GetWidget()->GetView(); - view->SetBackgroundColor(SK_ColorTRANSPARENT); - view->SetIsInVR(true); -} - -void VrShell::MainFrameWasResized(bool width_changed) { - display::Display display = - display::Screen::GetScreen()->GetDisplayNearestView( - ui_contents_->GetNativeView()); - PostToGlThreadWhenReady( - base::Bind(&VrShellGl::UIBoundsChanged, gl_thread_->GetVrShellGl(), - display.size().width(), display.size().height())); -} - void VrShell::ContentFrameWasResized(bool width_changed) { display::Display display = - display::Screen::GetScreen()->GetDisplayNearestWindow(content_window_); + display::Screen::GetScreen()->GetDisplayNearestWindow(window_); PostToGlThreadWhenReady( base::Bind(&VrShellGl::ContentBoundsChanged, gl_thread_->GetVrShellGl(), display.size().width(), display.size().height())); } -void VrShell::WebContentsDestroyed() { - ui_input_manager_.reset(); - ui_contents_ = nullptr; - // TODO(mthiesse): Handle web contents being destroyed. - ForceExitVr(); -} - void VrShell::ContentWebContentsDestroyed() { - content_input_manager_.reset(); - main_contents_ = nullptr; + input_manager_.reset(); + web_contents_ = nullptr; // TODO(mthiesse): Handle web contents being destroyed. ForceExitVr(); } void VrShell::ContentWasHidden() { // Ensure we don't continue sending input to it. - content_input_manager_ = nullptr; + input_manager_ = nullptr; } void VrShell::ContentWasShown() { - if (main_contents_) - content_input_manager_ = base::MakeUnique<VrInputManager>(main_contents_); + if (web_contents_) + input_manager_ = base::MakeUnique<VrInputManager>(web_contents_); } void VrShell::ForceExitVr() { @@ -635,21 +521,10 @@ dpr); } -void VrShell::SetUiCssSize(float width, float height, float dpr) { - JNIEnv* env = base::android::AttachCurrentThread(); - Java_VrShellImpl_setUiCssSize(env, j_vr_shell_.obj(), width, height, dpr); -} - -void VrShell::ProcessUIGesture(std::unique_ptr<blink::WebInputEvent> event) { - if (ui_input_manager_) { - ui_input_manager_->ProcessUpdatedGesture(std::move(event)); - } -} - void VrShell::ProcessContentGesture( std::unique_ptr<blink::WebInputEvent> event) { - if (content_input_manager_) { - content_input_manager_->ProcessUpdatedGesture(std::move(event)); + if (input_manager_) { + input_manager_->ProcessUpdatedGesture(std::move(event)); } else if (android_ui_gesture_target_) { android_ui_gesture_target_->DispatchWebInputEvent(std::move(event)); } @@ -683,18 +558,14 @@ jlong Init(JNIEnv* env, const JavaParamRef<jobject>& obj, - const JavaParamRef<jobject>& ui_web_contents, - jlong content_window_android, - jlong ui_window_android, - jboolean for_web_vr, const base::android::JavaParamRef<jobject>& delegate, + jlong window_android, + jboolean for_web_vr, jlong gvr_api, jboolean reprojected_rendering) { return reinterpret_cast<intptr_t>(new VrShell( - env, obj, reinterpret_cast<ui::WindowAndroid*>(content_window_android), - content::WebContents::FromJavaWebContents(ui_web_contents), - reinterpret_cast<ui::WindowAndroid*>(ui_window_android), for_web_vr, - VrShellDelegate::GetNativeVrShellDelegate(env, delegate), + env, obj, reinterpret_cast<ui::WindowAndroid*>(window_android), + for_web_vr, VrShellDelegate::GetNativeVrShellDelegate(env, delegate), reinterpret_cast<gvr_context*>(gvr_api), reprojected_rendering)); }
diff --git a/chrome/browser/android/vr_shell/vr_shell.h b/chrome/browser/android/vr_shell/vr_shell.h index ae3d136..20572cc 100644 --- a/chrome/browser/android/vr_shell/vr_shell.h +++ b/chrome/browser/android/vr_shell/vr_shell.h
@@ -23,10 +23,6 @@ #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr.h" #include "third_party/gvr-android-sdk/src/libraries/headers/vr/gvr/capi/include/gvr_types.h" -namespace base { -class ListValue; -} - namespace blink { class WebInputEvent; } @@ -58,15 +54,9 @@ HISTORY_BACK = 0, HISTORY_FORWARD, RELOAD, - ZOOM_OUT, - ZOOM_IN, - RELOAD_UI, - LOAD_URL, - OMNIBOX_CONTENT, SET_CONTENT_PAUSED, SHOW_TAB, OPEN_NEW_TAB, - KEY_EVENT, EXIT_PRESENT, }; @@ -75,14 +65,11 @@ // The native instance of the Java VrShell. This class is not threadsafe and // must only be used on the UI thread. class VrShell : public device::PresentingGvrDelegate, - content::WebContentsObserver, device::GvrGamepadDataProvider { public: VrShell(JNIEnv* env, jobject obj, - ui::WindowAndroid* content_window, - content::WebContents* ui_contents, - ui::WindowAndroid* ui_window, + ui::WindowAndroid* window, bool for_web_vr, VrShellDelegate* delegate, gvr_context* gvr_api, @@ -137,14 +124,6 @@ void ContentWasHidden(); void ContentWasShown(); - // html/js UI hooks. - static base::WeakPtr<VrShell> GetWeakPtr( - const content::WebContents* web_contents); - - UiInterface* GetUiInterface(); - void OnDomContentsLoaded(); - - void UiSurfaceChanged(jobject surface); void ContentSurfaceChanged(jobject surface); void GvrDelegateReady(); void AppButtonGesturePerformed(UiInterface::Direction direction); @@ -157,27 +136,16 @@ jint height, jfloat dpr); - void UIPhysicalBoundsChanged( - JNIEnv* env, - const base::android::JavaParamRef<jobject>& object, - jint width, - jint height, - jfloat dpr); - - void UpdateScene(const base::ListValue* args); - // Perform a UI action triggered by the javascript API. void DoUiAction(const UiAction action, const base::DictionaryValue* arguments); void SetContentCssSize(float width, float height, float dpr); - void SetUiCssSize(float width, float height, float dpr); void ContentFrameWasResized(bool width_changed); void ForceExitVr(); - void ProcessUIGesture(std::unique_ptr<blink::WebInputEvent> event); void ProcessContentGesture(std::unique_ptr<blink::WebInputEvent> event); void SubmitControllerModel(std::unique_ptr<VrControllerModel> model); @@ -191,12 +159,6 @@ void SetContentPaused(bool paused); void SetUiState(); - // content::WebContentsObserver implementation. - void RenderViewHostChanged(content::RenderViewHost* old_host, - content::RenderViewHost* new_host) override; - void MainFrameWasResized(bool width_changed) override; - void WebContentsDestroyed() override; - // device::GvrDelegate implementation. void SetWebVRSecureOrigin(bool secure_origin) override; void SubmitWebVRFrame(int16_t frame_index, @@ -221,25 +183,22 @@ bool vr_shell_enabled_; - std::unique_ptr<UiInterface> html_interface_; + std::unique_ptr<UiInterface> ui_; bool content_paused_ = false; bool webvr_mode_ = false; - content::WebContents* main_contents_ = nullptr; + content::WebContents* web_contents_ = nullptr; base::android::ScopedJavaGlobalRef<jobject> j_motion_event_synthesizer_; - ui::WindowAndroid* content_window_; - std::unique_ptr<VrCompositor> content_compositor_; - content::WebContents* ui_contents_; - std::unique_ptr<VrCompositor> ui_compositor_; + ui::WindowAndroid* window_; + std::unique_ptr<VrCompositor> compositor_; std::unique_ptr<VrWebContentsObserver> vr_web_contents_observer_; VrShellDelegate* delegate_provider_ = nullptr; base::android::ScopedJavaGlobalRef<jobject> j_vr_shell_; - std::unique_ptr<VrInputManager> content_input_manager_; + std::unique_ptr<VrInputManager> input_manager_; std::unique_ptr<AndroidUiGestureTarget> android_ui_gesture_target_; - std::unique_ptr<VrInputManager> ui_input_manager_; std::unique_ptr<VrMetricsHelper> metrics_helper_; scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_;
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.cc b/chrome/browser/android/vr_shell/vr_shell_gl.cc index 81397edd8..05a6c98f 100644 --- a/chrome/browser/android/vr_shell/vr_shell_gl.cc +++ b/chrome/browser/android/vr_shell/vr_shell_gl.cc
@@ -18,6 +18,7 @@ #include "chrome/browser/android/vr_shell/ui_element.h" #include "chrome/browser/android/vr_shell/ui_interface.h" #include "chrome/browser/android/vr_shell/ui_scene.h" +#include "chrome/browser/android/vr_shell/ui_scene_manager.h" #include "chrome/browser/android/vr_shell/vr_controller.h" #include "chrome/browser/android/vr_shell/vr_gl_util.h" #include "chrome/browser/android/vr_shell/vr_shell.h" @@ -203,13 +204,13 @@ bool initially_web_vr, bool reprojected_rendering, UiScene* scene) - : scene_(scene), - web_vr_mode_(initially_web_vr), + : web_vr_mode_(initially_web_vr), surfaceless_rendering_(reprojected_rendering), task_runner_(base::ThreadTaskRunnerHandle::Get()), binding_(this), weak_vr_shell_(weak_vr_shell), main_thread_task_runner_(std::move(main_thread_task_runner)), + scene_(scene), #if DCHECK_IS_ON() fps_meter_(new FPSMeter()), #endif @@ -277,26 +278,20 @@ return; } - unsigned int textures[3]; - glGenTextures(3, textures); - ui_texture_id_ = textures[0]; - content_texture_id_ = textures[1]; - webvr_texture_id_ = textures[2]; - ui_surface_texture_ = gl::SurfaceTexture::Create(ui_texture_id_); + unsigned int textures[2]; + glGenTextures(2, textures); + content_texture_id_ = textures[0]; + webvr_texture_id_ = textures[1]; content_surface_texture_ = gl::SurfaceTexture::Create(content_texture_id_); webvr_surface_texture_ = gl::SurfaceTexture::Create(webvr_texture_id_); - CreateUiSurface(); CreateContentSurface(); - ui_surface_texture_->SetFrameAvailableCallback(base::Bind( - &VrShellGl::OnUIFrameAvailable, weak_ptr_factory_.GetWeakPtr())); content_surface_texture_->SetFrameAvailableCallback(base::Bind( &VrShellGl::OnContentFrameAvailable, weak_ptr_factory_.GetWeakPtr())); webvr_surface_texture_->SetFrameAvailableCallback(base::Bind( &VrShellGl::OnWebVRFrameAvailable, weak_ptr_factory_.GetWeakPtr())); - ui_surface_texture_->SetDefaultBufferSize(ui_tex_physical_size_.width(), - ui_tex_physical_size_.height()); content_surface_texture_->SetDefaultBufferSize( content_tex_physical_size_.width(), content_tex_physical_size_.height()); + InitializeRenderer(); gfx::Size webvr_size = @@ -320,14 +315,6 @@ content_surface_->j_surface().obj())); } -void VrShellGl::CreateUiSurface() { - ui_surface_ = - base::MakeUnique<gl::ScopedJavaSurface>(ui_surface_texture_.get()); - main_thread_task_runner_->PostTask( - FROM_HERE, base::Bind(&VrShell::UiSurfaceChanged, weak_vr_shell_, - ui_surface_->j_surface().obj())); -} - void VrShellGl::CreateOrResizeWebVRSurface(const gfx::Size& size) { if (!webvr_surface_texture_) { DLOG(ERROR) << "No WebVR surface texture available"; @@ -395,10 +382,6 @@ submit_client_.Bind(std::move(submit_client_info)); } -void VrShellGl::OnUIFrameAvailable() { - ui_surface_texture_->UpdateTexImage(); -} - void VrShellGl::OnContentFrameAvailable() { content_surface_texture_->UpdateTexImage(); received_frame_ = true; @@ -639,29 +622,12 @@ int pixel_x = 0; int pixel_y = 0; - if (target_element_ != nullptr) { - gfx::RectF pixel_rect; - if (target_element_->fill == Fill::CONTENT) { - pixel_rect.SetRect(0, 0, content_tex_css_width_, content_tex_css_height_); - } else { - pixel_rect.SetRect(target_element_->copy_rect.x(), - target_element_->copy_rect.y(), - target_element_->copy_rect.width(), - target_element_->copy_rect.height()); - } + if (target_element_ != nullptr && target_element_->fill == Fill::CONTENT) { + input_target = InputTarget::CONTENT; + gfx::RectF pixel_rect(0, 0, content_tex_css_width_, + content_tex_css_height_); pixel_x = pixel_rect.x() + pixel_rect.width() * target_x; pixel_y = pixel_rect.y() + pixel_rect.height() * target_y; - - switch (target_element_->fill) { - case Fill::CONTENT: - input_target = InputTarget::CONTENT; - break; - case Fill::SPRITE: - input_target = InputTarget::UI; - break; - default: - break; - } } SendEventsToTarget(input_target, pixel_x, pixel_y); } @@ -788,12 +754,9 @@ void VrShellGl::SendGesture(InputTarget input_target, std::unique_ptr<blink::WebInputEvent> event) { DCHECK(input_target != InputTarget::NONE); - auto&& target = input_target == InputTarget::CONTENT - ? &VrShell::ProcessContentGesture - : &VrShell::ProcessUIGesture; main_thread_task_runner_->PostTask( - FROM_HERE, - base::Bind(target, weak_vr_shell_, base::Passed(std::move(event)))); + FROM_HERE, base::Bind(&VrShell::ProcessContentGesture, weak_vr_shell_, + base::Passed(std::move(event)))); } void VrShellGl::DrawFrame(int16_t frame_index) { @@ -1031,15 +994,7 @@ vr::MatrixMul(view_proj_matrix, rect->TransformMatrix(), &transform); switch (rect->fill) { - case Fill::SPRITE: { - gfx::RectF copy_rect( - static_cast<float>(rect->copy_rect.x()) / ui_tex_css_width_, - static_cast<float>(rect->copy_rect.y()) / ui_tex_css_height_, - static_cast<float>(rect->copy_rect.width()) / ui_tex_css_width_, - static_cast<float>(rect->copy_rect.height()) / ui_tex_css_height_); - jint texture_handle = ui_texture_id_; - vr_shell_renderer_->GetTexturedQuadRenderer()->AddQuad( - texture_handle, transform, copy_rect, rect->computed_opacity); + case Fill::SKIA: { break; } case Fill::OPAQUE_GRADIENT: { @@ -1057,10 +1012,9 @@ break; } case Fill::CONTENT: { - gfx::RectF copy_rect = {0, 0, 1, 1}; - jint texture_handle = content_texture_id_; + gfx::RectF copy_rect(0, 0, 1, 1); vr_shell_renderer_->GetTexturedQuadRenderer()->AddQuad( - texture_handle, transform, copy_rect, rect->computed_opacity); + content_texture_id_, transform, copy_rect, rect->computed_opacity); break; } default: @@ -1274,18 +1228,6 @@ content_tex_physical_size_.set_height(height); } -void VrShellGl::UIBoundsChanged(int width, int height) { - ui_tex_css_width_ = width; - ui_tex_css_height_ = height; -} - -void VrShellGl::UIPhysicalBoundsChanged(int width, int height) { - if (ui_surface_texture_.get()) - ui_surface_texture_->SetDefaultBufferSize(width, height); - ui_tex_physical_size_.set_width(width); - ui_tex_physical_size_.set_height(height); -} - base::WeakPtr<VrShellGl> VrShellGl::GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); }
diff --git a/chrome/browser/android/vr_shell/vr_shell_gl.h b/chrome/browser/android/vr_shell/vr_shell_gl.h index 2d14b66..8d3d2fcf 100644 --- a/chrome/browser/android/vr_shell/vr_shell_gl.h +++ b/chrome/browser/android/vr_shell/vr_shell_gl.h
@@ -65,7 +65,6 @@ enum class InputTarget { NONE = 0, CONTENT, - UI, }; VrShellGl(const base::WeakPtr<VrShell>& weak_vr_shell, @@ -153,22 +152,16 @@ void SendVSync(base::TimeDelta time, const GetVSyncCallback& callback); - // samplerExternalOES texture data for UI content image. - int ui_texture_id_ = 0; // samplerExternalOES texture data for main content image. int content_texture_id_ = 0; // samplerExternalOES texture data for WebVR content image. int webvr_texture_id_ = 0; - UiScene* scene_; - scoped_refptr<gl::GLSurface> surface_; scoped_refptr<gl::GLContext> context_; - scoped_refptr<gl::SurfaceTexture> ui_surface_texture_; scoped_refptr<gl::SurfaceTexture> content_surface_texture_; scoped_refptr<gl::SurfaceTexture> webvr_surface_texture_; - std::unique_ptr<gl::ScopedJavaSurface> ui_surface_; std::unique_ptr<gl::ScopedJavaSurface> content_surface_; std::unique_ptr<gvr::GvrApi> gvr_api_; @@ -202,13 +195,10 @@ InputTarget current_input_target_ = InputTarget::NONE; InputTarget current_scroll_target_ = InputTarget::NONE; InputTarget current_fling_target_ = InputTarget::NONE; - int ui_tex_css_width_ = 0; - int ui_tex_css_height_ = 0; int content_tex_css_width_ = 0; int content_tex_css_height_ = 0; gfx::Size content_tex_physical_size_ = {0, 0}; gfx::Size webvr_surface_size_ = {0, 0}; - gfx::Size ui_tex_physical_size_ = {0, 0}; std::vector<vr::Mat4f> webvr_head_pose_; bool web_vr_mode_; @@ -232,6 +222,8 @@ base::WeakPtr<VrShell> weak_vr_shell_; scoped_refptr<base::SingleThreadTaskRunner> main_thread_task_runner_; + UiScene* scene_ = nullptr; + uint8_t frame_index_ = 0; // Larger than frame_index_ so it can be initialized out-of-band. uint16_t last_frame_index_ = -1;
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd index 23b1f50..65959b76 100644 --- a/chrome/browser/browser_resources.grd +++ b/chrome/browser/browser_resources.grd
@@ -235,15 +235,6 @@ <include name="IDR_SNIPPETS_INTERNALS_HTML" file="resources\snippets_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" /> <include name="IDR_SNIPPETS_INTERNALS_CSS" file="resources\snippets_internals.css" compress="gzip" type="BINDATA" /> <include name="IDR_SNIPPETS_INTERNALS_JS" file="resources\snippets_internals.js" compress="gzip" type="BINDATA" /> - <if expr="enable_vr"> - <include name="IDR_VR_SHELL_UI_HTML" file="resources\vr_shell\vr_shell_ui.html" allowexternalscript="true" flattenhtml="true" compress="gzip" type="BINDATA" /> - <include name="IDR_VR_SHELL_UI_CSS" file="resources\vr_shell\vr_shell_ui.css" compress="gzip" type="BINDATA" /> - <include name="IDR_VR_SHELL_UI_JS" file="resources\vr_shell\vr_shell_ui.js" compress="gzip" type="BINDATA" /> - <include name="IDR_VR_SHELL_UI_API_JS" file="resources\vr_shell\vr_shell_ui_api.js" compress="gzip" type="BINDATA" /> - <include name="IDR_VR_SHELL_UI_SCENE_JS" file="resources\vr_shell\vr_shell_ui_scene.js" compress="gzip" type="BINDATA" /> - <include name="IDR_VR_SHELL_UI_VK_CSS" file="resources\vr_shell\vk.css" compress="gzip" type="BINDATA" /> - <include name="IDR_VR_SHELL_UI_VK_JS" file="resources\vr_shell\vk.js" compress="gzip" type="BINDATA" /> - </if> </if> <include name="IDR_SUPERVISED_USER_INTERNALS_HTML" file="resources\supervised_user_internals.html" allowexternalscript="true" compress="gzip" type="BINDATA" /> <include name="IDR_SUPERVISED_USER_INTERNALS_CSS" file="resources\supervised_user_internals.css" compress="gzip" type="BINDATA" />
diff --git a/chrome/browser/chromeos/display/display_preferences_unittest.cc b/chrome/browser/chromeos/display/display_preferences_unittest.cc index 3a82ed5..1cf0597 100644 --- a/chrome/browser/chromeos/display/display_preferences_unittest.cc +++ b/chrome/browser/chromeos/display/display_preferences_unittest.cc
@@ -573,10 +573,9 @@ new display::ManagedDisplayMode(gfx::Size(400, 300))); scoped_refptr<display::ManagedDisplayMode> new_mode( new display::ManagedDisplayMode(gfx::Size(500, 400))); - if (shell->display_manager()->SetDisplayMode(id, new_mode)) { - shell->resolution_notification_controller()->PrepareNotification( - id, old_mode, new_mode, base::Closure()); - } + EXPECT_TRUE(shell->resolution_notification_controller() + ->PrepareNotificationAndSetDisplayMode(id, old_mode, new_mode, + base::Closure())); UpdateDisplay("500x400#500x400|400x300|300x200"); const base::DictionaryValue* properties =
diff --git a/chrome/browser/chromeos/printing/cups_print_job_notification.cc b/chrome/browser/chromeos/printing/cups_print_job_notification.cc index f4908eff..8dee4784 100644 --- a/chrome/browser/chromeos/printing/cups_print_job_notification.cc +++ b/chrome/browser/chromeos/printing/cups_print_job_notification.cc
@@ -149,10 +149,12 @@ UpdateNotificationType(); UpdateNotificationButtons(); - // |STATE_PAGE_DONE| is special since if the user closes the notification in - // the middle, which means they're not interested in the printing progress, we - // should prevent showing the following printing progress. - if (print_job_->state() == CupsPrintJob::State::STATE_PAGE_DONE) { + // |STATE_STARTED| and |STATE_PAGE_DONE| are special since if the user closes + // the notification in the middle, which means they're not interested in the + // printing progress, we should prevent showing the following printing + // progress to the user. + if (print_job_->state() == CupsPrintJob::State::STATE_STARTED || + print_job_->state() == CupsPrintJob::State::STATE_PAGE_DONE) { if (closed_in_middle_) { // If the notification was closed during the printing, prevent showing the // following printing progress. @@ -186,9 +188,6 @@ ResourceBundle& bundle = ResourceBundle::GetSharedInstance(); switch (print_job_->state()) { case CupsPrintJob::State::STATE_WAITING: - notification_->set_icon( - bundle.GetImageNamed(IDR_PRINT_NOTIFICATION_WAITING)); - break; case CupsPrintJob::State::STATE_STARTED: case CupsPrintJob::State::STATE_PAGE_DONE: case CupsPrintJob::State::STATE_SUSPENDED: @@ -215,11 +214,6 @@ case CupsPrintJob::State::STATE_NONE: break; case CupsPrintJob::State::STATE_WAITING: - message = l10n_util::GetStringFUTF16( - IDS_PRINT_JOB_WAITING_NOTIFICATION_MESSAGE, - base::IntToString16(print_job_->total_page_number()), - base::UTF8ToUTF16(print_job_->printer().display_name())); - break; case CupsPrintJob::State::STATE_STARTED: case CupsPrintJob::State::STATE_PAGE_DONE: case CupsPrintJob::State::STATE_SUSPENDED: @@ -251,6 +245,7 @@ void CupsPrintJobNotification::UpdateNotificationType() { switch (print_job_->state()) { + case CupsPrintJob::State::STATE_WAITING: case CupsPrintJob::State::STATE_STARTED: case CupsPrintJob::State::STATE_PAGE_DONE: case CupsPrintJob::State::STATE_SUSPENDED: @@ -260,7 +255,6 @@ print_job_->total_page_number()); break; case CupsPrintJob::State::STATE_NONE: - case CupsPrintJob::State::STATE_WAITING: case CupsPrintJob::State::STATE_DOCUMENT_DONE: case CupsPrintJob::State::STATE_ERROR: case CupsPrintJob::State::STATE_CANCELLED:
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc index c8f65ab9..3a0a01a 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api.cc
@@ -269,7 +269,8 @@ base::Closure task = base::Bind( &EPKPChallengeMachineKey::Run, base::Unretained(impl_), scoped_refptr<UIThreadExtensionFunction>(AsUIThreadExtensionFunction()), - callback, StringFromVector(params->challenge)); + callback, StringFromVector(params->challenge), + params->register_key ? *params->register_key : false); content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, task); return RespondLater(); }
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc index e48d908..29bda41 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys/enterprise_platform_keys_api_unittest.cc
@@ -262,8 +262,24 @@ } std::unique_ptr<base::ListValue> CreateArgs() { + return CreateArgsInternal(nullptr); + } + + std::unique_ptr<base::ListValue> CreateArgsNoRegister() { + return CreateArgsInternal(base::MakeUnique<bool>(false)); + } + + std::unique_ptr<base::ListValue> CreateArgsRegister() { + return CreateArgsInternal(base::MakeUnique<bool>(true)); + } + + std::unique_ptr<base::ListValue> CreateArgsInternal( + std::unique_ptr<bool> register_key) { std::unique_ptr<base::ListValue> args(new base::ListValue); args->Append(base::Value::CreateWithCopiedBuffer("challenge", 9)); + if (register_key) { + args->AppendBoolean(*register_key); + } return args; } @@ -320,6 +336,15 @@ RunFunctionAndReturnError(func_.get(), CreateArgs(), browser())); } +TEST_F(EPKChallengeMachineKeyTest, KeyRegistrationFailed) { + EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) + .WillRepeatedly(Invoke(RegisterKeyCallbackFalse)); + + EXPECT_EQ( + EPKPChallengeMachineKey::kKeyRegistrationFailedError, + RunFunctionAndReturnError(func_.get(), CreateArgsRegister(), browser())); +} + TEST_F(EPKChallengeMachineKeyTest, KeyExists) { EXPECT_CALL(mock_cryptohome_client_, TpmAttestationDoesKeyExist(_, _, _, _)) .WillRepeatedly(WithArgs<3>(Invoke( @@ -331,6 +356,22 @@ utils::RunFunction(func_.get(), CreateArgs(), browser(), utils::NONE)); } +TEST_F(EPKChallengeMachineKeyTest, KeyNotRegisteredByDefault) { + EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) + .Times(0); + + EXPECT_TRUE( + utils::RunFunction(func_.get(), CreateArgs(), browser(), utils::NONE)); +} + +TEST_F(EPKChallengeMachineKeyTest, KeyNotRegistered) { + EXPECT_CALL(mock_async_method_caller_, TpmAttestationRegisterKey(_, _, _, _)) + .Times(0); + + EXPECT_TRUE(utils::RunFunction(func_.get(), CreateArgsNoRegister(), browser(), + utils::NONE)); +} + TEST_F(EPKChallengeMachineKeyTest, Success) { // GetCertificate must be called exactly once. EXPECT_CALL(mock_attestation_flow_, @@ -355,6 +396,36 @@ std::string(response->GetBuffer(), response->GetSize())); } +TEST_F(EPKChallengeMachineKeyTest, KeyRegisteredSuccess) { + // GetCertificate must be called exactly once. + EXPECT_CALL(mock_attestation_flow_, + GetCertificate( + chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, + _, _, _, _)) + .Times(1); + // TpmAttestationRegisterKey must be called exactly once. + EXPECT_CALL(mock_async_method_caller_, + TpmAttestationRegisterKey(chromeos::attestation::KEY_DEVICE, + _ /* Unused by the API. */, + "attest-ent-machine", _)) + .Times(1); + // SignEnterpriseChallenge must be called exactly once. + EXPECT_CALL( + mock_async_method_caller_, + TpmAttestationSignEnterpriseChallenge( + chromeos::attestation::KEY_DEVICE, cryptohome::Identification(), + "attest-ent-machine", "google.com", "device_id", _, "challenge", _)) + .Times(1); + + std::unique_ptr<base::Value> value(RunFunctionAndReturnSingleResult( + func_.get(), CreateArgsRegister(), browser())); + + const base::Value* response; + ASSERT_TRUE(value->GetAsBinary(&response)); + EXPECT_EQ("response", + std::string(response->GetBuffer(), response->GetSize())); +} + TEST_F(EPKChallengeMachineKeyTest, AttestationNotPrepared) { EXPECT_CALL(mock_cryptohome_client_, TpmAttestationIsPrepared(_)) .WillRepeatedly(Invoke(
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc index 972e8ff..4b69f12d 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.cc
@@ -287,6 +287,8 @@ const char EPKPChallengeMachineKey::kGetCertificateFailedError[] = "Failed to get Enterprise machine certificate. Error code = %d"; +const char EPKPChallengeMachineKey::kKeyRegistrationFailedError[] = + "Machine key registration failed."; const char EPKPChallengeMachineKey::kNonEnterpriseDeviceError[] = "The device is not enterprise enrolled."; @@ -312,7 +314,8 @@ void EPKPChallengeMachineKey::Run( scoped_refptr<UIThreadExtensionFunction> caller, const ChallengeKeyCallback& callback, - const std::string& challenge) { + const std::string& challenge, + bool register_key) { callback_ = callback; profile_ = ChromeExtensionFunctionDetails(caller.get()).GetProfile(); extension_id_ = caller->extension_id(); @@ -337,23 +340,26 @@ // Check if RA is enabled in the device policy. GetDeviceAttestationEnabled( base::Bind(&EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback, - base::Unretained(this), challenge)); + base::Unretained(this), challenge, register_key)); } void EPKPChallengeMachineKey::DecodeAndRun( scoped_refptr<UIThreadExtensionFunction> caller, const ChallengeKeyCallback& callback, - const std::string& encoded_challenge) { + const std::string& encoded_challenge, + bool register_key) { std::string challenge; if (!base::Base64Decode(encoded_challenge, &challenge)) { callback.Run(false, kChallengeBadBase64Error); return; } - Run(caller, callback, challenge); + Run(caller, callback, challenge, register_key); } void EPKPChallengeMachineKey::GetDeviceAttestationEnabledCallback( - const std::string& challenge, bool enabled) { + const std::string& challenge, + bool register_key, + bool enabled) { if (!enabled) { callback_.Run(false, kDevicePolicyDisabledError); return; @@ -365,11 +371,12 @@ chromeos::attestation::PROFILE_ENTERPRISE_MACHINE_CERTIFICATE, false, // user consent is not required. base::Bind(&EPKPChallengeMachineKey::PrepareKeyCallback, - base::Unretained(this), challenge)); + base::Unretained(this), challenge, register_key)); } -void EPKPChallengeMachineKey::PrepareKeyCallback( - const std::string& challenge, PrepareKeyResult result) { +void EPKPChallengeMachineKey::PrepareKeyCallback(const std::string& challenge, + bool register_key, + PrepareKeyResult result) { if (result != PREPARE_KEY_OK) { callback_.Run(false, base::StringPrintf(kGetCertificateFailedError, result)); @@ -381,17 +388,41 @@ chromeos::attestation::KEY_DEVICE, cryptohome::Identification(), // Not used. kKeyName, GetEnterpriseDomain(), GetDeviceId(), - chromeos::attestation::CHALLENGE_OPTION_NONE, challenge, + register_key ? chromeos::attestation::CHALLENGE_INCLUDE_SIGNED_PUBLIC_KEY + : chromeos::attestation::CHALLENGE_OPTION_NONE, + challenge, base::Bind(&EPKPChallengeMachineKey::SignChallengeCallback, - base::Unretained(this))); + base::Unretained(this), register_key)); } void EPKPChallengeMachineKey::SignChallengeCallback( - bool success, const std::string& response) { + bool register_key, + bool success, + const std::string& response) { if (!success) { callback_.Run(false, kSignChallengeFailedError); return; } + if (register_key) { + async_caller_->TpmAttestationRegisterKey( + chromeos::attestation::KEY_DEVICE, + cryptohome::Identification(), // Not used. + kKeyName, + base::Bind(&EPKPChallengeMachineKey::RegisterKeyCallback, + base::Unretained(this), response)); + } else { + RegisterKeyCallback(response, true, cryptohome::MOUNT_ERROR_NONE); + } +} + +void EPKPChallengeMachineKey::RegisterKeyCallback( + const std::string& response, + bool success, + cryptohome::MountError return_code) { + if (!success || return_code != cryptohome::MOUNT_ERROR_NONE) { + callback_.Run(false, kKeyRegistrationFailedError); + return; + } callback_.Run(true, response); } @@ -579,7 +610,7 @@ base::Closure task = base::Bind( &EPKPChallengeMachineKey::DecodeAndRun, base::Unretained(impl_), scoped_refptr<UIThreadExtensionFunction>(AsUIThreadExtensionFunction()), - callback, params->challenge); + callback, params->challenge, false); content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, task); return RespondLater(); }
diff --git a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h index 2923b34..99e0b998 100644 --- a/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h +++ b/chrome/browser/extensions/api/enterprise_platform_keys_private/enterprise_platform_keys_private_api.h
@@ -163,6 +163,7 @@ class EPKPChallengeMachineKey : public EPKPChallengeKeyBase { public: static const char kGetCertificateFailedError[]; + static const char kKeyRegistrationFailedError[]; static const char kNonEnterpriseDeviceError[]; EPKPChallengeMachineKey(); @@ -177,21 +178,30 @@ // context. void Run(scoped_refptr<UIThreadExtensionFunction> caller, const ChallengeKeyCallback& callback, - const std::string& encoded_challenge); + const std::string& encoded_challenge, + bool register_key); // Like |Run| but expects a Base64 |encoded_challenge|. void DecodeAndRun(scoped_refptr<UIThreadExtensionFunction> caller, const ChallengeKeyCallback& callback, - const std::string& encoded_challenge); + const std::string& encoded_challenge, + bool register_key); private: static const char kKeyName[]; void GetDeviceAttestationEnabledCallback(const std::string& challenge, + bool register_key, bool enabled); void PrepareKeyCallback(const std::string& challenge, + bool register_key, PrepareKeyResult result); - void SignChallengeCallback(bool success, const std::string& response); + void SignChallengeCallback(bool register_key, + bool success, + const std::string& response); + void RegisterKeyCallback(const std::string& response, + bool success, + cryptohome::MountError return_code); }; class EPKPChallengeUserKey : public EPKPChallengeKeyBase {
diff --git a/chrome/browser/extensions/display_info_provider_chromeos.cc b/chrome/browser/extensions/display_info_provider_chromeos.cc index d981386..a7dc13e 100644 --- a/chrome/browser/extensions/display_info_provider_chromeos.cc +++ b/chrome/browser/extensions/display_info_provider_chromeos.cc
@@ -352,19 +352,19 @@ return false; } - if (!display_manager->SetDisplayMode(id, new_mode)) { + // If it's the internal display, the display mode will be applied directly, + // otherwise a confirm/revert notification will be prepared first, and the + // display mode will be applied. If the user accepts the mode change by + // dismissing the notification, StoreDisplayPrefs() will be called back to + // persist the new preferences. + if (!ash::Shell::Get() + ->resolution_notification_controller() + ->PrepareNotificationAndSetDisplayMode( + id, current_mode, new_mode, + base::Bind(&chromeos::StoreDisplayPrefs))) { *error = "Unable to set the display mode."; return false; } - - if (!display::Display::IsInternalDisplayId(id)) { - // For external displays, show a notification confirming the resolution - // change. - ash::Shell::Get() - ->resolution_notification_controller() - ->PrepareNotification(id, current_mode, new_mode, - base::Bind(&chromeos::StoreDisplayPrefs)); - } } return true; }
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 7ebc199..9c75bce 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc
@@ -2453,6 +2453,13 @@ const char kEnableAutofillCreditCardLastUsedDateDisplayDescription[] = "If enabled, display the last used date of a credit card in autofill."; +const char kEnableAutofillCreditCardUploadCvcPrompt[] = + "Enable requesting missing CVC during Autofill credit card upload"; + +const char kEnableAutofillCreditCardUploadCvcPromptDescription[] = + "If enabled, requests missing CVC when offering to upload credit cards to " + "Google Payments."; + #if !defined(OS_ANDROID) && defined(GOOGLE_CHROME_BUILD) const char kGoogleBrandedContextMenuName[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index a09ecf6..0c08081 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h
@@ -2664,6 +2664,14 @@ // card in autofill. extern const char kEnableAutofillCreditCardLastUsedDateDisplayDescription[]; +// Name for the flag to enable requesting CVC in the credit card upload "offer +// to save" bubble if it was not already detected in the submitted form. +extern const char kEnableAutofillCreditCardUploadCvcPrompt[]; + +// Description for the flag to enable requesting CVC in the credit card upload +// offer to save bubble if it was not detected during the checkout flow. +extern const char kEnableAutofillCreditCardUploadCvcPromptDescription[]; + #if !defined(OS_ANDROID) && defined(GOOGLE_CHROME_BUILD) // Name for the flag to enable Google branding in the context menu.
diff --git a/chrome/browser/resources/chromeos/login/encryption_migration.css b/chrome/browser/resources/chromeos/login/encryption_migration.css index 41bd2f87..7b948b0d 100644 --- a/chrome/browser/resources/chromeos/login/encryption_migration.css +++ b/chrome/browser/resources/chromeos/login/encryption_migration.css
@@ -2,13 +2,48 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ +oobe-dialog { + max-width: 768px; +} + +oobe-dialog div { + line-height: 1.54; /* 20px height for 13px font. */ +} + paper-progress { --paper-progress-active-color: var(--google-blue-500); - bottom: 0; height: 3px; + margin-bottom: 28px; width: 100%; } .bottom-buttons { -webkit-padding-end: 16px; } + +oobe-text-button { + color: rgb(90, 90, 90); +} + +oobe-dialog > iron-icon { + height: 32px; + width: 32px; +} + +oobe-dialog > iron-icon.warning { + --iron-icon-fill-color: rgb(219, 68, 55); +} + +.chrome-logo { + background: url(chrome://oobe/product-logo.png) center/100% no-repeat; +} + +.footer div { + color: rgb(51, 51, 51); + font-size: 13px; +} + +.footer div.warning { + color: rgb(197, 57, 41); + font-weight: 500; +}
diff --git a/chrome/browser/resources/chromeos/login/encryption_migration.html b/chrome/browser/resources/chromeos/login/encryption_migration.html index 24b4951..95ab26e9 100644 --- a/chrome/browser/resources/chromeos/login/encryption_migration.html +++ b/chrome/browser/resources/chromeos/login/encryption_migration.html
@@ -2,6 +2,8 @@ Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> +<link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-progress/paper-progress.html"> <link rel="import" href="chrome://resources/polymer/v1_0/paper-styles/color.html"> @@ -12,95 +14,106 @@ <link rel="stylesheet" href="encryption_migration.css"> <link rel="stylesheet" href="oobe_dialog_parameters.css"> <template is="dom-if" if="[[isInitial_(uiState)]]"> - <oobe-dialog tabindex="0" has-buttons> - <p>Checking the system...</p> - </oobe-dialog> + <oobe-dialog tabindex="0" has-buttons></oobe-dialog> </template> <template is="dom-if" if="[[isReady_(uiState)]]"> <oobe-dialog tabindex="0" has-buttons> + <iron-icon class="oobe-icon chrome-logo"></iron-icon> <div class="header"> - <h1 class="title">Your file system needs upgrade</h1> - <div class="subtitle"> - Your file system needs to be upgraded to use Android applications. - String TBD. - </div> + <h1 class="title">$i18n{migrationReadyTitle}</h1> + <div class="subtitle">$i18n{migrationReadyDescription}</div> </div> - <template is="dom-if" if="[[!isEnoughBattery]]"> - <div class="footer flex layout vertical"> - <div>Battery too low for update. <span>[[batteryPercent]]</span>% - </div> - <div>Please plug your Chromebook into a power source.</div> + <template is="dom-if" if="[[isEnoughBattery]]"> + <div class="footer layout vertical center"> + <img srcset="images/security_update_1x.png 1x, + images/security_update_2x.png 2x"> </div> </template> + <template is="dom-if" if="[[!isEnoughBattery]]"> + <div class="footer layout vertical"> + <div class="warning"> + [[computeBatteryWarningLabel_(batteryPercent)]] + </div> + <template is="dom-if" if="[[isCharging]]"> + <div>$i18n{migrationChargingLabel}</div> + </template> + <template is="dom-if" if="[[!isCharging]]"> + <div>$i18n{migrationAskChargeMessage}</div> + </template> + <div>[[computeNecessaryBatteryLevelLabel_()]]</div> + </div> + </template> + </div> <template is="dom-if" if="[[!isResuming]]"> <div class="bottom-buttons flex layout horizontal"> <div class="flex"></div> - <oobe-text-button on-tap="onSkip_">Skip</oobe-text-button> + <oobe-text-button border on-tap="onSkip_"> + <div>$i18n{migrationButtonSkip}</div> + </oobe-text-button> <oobe-text-button inverse on-tap="onUpgrade_" - disabled="[[isMigrationAccepted]]">Upgrade</oobe-text-button> + disabled="[[isMigrationAccepted]]"> + <div>$i18n{migrationButtonUpdate}</div> + </oobe-text-button> </div> </template> </oobe-dialog> </template> <template is="dom-if" if="[[isMigrating_(uiState)]]"> <oobe-dialog tabindex="0"> + <iron-icon class="oobe-icon chrome-logo"></iron-icon> <div class="header"> - <h1 class="title">Upgrading...</h1> - <div class="subtitle">Please do not shutdown. String TBD.</div> + <h1 class="title">$i18n{migrationMigratingTitle}</h1> + <div class="subtitle">$i18n{migrationMigratingDescription}</div> </div> <div class="footer flex layout vertical"> <paper-progress value="[[progress]]" max="1" step="0.001" indeterminate="[[isProgressIndeterminate_(progress)]]"> </paper-progress> - </div> - </oobe-dialog> - </template> - <template is="dom-if" if="[[isMigrationSucceeded_(uiState)]]"> - <oobe-dialog tabindex="0" has-buttons> - <div class="header"> - <h1 class="title">Upgrade successfully finished</h1> - <div class="subtitle"> - The system will restart automatically. String TBD. - </div> - </div> - <div class="bottom-buttons flex layout horizontal"> - <div class="flex"></div> - <oobe-text-button inverse on-tap="onRestart_">Restart Now - </oobe-text-button> + <template is="dom-if" if="[[!isProgressIndeterminate_(progress)]]"> + <div>[[computeProgressLabel_(progress)]]</div> + </template> </div> </oobe-dialog> </template> <template is="dom-if" if="[[isMigrationFailed_(uiState)]]"> <oobe-dialog tabindex="0" has-buttons> + <iron-icon icon="cr:warning" class="oobe-icon warning"></iron-icon> <div class="header"> - <h1 class="title">Upgrade failed</h1> - <div class="subtitle"> - The system encountered a problem during migration. String TBD. - </div> + <h1 class="title">$i18n{migrationFailedTitle}</h1> + <div class="subtitle">$i18n{migrationFailedSubtitle}</div> </div> + <div class="footer"><div>$i18n{migrationFailedMessage}</div></div> <div class="bottom-buttons flex layout horizontal"> <div class="flex"></div> - <oobe-text-button inverse on-tap="onRestart_">Restart + <oobe-text-button inverse on-tap="onRestart_"> + <div>$i18n{migrationButtonRestart}</div> </oobe-text-button> </div> </oobe-dialog> </template> <template is="dom-if" if="[[isNotEnoughSpace_(uiState)]]"> <oobe-dialog tabindex="0" has-buttons> + <iron-icon class="oobe-icon chrome-logo"></iron-icon> <div class="header"> - <h1 class="title">Not enough storage for update</h1> - <div class="subtitle"> - Please free up some space on your device and sign in again. - </div> + <h1 class="title">$i18n{migrationReadyTitle}</h1> + <div class="subtitle">$i18n{migrationReadyDescription}</div> + </div> + <div class="footer layout vertical"> + <div class="warning">$i18n{migrationNospaceWarningLabel}</div> + <div>$i18n{migrationAskFreeSpaceMessage}</div> + <div>[[computeAvailableSpaceLabel_(availableSpaceInString)]]</div> + <div>[[computeNecessarySpaceLabel_(necessarySpaceInString)]]</div> </div> <div class="bottom-buttons flex layout horizontal"> <div class="flex"></div> <template is="dom-if" if="[[!isResuming]]"> - <oobe-text-button inverse on-tap="onSkip_">Continue + <oobe-text-button inverse on-tap="onSkip_"> + <div>$i18n{migrationButtonContinue}</div> </oobe-text-button> </template> <template is="dom-if" if="[[isResuming]]"> - <oobe-text-button inverse on-tap="onRestart_">Restart + <oobe-text-button inverse on-tap="onRestart_"> + <div>$i18n{migrationButtonRestart}</div> </oobe-text-button> </template> </div>
diff --git a/chrome/browser/resources/chromeos/login/encryption_migration.js b/chrome/browser/resources/chromeos/login/encryption_migration.js index 494176a..1ca09d2 100644 --- a/chrome/browser/resources/chromeos/login/encryption_migration.js +++ b/chrome/browser/resources/chromeos/login/encryption_migration.js
@@ -16,14 +16,15 @@ INITIAL: 0, READY: 1, MIGRATING: 2, - MIGRATION_SUCCEEDED: 3, - MIGRATION_FAILED: 4, - NOT_ENOUGH_SPACE: 5 + MIGRATION_FAILED: 3, + NOT_ENOUGH_SPACE: 4 }; Polymer({ is: 'encryption-migration', + behaviors: [I18nBehavior], + properties: { /** * Current UI state which corresponds to a sub step in migration process. @@ -68,12 +69,36 @@ }, /** + * True if the device is charging. + */ + isCharging: { + type: Boolean, + value: false + }, + + /** * True if the user already accepted the migration. */ isMigrationAccepted: { type: Boolean, value: false }, + + /** + * Formatted string of the current available space size. + */ + availableSpaceInString: { + type: String, + value: '' + }, + + /** + * Formatted string of the necessary space size for migration. + */ + necessarySpaceInString: { + type: String, + value: '' + }, }, /** @@ -104,15 +129,6 @@ }, /** - * Returns true if the migration is finished successfully. - * @param {EncryptionMigrationUIState} state Current UI state - * @private - */ - isMigrationSucceeded_: function(state) { - return state == EncryptionMigrationUIState.MIGRATION_SUCCEEDED; - }, - - /** * Returns true if the migration failed. * @param {EncryptionMigrationUIState} state Current UI state * @private @@ -140,6 +156,55 @@ }, /** + * Computes the label shown under progress bar. + * @param {number} progress + * @return {string} + * @private + */ + computeProgressLabel_: function(progress) { + return this.i18n('migrationProgressLabel', Math.floor(progress * 100)); + }, + + /** + * Computes the warning label when battery level is not enough. + * @param {number} batteryPercent + * @return {string} + * @private + */ + computeBatteryWarningLabel_: function(batteryPercent) { + return this.i18n('migrationBatteryWarningLabel', batteryPercent); + }, + + /** + * Computes the label to show the necessary battery level for migration. + * @return {string} + * @private + */ + computeNecessaryBatteryLevelLabel_: function() { + return this.i18n('migrationNecessaryBatteryLevelLabel', 30); + }, + + /** + * Computes the label to show the current available space. + * @param {string} availableSpaceInString + * @return {string} + * @private + */ + computeAvailableSpaceLabel_: function(availableSpaceInString) { + return this.i18n('migrationAvailableSpaceLabel', availableSpaceInString); + }, + + /** + * Computes the label to show the necessary space to start migration. + * @param {string} necessarySpaceInString + * @return {string} + * @private + */ + computeNecessarySpaceLabel_: function(necessarySpaceInString) { + return this.i18n('migrationNecessarySpaceLabel', necessarySpaceInString); + }, + + /** * Handles tap on UPGRADE button. * @private */
diff --git a/chrome/browser/resources/chromeos/login/images/security_update_1x.png b/chrome/browser/resources/chromeos/login/images/security_update_1x.png new file mode 100644 index 0000000..ce542da --- /dev/null +++ b/chrome/browser/resources/chromeos/login/images/security_update_1x.png Binary files differ
diff --git a/chrome/browser/resources/chromeos/login/images/security_update_2x.png b/chrome/browser/resources/chromeos/login/images/security_update_2x.png new file mode 100644 index 0000000..56b01d4 --- /dev/null +++ b/chrome/browser/resources/chromeos/login/images/security_update_2x.png Binary files differ
diff --git a/chrome/browser/resources/chromeos/login/oobe_dialog.js b/chrome/browser/resources/chromeos/login/oobe_dialog.js index fc5ad21..7dc9d22 100644 --- a/chrome/browser/resources/chromeos/login/oobe_dialog.js +++ b/chrome/browser/resources/chromeos/login/oobe_dialog.js
@@ -39,7 +39,7 @@ */ show: function() { var focusedElements = this.getElementsByClassName('focus-on-show'); - if (focusedElements) + if (focusedElements.length > 0) focusedElements[0].focus(); this.fire('show-dialog');
diff --git a/chrome/browser/resources/chromeos/login/screen_encryption_migration.js b/chrome/browser/resources/chromeos/login/screen_encryption_migration.js index 200ede4..70740d3 100644 --- a/chrome/browser/resources/chromeos/login/screen_encryption_migration.js +++ b/chrome/browser/resources/chromeos/login/screen_encryption_migration.js
@@ -9,7 +9,9 @@ 'setUIState', 'setMigrationProgress', 'setIsResuming', - 'setBatteryPercent', + 'setBatteryState', + 'setAvailableSpaceInString', + 'setNecessarySpaceInString', ], /** @@ -73,11 +75,29 @@ * Updates battery level of the device. * @param {number} batteryPercent Battery level in percent. * @param {boolean} isEnoughBattery True if the battery is enough. + * @param {boolena} isCharging True if the device is connected to power. */ - setBatteryPercent: function(batteryPercent, isEnoughBattery) { - $('encryption-migration-element').batteryPercent = - Math.floor(batteryPercent); - $('encryption-migration-element').isEnoughBattery = isEnoughBattery; + setBatteryState: function(batteryPercent, isEnoughBattery, isCharging) { + var element = $('encryption-migration-element'); + element.batteryPercent = Math.floor(batteryPercent); + element.isEnoughBattery = isEnoughBattery; + element.isCharging = isCharging; + }, + + /** + * Updates the string representation of available space size. + * @param {string} space + */ + setAvailableSpaceInString: function(space) { + $('encryption-migration-element').availableSpaceInString = space; + }, + + /** + * Updates the string representation of necessary space size. + * @param {string} space + */ + setNecessarySpaceInString: function(space) { + $('encryption-migration-element').necessarySpaceInString = space; }, }; });
diff --git a/chrome/browser/resources/settings/icons.html b/chrome/browser/resources/settings/icons.html index 2b8a0168..93497c7 100644 --- a/chrome/browser/resources/settings/icons.html +++ b/chrome/browser/resources/settings/icons.html
@@ -69,7 +69,6 @@ </if> <g id="help-outline"><path d="M11 18h2v-2h-2v2zm1-16C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm0-14c-2.21 0-4 1.79-4 4h2c0-1.1.9-2 2-2s2 .9 2 2c0 2-3 1.75-3 5h2c0-2.25 3-2.5 3-5 0-2.21-1.79-4-4-4z"></path></g> <g id="info"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"></path></g> - <g id="input"><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3z"></path></g> <if expr="chromeos"> <g id="keyboard"><path d="M20 5H4c-1.1 0-1.99.9-1.99 2L2 17c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm-9 3h2v2h-2V8zm0 3h2v2h-2v-2zM8 8h2v2H8V8zm0 3h2v2H8v-2zm-1 2H5v-2h2v2zm0-3H5V8h2v2zm9 7H8v-2h8v2zm0-4h-2v-2h2v2zm0-3h-2V8h2v2zm3 3h-2v-2h2v2zm0-3h-2V8h2v2z"></path></g> </if> @@ -83,6 +82,7 @@ <g id="lock"><path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"></path></g> </if> <g id="mic"><path d="M12 14c1.66 0 2.99-1.34 2.99-3L15 5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3zm5.3-3c0 3-2.54 5.1-5.3 5.1S6.7 14 6.7 11H5c0 3.41 2.72 6.23 6 6.72V21h2v-3.28c3.28-.48 6-3.3 6-6.72h-1.7z"></path></g> + <g id="midi"><path d="M21,19.1H3V5h18V19.1z M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z"></path><path fill="none" d="M21,19.1H3V5h18V19.1z M21,3H3C1.9,3,1,3.9,1,5v14c0,1.1,0.9,2,2,2h18c1.1,0,2-0.9,2-2V5C23,3.9,22.1,3,21,3z"></path><path d="M14,5h3v8h-3V5z"></path><path d="M15,12h1v8h-1V12z"></path><path fill="none" d="M0,0h24v24H0V0z"></path><rect x="7" y="5" width="3" height="8"></rect><rect x="8" y="12" width="1" height="8"></rect></g> <if expr="chromeos"> <g id="mouse"><path d="M13 1.07V9h7c0-4.08-3.05-7.44-7-7.93zM4 15c0 4.42 3.58 8 8 8s8-3.58 8-8v-4H4v4zm7-13.93C7.05 1.56 4 4.92 4 9h7V1.07z"></path></g> </if>
diff --git a/chrome/browser/resources/settings/internet_page/network_summary_item.js b/chrome/browser/resources/settings/internet_page/network_summary_item.js index 23f7b60c..377c807 100644 --- a/chrome/browser/resources/settings/internet_page/network_summary_item.js +++ b/chrome/browser/resources/settings/internet_page/network_summary_item.js
@@ -159,12 +159,17 @@ * @private */ onShowDetailsTap_: function(event) { - if (this.shouldShowList_()) + if (!this.deviceIsEnabled_(this.deviceState)) { + this.fire( + 'device-enabled-toggled', + {enabled: true, type: this.deviceState.Type}); + } else if (this.shouldShowList_()) { this.fire('show-networks', this.deviceState); - else if (this.activeNetworkState.GUID) + } else if (this.activeNetworkState.GUID) { this.fire('show-detail', this.activeNetworkState); - else if (this.networkStateList.length > 0) + } else if (this.networkStateList.length > 0) { this.fire('show-detail', this.networkStateList[0]); + } event.stopPropagation(); },
diff --git a/chrome/browser/resources/settings/privacy_page/privacy_page.html b/chrome/browser/resources/settings/privacy_page/privacy_page.html index 7b23e1a..b6e0dce6 100644 --- a/chrome/browser/resources/settings/privacy_page/privacy_page.html +++ b/chrome/browser/resources/settings/privacy_page/privacy_page.html
@@ -287,7 +287,7 @@ <category-setting-exceptions category="{{ContentSettingsTypes.COOKIES}}"> </category-setting-exceptions> - <site-data></site-data> + <site-data focus-config="[[focusConfig_]]"></site-data> </settings-subpage> </template> <template is="dom-if" route-path="/content/images" no-search>
diff --git a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js index 5e71132e..3da7e7a 100644 --- a/chrome/browser/resources/settings/settings_page/settings_animated_pages.js +++ b/chrome/browser/resources/settings/settings_page/settings_animated_pages.js
@@ -59,10 +59,18 @@ if (!this.focusConfig || !this.previousRoute_) return; + // Don't attempt to focus any anchor element, unless last navigation was a + // 'pop' (backwards) navigation. + if (!settings.lastRouteChangeWasPopstate()) + return; + // Only handle iron-select events from neon-animatable elements and the - // SITE_SETTINGS subpage only. + // given whitelist of settings-subpage instances. if (!e.detail.item.matches( - 'neon-animatable, settings-subpage#site-settings')) { + 'neon-animatable, ' + + 'settings-subpage#site-settings, ' + + 'settings-subpage[route-path=\"' + + settings.Route.SITE_SETTINGS_COOKIES.path + '\"]')) { return; }
diff --git a/chrome/browser/resources/settings/site_settings/site_data.js b/chrome/browser/resources/settings/site_settings/site_data.js index 83e9ac0..d7dca1a 100644 --- a/chrome/browser/resources/settings/site_settings/site_data.js +++ b/chrome/browser/resources/settings/site_settings/site_data.js
@@ -34,6 +34,30 @@ /** @private */ confirmationDeleteMsg_: String, + + /** @type {!Map<string, string>} */ + focusConfig: { + type: Object, + observer: 'focusConfigChanged_', + }, + }, + + /** + * @param {!Map<string, string>} newConfig + * @param {?Map<string, string>} oldConfig + * @private + */ + focusConfigChanged_: function(newConfig, oldConfig) { + // focusConfig is set only once on the parent, so this observer should only + // fire once. + assert(!oldConfig); + + // Populate the |focusConfig| map of the parent <settings-animated-pages> + // element, with additional entries that correspond to subpage trigger + // elements residing in this element's Shadow DOM. + this.focusConfig.set( + settings.Route.SITE_SETTINGS_DATA_DETAILS.path, + '* /deep/ #filter /deep/ #searchInput'); }, /** @override */
diff --git a/chrome/browser/resources/settings/site_settings/site_details.html b/chrome/browser/resources/settings/site_settings/site_details.html index 029e887b..d65bdd6 100644 --- a/chrome/browser/resources/settings/site_settings/site_details.html +++ b/chrome/browser/resources/settings/site_settings/site_details.html
@@ -83,7 +83,7 @@ label="$i18n{siteSettingsNotifications}" site="[[site]]"> </site-details-permission> <site-details-permission category="{{ContentSettingsTypes.JAVASCRIPT}}" - icon="settings:input" id="javascript" + icon="settings:code" id="javascript" label="$i18n{siteSettingsJavascript}" site="[[site]]"> </site-details-permission> <site-details-permission category="{{ContentSettingsTypes.PLUGINS}}"
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html index 8451c84..17cfd6e 100644 --- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.html +++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.html
@@ -119,7 +119,7 @@ category$="[[ContentSettingsTypes.JAVASCRIPT]]" data-route="SITE_SETTINGS_JAVASCRIPT" on-tap="onTapNavigate_" actionable> - <iron-icon icon="settings:input"></iron-icon> + <iron-icon icon="settings:code"></iron-icon> <div class="middle"> $i18n{siteSettingsJavascript} <div class="secondary" id="javascriptSecondary"> @@ -283,7 +283,7 @@ category$="[[ContentSettingsTypes.MIDI_DEVICES]]" data-route="SITE_SETTINGS_MIDI_DEVICES" on-tap="onTapNavigate_" actionable> - <iron-icon icon="settings:music-note"></iron-icon> + <iron-icon icon="settings:midi"></iron-icon> <div class="middle"> $i18n{siteSettingsMidiDevices} <div class="secondary" id="midiDevicesSecondary">
diff --git a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js index 5c489d72..a3d54f2 100644 --- a/chrome/browser/resources/settings/site_settings_page/site_settings_page.js +++ b/chrome/browser/resources/settings/site_settings_page/site_settings_page.js
@@ -49,7 +49,6 @@ } }, - /** @type {!Map<string, string>} */ focusConfig: { type: Object,
diff --git a/chrome/browser/resources/vr_shell/OWNERS b/chrome/browser/resources/vr_shell/OWNERS deleted file mode 100644 index f104584a8..0000000 --- a/chrome/browser/resources/vr_shell/OWNERS +++ /dev/null
@@ -1,6 +0,0 @@ -bshe@chromium.org -girard@chromium.org -mthiesse@chromium.org -cjgrant@chromium.org - -# COMPONENT: UI>Browser>VR
diff --git a/chrome/browser/resources/vr_shell/compiled_resources2.gyp b/chrome/browser/resources/vr_shell/compiled_resources2.gyp deleted file mode 100644 index 3c01fc12dc..0000000 --- a/chrome/browser/resources/vr_shell/compiled_resources2.gyp +++ /dev/null
@@ -1,37 +0,0 @@ -# Copyright 2016 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. -{ - 'targets': [ - { - 'target_name': 'vr_shell_ui_api', - 'dependencies': [ - '<(EXTERNS_GYP):chrome_send', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { - 'target_name': 'vr_shell_ui_scene', - 'dependencies': [ - 'vr_shell_ui_api', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { - 'target_name': 'vk', - 'dependencies': [ - 'vr_shell_ui_api', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - { - 'target_name': 'vr_shell_ui', - 'dependencies': [ - 'vr_shell_ui_api', - 'vr_shell_ui_scene', - 'vk', - ], - 'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'], - }, - ], -}
diff --git a/chrome/browser/resources/vr_shell/vk.css b/chrome/browser/resources/vr_shell/vk.css deleted file mode 100644 index 4604f4d0..0000000 --- a/chrome/browser/resources/vr_shell/vk.css +++ /dev/null
@@ -1,124 +0,0 @@ -/* Copyright 2017 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -.inputview-container { - float: left; - font-family: Roboto2, Roboto, 'Noto Sans', sans-serif; - height: 340px; - user-select: none; - width: 1228px; -} - -.inputview-spacerview { - display: inline-block; - float: left; - overflow: hidden; - padding: 32px; - position: relative; -} - -.inputview-layoutview { - background-color: rgb(71, 71, 71); - border: 2px solid rgb(128, 128, 128); - border-radius: 10px; - display: inline-block; - float: left; - overflow: hidden; - padding: 32px; - position: relative; -} - -.inputview-softkey-view { - box-sizing: border-box; - display: inline-block; - overflow: hidden; - padding: 3px 2px; -} - -.inputview-softkey { - background-color: rgb(71, 71, 71); - border-radius: 5px; - display: block; - height: 100%; - position: relative; - width: 100%; -} - -.inputview-softkey:hover { - background-color: rgb(110, 112, 114); -} - -.inputview-softkey:active { - background-color: rgb(90, 93, 95); -} - -.inputview-softkey-spacer:hover { - background-color: inherit; -} - -.inputview-ch { - color: rgba(236, 236, 236, 0.8); - display: inline-block; - font-size: 30px; - position: absolute; - text-align: center; - top: 15%; - width: 100%; -} - -.inputview-ch { - display: block; -} - -.inputview-level { - display: none; -} - -.inputview-active-level-0 .inputview-level-0, -.inputview-active-level-1 .inputview-level-1, -.inputview-active-level-2 .inputview-level-2, -.inputview-active-level-3 .inputview-level-3 { - display: block; -} - -.vk-icon { - display: block; - left: 50%; - position: absolute; - top: 50%; -} - -.vk-escape-icon { - background: url(../../../../ui/webui/resources/images/hidekeyboard.svg) - transparent no-repeat 0 0/32px 32px; - height: 32px; - margin: -16px 0 0 -16px; - width: 32px; -} - -.vk-backspace-icon { - background: url(../../../../ui/webui/resources/images/backspace.svg) - transparent no-repeat 0 0/24px 24px; - height: 24px; - margin: -12px 0 0 -12px; - width: 24px; -} - -.vk-space-icon { - background-color: rgba(165, 161, 169, 0.35); - border-radius: 40px; - height: 100%; - left: 0; - top: 0; - width: 100%; -} - -.inputview-code-space:hover { - background-color: rgba(165, 161, 169, 0.35); - border-radius: 40px; -} -.inputview-code-space:active { - background-color: rgb(90, 93, 95); -} -
diff --git a/chrome/browser/resources/vr_shell/vk.js b/chrome/browser/resources/vr_shell/vk.js deleted file mode 100644 index 6a47eed..0000000 --- a/chrome/browser/resources/vr_shell/vk.js +++ /dev/null
@@ -1,439 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var vrShellVK = (function() { - /** @const */ var VK_L_PANEL_LAYOUT = [ - [ - {'code': 'Key1', 'key': '1'}, - {'code': 'Key2', 'key': '2'}, - {'code': 'Key3', 'key': '3'}, - ], - [ - {'code': 'Key4', 'key': '4'}, - {'code': 'Key5', 'key': '5'}, - {'code': 'Key6', 'key': '6'}, - ], - [ - {'code': 'Key7', 'key': '7'}, - {'code': 'Key8', 'key': '8'}, - {'code': 'Key9', 'key': '9'}, - ], - [ - {'code': 'KeyNegative', 'key': '-'}, - {'code': 'Key0', 'key': '0'}, - {'code': 'KeyPoint', 'key': '.'}, - ] - ]; - - /** @const */ var VK_R_PANEL_LAYOUT = [ - [ - { - 'code': 'Backspace', - 'key': 'Backspace', - 'image-class': 'vk-backspace-icon' - }, - ], - [ - { - 'code': 'Enter', - 'key': 'Enter', - 'height': 2.0, - 'text' : '\u{021B2}', - }, - ], - [ - { - 'code': 'Abort', - 'key': 'Escape', - 'image-class': 'vk-escape-icon', - 'action': vkHide - }, - ], - ]; - - /** @const */ var VK_LAYOUTS = { - 'en-us-compact': { - 'levels': [ - [ - // Level 0: Unshifted. - [ - {'code': 'KeyQ', 'key': 'q'}, - {'code': 'KeyW', 'key': 'w'}, - {'code': 'KeyE', 'key': 'e'}, - {'code': 'KeyR', 'key': 'r'}, - {'code': 'KeyT', 'key': 't'}, - {'code': 'KeyY', 'key': 'y'}, - {'code': 'KeyU', 'key': 'u'}, - {'code': 'KeyI', 'key': 'i'}, - {'code': 'KeyO', 'key': 'o'}, - {'code': 'KeyP', 'key': 'p'}, - ], - [ - {'width': 0.5, 'display': 'spacer'}, - {'code': 'KeyA', 'key': 'a'}, - {'code': 'KeyS', 'key': 's'}, - {'code': 'KeyD', 'key': 'd'}, - {'code': 'KeyF', 'key': 'f'}, - {'code': 'KeyG', 'key': 'g'}, - {'code': 'KeyH', 'key': 'h'}, - {'code': 'KeyJ', 'key': 'j'}, - {'code': 'KeyK', 'key': 'k'}, - {'code': 'KeyL', 'key': 'l'}, - ], - [ - { - 'code': 'ShiftLeft', - 'key': 'Shift', - 'text' : '\u{021E7}', - 'action': vkLevel, - 'level': 1 - }, - {'code': 'KeyZ', 'key': 'z'}, - {'code': 'KeyX', 'key': 'x'}, - {'code': 'KeyC', 'key': 'c'}, - {'code': 'KeyV', 'key': 'v'}, - {'code': 'KeyB', 'key': 'b'}, - {'code': 'KeyN', 'key': 'n'}, - {'code': 'KeyM', 'key': 'm'}, - {'code': '', 'key': '!'}, - {'code': '', 'key': '?'}, - ], - [ - { - 'code': 'AltRight', - 'key': 'AltGraph', - 'text': '=\\<', - 'action': vkLevel, - 'level': 2 - }, - {'code': 'Slash', 'key': '/'}, - { - 'code': 'space', - 'key': ' ', - 'width': 6.00, - 'image-class': 'vk-space-icon' - }, - {'code': 'Comma', 'key': ','}, - {'code': 'Period', 'key': '.'}, - ] - ], - [ - // Level 1: Shifted. - [ - {'code': 'KeyQ', 'key': 'Q'}, - {'code': 'KeyW', 'key': 'W'}, - {'code': 'KeyE', 'key': 'E'}, - {'code': 'KeyR', 'key': 'R'}, - {'code': 'KeyT', 'key': 'T'}, - {'code': 'KeyY', 'key': 'Y'}, - {'code': 'KeyU', 'key': 'U'}, - {'code': 'KeyI', 'key': 'I'}, - {'code': 'KeyO', 'key': 'O'}, - {'code': 'KeyP', 'key': 'P'}, - ], - [ - {'width': 0.5, 'display': 'spacer'}, - {'code': 'KeyA', 'key': 'A'}, - {'code': 'KeyS', 'key': 'S'}, - {'code': 'KeyD', 'key': 'D'}, - {'code': 'KeyF', 'key': 'F'}, - {'code': 'KeyG', 'key': 'G'}, - {'code': 'KeyH', 'key': 'H'}, - {'code': 'KeyJ', 'key': 'J'}, - {'code': 'KeyK', 'key': 'K'}, - {'code': 'KeyL', 'key': 'L'}, - ], - [ - { - 'code': 'ShiftLeft', - 'key': 'Shift', - 'text' : '\u{021E7}', - 'action': vkLevel, - 'level': 0, - }, - {'code': 'KeyZ', 'key': 'Z'}, - {'code': 'KeyX', 'key': 'X'}, - {'code': 'KeyC', 'key': 'C'}, - {'code': 'KeyV', 'key': 'V'}, - {'code': 'KeyB', 'key': 'B'}, - {'code': 'KeyN', 'key': 'N'}, - {'code': 'KeyM', 'key': 'M'}, - {'code': '', 'key': '!'}, - {'code': '', 'key': '?'}, - ], - [ - { - 'code': 'AltRight', - 'key': 'AltGraph', - 'text': '=\\<', - 'action': vkLevel, - 'level': 2 - }, - {'code': 'Slash', 'key': '/'}, - { - 'code': 'space', - 'key': ' ', - 'width': 6.00, - 'image-class': 'vk-space-icon' - }, - {'code': 'Comma', 'key': ','}, - {'code': 'Period', 'key': '.'}, - ] - ], - [ - // Level 2: Symbols. - [ - {'code': 'KeyQ', 'key': '!'}, - {'code': 'KeyW', 'key': '@'}, - {'code': 'KeyE', 'key': '#'}, - {'code': 'KeyR', 'key': '$'}, - {'code': 'KeyT', 'key': '%'}, - {'code': 'KeyY', 'key': '^'}, - {'code': 'KeyU', 'key': '&'}, - {'code': 'KeyI', 'key': '*'}, - {'code': 'KeyO', 'key': '('}, - {'code': 'KeyP', 'key': ')'}, - ], - [ - {'width': 0.5, 'display': 'spacer'}, - {'code': 'KeyA', 'key': '~'}, - {'code': 'KeyS', 'key': '`'}, - {'code': 'KeyD', 'key': '|'}, - {'code': 'KeyF', 'key': '{'}, - {'code': 'KeyG', 'key': '}'}, - {'code': 'KeyH', 'key': '['}, - {'code': 'KeyJ', 'key': ']'}, - {'code': 'KeyK', 'key': '-'}, - {'code': 'KeyL', 'key': '_'}, - ], - [ - {'code': 'ShiftLeft', 'key': '/'}, - {'code': 'KeyZ', 'key': '\\'}, - {'code': 'KeyX', 'key': '+'}, - {'code': 'KeyC', 'key': '='}, - {'code': 'KeyV', 'key': ':'}, - {'code': 'KeyB', 'key': ';'}, - {'code': 'KeyN', 'key': '\''}, - {'code': 'KeyM', 'key': '<'}, - {'key': '>'}, - {'key': '"'}, - ], - [ - { - 'code': 'AltRight', - 'key': 'AltGraph', - 'text': 'ABC', - 'action': vkLevel, - 'level': 0, - }, - {'code': 'Slash', 'key': '/'}, - { - 'code': 'space', - 'key': ' ', - 'width': 6.00, - 'image-class': 'vk-space-icon' - }, - {'code': 'Comma', 'key': ','}, - {'code': 'Period', 'key': '.'}, - ] - ] - ] - } - }; - - /** @const */ var VK_BUTTON_SIZE = [64, 64, 'px']; - - var vkState = {'query': {'language': 'en-us', 'layout': 'compact'}}; - - function vkHide(button) { - // TODO(asimjour): Send a key event to support vkHide. - } - - function vkLevel(button) { - vkCh(button); - vkActivateLevel(button.level); - } - - function vkLevel0(button) { - vkCh(button); - vkActivateLevel(0); - } - - function vkCh(button) { - if (button.key || button.code) { - // This code limits use of the HTML keyboard to the omnibox. - document.querySelector("#omnibox-input-field").focus(); - - if (button.code != 'AltRight') - sendKey('key', button.code, button.key); - if (vkState.level == 1) - vkActivateLevel(0); - } - } - - function vkOnClick(e) { - var button = e.vkButtonData; - button.action(button); - } - - function vkActivateLevel(n) { - vkState.view.classList.remove('inputview-active-level-' + vkState.level); - vkState.level = n; - vkState.view.classList.add('inputview-active-level-' + vkState.level); - } - - function vkNormalizeButtonData(buttonData) { - if (!buttonData.text) { - if (buttonData.key && !buttonData['image-class']) - buttonData.text = buttonData.key; - else - buttonData.text = null; - } - buttonData.action = buttonData.action || vkCh; - buttonData.width = buttonData.width || 1.00; - buttonData.height = buttonData.height || 1.00; - - if (buttonData.display) - buttonData.display = 'inputview-softkey-' + buttonData.display; - else if (buttonData['image-class']) - buttonData.display = 'inputview-softkey-0'; - else if (buttonData.text) - buttonData.display = 'inputview-softkey-1'; - else - buttonData.display = 'inputview-softkey-none'; - - buttonData.styleWidth = - (buttonData.width * VK_BUTTON_SIZE[0]) + VK_BUTTON_SIZE[2]; - buttonData.styleHeight = - (buttonData.height * VK_BUTTON_SIZE[1]) + VK_BUTTON_SIZE[2]; - return buttonData; - } - - function vkMkButton(index, buttonData) { - var button = document.createElement('div'); - - buttonData = vkNormalizeButtonData(buttonData); - button.vkButtonData = buttonData - button.classList.add('inputview-softkey-view'); - button.style.width = buttonData.styleWidth; - button.style.height = buttonData.styleHeight; - button.onclick = function() { - vkOnClick(button); - }; - - var key = document.createElement('div'); - key.classList.add('inputview-softkey', buttonData.display); - if (button.vkButtonData.code) - key.classList.add('inputview-code-' + buttonData.code); - var keyContent = key; - if (button.vkButtonData['image-class']) { - keyContent = document.createElement('div'); - keyContent.classList.add(button.vkButtonData['image-class'], 'vk-icon'); - key.appendChild(keyContent); - } - if (button.vkButtonData.class) - keyContent.classList.add(button.vkButtonData.class); - - if (buttonData.text) { - var charKey = document.createElement('div'); - charKey.classList.add('inputview-ch'); - charKey.textContent = buttonData.text; - keyContent.appendChild(charKey); - } - - button.appendChild(key); - return button; - } - - function vkMkRow(index, rowData) { - var row = document.createElement('div'); - row.id = 'row' + index; - row.classList.add('inputview-row'); - for (var c = 0; c < rowData.length; ++c) - row.appendChild(vkMkButton(c, rowData[c])); - return row; - } - - function vkMkLevel(index, levelData) { - var level = document.createElement('div'); - level.id = 'level' + index; - level.classList.add('inputview-level', 'inputview-level-' + index); - for (var r = 0; r < levelData.length; ++r) - level.appendChild(vkMkRow(r, levelData[r])); - return level; - } - - function vkMkKb(view, layout) { - vkState.view = view; - vkState.layout = layout; - vkState.levels = layout.levels.length; - for (var key = 0; key < layout.levels.length; ++key) - view.appendChild(vkMkLevel(key, vkState.layout.levels[key])); - } - - function vkMkPanel(view, lPanelData) { - for (var r = 0; r < lPanelData.length; ++r) - view.appendChild(vkMkRow(r, lPanelData[r])); - } - - function vkOnLoad() { - // Build keyboard. - vkState.layoutName = vkState.query.language + '-' + vkState.query.layout; - vkMkKb( - document.querySelector('#keyboardView'), - VK_LAYOUTS[vkState.layoutName]); - vkMkPanel(document.querySelector('#numberView'), VK_L_PANEL_LAYOUT); - vkMkPanel(document.querySelector('#specialkeyView'), VK_R_PANEL_LAYOUT); - - vkActivateLevel(0); - } - - // Flag values for ctrl, alt and shift as defined by EventFlags - // in "event_constants.h". - // @enum {number} - var Modifier = {NONE: 0, ALT: 8, CONTROL: 4, SHIFT: 2}; - - /** @const */ var DOM_KEYS = { - 'Backspace': 0x08, - 'Enter': 0x0D, - 'Escape': 0x1B, - 'AltGraph': 0x200103, - }; - - function domKeyValue(key) { - if (key) { - if (typeof key == 'number') - return key; - if (key.length == 1) - return key.charCodeAt(0); - if (DOM_KEYS.hasOwnProperty(key)) - return DOM_KEYS[key]; - } - return 0; - } - - /** - * Dispatches a virtual key event. The system VK does not use the IME - * API as its primary role is to work in conjunction with a non-VK aware - * IME. - */ - function sendKeyEvent(type, ch, code, name, modifiers) { - var event = { - type: type, - charValue: ch, - keyCode: code, - keyName: name, - modifiers: modifiers - }; - api.doAction(api.Action.KEY_EVENT, event); - } - - function sendKey(type, codeName, key) { - sendKeyEvent(type, domKeyValue(key), 0, codeName, 0); - } - - return {vkOnLoad: vkOnLoad}; -})(); - -document.addEventListener('DOMContentLoaded', vrShellVK.vkOnLoad);
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.css b/chrome/browser/resources/vr_shell/vr_shell_ui.css deleted file mode 100644 index 025a3bc3..0000000 --- a/chrome/browser/resources/vr_shell/vr_shell_ui.css +++ /dev/null
@@ -1,350 +0,0 @@ -/* Copyright 2016 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. */ - -html { - background-color: rgba(255, 255, 255, 0); -} - -#ui { - left: 0; - position: absolute; - top: 0; - transform-origin: left top; - width: 1920px; -} - -/* This class manages the position of elements on the texture page. - * Each UI element should have a bounding div of this class. */ -.ui-element { - float: left; - margin: 2px; -} - -.webvr-message-box { - align-items: center; - display: flex; - flex-direction: column; - justify-content: center; -} - -#webvr-not-secure-permanent .webvr-not-secure-icon { - display: inline-block; - margin: 20px 0; - vertical-align: middle; -} - -#webvr-not-secure-permanent .webvr-string { - display: inline-block; - margin: 20px 10.5px; - vertical-align: middle; -} - -/* This is a single-line (nowrap) short message. The width is elastic for - * translations, and the underlying string had a request to translators - * to keep it short. */ -#webvr-not-secure-permanent .webvr-box { - background-color: white; - border-radius: 6px; - box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); - box-sizing: border-box; - color: #444; - font-size: 26px; - height: 78px; - min-width: 226px; - overflow: hidden; - padding: 0 20px; - white-space: nowrap; -} - -/* This uses fixed width but the height is elastic for translations. */ -#webvr-not-secure-transient > div { - background-color: rgba(26, 26, 26, 0.8); - border-radius: 6px; - box-sizing: border-box; - color: white; - display: flex; - flex-direction: column; - font-size: 26px; - justify-content: center; - line-height: 1.4; - min-height: 160px; - overflow: hidden; - padding: 20px; - text-align: center; - width: 512px; -} - -.gesture-indicator { - background-color: rgba(0, 0, 0, 0); - background-position: center; - background-repeat: no-repeat; - background-size: contain; - height: 192px; - margin: auto auto; - width: 192px; -} - -.round-button { - background-color: #fbfbfb; - background-position: center; - background-repeat: no-repeat; - background-size: 70px; - border-radius: 6px; - height: 112px; - margin: auto auto; - opacity: 0.8; - transition: opacity 50ms ease-in-out; - width: 112px; -} - -.button-caption { - color: white; - font-size: 24px; - max-width: 192px; - opacity: 0; - overflow: hidden; - text-align: center; - transition: opacity 50ms ease-in-out; - white-space: nowrap; -} - -.rect-button { - background-color: #eee; - border-radius: 6px; - color: black; - font-size: 20px; - line-height: 96px; - opacity: 0.8; - overflow: hidden; - text-align: center; - text-transform: uppercase; - vertical-align: middle; - white-space: nowrap; - width: 300px; -} - -.rect-button:hover { - opacity: 1; -} - -.disabled-button { - background-color: #bbb; -} - -#back-button, -#forward-button, -#exit-present-button, -#back-indicator, -#forward-indicator { - background-image: url(../../../../ui/webui/resources/images/vr_back.svg); -} - -#reload-button { - background-image: url(../../../../ui/webui/resources/images/vr_reload.svg); -} - -#forward-button, -#forward-indicator { - transform: scaleX(-1); -} - -#reload-ui-button { - --rotX: -0.2; - --rotY: 0; - --rotZ: 0; - --scale: 1; - --tranX: 0; - --tranY: -0.7; - --tranZ: -0.4; - background-color: rgba(255,255,255,0.25); - color: white; - font-size: 24px; - padding: 12px; -} - -#reload-ui-button:hover { - background-color: turquoise; -} - -#content-interceptor { - height: 1px; - opacity: 0; - width: 1px; -} - -.tab { - background-color: #eee; - border-radius: 6px; - color: black; - display: inline-block; - font-size: 20px; - height: 30px; - line-height: 30px; - margin: 0 15px 0 15px; - overflow: hidden; - padding: 12px; - vertical-align: middle; - white-space: nowrap; - width: 300px; -} - -.tab-incognito { - background-color: #525252; - color: white; -} - -/* The tab container element behaves like a scroll view (in conjunction with the - * clip view). */ -#tab-container { - height: 54px; - overflow-x: scroll; - overflow-y: hidden; - white-space: nowrap; - width: 1000px; -} - -/* The tab clip element's width will be programmatically set to the total width - * of all it's children (the tabs). By doing so, the tabs can be scrolled - * horizontally in the tab container element. */ -#tab-clip { - margin: 0 auto; - overflow: hidden; -} - -#url-indicator-container { - --scale: 1.2; - --tranX: 0; - --tranY: -0.75; - --tranZ: -1.8; -} - -#url-indicator-border { - --fadeTimeMs: 200; - --fadeYOffset: -0.05; - --opacity: 0.9; - --statusBarColor: rgb(66, 133, 244); - background-color: #ececec; - border-radius: 6px; - padding: 6px; -} - -#url-indicator { - align-items: center; - background-color: #ececec; - border-radius: 6px; - box-sizing: border-box; - display: flex; - height: 104px; - justify-content: center; - overflow: hidden; - white-space: nowrap; - width: 512px; -} - -#url-indicator-content { - align-items: center; - display: flex; - max-width: 448px; -} - -.url-indicator-icon { - -webkit-mask-size: 50px; - display: none; - flex: none; - height: 50px; - margin-right: 10px; - width: 50px; -} - -#url-indicator-info-icon { - -webkit-mask-image: url(../../../../ui/webui/resources/images/i_circle.svg); - background-color: rgb(90, 90, 90); -} - -#url-indicator-lock-icon { - -webkit-mask-image: url(../../../../ui/webui/resources/images/lock.svg); - background-color: rgb(11, 128, 67); -} - -#url-indicator-warning-icon { - -webkit-mask-image: url(../../../../ui/webui/resources/images/warning.svg); - background-color: rgb(199, 56, 33); -} - -#url-indicator #url { - color: #252525; - font-size: 48px; - overflow: hidden; - white-space: nowrap; - width: 100%; -} - -#url-indicator #path { - color: #868686; -} - -#omnibox-ui-element { - background-color: transparent; - box-sizing: border-box; - font-size: 16px; - width: 368px; -} - -#suggestions { - border: 1px solid transparent; - box-sizing: border-box; -} - -.suggestion { - align-items: center; /* Vertically center text in div. */ - background-color: white; - /* Use a transparent border to hide text overflow, but allow background to - * color show through. */ - border-left: 5px solid transparent; - border-right: 5px solid transparent; - box-sizing: border-box; - display: flex; - height: 24px; - overflow: hidden; - white-space: nowrap; -} - -.suggestion:hover { - background-color: orange; -} - -#omnibox-border { - --statusBarColor: rgb(66, 133, 244); - background-color: white; - border-radius: 8px; - padding: 3px; -} - -#omnibox-contents { - background-color: white; - border-radius: 6px; - box-sizing: border-box; - display: flex; - flex-direction: row-reverse; /* Right-justify for convienence. */ - height: 64px; - margin-top: 2px; - padding: 8px; - transition: background-color 50ms ease-in-out; -} - -#omnibox-input-field { - background-color: transparent; - border: none; - font-size: 27px; - outline: none; /* Do not show an outline when focused. */ - overflow: hidden; - text-align: center; - white-space: nowrap; - width: 100%; -} - -#omnibox-clear-button { - background: url(../../../../ui/webui/resources/images/x-hover.png) no-repeat center center; - width: 18px; -}
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.html b/chrome/browser/resources/vr_shell/vr_shell_ui.html deleted file mode 100644 index 8d65649..0000000 --- a/chrome/browser/resources/vr_shell/vr_shell_ui.html +++ /dev/null
@@ -1,118 +0,0 @@ -<!-- -Copyright 2016 The Chromium Authors. All rights reserved. -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> -<!DOCTYPE html> -<html> -<head> -<meta charset="utf-8"> -<if expr="is_android or is_ios"> -<meta name="viewport" content="width=device-width, initial-scale=1.0, - maximum-scale=1.0, user-scalable=no"> -<meta http-equiv="cache-control" content="no-cache"> -<meta http-equiv="pragma" content="no-cache"> -</if> -<title>Vr Shell UIs</title> -<link rel="stylesheet" href="vr_shell_ui.css"> -<link rel="stylesheet" href="vk.css"> -<link rel="stylesheet" href="chrome://resources/css/text_defaults_md.css"> -</head> -<body> - <div id="ui"> - <div id="webvr-not-secure-permanent" class="webvr-message-box ui-element"> - <div class="webvr-box"> - <img class="webvr-not-secure-icon" width="36" height="36" - src="../../../../ui/webui/resources/images/i_circle.svg"> - <div class="webvr-string">$i18n{insecureWebVrContentPermanent}</div> - </div> - </div> - <div id="webvr-not-secure-transient" class="webvr-message-box ui-element"> - <div> - <div>$i18n{insecureWebVrContentTransient}</div> - </div> - </div> - <div id="url-indicator-container" class="ui-element"> - <div id="url-indicator-border"> - <div id="url-indicator"> - <div id="url-indicator-content"> - <div id="url-indicator-info-icon" class="url-indicator-icon"></div> - <div id="url-indicator-lock-icon" class="url-indicator-icon"></div> - <div id="url-indicator-warning-icon" class="url-indicator-icon"> - </div> - <div id="url"> - <span id="domain"></span><span id="path"></span> - </div> - </div> - </div> - </div> - </div> - <div id="omnibox-ui-element" class="ui-element"> - <div id="suggestions"> - <div id="suggestion-4" class="suggestion"></div> - <div id="suggestion-3" class="suggestion"></div> - <div id="suggestion-2" class="suggestion"></div> - <div id="suggestion-1" class="suggestion"></div> - <div id="suggestion-0" class="suggestion"></div> - </div> - <div id="omnibox-border"> - <div id="omnibox-contents"> - <div id="omnibox-clear-button"></div> - <input id="omnibox-input-field" type="url"></input> - </div> - </div> - </div> - <div id="back-indicator" class="gesture-indicator ui-element"></div> - <div id="forward-indicator" class="gesture-indicator ui-element"></div> - <div id="back-button" class="round-button ui-element"></div> - <div id="reload-button" class="round-button ui-element"></div> - <div id="forward-button" class="round-button ui-element"></div> - <div id="exit-present-button" class="round-button ui-element"></div> - <div id="back-button-caption" class="button-caption ui-element"> - $i18n{back} - </div> - <div id="reload-button-caption" class="button-caption ui-element"> - $i18n{reload} - </div> - <div id="forward-button-caption" class="button-caption ui-element"> - $i18n{forward} - </div> - <div id="exit-present-button-caption" class="button-caption ui-element"> - $i18n{exitPresent} - </div> - <div id="reload-ui-button" class="ui-element">Reload UI</div> - <div id="content-interceptor" class="ui-element"></div> - <div id="tab-template" class="tab"></div> - <!--The tab container element behaves like a scroll view (in conjunction - with the clip view). --> - <div id="tab-container" class="ui-element"> - <!--The tab clip element's width will be programmatically set to the total - width of all it's children (the tabs). By doing so, the tabs can be - scrolled horizontally in the tab container element.--> - <div id="tab-clip"> - </div> - </div> - <div id="new-tab" class="ui-element rect-button"> - $i18n{newTab} - </div> - <div id="new-incognito-tab" class="ui-element rect-button"> - $i18n{newIncognitoTab} - </div> - <div id="vkb-ui-element" class="ui-element" lang="en" dir="ltr"> - <div id="vkb" class="inputview-container"> - <div id="numberView" class="inputview-layoutview"></div> - <div class="inputview-spacerview"></div> - <div class="inputview-layoutview" id="keyboardView"></div> - <div class="inputview-spacerview"></div> - <div id="specialkeyView" class="inputview-layoutview"></div> - </div> - </div> - </div> -</body> - -<!-- Run script after creating body, to let it add its own elements. --> -<script src="vr_shell_ui_api.js"></script> -<script src="vr_shell_ui_scene.js"></script> -<script src="vr_shell_ui.js"></script> -<script src="vk.js"></script> -</html>
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui.js b/chrome/browser/resources/vr_shell/vr_shell_ui.js deleted file mode 100644 index 214da76..0000000 --- a/chrome/browser/resources/vr_shell/vr_shell_ui.js +++ /dev/null
@@ -1,1380 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var vrShellUi = (function() { - 'use strict'; - - let ui = new scene.Scene(); - let uiManager; - let nativeCommandHandler; - - let uiRootElement = document.querySelector('#ui'); - let uiStyle = window.getComputedStyle(uiRootElement); - /** @const */ var ANIM_DURATION = 150; - - // This value should match the one in VrShellImpl.java - /** @const */ var UI_DPR = 1.2; - - function getStyleFloat(style, property, defaultValue) { - let value = parseFloat(style.getPropertyValue(property)); - return isNaN(value) ? defaultValue : value; - } - - function getStyleString(style, property) { - let str = style.getPropertyValue(property); - return !str || 0 === str.length ? '' : str; - } - - // Generate a two-color progress bar style background using a gradient. - function makeProgressBackground(loading, percentage, color, background) { - if (!loading) { - return background; - } - return 'linear-gradient(to right, ' + color + ' 0%, ' + color + ' ' + - percentage * 100.0 + '%, ' + background + ' ' + percentage * 100 + - '%, ' + background + ' 100%)'; - } - - class ContentQuad { - constructor() { - /** @const */ this.SCREEN_HEIGHT = 1.375; - /** @const */ this.SCREEN_RATIO = 9.6 / 6.4; - /** @const */ this.BROWSING_SCREEN_ELEVATION = -0.15; - /** @const */ this.BROWSING_SCREEN_DISTANCE = 2.0; - /** @const */ this.FULLSCREEN_DISTANCE = 3.0; - /** @const */ this.CSS_WIDTH_PIXELS = 960.0; - /** @const */ this.CSS_HEIGHT_PIXELS = 640.0; - /** @const */ this.DPR = 1.2; - /** @const */ this.MENU_MODE_SCREEN_DISTANCE = 2.0; - /** @const */ this.MENU_MODE_SCREEN_HEIGHT = 0.48; - /** @const */ this.MENU_MODE_SCREEN_ELEVATION = -0.125; - /** @const */ this.BACKGROUND_DISTANCE_MULTIPLIER = 1.414; - /** @const */ this.DOM_INTERCEPTOR_SELECTOR = '#content-interceptor'; - /** @const */ this.DOM_INTERCEPTOR_ELEVATION = 0.01; - - this.menuMode = false; - this.fullscreen = false; - - let element = new api.UiElement(0, 0, 0, 0); - element.setName('Content'); - element.setFill(new api.Content()); - element.setVisible(false); - element.setSize( - this.SCREEN_HEIGHT * this.SCREEN_RATIO, this.SCREEN_HEIGHT); - element.setTranslation( - 0, this.BROWSING_SCREEN_ELEVATION, -this.BROWSING_SCREEN_DISTANCE); - this.elementId = ui.addElement(element); - - // Place an invisible (fill none) but hittable plane behind the content - // quad, to keep the reticle roughly planar with the content if near - // content. - let backPlane = new api.UiElement(0, 0, 0, 0); - backPlane.setName('Content backing'); - backPlane.setSize(1000, 1000); - backPlane.setTranslation(0, 0, -0.01); - backPlane.setParentId(this.elementId); - backPlane.setFill(new api.NoFill()); - ui.addElement(backPlane); - - // Place invisible plane on top of content quad, to intercept the clicks - // while on menu mode. - this.interceptor = new DomUiElement(this.DOM_INTERCEPTOR_SELECTOR); - let update = new api.UiElementUpdate(); - update.setTranslation(0, 0, this.DOM_INTERCEPTOR_ELEVATION); - update.setVisible(false); - update.setParentId(this.elementId); - update.setSize( - this.MENU_MODE_SCREEN_HEIGHT * this.SCREEN_RATIO, - this.MENU_MODE_SCREEN_HEIGHT); - ui.updateElement(this.interceptor.id, update); - let interceptorButton = - document.querySelector(this.DOM_INTERCEPTOR_SELECTOR); - interceptorButton.addEventListener('click', function() { - uiManager.exitMenuMode(); - ui.flush(); - }); - - this.updateState(); - } - - setEnabled(enabled) { - let update = new api.UiElementUpdate(); - update.setVisible(enabled); - ui.updateElement(this.elementId, update); - if (enabled) { - api.setContentCssSize( - this.CSS_WIDTH_PIXELS, this.CSS_HEIGHT_PIXELS, this.DPR); - } else { - // TODO(mthiesse): Restore the webVR resolution (which matches native - // display resolution). - } - } - - setMenuMode(enabled) { - if (this.menuMode == enabled) - return; - this.menuMode = enabled; - this.updateState(); - } - - setFullscreen(enabled) { - if (this.fullscreen == enabled) - return; - this.fullscreen = enabled; - this.updateState(); - } - - updateState() { - // Defaults content quad parameters. - let y = this.BROWSING_SCREEN_ELEVATION; - let distance = this.BROWSING_SCREEN_DISTANCE; - let height = this.SCREEN_HEIGHT; - - // Mode-specific overrides. - if (this.menuMode) { - y = this.MENU_MODE_SCREEN_ELEVATION; - distance = this.MENU_MODE_SCREEN_DISTANCE; - height = this.MENU_MODE_SCREEN_HEIGHT; - } else if (this.fullscreen) { - distance = this.FULLSCREEN_DISTANCE; - } - - let update = new api.UiElementUpdate(); - update.setVisible(this.menuMode); - ui.updateElement(this.interceptor.id, update); - - let anim; - anim = new api.Animation(this.elementId, ANIM_DURATION); - anim.setTranslation(0, y, -distance); - anim.setEasing(new api.InOutEasing()); - ui.addAnimation(anim); - anim = new api.Animation(this.elementId, ANIM_DURATION); - anim.setSize(height * this.SCREEN_RATIO, height); - anim.setEasing(new api.InOutEasing()); - ui.addAnimation(anim); - - ui.setBackgroundDistance(distance * this.BACKGROUND_DISTANCE_MULTIPLIER); - } - - // TODO(crbug/643815): Add a method setting aspect ratio (and possible - // animation of changing it). - - getElementId() { - return this.elementId; - } - }; - - class DomUiElement { - constructor(domId) { - let domElement = document.querySelector(domId); - - // Pull copy rectangle from the position of the element. - let rect = domElement.getBoundingClientRect(); - let pixelX = Math.floor(rect.left); - let pixelY = Math.floor(rect.top); - let pixelWidth = Math.ceil(rect.right) - pixelX; - let pixelHeight = Math.ceil(rect.bottom) - pixelY; - - let element = new api.UiElement(pixelX, pixelY, pixelWidth, pixelHeight); - this.sizeX = pixelWidth / 1000; - this.sizeY = pixelHeight / 1000; - element.setSize(this.sizeX, this.sizeY); - - // Parse element CSS properties. - let style = window.getComputedStyle(domElement); - - this.translationX = getStyleFloat(style, '--tranX', 0); - this.translationY = getStyleFloat(style, '--tranY', 0); - this.translationZ = getStyleFloat(style, '--tranZ', 0); - if (this.translationX != 0 || this.translationY != 0 || - this.translationZ != 0) { - element.setTranslation( - this.translationX, this.translationY, this.translationZ); - } - - this.scale = getStyleFloat(style, '--scale', 1); - if (this.scale != 1) { - element.setScale(this.scale, this.scale, this.scale); - } - - this.rotationX = getStyleFloat(style, '--rotX', 0); - this.rotationY = getStyleFloat(style, '--rotY', 0); - this.rotationZ = getStyleFloat(style, '--rotZ', 0); - if (this.rotationX != 0 || this.rotationY != 0 || this.rotationZ != 0) { - element.setRotation( - this.rotationX, this.rotationY, this.rotationZ, 2 * Math.PI); - } - - element.setName(domId); - this.id = ui.addElement(element); - this.domElement = domElement; - } - }; - - class Button { - constructor(domId, callback, parentId) { - let captionId = domId + '-caption'; - this.button = document.querySelector(domId); - this.caption = document.querySelector(captionId); - this.callback = callback; - this.enabled = true; - - // Create an invisible parent, from which the button will hover. - let backing = new api.UiElement(0, 0, 0, 0); - backing.setName(domId + '-backing'); - backing.setParentId(parentId); - backing.setVisible(false); - this.backingElementId = ui.addElement(backing); - - this.buttonElement = new DomUiElement(domId); - let update = new api.UiElementUpdate(); - update.setParentId(this.backingElementId); - ui.updateElement(this.buttonElement.id, update); - - this.captionElement = new DomUiElement(captionId); - update = new api.UiElementUpdate(); - update.setParentId(this.buttonElement.id); - update.setTranslation(0, -this.captionElement.sizeY / 2, 0); - update.setAnchoring(api.XAnchoring.XNONE, api.YAnchoring.YBOTTOM); - ui.updateElement(this.captionElement.id, update); - - this.button.addEventListener('mouseenter', this.onMouseEnter.bind(this)); - this.button.addEventListener('mouseleave', this.onMouseLeave.bind(this)); - this.button.addEventListener('click', this.callback); - } - - setTranslation(x, y, z) { - let update = new api.UiElementUpdate(); - update.setTranslation(x, y, z); - ui.updateElement(this.backingElementId, update); - } - - setVisible(visible) { - let update = new api.UiElementUpdate(); - update.setVisible(visible); - ui.updateElement(this.buttonElement.id, update); - update = new api.UiElementUpdate(); - update.setVisible(visible); - ui.updateElement(this.captionElement.id, update); - } - - setEnabled(enabled) { - this.enabled = enabled; - if (enabled) { - this.button.classList.remove('disabled-button'); - this.button.addEventListener('click', this.callback); - } else { - this.button.classList.add('disabled-button'); - this.button.removeEventListener('click', this.callback); - } - } - - configure(buttonOpacity, captionOpacity, distanceForward) { - this.button.style.opacity = buttonOpacity; - this.caption.style.opacity = captionOpacity; - - let anim = new api.Animation(this.buttonElement.id, ANIM_DURATION); - anim.setTranslation(0, 0, distanceForward); - ui.addAnimation(anim); - ui.flush(); - } - - onMouseEnter() { - if (this.enabled) { - this.configure(1, 1, 0.015); - } - } - - onMouseLeave() { - this.configure(0.8, 0, 0); - } - }; - - class GestureHandlers { - constructor() { - /** @const */ var BACKING_DISTANCE = 0.8; - /** @const */ var INDICATOR_DISTANCE = 0.15; - this.enabled = false; - - let backing = new api.UiElement(0, 0, 0, 0); - backing.setName('Navigation indicator backing'); - backing.setVisible(false); - backing.setTranslation(0, 0, -BACKING_DISTANCE); - backing.setLockToFieldOfView(true); - this.backingElementId = ui.addElement(backing); - - this.indicators = {}; - this.indicators[api.Direction.LEFT] = - new GestureHandler( - '#back-indicator', - function() { - api.doAction(api.Action.HISTORY_BACK, {}); - }, - this.backingElementId, - [-INDICATOR_DISTANCE, 0]); - this.indicators[api.Direction.RIGHT] = - new GestureHandler( - '#forward-indicator', - function() { - api.doAction(api.Action.HISTORY_FORWARD, {}); - }, - this.backingElementId, - [INDICATOR_DISTANCE, 0]); - } - - setEnabled(enabledIndicators) { - for (let key in enabledIndicators) { - if (key in this.indicators) { - this.indicators[key].setEnabled(enabledIndicators[key]); - } - } - } - - run(direction) { - if (direction in this.indicators) { - this.indicators[direction].run(); - } - } - } - - class GestureHandler { - constructor(selector, callback, parentId, position) { - /** @const */ this.ANIM_DURATION = 250; - this.enabled = false; - - this.element = new DomUiElement(selector); - this.callback = callback; - - let update = new api.UiElementUpdate(); - update.setParentId(parentId); - update.setVisible(true); - update.setOpacity(0); - update.setScale(0, 0, 0); - update.setTranslation(position[0], position[1], 0); - ui.updateElement(this.element.id, update); - } - - setTranslation(x, y, z) { - let update = new api.UiElementUpdate(); - update.setTranslation(x, y, z); - ui.updateElement(this.element.id, update); - } - - setEnabled(enabled) { - this.enabled = enabled; - } - - run() { - if (!this.enabled) { - return; - } - this.callback(); - - let update = new api.UiElementUpdate(); - update.setScale(0.5, 0.5, 0.5); - update.setOpacity(0.5); - ui.updateElement(this.element.id, update); - let anim = new api.Animation(this.element.id, this.ANIM_DURATION); - anim.setOpacity(1); - ui.addAnimation(anim); - anim = new api.Animation(this.element.id, this.ANIM_DURATION); - anim.setOpacity(0); - anim.setDelayedStart(this.ANIM_DURATION); - ui.addAnimation(anim); - anim = new api.Animation(this.element.id, this.ANIM_DURATION * 2); - anim.setScale(1, 1, 1); - ui.addAnimation(anim); - } - } - - class Controls { - constructor(contentQuadId) { - this.enabled = false; - this.exitPresentButtonVisible = false; - - this.buttons = { - backButton: null, - reloadButton: null, - forwardButton: null - }; - let descriptors = [ - [ - 0, 'backButton', '#back-button', - function() { - api.doAction(api.Action.HISTORY_BACK, {}); - } - ], - [ - 1, 'reloadButton', '#reload-button', - function() { - api.doAction(api.Action.RELOAD, {}); - } - ], - [ - 2, 'forwardButton', '#forward-button', - function() { - api.doAction(api.Action.HISTORY_FORWARD, {}); - } - ], - [ - 0, 'exitPresentButton', '#exit-present-button', - function() { - api.doAction(api.Action.EXIT_PRESENT, {}); - } - ], - ]; - - /** @const */ var BUTTON_Y = -0.53; - /** @const */ var BUTTON_Z = -2; - /** @const */ var BUTTON_SPACING = 0.14; - - let controls = new api.UiElement(0, 0, 0, 0); - controls.setName('Controls'); - controls.setVisible(false); - controls.setTranslation(0, BUTTON_Y, BUTTON_Z); - this.controlsId = ui.addElement(controls); - - let slotCount = 0; - descriptors.forEach(function(descriptor) { - slotCount = Math.max(descriptor[0] + 1, slotCount); - }); - let startPosition = -BUTTON_SPACING * (slotCount / 2.0 - 0.5); - - for (let i = 0; i < descriptors.length; i++) { - let slot = descriptors[i][0]; - let name = descriptors[i][1]; - let domId = descriptors[i][2]; - let callback = descriptors[i][3]; - let button = new Button(domId, callback, this.controlsId); - button.setTranslation(startPosition + slot * BUTTON_SPACING, 0, 0); - this.buttons[name] = button; - } - } - - setEnabled(enabled) { - this.enabled = enabled; - this.configure(); - } - - configure() { - for (let key in this.buttons) { - this.buttons[key].setVisible(this.enabled); - } - if (this.enabled) { - this.buttons['exitPresentButton'].setVisible( - this.exitPresentButtonVisible); - this.buttons['backButton'].setVisible(!this.exitPresentButtonVisible); - } - } - - setBackButtonEnabled(enabled) { - this.buttons.backButton.setEnabled(enabled); - } - - setForwardButtonEnabled(enabled) { - this.buttons.forwardButton.setEnabled(enabled); - } - - /** If true shows the exit present button instead of the back button. */ - setExitPresentButtonVisible(visible) { - this.exitPresentButtonVisible = visible; - this.configure(); - } - }; - - /** - * A button to trigger a reload of the HTML UI for development purposes. - */ - class ReloadUiButton { - constructor() { - this.enabled = false; - this.devMode = false; - - this.uiElement = new DomUiElement('#reload-ui-button'); - this.uiElement.domElement.addEventListener('click', function() { - ui.purge(); - api.doAction(api.Action.RELOAD_UI, {}); - }); - - let update = new api.UiElementUpdate(); - update.setVisible(false); - ui.updateElement(this.uiElement.id, update); - } - - setEnabled(enabled) { - this.enabled = enabled; - this.updateState(); - } - - setDevMode(enabled) { - this.devMode = enabled; - this.updateState(); - } - - updateState() { - let update = new api.UiElementUpdate(); - update.setVisible(this.enabled && this.devMode); - ui.updateElement(this.uiElement.id, update); - } - }; - - class SecureOriginWarnings { - constructor() { - /** @const */ var DISTANCE = 0.7; - /** @const */ var ANGLE_UP = 16.3 * Math.PI / 180.0; - - this.enabled = false; - this.secure = false; - this.secureOriginTimer = null; - - // Permanent WebVR security warning. This warning is shown near the top of - // the field of view. - this.webVrSecureWarning = new DomUiElement('#webvr-not-secure-permanent'); - let update = new api.UiElementUpdate(); - update.setScale(DISTANCE, DISTANCE, 1); - update.setTranslation( - 0, DISTANCE * Math.sin(ANGLE_UP), -DISTANCE * Math.cos(ANGLE_UP)); - update.setRotation(1.0, 0.0, 0.0, ANGLE_UP); - update.setHitTestable(false); - update.setVisible(false); - update.setLockToFieldOfView(true); - ui.updateElement(this.webVrSecureWarning.id, update); - - // Temporary WebVR security warning. This warning is shown in the center - // of the field of view, for a limited period of time. - this.transientWarning = new DomUiElement('#webvr-not-secure-transient'); - update = new api.UiElementUpdate(); - update.setScale(DISTANCE, DISTANCE, 1); - update.setTranslation(0, 0, -DISTANCE); - update.setHitTestable(false); - update.setVisible(false); - update.setLockToFieldOfView(true); - ui.updateElement(this.transientWarning.id, update); - } - - setEnabled(enabled) { - this.enabled = enabled; - this.updateState(); - } - - setSecure(secure) { - this.secure = secure; - this.updateState(); - } - - updateState() { - /** @const */ var TRANSIENT_TIMEOUT_MS = 30000; - - let visible = (this.enabled && !this.secure); - if (this.secureOriginTimer) { - clearInterval(this.secureOriginTimer); - this.secureOriginTimer = null; - } - if (visible) { - this.secureOriginTimer = - setTimeout(this.onTransientTimer.bind(this), TRANSIENT_TIMEOUT_MS); - } - this.showOrHideWarnings(visible); - } - - showOrHideWarnings(visible) { - let update = new api.UiElementUpdate(); - update.setVisible(visible); - ui.updateElement(this.webVrSecureWarning.id, update); - update = new api.UiElementUpdate(); - update.setVisible(visible); - ui.updateElement(this.transientWarning.id, update); - } - - onTransientTimer() { - let update = new api.UiElementUpdate(); - update.setVisible(false); - ui.updateElement(this.transientWarning.id, update); - this.secureOriginTimer = null; - ui.flush(); - } - }; - - class UrlIndicator { - constructor() { - this.domUiElement = new DomUiElement('#url-indicator-container'); - this.enabled = false; - this.hidden = false; - this.loading = false; - this.loadProgress = 0; - this.level = 0; - this.visibilityTimeout = 0; - this.visibilityTimer = null; - this.nativeState = {}; - - // Initially invisible. - let update = new api.UiElementUpdate(); - update.setVisible(false); - ui.updateElement(this.domUiElement.id, update); - this.nativeState.visible = false; - - // Pull some CSS properties so that Javascript can reconfigure the - // indicator programmatically. - let border = - this.domUiElement.domElement.querySelector('#url-indicator-border'); - let style = window.getComputedStyle(border); - this.statusBarColor = getStyleString(style, '--statusBarColor'); - this.backgroundColor = style.backgroundColor; - this.fadeTimeMs = getStyleFloat(style, '--fadeTimeMs', 0); - this.fadeYOffset = getStyleFloat(style, '--fadeYOffset', 0); - this.opacity = getStyleFloat(style, '--opacity', 1); - } - - getSecurityIconElementId(level) { - // See security_state.h and getSecurityIconResource() for this mapping. - switch (level) { - case 0: // NONE - case 1: // HTTP_SHOW_WARNING - case 4: // SECURITY_WARNING - return '#url-indicator-info-icon'; - case 2: // SECURE: - case 3: // EV_SECURE: - return '#url-indicator-lock-icon'; - case 5: // SECURE_WITH_POLICY_INSTALLED_CERT (ChromeOS only) - case 6: // DANGEROUS - default: - return '#url-indicator-warning-icon'; - } - } - - setEnabled(enabled) { - this.enabled = enabled; - this.resetVisibilityTimer(); - this.updateState(); - } - - setLoading(loading) { - this.loading = loading; - this.loadProgress = 0; - this.resetVisibilityTimer(); - this.updateState(); - } - - setLoadProgress(progress) { - this.loadProgress = progress; - this.updateState(); - } - - setURL(host, path) { - let indicator = this.domUiElement.domElement; - indicator.querySelector('#domain').textContent = host; - indicator.querySelector('#path').textContent = path; - this.resetVisibilityTimer(); - this.updateState(); - } - - setSecurityLevel(level) { - document.querySelector('#url-indicator-warning-icon').style.display = - 'none'; - document.querySelector('#url-indicator-info-icon').style.display = 'none'; - document.querySelector('#url-indicator-lock-icon').style.display = 'none'; - let icon = this.getSecurityIconElementId(level); - document.querySelector(icon).style.display = 'block'; - - this.resetVisibilityTimer(); - this.updateState(); - } - - setVisibilityTimeout(milliseconds) { - this.visibilityTimeout = milliseconds; - this.resetVisibilityTimer(); - this.updateState(); - } - - resetVisibilityTimer() { - if (this.visibilityTimer) { - clearInterval(this.visibilityTimer); - this.visibilityTimer = null; - } - if (this.enabled && this.visibilityTimeout > 0 && !this.loading) { - this.visibilityTimer = setTimeout( - this.onVisibilityTimer.bind(this), this.visibilityTimeout); - } - } - - onVisibilityTimer() { - this.visibilityTimer = null; - this.updateState(); - } - - updateState() { - this.setNativeVisibility(this.enabled); - - if (!this.enabled) { - return; - } - - let indicator = document.querySelector('#url-indicator-border'); - indicator.style.background = makeProgressBackground( - this.loading, this.loadProgress, this.statusBarColor, - this.backgroundColor); - - let shouldBeHidden = - !this.loading && this.visibilityTimeout > 0 && !this.visibilityTimer; - if (shouldBeHidden != this.hidden) { - // Make the box fade away if it's disappearing. - this.hidden = shouldBeHidden; - - // Fade-out or fade-in the box. - let opacityAnimation = - new api.Animation(this.domUiElement.id, this.fadeTimeMs); - opacityAnimation.setOpacity(this.hidden ? 0.0 : this.opacity); - ui.addAnimation(opacityAnimation); - - // Drop the position as it fades, or raise the position if appearing. - let yOffset = this.hidden ? this.fadeYOffset : 0; - let positionAnimation = - new api.Animation(this.domUiElement.id, this.fadeTimeMs); - positionAnimation.setTranslation( - this.domUiElement.translationX, - this.domUiElement.translationY + yOffset, - this.domUiElement.translationZ); - ui.addAnimation(positionAnimation); - } - - ui.flush(); - } - - setNativeVisibility(visible) { - if (visible == this.nativeState.visible) { - return; - } - this.nativeState.visible = visible; - let update = new api.UiElementUpdate(); - update.setVisible(visible); - ui.updateElement(this.domUiElement.id, update); - ui.flush(); - } - }; - - class Background { - constructor() { - /** @const */ this.SCENE_GROUND_SIZE = 25.0; - /** @const */ this.SCENE_HEIGHT = 4.0; - /** @const */ this.GRIDLINE_COUNT = 40; - /** @const */ this.HORIZON_COLOR = {r: 0.57, g: 0.57, b: 0.57, a: 1.0}; - /** @const */ this.CENTER_COLOR = {r: 0.48, g: 0.48, b: 0.48, a: 1.0}; - /** @const */ this.FULLSCREEN_BACKGROUND_COLOR = - {r: 0.1, g: 0.1, b: 0.1, a: 1.0}; - - // Make ground plane. - let groundPlane = new api.UiElementUpdate(); - groundPlane.setName('Ground plane'); - groundPlane.setVisible(true); - groundPlane.setSize(this.SCENE_GROUND_SIZE, this.SCENE_GROUND_SIZE); - groundPlane.setFill( - new api.OpaqueGradient(this.HORIZON_COLOR, this.CENTER_COLOR)); - groundPlane.setTranslation(0, -this.SCENE_HEIGHT / 2, 0); - groundPlane.setRotation(1.0, 0.0, 0.0, -Math.PI / 2); - groundPlane.setDrawPhase(0); - this.groundPlaneId = ui.addElement(groundPlane); - - // Make ceiling plane. - let ceilingPlane = new api.UiElementUpdate(); - ceilingPlane.setName('Ceiling'); - ceilingPlane.setVisible(true); - ceilingPlane.setSize(this.SCENE_GROUND_SIZE, this.SCENE_GROUND_SIZE); - ceilingPlane.setFill( - new api.OpaqueGradient(this.HORIZON_COLOR, this.CENTER_COLOR)); - ceilingPlane.setTranslation(0, this.SCENE_HEIGHT * 2, 0); - ceilingPlane.setRotation(1.0, 0.0, 0.0, Math.PI / 2); - ceilingPlane.setDrawPhase(0); - this.ceilingPlaneId = ui.addElement(ceilingPlane); - - // Ground grid. - let groundGrid = new api.UiElementUpdate(); - groundGrid.setName('Ground grid'); - groundGrid.setVisible(true); - groundGrid.setSize(this.SCENE_GROUND_SIZE, this.SCENE_GROUND_SIZE); - let transparentHorizonColor = { - r: this.HORIZON_COLOR.r, - g: this.HORIZON_COLOR.g, - b: this.HORIZON_COLOR.b, - a: 0 - }; - groundGrid.setFill(new api.GridGradient( - transparentHorizonColor, this.HORIZON_COLOR, this.GRIDLINE_COUNT)); - groundGrid.setTranslation(0, -this.SCENE_HEIGHT / 2 + 0.01, 0); - groundGrid.setRotation(1.0, 0.0, 0.0, -Math.PI / 2); - groundGrid.setDrawPhase(0); - this.groundGridId = ui.addElement(groundGrid); - - this.setHiddenBackground(); - } - - setElementVisible(elementId, visible) { - let update = new api.UiElementUpdate(); - update.setVisible(visible); - ui.updateElement(elementId, update); - } - - setLightBackground() { - this.setElementVisible(this.groundPlaneId, true); - this.setElementVisible(this.ceilingPlaneId, true); - this.setElementVisible(this.groundGridId, true); - ui.setBackgroundColor(this.HORIZON_COLOR); - } - - setDarkBackground() { - this.setElementVisible(this.groundPlaneId, false); - this.setElementVisible(this.ceilingPlaneId, false); - this.setElementVisible(this.groundGridId, true); - ui.setBackgroundColor(this.FULLSCREEN_BACKGROUND_COLOR); - } - - setHiddenBackground() { - this.setElementVisible(this.groundPlaneId, false); - this.setElementVisible(this.ceilingPlaneId, false); - this.setElementVisible(this.groundGridId, false); - ui.setBackgroundColor(this.FULLSCREEN_BACKGROUND_COLOR); - } - - setState(mode, menuMode, fullscreen) { - switch (mode) { - case api.Mode.STANDARD: - if (fullscreen) { - this.setDarkBackground(); - } else { - this.setLightBackground(); - } - break; - case api.Mode.WEB_VR: - if (menuMode) { - this.setLightBackground(); - } else { - this.setHiddenBackground(); - } - break; - } - } - }; - - class Omnibox { - constructor() { - /** @const */ this.ELEVATION = -0.7; - /** @const */ this.DISTANCE = -1.99; - /** @const */ this.SCALE = -this.DISTANCE; - - this.enabled = false; - - let root = document.querySelector('#omnibox-ui-element'); - this.domUiElement = new DomUiElement('#omnibox-border'); - this.inputField = root.querySelector('#omnibox-input-field'); - - let style = window.getComputedStyle(this.domUiElement.domElement); - this.statusBarColor = getStyleString(style, '--statusBarColor'); - this.backgroundColor = style.backgroundColor; - this.loading = false; - this.loadProgress = 0; - this.updateLoadingState(); - - // Initially invisible. - let update = new api.UiElementUpdate(); - update.setVisible(false); - update.setTranslation(0, this.ELEVATION, this.DISTANCE); - update.setScale(this.SCALE, this.SCALE, this.SCALE); - ui.updateElement(this.domUiElement.id, update); - - // Field-clearing button. - let clearButton = root.querySelector('#omnibox-clear-button'); - clearButton.addEventListener('click', function() { - this.inputField.value = ''; - api.doAction(api.Action.OMNIBOX_CONTENT, {'text': ''}); - }.bind(this)); - - // Watch for the enter key to trigger navigation. - this.inputField.addEventListener('keypress', function(e) { - if (e.keyCode == 13) { - this.setSuggestions([]); - api.doAction( - // TODO(crbug.com/683344): Properly choose prefix. - api.Action.LOAD_URL, {'url': 'http://' + e.target.value}); - } - }); - - // Watch for field input to generate suggestions. - this.inputField.addEventListener('input', function(e) { - api.doAction(api.Action.OMNIBOX_CONTENT, {'text': e.target.value}); - }); - - // Clicking on suggestions triggers navigation. - let elements = root.querySelectorAll('.suggestion'); - this.maxSuggestions = elements.length; - this.suggestions = []; - this.suggestionUiElements = []; - for (var i = 0; i < elements.length; i++) { - elements[i].addEventListener('click', function(index, e) { - if (e.target.url) { - api.doAction(api.Action.LOAD_URL, {'url': e.target.url}); - this.setSuggestions([]); - } - }.bind(this, i)); - - let elem = new DomUiElement('#suggestion-' + i); - this.suggestionUiElements.push(elem); - let update = new api.UiElementUpdate(); - update.setVisible(false); - update.setParentId(this.domUiElement.id); - update.setAnchoring(api.XAnchoring.XNONE, api.YAnchoring.YTOP); - // Vertically offset suggestions to stack on top of the omnibox. The 0.5 - // offset moves each anchor point from center to edge. - update.setTranslation(0, elem.sizeY * (i + 0.5), 0); - ui.updateElement(elem.id, update); - } - this.setSuggestions([]); - } - - setEnabled(enabled) { - this.enabled = enabled; - let update = new api.UiElementUpdate(); - update.setVisible(enabled); - ui.updateElement(this.domUiElement.id, update); - this.updateSuggestions(); - } - - setURL(url) { - this.inputField.value = url; - } - - setSuggestions(suggestions) { - this.suggestions = suggestions; - this.updateSuggestions(); - } - - setLoading(loading) { - this.loading = loading; - this.loadProgress = 0; - this.updateLoadingState(); - } - - setLoadProgress(progress) { - this.loadProgress = progress; - this.updateLoadingState(); - } - - updateLoadingState() { - let indicator = document.querySelector('#omnibox-border'); - indicator.style.background = makeProgressBackground( - this.loading, this.loadProgress, this.statusBarColor, - this.backgroundColor); - } - - updateSuggestions() { - for (var i = 0; i < this.maxSuggestions; i++) { - let element = document.querySelector('#suggestion-' + i); - let update = new api.UiElementUpdate(); - if (this.enabled && i < this.suggestions.length) { - element.textContent = this.suggestions[i].description; - element.url = this.suggestions[i].url; - update.setVisible(true); - } else { - element.textContent = ''; - element.url = null; - update.setVisible(false); - } - ui.updateElement(this.suggestionUiElements[i].id, update); - } - } - }; - - // Shows the open tabs. - // - // The tab container is made of three <div> nesting levels. The first is the - // tab container element, which acts like a scroll view. It has a fixed size - // and corresponds to a UI element in the scene. The second level is the clip - // element, which is programmatically set to the total width of all it's - // children (the third nesting level). The clip element is needed to enable - // horizontal scrolling and prevent the children from breaking to a new line. - // The third nesting level comprises the actual tabs. - // - // TODO(crbug/641487): currently, tabs cannot be scrolled because the - // scrolling event is not sent to UI elements. - class TabContainer { - constructor(contentQuadId) { - /** @const */ var DOM_TAB_TEMPLATE_SELECTOR = '#tab-template'; - /** @const */ var DOM_TAB_CONTAINER_SELECTOR = '#tab-container'; - /** @const */ var DOM_TAB_CLIP_SELECTOR = '#tab-clip'; - /** @const */ var DOM_NEW_TAB_BUTTON_SELECTOR = '#new-tab'; - /** @const */ var DOM_NEW_INCOGNITO_TAB_BUTTON_SELECTOR = - '#new-incognito-tab'; - /** @const */ var TAB_CONTAINER_Y_OFFSET = 0.3; - /** @const */ var TAB_CONTAINER_Z_OFFSET = -2; - - this.domTabs = {}; - this.contentQuadId = contentQuadId; - this.domTabTemplate = document.querySelector(DOM_TAB_TEMPLATE_SELECTOR); - this.domTabContainer = document.querySelector(DOM_TAB_CONTAINER_SELECTOR); - this.domTabClip = document.querySelector(DOM_TAB_CLIP_SELECTOR); - - // Add tab container to native scene. - this.tabContainerElement = new DomUiElement(DOM_TAB_CONTAINER_SELECTOR); - let positionUpdate = new api.UiElementUpdate(); - positionUpdate.setTranslation( - 0, TAB_CONTAINER_Y_OFFSET, TAB_CONTAINER_Z_OFFSET); - positionUpdate.setVisible(false); - ui.updateElement(this.tabContainerElement.id, positionUpdate); - - // Add the new tab buttons to the native scene. - let buttonConfigs = [ - { - selector: DOM_NEW_TAB_BUTTON_SELECTOR, x: -0.2, incognito: false - }, { - selector: DOM_NEW_INCOGNITO_TAB_BUTTON_SELECTOR, - x: 0.2, - incognito: true - } - ]; - this.buttonElements = []; - buttonConfigs.forEach(function(buttonConfig) { - let buttonElement = new DomUiElement(buttonConfig.selector); - let update = new api.UiElementUpdate(); - update.setTranslation(buttonConfig.x, 0.1, 0); - update.setVisible(false); - update.setAnchoring(api.XAnchoring.XNONE, api.YAnchoring.YTOP); - update.setParentId(this.tabContainerElement.id); - ui.updateElement(buttonElement.id, update); - buttonElement.domElement.addEventListener('click', function() { - api.doAction( - api.Action.OPEN_NEW_TAB, {'incognito': buttonConfig.incognito}); - }); - this.buttonElements.push(buttonElement); - }, this); - - // Calculate the width of one tab so that we can properly set the clip - // element's width. - this.domTabWidth = this.domTabTemplate.offsetWidth; - var domTabStyle = window.getComputedStyle(this.domTabTemplate); - this.domTabWidth += parseInt(domTabStyle.marginLeft, 10) + - parseInt(domTabStyle.marginRight, 10); - } - - makeDomTab(tab) { - // Make a copy of the template tab and add this copy to the tab container - // view. - let domTab = this.domTabTemplate.cloneNode(true); - domTab.removeAttribute('id'); - domTab.addEventListener('click', function() { - api.doAction(api.Action.SHOW_TAB, {'id': domTab.tab.id}); - }); - domTab.tab = tab; - this.domTabClip.appendChild(domTab); - this.domTabs[tab.id] = domTab; - return domTab; - } - - resizeClipElement() { - // Resize clip element so that scrolling works. - this.domTabClip.style.width = - (Object.keys(this.domTabs).length * this.domTabWidth) + 'px'; - } - - setTabs(tabs) { - // Remove all current tabs. - while (this.domTabClip.firstChild) { - this.domTabClip.removeChild(this.domTabClip.firstChild); - } - this.domTabs = {}; - - // Add new tabs. - for (let i = 0; i < tabs.length; i++) { - this.addTab(tabs[i]); - } - } - - hasTab(tab) { - return tab.id in this.domTabs; - } - - addTab(tab) { - this.makeDomTab(tab); - this.updateTab(tab); - this.resizeClipElement(); - } - - updateTab(tab) { - let domTab = this.domTabs[tab.id]; - domTab.textContent = tab.title; - domTab.classList.remove('tab-incognito'); - if (tab.incognito) { - domTab.classList.add('tab-incognito'); - } - } - - removeTab(tab) { - let qualifiedTabId = tab.id; - let domTab = this.domTabs[qualifiedTabId]; - delete this.domTabs[qualifiedTabId]; - this.domTabClip.removeChild(domTab); - this.resizeClipElement(); - } - - setEnabled(enabled) { - let update = new api.UiElementUpdate(); - update.setVisible(enabled); - ui.updateElement(this.tabContainerElement.id, update); - this.buttonElements.forEach(function(buttonElement) { - let update = new api.UiElementUpdate(); - update.setVisible(enabled); - ui.updateElement(buttonElement.id, update); - }, this); - } - }; - - class VirtualKeyboard { - constructor(contentQuadId) { - /** @const */ this.SCALE = 1.4; - /** @const */ this.ANGLE_UP = Math.PI / 8; - /** @const */ this.Y_OFFSET = -1.0; - /** @const */ this.Z_OFFSET = -1.8; - - this.element = new DomUiElement('#vkb'); - let update = new api.UiElementUpdate(); - update.setVisible(true); - update.setOpacity(0); - update.setRotation(1, 0, 0, -this.ANGLE_UP); - update.setScale(this.SCALE, this.SCALE, this.SCALE); - update.setTranslation(0, this.Y_OFFSET, this.Z_OFFSET); - ui.updateElement(this.element.id, update); - } - - setEnabled(enabled) { - let anim = new api.Animation(this.element.id, ANIM_DURATION); - anim.setOpacity(enabled ? 1 : 0); - ui.addAnimation(anim); - anim = new api.Animation(this.element.id, ANIM_DURATION); - let scale = enabled ? this.SCALE : 0; - anim.setScale(scale, scale, scale); - ui.addAnimation(anim); - } - }; - - class UiManager { - constructor() { - this.mode = api.Mode.UNKNOWN; - this.menuMode = false; - this.fullscreen = false; - this.canGoBack = false; - this.canGoForward = false; - - this.background = new Background(); - this.contentQuad = new ContentQuad(); - let contentId = this.contentQuad.getElementId(); - - this.gestureHandlers = new GestureHandlers(); - this.controls = new Controls(contentId); - this.secureOriginWarnings = new SecureOriginWarnings(); - this.urlIndicator = new UrlIndicator(); - this.omnibox = new Omnibox(); - this.reloadUiButton = new ReloadUiButton(); - this.tabContainer = new TabContainer(contentId); - this.keyboard = new VirtualKeyboard(contentId); - } - - setMode(mode) { - this.mode = mode; - this.updateState(); - } - - setFullscreen(fullscreen) { - this.fullscreen = fullscreen; - this.updateState(); - } - - handleAppButtonGesturePerformed(direction) { - this.gestureHandlers.run(direction); - } - - handleAppButtonClicked() { - this.menuMode = !this.menuMode; - this.updateState(); - } - - setHistoryButtonsEnabled(canGoBack, canGoForward) { - this.canGoBack = canGoBack; - this.canGoForward = canGoForward; - - canGoBack = canGoBack || this.fullscreen; - - /** No need to call updateState to adjust button properties. */ - this.controls.setBackButtonEnabled(canGoBack); - this.controls.setForwardButtonEnabled(canGoForward); - let enabledIndicators = {} - enabledIndicators[api.Direction.LEFT] = canGoBack; - enabledIndicators[api.Direction.RIGHT] = canGoForward; - this.gestureHandlers.setEnabled(enabledIndicators); - } - - exitMenuMode() { - if (this.menuMode) { - this.menuMode = false; - this.updateState(); - } - } - - updateState() { - /** @const */ var URL_INDICATOR_VISIBILITY_TIMEOUT_MS = 5000; - - let mode = this.mode; - let menuMode = this.menuMode; - let fullscreen = this.fullscreen; - - api.doAction(api.Action.SET_CONTENT_PAUSED, {'paused': menuMode}); - ui.setWebVrRenderingModeEnabled(mode == api.Mode.WEB_VR && !menuMode); - - this.contentQuad.setEnabled(mode == api.Mode.STANDARD || menuMode); - this.contentQuad.setFullscreen(fullscreen); - this.contentQuad.setMenuMode(menuMode); - // TODO(crbug/643815): Set aspect ratio on content quad when available. - this.controls.setEnabled(menuMode); - this.controls.setBackButtonEnabled(this.canGoBack || this.fullscreen); - // TODO(crbug/689139): Don't show exit present button if the page - // autopresented. - this.controls.setExitPresentButtonVisible(mode == api.Mode.WEB_VR); - let enabledIndicators = {} - enabledIndicators[api.Direction.LEFT] = this.canGoBack || this.fullscreen; - this.gestureHandlers.setEnabled(enabledIndicators); - this.omnibox.setEnabled(menuMode); - this.urlIndicator.setEnabled(mode == api.Mode.STANDARD && !menuMode); - this.urlIndicator.setVisibilityTimeout( - URL_INDICATOR_VISIBILITY_TIMEOUT_MS); - this.secureOriginWarnings.setEnabled( - mode == api.Mode.WEB_VR && !menuMode); - this.background.setState(mode, menuMode, fullscreen); - this.tabContainer.setEnabled(menuMode); - - this.reloadUiButton.setEnabled(mode == api.Mode.STANDARD); - this.keyboard.setEnabled(mode == api.Mode.STANDARD && menuMode); - - - api.setUiCssSize( - uiRootElement.clientWidth, uiRootElement.clientHeight, UI_DPR); - } - - setSecurityLevel(level) { - this.urlIndicator.setSecurityLevel(level); - } - - setWebVRSecureOrigin(secure) { - this.secureOriginWarnings.setSecure(secure); - } - }; - - function initialize() { - uiManager = new UiManager(); - nativeCommandHandler = new UiCommandHandler(uiManager); - ui.flush(); - - api.domLoaded(); - } - - class UiCommandHandler extends api.NativeCommandHandler { - constructor(uiManager) { - super(); - this.manager = uiManager; - } - - /** @override */ - onSetMode(mode) { - this.manager.setMode(mode); - } - - /** @override */ - onSetFullscreen(fullscreen) { - this.manager.setFullscreen(fullscreen); - } - - /** @override */ - onAppButtonGesturePerformed(direction) { - this.manager.handleAppButtonGesturePerformed(direction); - } - - /** @override */ - onAppButtonClicked() { - this.manager.handleAppButtonClicked(); - } - - /** @override */ - onSetSecurityLevel(level) { - this.manager.setSecurityLevel(level); - } - - /** @override */ - onSetWebVRSecureOrigin(secure) { - this.manager.setWebVRSecureOrigin(secure); - } - - /** @override */ - onSetReloadUiCapabilityEnabled(enabled) { - this.manager.reloadUiButton.setDevMode(enabled); - } - - /** @override */ - onSetUrl(host, path) { - this.manager.urlIndicator.setURL(host, path); - this.manager.omnibox.setURL(host, path); - } - - /** @override */ - onSetLoading(loading) { - this.manager.urlIndicator.setLoading(loading); - this.manager.omnibox.setLoading(loading); - } - - /** @override */ - onSetLoadingProgress(progress) { - this.manager.urlIndicator.setLoadProgress(progress); - this.manager.omnibox.setLoadProgress(progress); - } - - /** @override */ - onSetOmniboxSuggestions(suggestions) { - this.manager.omnibox.setSuggestions(suggestions); - } - - /** @override */ - onSetTabs(tabs) { - uiManager.tabContainer.setTabs(tabs); - } - - /** @override */ - onUpdateTab(tab) { - if (uiManager.tabContainer.hasTab(tab)) { - uiManager.tabContainer.updateTab(tab); - } else { - uiManager.tabContainer.addTab(tab); - } - } - - /** @override */ - onRemoveTab(tab) { - uiManager.tabContainer.removeTab(tab); - } - - /** @override */ - onSetHistoryButtonsEnabled(canGoBack, canGoForward) { - uiManager.setHistoryButtonsEnabled(canGoBack, canGoForward); - } - - /** @override */ - onCommandHandlerFinished() { - ui.flush(); - } - } - - function command(dict) { - nativeCommandHandler.handleCommand(dict); - } - - return { - initialize: initialize, - command: command, - }; -})(); - -window.addEventListener('load', vrShellUi.initialize);
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui_api.js b/chrome/browser/resources/vr_shell/vr_shell_ui_api.js deleted file mode 100644 index a21bedaf..0000000 --- a/chrome/browser/resources/vr_shell/vr_shell_ui_api.js +++ /dev/null
@@ -1,770 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var api = {}; - -/** - * Enumeration of scene update commands. - * @enum {number} - * @const - */ -api.Command = { - 'ADD_ELEMENT': 0, - 'UPDATE_ELEMENT': 1, - 'REMOVE_ELEMENT': 2, - 'ADD_ANIMATION': 3, - 'REMOVE_ANIMATION': 4, - 'CONFIGURE_SCENE': 5 -}; - -/** - * Sends one or more commands to native scene management. Commands are used - * to add, modify or remove elements and animations. For examples of how to - * format command parameters, refer to examples in scene.js. - * @param {Array<Object>} commands - */ -api.sendCommands = function(commands) { - if (commands.length > 0) { - chrome.send('updateScene', commands); - } -}; - -/** - * Enumeration of valid Anchroing for X axis. - * An element can either be anchored to the left, right, or center of the main - * content rect (or it can be absolutely positioned using NONE). Any - * translations applied will be relative to this anchoring. - * @enum {number} - * @const - */ -api.XAnchoring = { - 'XNONE': 0, - 'XLEFT': 1, - 'XRIGHT': 2 -}; - -/** - * Enumeration of valid Anchroing for Y axis. - * @enum {number} - * @const - */ -api.YAnchoring = { - 'YNONE': 0, - 'YTOP': 1, - 'YBOTTOM': 2 -}; - -/** - * Enumeration of actions that can be triggered by the HTML UI. - * @enum {number} - * @const - */ -api.Action = { - 'HISTORY_BACK': 0, - 'HISTORY_FORWARD': 1, - 'RELOAD': 2, - 'ZOOM_OUT': 3, - 'ZOOM_IN': 4, - 'RELOAD_UI': 5, - 'LOAD_URL': 6, - 'OMNIBOX_CONTENT': 7, - 'SET_CONTENT_PAUSED': 8, - 'SHOW_TAB': 9, - 'OPEN_NEW_TAB': 10, - 'KEY_EVENT': 11, - 'EXIT_PRESENT': 12 -}; - -/** - * Enumeration of modes that can be specified by the native side. - * @enum {number} - * @const - */ -api.Mode = { - 'UNKNOWN': -1, - 'STANDARD': 0, - 'WEB_VR': 1, -}; - -/** - * Enumeration of gesture directions - * @enum {number} - * @const - */ -api.Direction = { - 'LEFT': 1, - 'RIGHT': 2, - 'UP': 3, - 'DOWN': 4 -}; - -/** - * Triggers an Action. - * @param {api.Action} action - * @param {Object} parameters - */ -api.doAction = function(action, parameters) { - chrome.send('doAction', [action, parameters]); -}; - -/** - * Notify native scene management that DOM loading has completed, at the - * specified page size. - */ -api.domLoaded = function() { - chrome.send('domLoaded'); -}; - -/** - * Sets the CSS size for the content window. - * @param {number} width - * @param {number} height - * @param {number} dpr - */ -api.setContentCssSize = function(width, height, dpr) { - chrome.send('setContentCssSize', [width, height, dpr]); -}; - -/** - * Sets the CSS size for this page. - * @param {number} width - * @param {number} height - * @param {number} dpr - */ -api.setUiCssSize = function(width, height, dpr) { - chrome.send('setUiCssSize', [width, height, dpr]); -}; - -api.FillType = { - 'NONE': 0, - 'SPRITE': 1, - 'OPAQUE_GRADIENT': 2, - 'GRID_GRADIENT': 3, - 'CONTENT': 4 -}; - -/** - * Abstract fill base class. - * @abstract - */ -api.Fill = class { - constructor(type) { - this.properties = {}; - this.properties['fillType'] = type; - } -}; - -api.NoFill = class extends api.Fill { - constructor() { - super(api.FillType.NONE); - } -} - -api.Sprite = class extends api.Fill { - constructor(pixelX, pixelY, pixelWidth, pixelHeight) { - super(api.FillType.SPRITE); - this.properties['copyRectX'] = pixelX; - this.properties['copyRectY'] = pixelY; - this.properties['copyRectWidth'] = pixelWidth; - this.properties['copyRectHeight'] = pixelHeight; - } -}; - -api.OpaqueGradient = class extends api.Fill { - constructor(edgeColor, centerColor) { - super(api.FillType.OPAQUE_GRADIENT); - this.properties.edgeColor = edgeColor; - this.properties.centerColor = centerColor; - } -}; - -api.GridGradient = class extends api.Fill { - constructor(edgeColor, centerColor, gridlineCount) { - super(api.FillType.GRID_GRADIENT); - this.properties.edgeColor = edgeColor; - this.properties.centerColor = centerColor; - this.properties.gridlineCount = gridlineCount; - } -}; - -api.Content = class extends api.Fill { - constructor() { - super(api.FillType.CONTENT); - } -}; - -/** - * Represents updates to UI element properties. Any properties set on this - * object are relayed to an underlying native element via scene command. - * Properties that are not set on this object are left unchanged. - * @struct - */ -api.UiElementUpdate = class { - constructor() { - /** @private {!Object} */ - this.properties = {'id': -1}; - } - - /** - * Set the id of the element to update. - * @param {number} id - */ - setId(id) { - this.properties['id'] = id; - } - - /** - * Set the name of the element. This name is used for debug and testing. - * @param {string} name - */ - setName(name) { - this.properties['name'] = name; - } - - /** - * Specify a parent for this element. If set, this element is positioned - * relative to its parent element, rather than absolutely. This allows - * elements to automatically move with a parent. - * @param {number} id - */ - setParentId(id) { - this.properties['parentId'] = id; - } - - /** - * Specify the width and height (in meters) of an element. - * @param {number} x - * @param {number} y - */ - setSize(x, y) { - this.properties['sizeX'] = x; - this.properties['sizeY'] = y; - } - - /** - * Specify optional scaling of the element, and any children. - * @param {number} x - * @param {number} y - * @param {number} z - */ - setScale(x, y, z) { - this.properties['scaleX'] = x; - this.properties['scaleY'] = y; - this.properties['scaleZ'] = z; - } - - /** - * Specify rotation for the element. The rotation is specified in axis-angle - * representation (rotate around unit vector [x, y, z] by 'a' radians). - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} a - */ - setRotation(x, y, z, a) { - this.properties['rotationX'] = x; - this.properties['rotationY'] = y; - this.properties['rotationZ'] = z; - this.properties['rotationAngle'] = a; - } - - /** - * Specify the translation of the element. If anchoring is specified, the - * offset is applied to the anchoring position rather than the origin. - * Translation is applied after scaling and rotation. - * @param {number} x - * @param {number} y - * @param {number} z - */ - setTranslation(x, y, z) { - this.properties['translationX'] = x; - this.properties['translationY'] = y; - this.properties['translationZ'] = z; - } - - /** - * Anchoring allows a rectangle to be positioned relative to the edge of - * its parent, without being concerned about the size of the parent. - * Values should be XAnchoring and YAnchoring elements. - * Example: element.setAnchoring(XAnchoring.XNONE, YAnchoring.YBOTTOM); - * @param {number} x - * @param {number} y - */ - setAnchoring(x, y) { - this.properties['xAnchoring'] = x; - this.properties['yAnchoring'] = y; - } - - /** - * Visibility controls whether the element is rendered. - * @param {boolean} visible - */ - setVisible(visible) { - this.properties['visible'] = !!visible; - } - - /** - * Hit-testable implies that the reticle will hit the element, if visible. - * @param {boolean} testable - */ - setHitTestable(testable) { - this.properties['hitTestable'] = !!testable; - } - - /** - * Causes an element to be rendered relative to the field of view, rather - * than the scene. Elements locked in this way should not have a parent. - * @param {boolean} locked - */ - setLockToFieldOfView(locked) { - this.properties['lockToFov'] = !!locked; - } - - /** - * Causes an element to be rendered with a specified opacity, between 0.0 and - * 1.0. Opacity is inherited by children. - * @param {number} opacity - */ - setOpacity(opacity) { - this.properties['opacity'] = opacity; - } - - setFill(fill) { - Object.assign(this.properties, fill.properties); - } - - /** - * Sets the draw phase. Elements with a lower draw phase are rendered before - * elements with a higher draw phase. If elements have an equal draw phase - * the element with the larger distance is drawn first. The default draw phase - * is 1. - * @param {number} drawPhase - */ - setDrawPhase(drawPhase) { - this.properties['drawPhase'] = drawPhase; - } -}; - -/** - * Represents a new UI element. This object builds on UiElementUpdate, - * forcing the underlying texture coordinates to be specified. - * @struct - */ -api.UiElement = class extends api.UiElementUpdate { - /** - * Constructor of UiElement. - * pixelX and pixelY values indicate the left upper corner; pixelWidth and - * pixelHeight is width and height of the texture to be copied from the web - * contents. - * @param {number} pixelX - * @param {number} pixelY - * @param {number} pixelWidth - * @param {number} pixelHeight - */ - constructor(pixelX, pixelY, pixelWidth, pixelHeight) { - super(); - - // Apply defaults to new elements. - this.setVisible(true); - this.setHitTestable(true); - this.setFill(new api.Sprite(pixelX, pixelY, pixelWidth, pixelHeight)); - } -}; - -/** - * Enumeration of animatable properties. - * @enum {number} - * @const - */ -api.Property = { - 'COPYRECT': 0, - 'SIZE': 1, - 'TRANSLATION': 2, - 'SCALE': 3, - 'ROTATION': 4, - 'OPACITY': 5 -}; - -/** - * Enumeration of easing type. - * @enum {number} - * @const - */ -api.EasingType = { - 'LINEAR': 0, - 'CUBICBEZIER': 1, - 'EASEIN': 2, - 'EASEOUT': 3, - 'EASEINOUT': 4 -}; - -/** @const */ var DEFAULT_EASING_POW = 2; -/** @const */ var DEFAULT_CUBIC_BEZIER_P1X = 0.25; -/** @const */ var DEFAULT_CUBIC_BEZIER_P1Y = 0; -/** @const */ var DEFAULT_CUBIC_BEZIER_P2X = 0.75; -/** @const */ var DEFAULT_CUBIC_BEZIER_P2Y = 1; - -/** - * Abstract easing base class. - * @abstract - */ -api.Easing = class { - constructor(type) { - this.type = type; - } -}; - -api.LinearEasing = class extends api.Easing { - constructor() { - super(api.EasingType.LINEAR); - } -}; - -api.CubicBezierEasing = class extends api.Easing { - constructor( - p1x = DEFAULT_CUBIC_BEZIER_P1X, - p1y = DEFAULT_CUBIC_BEZIER_P1Y, - p2x = DEFAULT_CUBIC_BEZIER_P2X, - p2y = DEFAULT_CUBIC_BEZIER_P2Y) { - super(api.EasingType.CUBICBEZIER); - this.p1x = p1x; - this.p1y = p1y; - this.p2x = p2x; - this.p2y = p2y; - } -}; - -api.InEasing = class extends api.Easing { - constructor(pow = DEFAULT_EASING_POW) { - super(api.EasingType.EASEIN); - this.pow = pow; - } -}; - -api.OutEasing = class extends api.Easing { - constructor(pow = DEFAULT_EASING_POW) { - super(api.EasingType.EASEOUT); - this.pow = pow; - } -}; - -api.InOutEasing = class extends api.Easing { - constructor(pow = DEFAULT_EASING_POW) { - super(api.EasingType.EASEINOUT); - this.pow = pow; - } -} - -/** - * Base animation class. An animation can vary only one object property. - * @struct - */ -api.Animation = class { - constructor(elementId, durationMs) { - /** @private {number} */ - this.id = -1; - /** @private {number} */ - this.meshId = elementId; - /** @private {number} */ - this.property = -1; - /** @private {Object} */ - this.to = {}; - /** @private {Object} */ - this.easing = new api.LinearEasing(); - - // How many milliseconds in the future to start the animation. - /** @private {number} */ - this.startInMillis = 0.0; - - // Duration of the animation (milliseconds). - /** @private {number} */ - this.durationMillis = durationMs; - } - - /** - * Set the id of the animation. - * @param {number} id - */ - setId(id) { - this.id = id; - } - - /** - * Set the delay for starting the animation. - * @param {number} millis - */ - setDelayedStart(millis) { - this.startInMillis = millis; - } - - /** - * Set the animation's final element size. - * @param {number} width - * @param {number} height - */ - setSize(width, height) { - this.property = api.Property.SIZE; - this.to.x = width; - this.to.y = height; - } - - /** - * Set the animation's final element scale. - * @param {number} x - * @param {number} y - * @param {number} z - */ - setScale(x, y, z) { - this.property = api.Property.SCALE; - this.to.x = x; - this.to.y = y; - this.to.z = z; - } - - /** - * Set the animation's final element rotation. - * @param {number} x - * @param {number} y - * @param {number} z - * @param {number} a - */ - setRotation(x, y, z, a) { - this.property = api.Property.ROTATION; - this.to.x = x; - this.to.y = y; - this.to.z = z; - this.to.a = a; - } - - /** - * Set the animation's final element translation. - * @param {number} x - * @param {number} y - * @param {number} z - */ - setTranslation(x, y, z) { - this.property = api.Property.TRANSLATION; - this.to.x = x; - this.to.y = y; - this.to.z = z; - } - - /** - * Set the animation's final element opacity. - * @param {number} opacity - */ - setOpacity(opacity) { - this.property = api.Property.OPACITY; - this.to.x = opacity; - } - - /** - * Set the animation's easing. - * @param {api.Easing} easing - */ - setEasing(easing) { - this.easing = easing; - } -}; - -/** - * Scene configuration class. Use this object to generate the payload of a - * CONFIGURE_SCENE command. - * @struct - */ -api.SceneConfiguration = class { - constructor() { - /** @private {!Object} */ - this.properties = {}; - } - - getCommandPayload() { - return this.properties; - } - - /** - * Set the background color of the scene. - * @param {{r: number, b: number, g: number, a: number}} color - */ - setBackgroundColor(color) { - this.properties.backgroundColor = color; - } - - /** - * Set the radius of the background-bounding sphere. - * @param {number} distance - */ - setBackgroundDistance(distance) { - this.properties.backgroundDistance = distance; - } - - /** - * Enable or disable rendering of WebVR content in the foreground. Rendering - * defaults to enabled when on a WebVR page. This property allows rendering to - * be disabled, for purposes of showing an alternate UI (such as a menu). When - * disabled, the cursor is rendered. - * @param {boolean} enabled - */ - setWebVrRenderingModeEnabled(enabled) { - this.properties.drawWebVr = enabled; - } -}; - -/** - * Abstract class handling webui command calls from native. The UI must - * subclass this and override the handlers. - * @abstract - */ -api.NativeCommandHandler = class { - /** - * @param {api.Mode} mode - */ - onSetMode(mode) {} - - /** - * Handles entering or exiting full-screen mode. - * @param {boolean} fullscreen - */ - onSetFullscreen(fullscreen) {} - - /** - * A controller app button gesture has happened. - */ - onAppButtonGesturePerformed(direction) {} - - /** - * A controller app button click has happened. - */ - onAppButtonClicked() {} - - /** - * Handles a change in the visible page's security level. - * @param {number} level - */ - onSetSecurityLevel(level) {} - - /** - * Handles a change in the WebVR-specific secure-origin state. If |secure| is - * false, the UI must convey appropriate security warnings. - * @param {boolean} secure - */ - onSetWebVRSecureOrigin(secure) {} - - /** - * Handles enabling of a development-oriented control to reload the UI. - * @param {boolean} enabled - */ - onSetReloadUiCapabilityEnabled(enabled) {} - - /** - * Handles a new URL, specifying the host and path compoments. - * @param {string} host - * @param {string} path - */ - onSetUrl(host, path) {} - - /** - * Handle a change in loading state (used to show a spinner or other loading - * indicator). - * @param {boolean} loading - */ - onSetLoading(loading) {} - - /** - * Handle a change in loading progress. Progress is supplied as a number - * between 0.0 and 1.0. - * @param {boolean} progress - */ - onSetLoadingProgress(progress) {} - - /** - * Handle a change in the set of omnibox suggestions. - * @param {Array<Object>} suggestions Array of suggestions with string members - * |description| and |url|. - */ - onSetOmniboxSuggestions(suggestions) {} - - /** - * Handle a new set of tabs, overwriting the previous state. - * @param {Array<Object>} tabs Array of tab states. - */ - onSetTabs(tabs) {} - - /** - * Update (or add if not present) a tab. - * @param {Object} tab - */ - onUpdateTab(tab) {} - - /** - * Remove a tab. - * @param {Object} tab - */ - onRemoveTab(tab) {} - - /** - * Set back/forward history buttons to enabled/disabled. - * @param {boolean} canGoBack - * @param {boolean} canGoForward - */ - onSetHistoryButtonsEnabled(canGoBack, canGoForward) {} - - /** - * This function is executed after command parsing completes. - */ - onCommandHandlerFinished() {} - - /** @final */ - handleCommand(dict) { - if ('mode' in dict) { - this.onSetMode(dict['mode']); - } - if ('fullscreen' in dict) { - this.onSetFullscreen(dict['fullscreen']) - } - if ('appButtonGesturePerformed' in dict) { - let direction = dict['appButtonGesturePerformed']; - this.onAppButtonGesturePerformed(direction); - } - if ('appButtonClicked' in dict) { - this.onAppButtonClicked(); - } - if ('securityLevel' in dict) { - this.onSetSecurityLevel(dict['securityLevel']); - } - if ('webVRSecureOrigin' in dict) { - this.onSetWebVRSecureOrigin(dict['webVRSecureOrigin']); - } - if ('enableReloadUi' in dict) { - this.onSetReloadUiCapabilityEnabled(dict['enableReloadUi']); - } - if ('url' in dict) { - let url = dict['url']; - this.onSetUrl(url['host'], url['path']); - } - if ('loading' in dict) { - this.onSetLoading(dict['loading']); - } - if ('loadProgress' in dict) { - this.onSetLoadingProgress(dict['loadProgress']); - } - if ('suggestions' in dict) { - this.onSetOmniboxSuggestions(dict['suggestions']); - } - if ('setTabs' in dict) { - this.onSetTabs(dict['setTabs']); - } - if ('updateTab' in dict) { - this.onUpdateTab(dict['updateTab']); - } - if ('removeTab' in dict) { - this.onRemoveTab(dict['removeTab']); - } - if ('canGoBack' in dict) { - this.onSetHistoryButtonsEnabled(dict['canGoBack'], dict['canGoForward']); - } - - this.onCommandHandlerFinished() - } -};
diff --git a/chrome/browser/resources/vr_shell/vr_shell_ui_scene.js b/chrome/browser/resources/vr_shell/vr_shell_ui_scene.js deleted file mode 100644 index 1040382..0000000 --- a/chrome/browser/resources/vr_shell/vr_shell_ui_scene.js +++ /dev/null
@@ -1,185 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -var scene = {}; - -/** - * The scene class assists in managing element and animations in the UI. It - * allows UI update API commands to be queued in batches, and manages allocation - * of element and animation IDs. - * - * Examples: - * - * var ui = new scene.Scene(); - * - * // Add an element. - * var el = new api.UiElement(100, 200, 50, 50); - * el.setSize(buttonWidth, buttonHeight); - * - * // Anchor it to the bottom of the content quad. - * el.setParentId(contentQuadId); - * el.setAnchoring(api.XAnchoring.XNONE, api.YAnchoring.YBOTTOM); - * - * // Place it just below the content quad edge. - * el.setTranslation(0, -0.2, 0.0); - * - * // Add it to the ui. - * var buttonId = ui.addElement(el); - * ui.flush(); - * - * // Make the button twice as big. - * var update = new api.UiElementUpdate(); - * update.setSize(bunttonWidth * 2, buttonHeight * 2); - * ui.updateElement(buttonId, update); - * ui.flush(); - * - * // Animate the button size back to its original size, over 250 ms. - * var resize = new api.Animation(buttonId, 250); - * resize.setSize(buttonWidth, buttonHeight); - * ui.addAnimation(resize); - * ui.flush(); - * - * @struct - */ -scene.Scene = class { - constructor() { - /** @private {number} */ - this.idIndex = 1; - /** @private {Array<Object>} */ - this.commands = []; - /** @private {!Set<number>} */ - this.elements = new Set(); - /** @private {!Object} */ - this.animations = []; - } - - /** - * Flush all queued commands to native. - */ - flush() { - api.sendCommands(this.commands); - this.commands = []; - } - - /** - * Add a new UiElementUpdate to the scene, returning the ID assigned. - * @param {api.UiElementUpdate} element - */ - addElement(element) { - var id = this.idIndex++; - element.setId(id); - this.commands.push( - {'type': api.Command.ADD_ELEMENT, 'data': element.properties}); - this.elements.add(id); - return id; - } - - /** - * Update an existing element, according to a UiElementUpdate object. - * @param {number} id - * @param {api.UiElementUpdate} update - */ - updateElement(id, update) { - // To-do: Make sure ID exists. - update.setId(id); - this.commands.push( - {'type': api.Command.UPDATE_ELEMENT, 'data': update.properties}); - } - - /** - * Remove an element from the scene. - * @param {number} id - */ - removeElement(id) { - // To-do: Make sure ID exists. - this.commands.push( - {'type': api.Command.REMOVE_ELEMENT, 'data': {'id': id}}); - this.elements.delete(id); - } - - /** - * Add a new Animation to the scene, returning the ID assigned. - * @param {api.Animation} animation - */ - addAnimation(animation) { - var id = this.idIndex++; - animation.setId(id); - this.commands.push({'type': api.Command.ADD_ANIMATION, 'data': animation}); - this.animations[id] = animation.meshId; - return id; - } - - /** - * Remove an animation from the scene. - * - * Note that animations are flushed when they complete and are not required - * to be removed. Also new animations of the same type will effectively - * override the original so there is no need to remove in that scenario - * either. - * - * @param {number} id - */ - removeAnimation(id) { - // To-do: Make sure ID exists. - this.commands.push({ - 'type': api.Command.REMOVE_ANIMATION, - 'data': {'id': id, 'meshId': this.animations[id]} - }); - delete this.animations[id]; - } - - /** - * Configure scene parameters. - * @param {api.SceneConfiguration} configuration - */ - configureScene(configuration) { - this.commands.push({ - 'type': api.Command.CONFIGURE_SCENE, - 'data': configuration.getCommandPayload(), - }); - } - - /** - * @param {{r: number, b: number, g: number, a: number}} color - */ - setBackgroundColor(color) { - let configuration = new api.SceneConfiguration(); - configuration.setBackgroundColor(color); - this.configureScene(configuration); - } - - /** - * @param {number} distance - */ - setBackgroundDistance(distance) { - let configuration = new api.SceneConfiguration(); - configuration.setBackgroundDistance(distance); - this.configureScene(configuration); - } - - /** - * @param {boolean} enabled - */ - setWebVrRenderingModeEnabled(enabled) { - let configuration = new api.SceneConfiguration(); - configuration.setWebVrRenderingModeEnabled(enabled); - this.configureScene(configuration); - } - - /** - * Purge all elements in the scene. - */ - purge() { - var ids = Object.keys(this.animations); - for (let id_key of ids) { - var id = parseInt(id_key, 10); - this.removeAnimation(id); - } - var ids = this.elements.values(); - for (let id of ids) { - this.removeElement(id); - } - this.flush(); - } -};
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn index 0a43bf42..530dc71 100644 --- a/chrome/browser/ui/BUILD.gn +++ b/chrome/browser/ui/BUILD.gn
@@ -8,7 +8,6 @@ import("//build/config/ui.gni") import("//build/split_static_library.gni") import("//chrome/common/features.gni") -import("//device/vr/features.gni") import("//extensions/features/features.gni") import("//media/media_options.gni") import("//ppapi/features/features.gni") @@ -589,7 +588,6 @@ "//device/base", "//device/bluetooth/public/interfaces:experimental_interfaces", "//device/usb", - "//device/vr:features", "//extensions/features", "//media", "//net:net", @@ -1438,7 +1436,6 @@ } if (toolkit_views) { sources += [ - "autofill/save_card_bubble_controller.h", "autofill/save_card_bubble_controller_impl.cc", "autofill/save_card_bubble_controller_impl.h", "autofill/save_card_bubble_view.h", @@ -1466,6 +1463,8 @@ "views/apps/native_app_window_frame_view_mac.mm", "views/autofill/card_unmask_prompt_views.cc", "views/autofill/card_unmask_prompt_views.h", + "views/autofill/view_util.cc", + "views/autofill/view_util.h", "views/bookmarks/bookmark_bubble_view.cc", "views/bookmarks/bookmark_bubble_view.h", "views/bookmarks/bookmark_editor_view.cc", @@ -2321,20 +2320,6 @@ "webui/webapks_ui.cc", "webui/webapks_ui.h", ] - if (enable_vr) { - sources += [ - "webui/vr_shell/vr_shell_ui_message_handler.cc", - "webui/vr_shell/vr_shell_ui_message_handler.h", - "webui/vr_shell/vr_shell_ui_ui.cc", - "webui/vr_shell/vr_shell_ui_ui.h", - ] - configs += [ "//third_party/gvr-android-sdk:libgvr_config" ] - deps += [ "//chrome/browser/android/vr_shell:vr_common" ] - } - if (enable_vr_shell_ui_dev) { - assert(enable_vr) - defines += [ "ENABLE_VR_SHELL_UI_DEV" ] - } deps += [ "//chrome/browser:jni_headers", "//components/web_contents_delegate_android",
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.cc b/chrome/browser/ui/autofill/chrome_autofill_client.cc index 436b8d6..a3da1293 100644 --- a/chrome/browser/ui/autofill/chrome_autofill_client.cc +++ b/chrome/browser/ui/autofill/chrome_autofill_client.cc
@@ -24,7 +24,6 @@ #include "chrome/browser/ui/autofill/autofill_popup_controller_impl.h" #include "chrome/browser/ui/autofill/create_card_unmask_prompt_view.h" #include "chrome/browser/ui/autofill/credit_card_scanner_controller.h" -#include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/chrome_pages.h" #include "chrome/browser/ui/tabs/tab_strip_model_observer.h" @@ -60,6 +59,7 @@ #include "components/infobars/core/infobar.h" #include "content/public/browser/android/content_view_core.h" #else // !OS_ANDROID +#include "chrome/browser/ui/autofill/save_card_bubble_controller_impl.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_commands.h" #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" @@ -152,6 +152,14 @@ return g_browser_process->ukm_service(); } +SaveCardBubbleController* ChromeAutofillClient::GetSaveCardBubbleController() { +#if defined(OS_ANDROID) + return nullptr; +#else + return SaveCardBubbleControllerImpl::FromWebContents(web_contents()); +#endif +} + void ChromeAutofillClient::ShowAutofillSettings() { #if defined(OS_ANDROID) chrome::android::PreferencesLauncher::ShowAutofillSettings(); @@ -198,6 +206,7 @@ void ChromeAutofillClient::ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) { #if defined(OS_ANDROID) InfoBarService::FromWebContents(web_contents()) @@ -209,7 +218,8 @@ autofill::SaveCardBubbleControllerImpl::CreateForWebContents(web_contents()); autofill::SaveCardBubbleControllerImpl* controller = autofill::SaveCardBubbleControllerImpl::FromWebContents(web_contents()); - controller->ShowBubbleForUpload(card, std::move(legal_message), callback); + controller->ShowBubbleForUpload(card, std::move(legal_message), + should_cvc_be_requested, callback); #endif }
diff --git a/chrome/browser/ui/autofill/chrome_autofill_client.h b/chrome/browser/ui/autofill/chrome_autofill_client.h index ea70532..0ca505f 100644 --- a/chrome/browser/ui/autofill/chrome_autofill_client.h +++ b/chrome/browser/ui/autofill/chrome_autofill_client.h
@@ -45,6 +45,7 @@ IdentityProvider* GetIdentityProvider() override; rappor::RapporServiceImpl* GetRapporServiceImpl() override; ukm::UkmService* GetUkmService() override; + SaveCardBubbleController* GetSaveCardBubbleController() override; void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, UnmaskCardReason reason, @@ -55,6 +56,7 @@ void ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) override; void ConfirmCreditCardFillAssist(const CreditCard& card, const base::Closure& callback) override;
diff --git a/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc b/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc index 4165569..78ad6a8 100644 --- a/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc +++ b/chrome/browser/ui/autofill/save_card_bubble_controller_impl.cc
@@ -12,7 +12,9 @@ #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/location_bar/location_bar.h" #include "components/autofill/core/browser/autofill_metrics.h" +#include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_constants.h" +#include "components/grit/components_scaled_resources.h" #include "components/strings/grit/components_strings.h" #include "content/public/browser/navigation_handle.h" #include "ui/base/l10n/l10n_util.h" @@ -47,6 +49,7 @@ const base::Closure& save_card_callback) { is_uploading_ = false; is_reshow_ = false; + should_cvc_be_requested_ = false; legal_message_lines_.clear(); AutofillMetrics::LogSaveCardPromptMetric( @@ -61,9 +64,11 @@ void SaveCardBubbleControllerImpl::ShowBubbleForUpload( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& save_card_callback) { is_uploading_ = true; is_reshow_ = false; + should_cvc_be_requested_ = should_cvc_be_requested; AutofillMetrics::LogSaveCardPromptMetric( AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, is_uploading_, is_reshow_); @@ -121,7 +126,28 @@ return card_; } -void SaveCardBubbleControllerImpl::OnSaveButton() { +int SaveCardBubbleControllerImpl::GetCvcImageResourceId() const { + return card_.type() == kAmericanExpressCard ? IDR_CREDIT_CARD_CVC_HINT_AMEX + : IDR_CREDIT_CARD_CVC_HINT; +} + +bool SaveCardBubbleControllerImpl::ShouldRequestCvcFromUser() const { + return should_cvc_be_requested_; +} + +base::string16 SaveCardBubbleControllerImpl::GetCvcEnteredByUser() const { + DCHECK(!cvc_entered_by_user_.empty()); + return cvc_entered_by_user_; +} + +void SaveCardBubbleControllerImpl::OnSaveButton(const base::string16& cvc) { + if (!cvc.empty()) { + // Record the CVC entered by the user so it can be sent in the final + // request. + DCHECK(ShouldRequestCvcFromUser()); + DCHECK(InputCvcIsValid(cvc)); + base::TrimWhitespace(cvc, base::TRIM_ALL, &cvc_entered_by_user_); + } save_card_callback_.Run(); save_card_callback_.Reset(); AutofillMetrics::LogSaveCardPromptMetric( @@ -159,6 +185,13 @@ return legal_message_lines_; } +bool SaveCardBubbleControllerImpl::InputCvcIsValid( + const base::string16& input_text) const { + base::string16 trimmed_text; + base::TrimWhitespace(input_text, base::TRIM_ALL, &trimmed_text); + return IsValidCreditCardSecurityCode(trimmed_text, card_.type()); +} + base::TimeDelta SaveCardBubbleControllerImpl::Elapsed() const { return timer_->Elapsed(); }
diff --git a/chrome/browser/ui/autofill/save_card_bubble_controller_impl.h b/chrome/browser/ui/autofill/save_card_bubble_controller_impl.h index d5268d0..1f5382f 100644 --- a/chrome/browser/ui/autofill/save_card_bubble_controller_impl.h +++ b/chrome/browser/ui/autofill/save_card_bubble_controller_impl.h
@@ -9,8 +9,8 @@ #include "base/macros.h" #include "base/timer/elapsed_timer.h" -#include "chrome/browser/ui/autofill/save_card_bubble_controller.h" #include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" #include "content/public/browser/web_contents_observer.h" #include "content/public/browser/web_contents_user_data.h" @@ -32,8 +32,11 @@ // Sets up the controller for upload and shows the bubble. // |save_card_callback| will be invoked if and when the Save button is // pressed. The contents of |legal_message| will be displayed in the bubble. + // A field requesting CVC will appear in the bubble if + // |should_cvc_be_requested| is true. void ShowBubbleForUpload(const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& save_card_callback); void HideBubble(); @@ -49,7 +52,10 @@ base::string16 GetWindowTitle() const override; base::string16 GetExplanatoryMessage() const override; const CreditCard GetCard() const override; - void OnSaveButton() override; + int GetCvcImageResourceId() const override; + bool ShouldRequestCvcFromUser() const override; + base::string16 GetCvcEnteredByUser() const override; + void OnSaveButton(const base::string16& cvc = base::string16()) override; void OnCancelButton() override; void OnLearnMoreClicked() override; void OnLegalMessageLinkClicked(const GURL& url) override; @@ -57,6 +63,11 @@ const LegalMessageLines& GetLegalMessageLines() const override; + // Used to check if an entered CVC value is a valid CVC for the current card. + // Valid CVCs are a certain length for their card type (4 for AMEX, 3 + // otherwise) and are comprised only of numbers. + bool InputCvcIsValid(const base::string16& input_text) const override; + protected: explicit SaveCardBubbleControllerImpl(content::WebContents* web_contents); ~SaveCardBubbleControllerImpl() override; @@ -88,10 +99,16 @@ base::Closure save_card_callback_; // Governs whether the upload or local save version of the UI should be shown. - bool is_uploading_; + bool is_uploading_{false}; // Whether ReshowBubble() has been called since ShowBubbleFor*() was called. - bool is_reshow_; + bool is_reshow_{false}; + + // Whether the upload save version of the UI should ask the user for CVC. + bool should_cvc_be_requested_{false}; + + // The value of the CVC entered by the user (if it was requested). + base::string16 cvc_entered_by_user_; // Contains the details of the card that will be saved if the user accepts. CreditCard card_;
diff --git a/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc b/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc index 96893557..a1121ec0e 100644 --- a/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc +++ b/chrome/browser/ui/autofill/save_card_bubble_controller_impl_unittest.cc
@@ -69,7 +69,8 @@ return new SaveCardBubbleTestBrowserWindow(); } - void SetLegalMessage(const std::string& message_json) { + void SetLegalMessage(const std::string& message_json, + bool should_cvc_be_requested = false) { std::unique_ptr<base::Value> value(base::JSONReader::Read(message_json)); ASSERT_TRUE(value); base::DictionaryValue* dictionary; @@ -77,6 +78,7 @@ std::unique_ptr<base::DictionaryValue> legal_message = dictionary->CreateDeepCopy(); controller()->ShowBubbleForUpload(CreditCard(), std::move(legal_message), + should_cvc_be_requested, base::Bind(&SaveCardCallback)); } @@ -85,13 +87,14 @@ base::Bind(&SaveCardCallback)); } - void ShowUploadBubble() { + void ShowUploadBubble(bool should_cvc_be_requested = false) { SetLegalMessage( "{" " \"line\" : [ {" " \"template\": \"This is the entire message.\"" " } ]" - "}"); + "}", + should_cvc_be_requested); } void CloseAndReshowBubble() { @@ -140,6 +143,18 @@ EXPECT_TRUE(controller()->GetLegalMessageLines().empty()); } +TEST_F(SaveCardBubbleControllerImplTest, + PropagateShouldRequestCvcFromUserWhenFalse) { + ShowUploadBubble(); + EXPECT_FALSE(controller()->ShouldRequestCvcFromUser()); +} + +TEST_F(SaveCardBubbleControllerImplTest, + PropagateShouldRequestCvcFromUserWhenTrue) { + ShowUploadBubble(true /* should_cvc_be_requested */); + EXPECT_TRUE(controller()->ShouldRequestCvcFromUser()); +} + TEST_F(SaveCardBubbleControllerImplTest, Metrics_Local_FirstShow_ShowBubble) { base::HistogramTester histogram_tester; ShowLocalBubble(); @@ -164,7 +179,8 @@ Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1))); } -TEST_F(SaveCardBubbleControllerImplTest, Metrics_Upload_FirstShow_ShowBubble) { +TEST_F(SaveCardBubbleControllerImplTest, + Metrics_Upload_FirstShow_ShowBubble_NotRequestCvc) { base::HistogramTester histogram_tester; ShowUploadBubble(); @@ -175,6 +191,18 @@ Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1))); } +TEST_F(SaveCardBubbleControllerImplTest, + Metrics_Upload_FirstShow_ShowBubble_RequestCvc) { + base::HistogramTester histogram_tester; + ShowUploadBubble(true /* should_cvc_be_requested */); + + EXPECT_THAT( + histogram_tester.GetAllSamples( + "Autofill.SaveCreditCardPrompt.Upload.FirstShow"), + ElementsAre(Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOW_REQUESTED, 1), + Bucket(AutofillMetrics::SAVE_CARD_PROMPT_SHOWN, 1))); +} + TEST_F(SaveCardBubbleControllerImplTest, Metrics_Upload_Reshows_ShowBubble) { ShowUploadBubble();
diff --git a/chrome/browser/ui/cocoa/autofill/save_card_bubble_view_bridge.mm b/chrome/browser/ui/cocoa/autofill/save_card_bubble_view_bridge.mm index 2ff18c8..7b0ca85 100644 --- a/chrome/browser/ui/cocoa/autofill/save_card_bubble_view_bridge.mm +++ b/chrome/browser/ui/cocoa/autofill/save_card_bubble_view_bridge.mm
@@ -6,12 +6,12 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" -#include "chrome/browser/ui/autofill/save_card_bubble_controller.h" #import "chrome/browser/ui/cocoa/browser_window_controller.h" #include "chrome/browser/ui/cocoa/chrome_style.h" #import "chrome/browser/ui/cocoa/info_bubble_view.h" #import "chrome/browser/ui/cocoa/info_bubble_window.h" #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" #include "components/strings/grit/components_strings.h" #include "skia/ext/skia_utils_mac.h" #include "ui/base/cocoa/cocoa_base_utils.h" @@ -71,7 +71,7 @@ void SaveCardBubbleViewBridge::OnSaveButton() { if (controller_) - controller_->OnSaveButton(); + controller_->OnSaveButton(base::string16()); Hide(); }
diff --git a/chrome/browser/ui/cocoa/autofill/save_card_bubble_view_unittest.mm b/chrome/browser/ui/cocoa/autofill/save_card_bubble_view_unittest.mm index ef04955..e8e200c 100644 --- a/chrome/browser/ui/cocoa/autofill/save_card_bubble_view_unittest.mm +++ b/chrome/browser/ui/cocoa/autofill/save_card_bubble_view_unittest.mm
@@ -8,11 +8,12 @@ #include "base/json/json_reader.h" #include "base/values.h" -#include "chrome/browser/ui/autofill/save_card_bubble_controller.h" #import "chrome/browser/ui/cocoa/autofill/save_card_bubble_view_bridge.h" #import "chrome/browser/ui/cocoa/browser_window_controller.h" #include "chrome/browser/ui/cocoa/test/cocoa_profile_test.h" #include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #import "ui/events/test/cocoa_test_event_utils.h" @@ -33,17 +34,16 @@ } // SaveCardBubbleController: - base::string16 GetWindowTitle() const override { return base::string16(); } + MOCK_CONST_METHOD0(GetWindowTitle, base::string16()); + MOCK_CONST_METHOD0(GetExplanatoryMessage, base::string16()); + MOCK_CONST_METHOD0(GetCard, const CreditCard()); + MOCK_CONST_METHOD0(GetCvcImageResourceId, int()); + MOCK_CONST_METHOD0(ShouldRequestCvcFromUser, bool()); + MOCK_CONST_METHOD0(GetCvcEnteredByUser, base::string16()); - base::string16 GetExplanatoryMessage() const override { - return base::string16(); + void OnSaveButton(const base::string16& cvc) override { + on_save_button_was_called_ = true; } - - const CreditCard GetCard() const override { - return CreditCard(); - } - - void OnSaveButton() override { on_save_button_was_called_ = true; } void OnCancelButton() override { on_cancel_button_was_called_ = true; } void OnLearnMoreClicked() override { on_learn_more_was_called_ = true; } void OnLegalMessageLinkClicked(const GURL& url) override { @@ -56,6 +56,8 @@ return lines_; } + MOCK_CONST_METHOD1(InputCvcIsValid, bool(const base::string16& input_text)); + // Testing state. bool on_save_button_was_called() { return on_save_button_was_called_; } bool on_cancel_button_was_called() { return on_cancel_button_was_called_; }
diff --git a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc index 2d35985..10d7e221 100644 --- a/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc +++ b/chrome/browser/ui/views/autofill/card_unmask_prompt_views.cc
@@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "chrome/browser/ui/autofill/create_card_unmask_prompt_view.h" +#include "chrome/browser/ui/views/autofill/view_util.h" #include "chrome/grit/generated_resources.h" #include "chrome/grit/theme_resources.h" #include "components/autofill/core/browser/ui/card_unmask_prompt_controller.h" @@ -436,12 +437,8 @@ input_row_->child_at(i)->SetVisible(false); } - cvc_input_ = new views::Textfield(); - cvc_input_->set_placeholder_text( - l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC)); + cvc_input_ = CreateCvcTextfield(); cvc_input_->set_controller(this); - cvc_input_->set_default_width_in_chars(8); - cvc_input_->SetTextInputType(ui::TextInputType::TEXT_INPUT_TYPE_NUMBER); input_row_->AddChildView(cvc_input_); views::ImageView* cvc_image = new views::ImageView();
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views.cc b/chrome/browser/ui/views/autofill/save_card_bubble_views.cc index 2549a175..ecddc83 100644 --- a/chrome/browser/ui/views/autofill/save_card_bubble_views.cc +++ b/chrome/browser/ui/views/autofill/save_card_bubble_views.cc
@@ -8,9 +8,10 @@ #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" -#include "chrome/browser/ui/autofill/save_card_bubble_controller.h" +#include "chrome/browser/ui/views/autofill/view_util.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/legal_message_line.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" #include "components/strings/grit/components_strings.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/resource/resource_bundle.h" @@ -21,14 +22,16 @@ #include "ui/views/controls/label.h" #include "ui/views/controls/link.h" #include "ui/views/controls/styled_label.h" +#include "ui/views/controls/textfield/textfield.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_constants.h" +#include "ui/views/window/dialog_client_view.h" namespace autofill { namespace { -// Fixed width of the bubble. +// Fixed width of the bubble, in dip. const int kBubbleWidth = 395; std::unique_ptr<views::StyledLabel> CreateLegalMessageLineLabel( @@ -50,6 +53,7 @@ SaveCardBubbleController* controller) : LocationBarBubbleDelegateView(anchor_view, web_contents), controller_(controller), + cvc_textfield_(nullptr), learn_more_link_(nullptr) { DCHECK(controller); views::BubbleDialogDelegateView::CreateBubble(this); @@ -92,7 +96,8 @@ bool SaveCardBubbleViews::Accept() { if (controller_) - controller_->OnSaveButton(); + controller_->OnSaveButton(cvc_textfield_ ? cvc_textfield_->text() + : base::string16()); return true; } @@ -200,6 +205,10 @@ description_view->AddChildView( new views::Label(card.AbbreviatedExpirationDateForDisplay())); + // Optionally add CVC request field if CVC was missing. + if (controller_->ShouldRequestCvcFromUser()) + view->AddChildView(CreateRequestCvcView().release()); + // Optionally add label that will contain an explanation for upload. base::string16 explanation = controller_->GetExplanatoryMessage(); if (!explanation.empty()) { @@ -212,6 +221,42 @@ return view; } +std::unique_ptr<views::View> SaveCardBubbleViews::CreateRequestCvcView() { + std::unique_ptr<View> request_cvc_view = base::MakeUnique<views::View>(); + request_cvc_view->SetLayoutManager(new views::BoxLayout( + views::BoxLayout::kHorizontal, 0, 0, views::kRelatedButtonHSpacing)); + + DCHECK(!cvc_textfield_); + cvc_textfield_ = CreateCvcTextfield(); + cvc_textfield_->set_controller(this); + request_cvc_view->AddChildView(cvc_textfield_); + + views::ImageView* cvc_image = new views::ImageView(); + ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); + cvc_image->SetImage( + rb.GetImageSkiaNamed(controller_->GetCvcImageResourceId())); + request_cvc_view->AddChildView(cvc_image); + + request_cvc_view->AddChildView(new views::Label( + l10n_util::GetStringUTF16(IDS_AUTOFILL_SAVE_CARD_PROMPT_ENTER_CVC))); + return request_cvc_view; +} + +bool SaveCardBubbleViews::IsDialogButtonEnabled(ui::DialogButton button) const { + if (button == ui::DIALOG_BUTTON_CANCEL) + return true; + + DCHECK_EQ(ui::DIALOG_BUTTON_OK, button); + return !cvc_textfield_ || + controller_->InputCvcIsValid(cvc_textfield_->text()); +} + +void SaveCardBubbleViews::ContentsChanged(views::Textfield* sender, + const base::string16& new_contents) { + DCHECK_EQ(cvc_textfield_, sender); + GetDialogClientView()->UpdateDialogButtons(); +} + void SaveCardBubbleViews::Init() { SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0)); AddChildView(CreateMainContentView().release());
diff --git a/chrome/browser/ui/views/autofill/save_card_bubble_views.h b/chrome/browser/ui/views/autofill/save_card_bubble_views.h index a7390a2..ca03dfc 100644 --- a/chrome/browser/ui/views/autofill/save_card_bubble_views.h +++ b/chrome/browser/ui/views/autofill/save_card_bubble_views.h
@@ -6,11 +6,12 @@ #define CHROME_BROWSER_UI_VIEWS_AUTOFILL_SAVE_CARD_BUBBLE_VIEWS_H_ #include "base/macros.h" -#include "chrome/browser/ui/autofill/save_card_bubble_controller.h" #include "chrome/browser/ui/autofill/save_card_bubble_view.h" #include "chrome/browser/ui/views/location_bar/location_bar_bubble_delegate_view.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" #include "ui/views/controls/link_listener.h" #include "ui/views/controls/styled_label_listener.h" +#include "ui/views/controls/textfield/textfield_controller.h" namespace content { class WebContents; @@ -19,6 +20,7 @@ namespace views { class Link; class StyledLabel; +class Textfield; } namespace autofill { @@ -29,7 +31,8 @@ class SaveCardBubbleViews : public SaveCardBubbleView, public LocationBarBubbleDelegateView, public views::LinkListener, - public views::StyledLabelListener { + public views::StyledLabelListener, + public views::TextfieldController { public: // Bubble will be anchored to |anchor_view|. SaveCardBubbleViews(views::View* anchor_view, @@ -49,6 +52,7 @@ bool Close() override; int GetDialogButtons() const override; base::string16 GetDialogButtonLabel(ui::DialogButton button) const override; + bool IsDialogButtonEnabled(ui::DialogButton button) const override; bool ShouldDefaultButtonBeBlue() const override; // views::View @@ -66,16 +70,23 @@ const gfx::Range& range, int event_flags) override; + // views::TextfieldController + void ContentsChanged(views::Textfield* sender, + const base::string16& new_contents) override; + private: ~SaveCardBubbleViews() override; std::unique_ptr<views::View> CreateMainContentView(); + std::unique_ptr<views::View> CreateRequestCvcView(); // views::BubbleDialogDelegateView void Init() override; SaveCardBubbleController* controller_; // Weak reference. + views::Textfield* cvc_textfield_; + views::Link* learn_more_link_; DISALLOW_COPY_AND_ASSIGN(SaveCardBubbleViews);
diff --git a/chrome/browser/ui/views/autofill/view_util.cc b/chrome/browser/ui/views/autofill/view_util.cc new file mode 100644 index 0000000..9e3a1ba --- /dev/null +++ b/chrome/browser/ui/views/autofill/view_util.cc
@@ -0,0 +1,22 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/ui/views/autofill/view_util.h" + +#include "components/strings/grit/components_strings.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/views/controls/textfield/textfield.h" + +namespace autofill { + +views::Textfield* CreateCvcTextfield() { + views::Textfield* textfield = new views::Textfield(); + textfield->set_placeholder_text( + l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC)); + textfield->set_default_width_in_chars(8); + textfield->SetTextInputType(ui::TextInputType::TEXT_INPUT_TYPE_NUMBER); + return textfield; +} + +} // namespace autofill
diff --git a/chrome/browser/ui/views/autofill/view_util.h b/chrome/browser/ui/views/autofill/view_util.h new file mode 100644 index 0000000..64c7abb --- /dev/null +++ b/chrome/browser/ui/views/autofill/view_util.h
@@ -0,0 +1,17 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_UI_VIEWS_AUTOFILL_VIEW_UTIL_H_ +#define CHROME_BROWSER_UI_VIEWS_AUTOFILL_VIEW_UTIL_H_ + +#include "ui/views/controls/textfield/textfield.h" + +namespace autofill { + +// Creates and returns a small Textfield intended to be used for CVC entry. +views::Textfield* CreateCvcTextfield(); + +} // namespace autofill + +#endif // CHROME_BROWSER_UI_VIEWS_AUTOFILL_VIEW_UTIL_H_
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc index c7706ff..ef5295b 100644 --- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc +++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.cc
@@ -186,6 +186,40 @@ return layout; } +std::unique_ptr<views::View> CreateCheckingSpinnerView() { + std::unique_ptr<views::View> container = base::MakeUnique<views::View>(); + + std::unique_ptr<views::Throbber> throbber = + base::MakeUnique<views::Throbber>(); + throbber->Start(); + std::unique_ptr<views::GridLayout> layout = + base::MakeUnique<views::GridLayout>(container.get()); + views::ColumnSet* throbber_columns = layout->AddColumnSet(0); + throbber_columns->AddPaddingColumn(0.5, 0); + throbber_columns->AddColumn(views::GridLayout::Alignment::CENTER, + views::GridLayout::Alignment::TRAILING, 0, + views::GridLayout::SizeType::USE_PREF, 0, 0); + throbber_columns->AddPaddingColumn(0.5, 0); + + views::ColumnSet* label_columns = layout->AddColumnSet(1); + label_columns->AddPaddingColumn(0.5, 0); + label_columns->AddColumn(views::GridLayout::Alignment::CENTER, + views::GridLayout::Alignment::LEADING, 0, + views::GridLayout::SizeType::USE_PREF, 0, 0); + label_columns->AddPaddingColumn(0.5, 0); + + layout->StartRow(0.5, 0); + layout->AddView(throbber.release()); + + layout->StartRow(0.5, 1); + layout->AddView(new views::Label( + l10n_util::GetStringUTF16(IDS_PAYMENTS_CHECKING_OPTION))); + + container->SetLayoutManager(layout.release()); + + return container; +} + } // namespace PaymentSheetViewController::PaymentSheetViewController( @@ -194,7 +228,8 @@ PaymentRequestDialogView* dialog) : PaymentRequestSheetController(spec, state, dialog), pay_button_(nullptr), - widest_name_column_view_width_(ComputeWidestNameColumnViewWidth()) { + widest_name_column_view_width_(ComputeWidestNameColumnViewWidth()), + current_update_reason_(PaymentRequestSpec::UpdateReason::NONE) { spec->AddObserver(this); state->AddObserver(this); } @@ -204,7 +239,14 @@ state()->RemoveObserver(this); } +void PaymentSheetViewController::OnStartUpdating( + PaymentRequestSpec::UpdateReason reason) { + current_update_reason_ = reason; + UpdateContentView(); +} + void PaymentSheetViewController::OnSpecUpdated() { + current_update_reason_ = PaymentRequestSpec::UpdateReason::NONE; UpdateContentView(); } @@ -394,12 +436,17 @@ std::unique_ptr<views::View> PaymentSheetViewController::CreateShippingSectionContent() { - auto* profile = state()->selected_shipping_profile(); + if (current_update_reason_ == + PaymentRequestSpec::UpdateReason::SHIPPING_ADDRESS) { + return CreateCheckingSpinnerView(); + } else { + auto* profile = state()->selected_shipping_profile(); - return profile ? GetShippingAddressLabel(AddressStyleType::SUMMARY, - state()->GetApplicationLocale(), - *profile) - : base::MakeUnique<views::Label>(base::string16()); + return profile ? GetShippingAddressLabel(AddressStyleType::SUMMARY, + state()->GetApplicationLocale(), + *profile) + : base::MakeUnique<views::Label>(base::string16()); + } } // Creates the Shipping row, which contains a "Shipping address" label, the @@ -501,16 +548,25 @@ PaymentSheetViewController::CreateShippingOptionRow() { mojom::PaymentShippingOption* selected_option = spec()->selected_shipping_option(); - if (!selected_option) + if (!selected_option && + current_update_reason_ != + PaymentRequestSpec::UpdateReason::SHIPPING_OPTION) { return nullptr; + } - std::unique_ptr<views::View> option_label = CreateShippingOptionLabel( - selected_option, selected_option ? spec()->GetFormattedCurrencyAmount( - selected_option->amount->value) - : base::ASCIIToUTF16("")); + std::unique_ptr<views::View> option_row_content = + current_update_reason_ == + PaymentRequestSpec::UpdateReason::SHIPPING_OPTION + ? CreateCheckingSpinnerView() + : CreateShippingOptionLabel(selected_option, + selected_option + ? spec()->GetFormattedCurrencyAmount( + selected_option->amount->value) + : base::ASCIIToUTF16("")); + std::unique_ptr<views::Button> section = CreatePaymentSheetRow( this, GetShippingOptionSectionString(spec()->shipping_type()), - std::move(option_label), std::unique_ptr<views::View>(nullptr), + std::move(option_row_content), std::unique_ptr<views::View>(nullptr), widest_name_column_view_width_); section->set_tag(static_cast<int>( PaymentSheetViewControllerTags::SHOW_SHIPPING_OPTION_BUTTON));
diff --git a/chrome/browser/ui/views/payments/payment_sheet_view_controller.h b/chrome/browser/ui/views/payments/payment_sheet_view_controller.h index 430b04c..bf202f2 100644 --- a/chrome/browser/ui/views/payments/payment_sheet_view_controller.h +++ b/chrome/browser/ui/views/payments/payment_sheet_view_controller.h
@@ -30,6 +30,7 @@ // PaymentRequestSpec::Observer: void OnInvalidSpecProvided() override {} + void OnStartUpdating(PaymentRequestSpec::UpdateReason reason) override; void OnSpecUpdated() override; // PaymentRequestState::Observer: @@ -57,6 +58,7 @@ views::Button* pay_button_; const int widest_name_column_view_width_; + PaymentRequestSpec::UpdateReason current_update_reason_; DISALLOW_COPY_AND_ASSIGN(PaymentSheetViewController); };
diff --git a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc index 53e6ac0..03fb00a 100644 --- a/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc +++ b/chrome/browser/ui/webui/chrome_web_ui_controller_factory.cc
@@ -81,7 +81,6 @@ #include "content/public/browser/web_ui.h" #include "content/public/common/content_client.h" #include "content/public/common/url_utils.h" -#include "device/vr/features.h" #include "extensions/features/features.h" #include "media/media_features.h" #include "ppapi/features/features.h" @@ -117,9 +116,6 @@ #include "chrome/browser/ui/webui/popular_sites_internals_ui.h" #include "chrome/browser/ui/webui/snippets_internals_ui.h" #include "chrome/browser/ui/webui/webapks_ui.h" -#if BUILDFLAG(ENABLE_VR) -#include "chrome/browser/ui/webui/vr_shell/vr_shell_ui_ui.h" -#endif #else #include "chrome/browser/signin/easy_unlock_service.h" #include "chrome/browser/signin/easy_unlock_service_factory.h" @@ -530,10 +526,6 @@ return &NewWebUI<SnippetsInternalsUI>; if (url.host_piece() == chrome::kChromeUIWebApksHost) return &NewWebUI<WebApksUI>; -#if BUILDFLAG(ENABLE_VR) - if (url.host_piece() == chrome::kChromeUIVrShellUIHost) - return &NewWebUI<VrShellUIUI>; -#endif #else if (url.SchemeIs(content::kChromeDevToolsScheme)) { if (!DevToolsUIBindings::IsValidFrontendURL(url))
diff --git a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc index 729ff1a2..bfd56ed 100644 --- a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc +++ b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.cc
@@ -7,17 +7,21 @@ #include <string> #include <utility> +#include "ash/system/devicetype_utils.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/sys_info.h" #include "base/task_scheduler/post_task.h" #include "chrome/browser/lifetime/application_lifetime.h" +#include "chrome/grit/generated_resources.h" #include "chromeos/chromeos_switches.h" #include "chromeos/cryptohome/homedir_methods.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/power_manager/power_supply_properties.pb.h" #include "chromeos/dbus/power_manager_client.h" +#include "components/login/localized_values_builder.h" +#include "ui/base/text/bytes_formatting.h" namespace { @@ -95,7 +99,49 @@ } void EncryptionMigrationScreenHandler::DeclareLocalizedValues( - ::login::LocalizedValuesBuilder* builder) {} + ::login::LocalizedValuesBuilder* builder) { + builder->Add("migrationReadyTitle", IDS_ENCRYPTION_MIGRATION_READY_TITLE); + builder->Add("migrationReadyDescription", + ash::SubstituteChromeOSDeviceType( + IDS_ENCRYPTION_MIGRATION_READY_DESCRIPTION)); + builder->Add("migrationMigratingTitle", + IDS_ENCRYPTION_MIGRATION_MIGRATING_TITLE); + builder->Add("migrationMigratingDescription", + ash::SubstituteChromeOSDeviceType( + IDS_ENCRYPTION_MIGRATION_MIGRATING_DESCRIPTION)); + builder->Add("migrationProgressLabel", + IDS_ENCRYPTION_MIGRATION_PROGRESS_LABEL); + builder->Add("migrationBatteryWarningLabel", + IDS_ENCRYPTION_MIGRATION_BATTERY_WARNING_LABEL); + builder->Add("migrationAskChargeMessage", + ash::SubstituteChromeOSDeviceType( + IDS_ENCRYPTION_MIGRATION_ASK_CHARGE_MESSAGE)); + builder->Add("migrationNecessaryBatteryLevelLabel", + IDS_ENCRYPTION_MIGRATION_NECESSARY_BATTERY_LEVEL_MESSAGE); + builder->Add("migrationChargingLabel", + IDS_ENCRYPTION_MIGRATION_CHARGING_LABEL); + builder->Add("migrationFailedTitle", IDS_ENCRYPTION_MIGRATION_FAILED_TITLE); + builder->Add("migrationFailedSubtitle", + IDS_ENCRYPTION_MIGRATION_FAILED_SUBTITLE); + builder->Add("migrationFailedMessage", + ash::SubstituteChromeOSDeviceType( + IDS_ENCRYPTION_MIGRATION_FAILED_MESSAGE)); + builder->Add("migrationNospaceWarningLabel", + IDS_ENCRYPTION_MIGRATION_NOSPACE_WARNING_LABEL); + builder->Add("migrationAskFreeSpaceMessage", + IDS_ENCRYPTION_MIGRATION_ASK_FREE_SPACE_MESSAGE); + builder->Add("migrationAvailableSpaceLabel", + IDS_ENCRYPTION_MIGRATION_AVAILABLE_SPACE_LABEL); + builder->Add("migrationNecessarySpaceLabel", + IDS_ENCRYPTION_MIGRATION_NECESSARY_SPACE_LABEL); + builder->Add("migrationButtonUpdate", IDS_ENCRYPTION_MIGRATION_BUTTON_UPDATE); + builder->Add("migrationButtonSkip", IDS_ENCRYPTION_MIGRATION_BUTTON_SKIP); + builder->Add("migrationButtonRestart", + IDS_ENCRYPTION_MIGRATION_BUTTON_RESTART); + builder->Add("migrationButtonContinue", + IDS_ENCRYPTION_MIGRATION_BUTTON_CONTINUE); + builder->Add("migrationButtonSignIn", IDS_ENCRYPTION_MIGRATION_BUTTON_SIGNIN); +} void EncryptionMigrationScreenHandler::Initialize() { if (!page_is_ready() || !delegate_) @@ -121,8 +167,10 @@ void EncryptionMigrationScreenHandler::PowerChanged( const power_manager::PowerSupplyProperties& proto) { current_battery_percent_ = proto.battery_percent(); - CallJS("setBatteryPercent", current_battery_percent_, - current_battery_percent_ >= kMinimumBatteryPercent); + CallJS("setBatteryState", current_battery_percent_, + current_battery_percent_ >= kMinimumBatteryPercent, + proto.battery_state() == + power_manager::PowerSupplyProperties_BatteryState_CHARGING); // If the migration was already requested and the bettery level is enough now, // The migration should start immediately. @@ -150,9 +198,6 @@ } void EncryptionMigrationScreenHandler::HandleRequestRestart() { - // TODO(fukino): If the migration finished successfully, we don't need to - // restart the device. Let's sign in to the desktop using the already-provided - // user credential. chrome::AttemptRestart(); } @@ -188,6 +233,9 @@ UpdateUIState(UIState::READY); } } else { + CallJS("setAvailableSpaceInString", ui::FormatBytes(size)); + CallJS("setNecessarySpaceInString", + ui::FormatBytes(kMinimumAvailableStorage)); UpdateUIState(UIState::NOT_ENOUGH_STORAGE); } } @@ -244,10 +292,11 @@ CallJS("setMigrationProgress", static_cast<double>(current) / total); break; case cryptohome::DIRCRYPTO_MIGRATION_SUCCESS: + // Restart immediately after successful migration. + chrome::AttemptRestart(); + break; case cryptohome::DIRCRYPTO_MIGRATION_FAILED: - UpdateUIState(status == cryptohome::DIRCRYPTO_MIGRATION_SUCCESS - ? UIState::MIGRATION_SUCCEEDED - : UIState::MIGRATION_FAILED); + UpdateUIState(UIState::MIGRATION_FAILED); // Stop listening to the progress updates. DBusThreadManager::Get() ->GetCryptohomeClient()
diff --git a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h index b4e9cd6..de278cd6 100644 --- a/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h +++ b/chrome/browser/ui/webui/chromeos/login/encryption_migration_screen_handler.h
@@ -43,9 +43,8 @@ INITIAL = 0, READY = 1, MIGRATING = 2, - MIGRATION_SUCCEEDED = 3, - MIGRATION_FAILED = 4, - NOT_ENOUGH_STORAGE = 5, + MIGRATION_FAILED = 3, + NOT_ENOUGH_STORAGE = 4, }; // WebUIMessageHandler implementation:
diff --git a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc index 3b6cbfd..c7941c628 100644 --- a/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc +++ b/chrome/browser/ui/webui/chromeos/login/oobe_ui.cc
@@ -118,6 +118,7 @@ const char kArcPlaystoreCSSPath[] = "playstore.css"; const char kArcPlaystoreJSPath[] = "playstore.js"; const char kArcPlaystoreLogoPath[] = "playstore.svg"; +const char kProductLogoPath[] = "product-logo.png"; // Creates a WebUIDataSource for chrome://oobe content::WebUIDataSource* CreateOobeUIDataSource( @@ -163,6 +164,9 @@ source->AddResourcePath(kArcPlaystoreLogoPath, IDR_ARC_SUPPORT_PLAYSTORE_LOGO); + // Required in encryption migration screen. + source->AddResourcePath(kProductLogoPath, IDR_PRODUCT_LOGO_64); + source->AddResourcePath(kKeyboardUtilsJSPath, IDR_KEYBOARD_UTILS_JS); source->OverrideContentSecurityPolicyChildSrc( base::StringPrintf(
diff --git a/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc b/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc index 4ea500a5..fc794b79 100644 --- a/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc +++ b/chrome/browser/ui/webui/options/chromeos/display_options_handler.cc
@@ -491,17 +491,20 @@ display::DisplayManager* display_manager = GetDisplayManager(); scoped_refptr<display::ManagedDisplayMode> current_mode = display_manager->GetActiveModeForDisplayId(display_id); - if (!display_manager->SetDisplayMode(display_id, mode)) { - LOG(ERROR) << "Unable to set display mode for: " << display_id - << " Mode: " << *mode_data; + + if (mode->IsEquivalent(current_mode)) { + LOG(ERROR) << "New display mode matches current mode."; return; } - if (display::Display::IsInternalDisplayId(display_id)) - return; - // For external displays, show a notification confirming the resolution - // change. - ash::Shell::Get()->resolution_notification_controller()->PrepareNotification( - display_id, current_mode, mode, base::Bind(&chromeos::StoreDisplayPrefs)); + + if (!ash::Shell::Get() + ->resolution_notification_controller() + ->PrepareNotificationAndSetDisplayMode( + display_id, current_mode, mode, + base::Bind(&chromeos::StoreDisplayPrefs))) { + LOG(ERROR) << "Unable to set display mode for: " << display_id + << " Mode: " << *mode_data; + } } void DisplayOptionsHandler::HandleSetRotation(const base::ListValue* args) {
diff --git a/chrome/browser/ui/webui/vr_shell/OWNERS b/chrome/browser/ui/webui/vr_shell/OWNERS deleted file mode 100644 index f104584a8..0000000 --- a/chrome/browser/ui/webui/vr_shell/OWNERS +++ /dev/null
@@ -1,6 +0,0 @@ -bshe@chromium.org -girard@chromium.org -mthiesse@chromium.org -cjgrant@chromium.org - -# COMPONENT: UI>Browser>VR
diff --git a/chrome/browser/ui/webui/vr_shell/PRESUBMIT.py b/chrome/browser/ui/webui/vr_shell/PRESUBMIT.py deleted file mode 100644 index e24a7c0..0000000 --- a/chrome/browser/ui/webui/vr_shell/PRESUBMIT.py +++ /dev/null
@@ -1,41 +0,0 @@ -# Copyright 2017 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Presubmit script for changes affecting chrome/browser/ui/webui/vr_shell - -See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts -for more details about the presubmit API built into depot_tools. -""" - -import re - -# chrome/PRESUBMIT.py blocks several linters due to the infeasibility of -# enforcing them on a large codebase. Here we'll start by enforcing all -# linters, and add exclusions if necessary. -# -# Note that this list must be non-empty, or cpplint will use its default set of -# filters. Therefore, explicitly enable a single dummy linter. -LINT_FILTERS = [ - '+build/include', -] - -VERBOSITY_LEVEL = 4 - -INCLUDE_CPP_FILES_ONLY = (r'.*\.(cc|h)$',) - -def _CheckChangeLintsClean(input_api, output_api): - sources = lambda x: input_api.FilterSourceFile( - x, white_list=INCLUDE_CPP_FILES_ONLY) - return input_api.canned_checks.CheckChangeLintsClean( - input_api, output_api, sources, LINT_FILTERS, VERBOSITY_LEVEL) - -def CheckChangeOnUpload(input_api, output_api): - results = [] - results.extend(_CheckChangeLintsClean(input_api, output_api)) - return results - -def CheckChangeOnCommit(input_api, output_api): - results = [] - results.extend(_CheckChangeLintsClean(input_api, output_api)) - return results
diff --git a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.cc b/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.cc deleted file mode 100644 index db9b4f1..0000000 --- a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.cc +++ /dev/null
@@ -1,107 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h" - -#include <string> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/callback.h" -#include "base/values.h" -#include "chrome/browser/android/vr_shell/ui_interface.h" -#include "chrome/browser/android/vr_shell/ui_scene.h" -#include "chrome/browser/android/vr_shell/vr_shell.h" -#include "content/public/browser/web_ui.h" - -VrShellUIMessageHandler::VrShellUIMessageHandler() = default; - -VrShellUIMessageHandler::~VrShellUIMessageHandler() { - if (vr_shell_) { - vr_shell_->GetUiInterface()->SetUiCommandHandler(nullptr); - } -} - -void VrShellUIMessageHandler::RegisterMessages() { - vr_shell_ = vr_shell::VrShell::GetWeakPtr(web_ui()->GetWebContents()); - - web_ui()->RegisterMessageCallback( - "domLoaded", base::Bind(&VrShellUIMessageHandler::HandleDomLoaded, - base::Unretained(this))); - web_ui()->RegisterMessageCallback( - "updateScene", - base::Bind(&VrShellUIMessageHandler::HandleUpdateScene, - base::Unretained(this))); - web_ui()->RegisterMessageCallback( - "doAction", base::Bind(&VrShellUIMessageHandler::HandleDoAction, - base::Unretained(this))); - web_ui()->RegisterMessageCallback( - "setContentCssSize", - base::Bind(&VrShellUIMessageHandler::HandleSetContentCssSize, - base::Unretained(this))); - web_ui()->RegisterMessageCallback( - "setUiCssSize", base::Bind(&VrShellUIMessageHandler::HandleSetUiCssSize, - base::Unretained(this))); -} - -void VrShellUIMessageHandler::HandleDomLoaded(const base::ListValue* args) { - AllowJavascript(); -} - -void VrShellUIMessageHandler::OnJavascriptAllowed() { - // If we don't have a VR Shell here, it means either the user manually loaded - // this webui page and we want to silently fail to connect to native vr shell, - // or VR Shell was deleted, and this webui content is also about to be - // deleted. - if (!vr_shell_) - return; - vr_shell_->GetUiInterface()->SetUiCommandHandler(this); - vr_shell_->OnDomContentsLoaded(); -} - -void VrShellUIMessageHandler::HandleUpdateScene(const base::ListValue* args) { - if (!vr_shell_) - return; - vr_shell_->UpdateScene(args); -} - -void VrShellUIMessageHandler::HandleDoAction(const base::ListValue* args) { - int action; - const base::DictionaryValue* arguments = nullptr; - CHECK(args->GetInteger(0, &action)); - CHECK(args->GetDictionary(1, &arguments)); - if (vr_shell_) - vr_shell_->DoUiAction(static_cast<vr_shell::UiAction>(action), arguments); -} - -void VrShellUIMessageHandler::HandleSetContentCssSize( - const base::ListValue* args) { - if (!vr_shell_) - return; - SetSize(args, false); -} - -void VrShellUIMessageHandler::HandleSetUiCssSize(const base::ListValue* args) { - if (!vr_shell_) - return; - SetSize(args, true); -} - -void VrShellUIMessageHandler::SetSize(const base::ListValue* args, - bool for_ui) { - CHECK(args->GetSize() == 3); - double width, height, dpr; - CHECK(args->GetDouble(0, &width)); - CHECK(args->GetDouble(1, &height)); - CHECK(args->GetDouble(2, &dpr)); - if (for_ui) { - vr_shell_->SetUiCssSize(width, height, dpr); - } else { - vr_shell_->SetContentCssSize(width, height, dpr); - } -} - -void VrShellUIMessageHandler::SendCommandToUi(const base::Value& value) { - CallJavascriptFunction("vrShellUi.command", value); -}
diff --git a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h b/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h deleted file mode 100644 index be0825e..0000000 --- a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_WEBUI_VR_SHELL_VR_SHELL_UI_MESSAGE_HANDLER_H_ -#define CHROME_BROWSER_UI_WEBUI_VR_SHELL_VR_SHELL_UI_MESSAGE_HANDLER_H_ - -#include "base/macros.h" -#include "base/memory/weak_ptr.h" -#include "chrome/browser/android/vr_shell/ui_interface.h" -#include "content/public/browser/web_ui_message_handler.h" - -namespace base { -class ListValue; -} - -namespace vr_shell { -class VrShell; -} - -class VrShellUIMessageHandler : public content::WebUIMessageHandler, - public vr_shell::UiCommandHandler { - public: - VrShellUIMessageHandler(); - ~VrShellUIMessageHandler() override; - - void SendCommandToUi(const base::Value& value) override; - - private: - // content::WebUIMessageHandler: - void RegisterMessages() override; - void OnJavascriptAllowed() override; - - void HandleDomLoaded(const base::ListValue* args); - void HandleUpdateScene(const base::ListValue* args); - void HandleDoAction(const base::ListValue* args); - void HandleSetContentCssSize(const base::ListValue* args); - void HandleSetUiCssSize(const base::ListValue* args); - void SetSize(const base::ListValue* args, bool for_ui); - - base::WeakPtr<vr_shell::VrShell> vr_shell_; - - DISALLOW_COPY_AND_ASSIGN(VrShellUIMessageHandler); -}; - -#endif // CHROME_BROWSER_UI_WEBUI_VR_SHELL_VR_SHELL_UI_MESSAGE_HANDLER_H_
diff --git a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_ui.cc b/chrome/browser/ui/webui/vr_shell/vr_shell_ui_ui.cc deleted file mode 100644 index 08fc70fd..0000000 --- a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_ui.cc +++ /dev/null
@@ -1,212 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "chrome/browser/ui/webui/vr_shell/vr_shell_ui_ui.h" - -#include <string> -#include <unordered_set> - -#include "base/memory/ptr_util.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/ui/webui/vr_shell/vr_shell_ui_message_handler.h" -#include "chrome/common/url_constants.h" -#include "content/public/browser/web_ui.h" - -#if !defined(ENABLE_VR_SHELL_UI_DEV) -#include "chrome/browser/browser_process.h" -#include "chrome/grit/browser_resources.h" -#include "chrome/grit/generated_resources.h" -#include "content/public/browser/web_ui_data_source.h" -#else -#include <map> -#include "base/macros.h" -#include "base/memory/ref_counted_memory.h" -#include "base/strings/string_util.h" -#include "content/public/browser/url_data_source.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_fetcher_delegate.h" -#include "net/url_request/url_request_context_getter.h" -#endif - -namespace { - -#if defined(ENABLE_VR_SHELL_UI_DEV) -std::string PathWithoutParams(const std::string& path) { - return GURL(std::string("chrome://vr-shell-ui/") + path).path().substr(1); -} - -const char kRemoteBase[] = "http://localhost:8080/"; -const char kRemoteDefaultPath[] = "vr_shell_ui.html"; -const char kHttpNotFound[] = "HTTP/1.1 404 Not Found\n\n"; - -// RemoteDataSource --------------------------------------------------------- - -std::string GetMimeTypeForPath(const std::string& path) { - std::string filename = PathWithoutParams(path); - if (base::EndsWith(filename, ".html", base::CompareCase::INSENSITIVE_ASCII)) { - return "text/html"; - } else if (base::EndsWith(filename, ".css", - base::CompareCase::INSENSITIVE_ASCII)) { - return "text/css"; - } else if (base::EndsWith(filename, ".js", - base::CompareCase::INSENSITIVE_ASCII)) { - return "application/javascript"; - } else if (base::EndsWith(filename, ".png", - base::CompareCase::INSENSITIVE_ASCII)) { - return "image/png"; - } else if (base::EndsWith(filename, ".gif", - base::CompareCase::INSENSITIVE_ASCII)) { - return "image/gif"; - } else if (base::EndsWith(filename, ".svg", - base::CompareCase::INSENSITIVE_ASCII)) { - return "image/svg+xml"; - } else if (base::EndsWith(filename, ".manifest", - base::CompareCase::INSENSITIVE_ASCII)) { - return "text/cache-manifest"; - } - return "text/html"; -} - -class RemoteDataSource : public content::URLDataSource, - public net::URLFetcherDelegate { - public: - using GotDataCallback = content::URLDataSource::GotDataCallback; - - explicit RemoteDataSource(net::URLRequestContextGetter* request_context); - - // content::URLDataSource implementation. - std::string GetSource() const override; - void StartDataRequest( - const std::string& path, - const content::ResourceRequestInfo::WebContentsGetter& wc_getter, - const GotDataCallback& callback) override; - - bool AllowCaching() const override { return false; } - - private: - // content::URLDataSource overrides. - std::string GetMimeType(const std::string& path) const override; - bool ShouldAddContentSecurityPolicy() const override; - bool ShouldDenyXFrameOptions() const override; - bool ShouldServeMimeTypeAsContentTypeHeader() const override; - - // net::URLFetcherDelegate overrides. - void OnURLFetchComplete(const net::URLFetcher* source) override; - - ~RemoteDataSource() override; - - scoped_refptr<net::URLRequestContextGetter> request_context_; - - using PendingRequestsMap = std::map<const net::URLFetcher*, GotDataCallback>; - PendingRequestsMap pending_; - - DISALLOW_COPY_AND_ASSIGN(RemoteDataSource); -}; - -RemoteDataSource::RemoteDataSource( - net::URLRequestContextGetter* request_context) - : request_context_(request_context) {} - -RemoteDataSource::~RemoteDataSource() { - for (const auto& pair : pending_) { - delete pair.first; - pair.second.Run( - new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound))); - } -} - -std::string RemoteDataSource::GetSource() const { - return chrome::kChromeUIVrShellUIHost; -} - -void RemoteDataSource::StartDataRequest( - const std::string& path, - const content::ResourceRequestInfo::WebContentsGetter& wc_getter, - const content::URLDataSource::GotDataCallback& callback) { - GURL url = GURL(kRemoteBase + - (path.empty() ? std::string(kRemoteDefaultPath) : path)); - if (!url.is_valid()) { - callback.Run( - new base::RefCountedStaticMemory(kHttpNotFound, strlen(kHttpNotFound))); - return; - } - net::URLFetcher* fetcher = - net::URLFetcher::Create(url, net::URLFetcher::GET, this).release(); - - fetcher->AddExtraRequestHeader("Cache-Control: no-cache"); - - pending_[fetcher] = callback; - fetcher->SetRequestContext(request_context_.get()); - fetcher->Start(); -} - -std::string RemoteDataSource::GetMimeType(const std::string& path) const { - return GetMimeTypeForPath(path); -} - -bool RemoteDataSource::ShouldAddContentSecurityPolicy() const { - return false; -} - -bool RemoteDataSource::ShouldDenyXFrameOptions() const { - return false; -} - -bool RemoteDataSource::ShouldServeMimeTypeAsContentTypeHeader() const { - return true; -} - -void RemoteDataSource::OnURLFetchComplete(const net::URLFetcher* source) { - DCHECK(source); - PendingRequestsMap::iterator it = pending_.find(source); - DCHECK(it != pending_.end()); - std::string response; - source->GetResponseAsString(&response); - delete source; - it->second.Run(base::RefCountedString::TakeString(&response)); - pending_.erase(it); -} -#else -content::WebUIDataSource* CreateVrShellUIHTMLSource() { - content::WebUIDataSource* source = - content::WebUIDataSource::Create(chrome::kChromeUIVrShellUIHost); - source->UseGzip(std::unordered_set<std::string>() /* excluded_paths */); - source->AddResourcePath("vr_shell_ui.css", IDR_VR_SHELL_UI_CSS); - source->AddResourcePath("vr_shell_ui.js", IDR_VR_SHELL_UI_JS); - source->AddResourcePath("vr_shell_ui_api.js", IDR_VR_SHELL_UI_API_JS); - source->AddResourcePath("vr_shell_ui_scene.js", IDR_VR_SHELL_UI_SCENE_JS); - source->AddResourcePath("vk.css", IDR_VR_SHELL_UI_VK_CSS); - source->AddResourcePath("vk.js", IDR_VR_SHELL_UI_VK_JS); - source->SetDefaultResource(IDR_VR_SHELL_UI_HTML); - source->AddLocalizedString("insecureWebVrContentPermanent", - IDS_PAGE_INFO_INSECURE_WEBVR_CONTENT_PERMANENT); - source->AddLocalizedString("insecureWebVrContentTransient", - IDS_PAGE_INFO_INSECURE_WEBVR_CONTENT_TRANSIENT); - source->AddLocalizedString("back", IDS_VR_SHELL_UI_BACK_BUTTON); - source->AddLocalizedString("forward", IDS_VR_SHELL_UI_FORWARD_BUTTON); - source->AddLocalizedString("reload", IDS_VR_SHELL_UI_RELOAD_BUTTON); - source->AddLocalizedString("exitPresent", - IDS_VR_SHELL_UI_EXIT_PRESENT_BUTTON); - source->AddLocalizedString("newTab", IDS_VR_SHELL_NEW_TAB_BUTTON); - source->AddLocalizedString("newIncognitoTab", - IDS_VR_SHELL_NEW_INCOGNITO_TAB_BUTTON); - - return source; -} -#endif - -} // namespace - -VrShellUIUI::VrShellUIUI(content::WebUI* web_ui) : WebUIController(web_ui) { - Profile* profile = Profile::FromWebUI(web_ui); -#if !defined(ENABLE_VR_SHELL_UI_DEV) - content::WebUIDataSource::Add(profile, CreateVrShellUIHTMLSource()); -#else - content::URLDataSource::Add( - profile, new RemoteDataSource(profile->GetRequestContext())); -#endif - web_ui->AddMessageHandler(base::MakeUnique<VrShellUIMessageHandler>()); -} - -VrShellUIUI::~VrShellUIUI() {}
diff --git a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_ui.h b/chrome/browser/ui/webui/vr_shell/vr_shell_ui_ui.h deleted file mode 100644 index a2ca1c7..0000000 --- a/chrome/browser/ui/webui/vr_shell/vr_shell_ui_ui.h +++ /dev/null
@@ -1,21 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_UI_WEBUI_VR_SHELL_VR_SHELL_UI_UI_H_ -#define CHROME_BROWSER_UI_WEBUI_VR_SHELL_VR_SHELL_UI_UI_H_ - -#include "base/macros.h" -#include "content/public/browser/web_ui_controller.h" - -// The implementation for the chrome://vr-shell-ui page. -class VrShellUIUI : public content::WebUIController { - public: - explicit VrShellUIUI(content::WebUI* web_ui); - ~VrShellUIUI() override; - - private: - DISALLOW_COPY_AND_ASSIGN(VrShellUIUI); -}; - -#endif // CHROME_BROWSER_UI_WEBUI_VR_SHELL_VR_SHELL_UI_UI_H_
diff --git a/chrome/common/BUILD.gn b/chrome/common/BUILD.gn index bbf02bf2..624c08c 100644 --- a/chrome/common/BUILD.gn +++ b/chrome/common/BUILD.gn
@@ -206,7 +206,6 @@ "//components/visitedlink/common", "//content/public/common", "//crypto", - "//device/vr:features", "//extensions/common:common_constants", "//extensions/features", "//google_apis",
diff --git a/chrome/common/extensions/api/enterprise_platform_keys.idl b/chrome/common/extensions/api/enterprise_platform_keys.idl index bb5cfb1..ecaf839b 100644 --- a/chrome/common/extensions/api/enterprise_platform_keys.idl +++ b/chrome/common/extensions/api/enterprise_platform_keys.idl
@@ -111,8 +111,16 @@ // policy. The Enterprise Machine Key does not reside in the // <code>"system"</code> token and is not accessible by any other API. // |challenge|: A challenge as emitted by the Verified Access Web API. + // |registerKey|: If set, the current Enterprise Machine Key is registered + // with the <code>"system"</code> token and relinquishes the + // Enterprise Machine Key role. The key can then be + // associated with a certificate and used like any other + // signing key. This key is 2048-bit RSA. Subsequent calls + // to this function will then generate a new Enterprise + // Machine Key. // |callback|: Called back with the challenge response. static void challengeMachineKey(ArrayBuffer challenge, + optional boolean registerKey, ChallengeCallback callback); // Challenges a hardware-backed Enterprise User Key and emits the response
diff --git a/chrome/common/extensions/docs/templates/public/apps/networking_onc.html b/chrome/common/extensions/docs/templates/public/apps/networking_onc.html new file mode 100644 index 0000000..cbb7d16 --- /dev/null +++ b/chrome/common/extensions/docs/templates/public/apps/networking_onc.html
@@ -0,0 +1 @@ +{{+partials.standard_apps_api api:apis.apps.networking_onc/}}
diff --git a/chrome/common/features.gni b/chrome/common/features.gni index d585b94..9364bd9 100644 --- a/chrome/common/features.gni +++ b/chrome/common/features.gni
@@ -61,9 +61,6 @@ enable_supervised_users = !is_chromecast - # Enables vr shell UI development on local or remote page. - enable_vr_shell_ui_dev = false - # Indicates if Wayland display server support is enabled. enable_wayland_server = is_chromeos
diff --git a/chrome/common/url_constants.cc b/chrome/common/url_constants.cc index 12829d3..47a6e73b 100644 --- a/chrome/common/url_constants.cc +++ b/chrome/common/url_constants.cc
@@ -296,10 +296,6 @@ const char kChromeUIWebApksHost[] = "webapks"; #endif -#if BUILDFLAG(ENABLE_VR) -const char kChromeUIVrShellUIHost[] = "vr-shell-ui"; -#endif - #if defined(OS_CHROMEOS) const char kChromeUIActivationMessageHost[] = "activationmessage"; const char kChromeUIAppLaunchHost[] = "app-launch";
diff --git a/chrome/common/url_constants.h b/chrome/common/url_constants.h index 320a624f..97971dcc3 100644 --- a/chrome/common/url_constants.h +++ b/chrome/common/url_constants.h
@@ -15,7 +15,6 @@ #include "build/build_config.h" #include "chrome/common/features.h" #include "content/public/common/url_constants.h" -#include "device/vr/features.h" #include "media/media_features.h" #include "ppapi/features/features.h" #include "printing/features/features.h" @@ -277,10 +276,6 @@ extern const char kChromeUIWebApksHost[]; #endif -#if BUILDFLAG(ENABLE_VR) -extern const char kChromeUIVrShellUIHost[]; -#endif - #if defined(OS_CHROMEOS) extern const char kChromeUIActivationMessageHost[]; extern const char kChromeUIAppLaunchHost[];
diff --git a/chrome/test/data/payments/metrics.js b/chrome/test/data/payments/metrics.js index f9d0db0..b280f15 100644 --- a/chrome/test/data/payments/metrics.js +++ b/chrome/test/data/payments/metrics.js
@@ -82,6 +82,29 @@ } /** + * Launches the PaymentRequest UI which accepts only Android Pay and does not + * require any other information. + */ +function androidPaySkipUiBuy() { // eslint-disable-line no-unused-vars + try { + request = new PaymentRequest( + [{supportedMethods: ['https://android.com/pay']}], { + total: {label: 'Total', amount: {currency: 'USD', value: '5.00'}}, + }); + request.show() + .then(function(resp) { + return resp.complete('success'); + }).then(function() { + print(JSON.stringify(resp, undefined, 2)); + }).catch(function(error) { + print(error); + }); + } catch (error) { + print(error.message); + } +} + +/** * Launches the PaymentRequest UI which accepts only an unsupported payment * method. */
diff --git a/chrome/test/data/payments/payment_request_metrics_test.html b/chrome/test/data/payments/payment_request_metrics_test.html index d2eab70c..d567b4a 100644 --- a/chrome/test/data/payments/payment_request_metrics_test.html +++ b/chrome/test/data/payments/payment_request_metrics_test.html
@@ -13,6 +13,7 @@ <body> <button onclick="ccBuy()" id="ccBuy">CC Buy Test</button><br> <button onclick="androidPayBuy()" id="androidPayBuy">Android Pay Buy Test</button><br> +<button onclick="androidPaySkipUiBuy()" id="androidPaySkipUiBuy">Android Pay Skip UI Buy Test</button><br> <button onclick="noSupported()" id="noSupported">No Supported Method Test</button><br> <button onclick="abort()" id="abort">Abort</button> <pre id="result"></pre>
diff --git a/chrome/test/data/webapks/bad-sig.apk b/chrome/test/data/webapks/bad-sig.apk new file mode 100644 index 0000000..8d65a109 --- /dev/null +++ b/chrome/test/data/webapks/bad-sig.apk Binary files differ
diff --git a/chrome/test/data/webapks/bad-utf8-fname.apk b/chrome/test/data/webapks/bad-utf8-fname.apk new file mode 100644 index 0000000..4d563ff --- /dev/null +++ b/chrome/test/data/webapks/bad-utf8-fname.apk Binary files differ
diff --git a/chrome/test/data/webapks/empty.apk b/chrome/test/data/webapks/empty.apk new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/chrome/test/data/webapks/empty.apk
diff --git a/chrome/test/data/webapks/example.apk b/chrome/test/data/webapks/example.apk new file mode 100644 index 0000000..af6d81a1 --- /dev/null +++ b/chrome/test/data/webapks/example.apk Binary files differ
diff --git a/chrome/test/data/webapks/extra-len-too-large.apk b/chrome/test/data/webapks/extra-len-too-large.apk new file mode 100644 index 0000000..920fae4 --- /dev/null +++ b/chrome/test/data/webapks/extra-len-too-large.apk Binary files differ
diff --git a/chrome/test/data/webapks/fcomment-too-large.apk b/chrome/test/data/webapks/fcomment-too-large.apk new file mode 100644 index 0000000..327aca0 --- /dev/null +++ b/chrome/test/data/webapks/fcomment-too-large.apk Binary files differ
diff --git a/chrome/test/data/webapks/java-example.apk b/chrome/test/data/webapks/java-example.apk new file mode 100644 index 0000000..ae6ec021 --- /dev/null +++ b/chrome/test/data/webapks/java-example.apk Binary files differ
diff --git a/chrome/test/data/webapks/no-cd.apk b/chrome/test/data/webapks/no-cd.apk new file mode 100644 index 0000000..f4b4bec --- /dev/null +++ b/chrome/test/data/webapks/no-cd.apk Binary files differ
diff --git a/chrome/test/data/webapks/no-comment.apk b/chrome/test/data/webapks/no-comment.apk new file mode 100644 index 0000000..7e28604 --- /dev/null +++ b/chrome/test/data/webapks/no-comment.apk Binary files differ
diff --git a/chrome/test/data/webapks/no-eocd.apk b/chrome/test/data/webapks/no-eocd.apk new file mode 100644 index 0000000..24b84ba --- /dev/null +++ b/chrome/test/data/webapks/no-eocd.apk Binary files differ
diff --git a/chrome/test/data/webapks/no-lfh.apk b/chrome/test/data/webapks/no-lfh.apk new file mode 100644 index 0000000..4e0e72a --- /dev/null +++ b/chrome/test/data/webapks/no-lfh.apk Binary files differ
diff --git a/chrome/test/data/webapks/not-an.apk b/chrome/test/data/webapks/not-an.apk new file mode 100644 index 0000000..2a54d36 --- /dev/null +++ b/chrome/test/data/webapks/not-an.apk
@@ -0,0 +1 @@ +Not an APK
diff --git a/chrome/test/data/webapks/public.der b/chrome/test/data/webapks/public.der new file mode 100644 index 0000000..8ee4259a --- /dev/null +++ b/chrome/test/data/webapks/public.der Binary files differ
diff --git a/chrome/test/data/webapks/too-many-metainf.apk b/chrome/test/data/webapks/too-many-metainf.apk new file mode 100644 index 0000000..18e6f131 --- /dev/null +++ b/chrome/test/data/webapks/too-many-metainf.apk Binary files differ
diff --git a/chrome/test/data/webapks/truncated.apk b/chrome/test/data/webapks/truncated.apk new file mode 100644 index 0000000..7c77a57 --- /dev/null +++ b/chrome/test/data/webapks/truncated.apk
@@ -0,0 +1 @@ +PK \ No newline at end of file
diff --git a/chrome/test/data/webapks/zeros-at-end.apk b/chrome/test/data/webapks/zeros-at-end.apk new file mode 100644 index 0000000..7ff1191 --- /dev/null +++ b/chrome/test/data/webapks/zeros-at-end.apk Binary files differ
diff --git a/chrome/test/data/webapks/zeros.apk b/chrome/test/data/webapks/zeros.apk new file mode 100644 index 0000000..d8141761 --- /dev/null +++ b/chrome/test/data/webapks/zeros.apk Binary files differ
diff --git a/chrome/test/data/webui/cr_elements/cr_dialog_test.js b/chrome/test/data/webui/cr_elements/cr_dialog_test.js index c61de46..41987dd 100644 --- a/chrome/test/data/webui/cr_elements/cr_dialog_test.js +++ b/chrome/test/data/webui/cr_elements/cr_dialog_test.js
@@ -145,6 +145,29 @@ expectEquals(button, document.activeElement); }); + // Ensuring that intersectionObserver does not fire any callbacks before the + // dialog has been opened. + test('body scrollable border not added before modal shown', function(done) { + document.body.innerHTML = ` + <dialog is="cr-dialog" show-scroll-borders> + <div class="title">title</div> + <div class="body">body</div> + </dialog>`; + + var dialog = document.body.querySelector('dialog'); + assertFalse(dialog.open); + var bodyContainer = dialog.$$('.body-container'); + assertTrue(!!bodyContainer); + + // Waiting for 1ms because IntersectionObserver fires one message loop after + // dialog.attached. + setTimeout(function() { + assertFalse(bodyContainer.classList.contains('top-scrollable')); + assertFalse(bodyContainer.classList.contains('bottom-scrollable')); + done(); + }, 1); + }); + test('dialog body scrollable border when appropriate', function(done) { document.body.innerHTML = ` <dialog is="cr-dialog" show-scroll-borders>
diff --git a/chrome/test/data/webui/settings/settings_animated_pages_test.js b/chrome/test/data/webui/settings/settings_animated_pages_test.js index fd47f641..1b2052e 100644 --- a/chrome/test/data/webui/settings/settings_animated_pages_test.js +++ b/chrome/test/data/webui/settings/settings_animated_pages_test.js
@@ -5,7 +5,8 @@ suite('settings-animated-pages', function() { test('focuses subpage trigger when exiting subpage', function(done) { document.body.innerHTML = ` - <settings-animated-pages section="test-section"> + <settings-animated-pages + section="${settings.Route.SEARCH_ENGINES.section}"> <neon-animatable route-path="default"> <button id="subpage-trigger"></button> </neon-animatable> @@ -19,14 +20,13 @@ animatedPages.focusConfig.set( settings.Route.SEARCH_ENGINES.path, '#subpage-trigger'); - animatedPages.$.animatedPages.selected = settings.Route.SEARCH_ENGINES.path; - var trigger = document.body.querySelector('#subpage-trigger'); assertTrue(!!trigger); trigger.addEventListener('focus', function() { done(); }); - // Trigger subpage exit. - animatedPages.currentRouteChanged( - settings.Route.BASIC, settings.Route.SEARCH_ENGINES); + // Trigger subpage exit navigation. + settings.navigateTo(settings.Route.BASIC); + settings.navigateTo(settings.Route.SEARCH_ENGINES); + settings.navigateToPreviousRoute(); }); });
diff --git a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java index 69caebf..5d57cd6 100644 --- a/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java +++ b/chromecast/browser/android/apk/src/org/chromium/chromecast/shell/CastWebContentsActivity.java
@@ -56,6 +56,7 @@ private WindowAndroid mWindow; private ContentViewCore mContentViewCore; private ContentView mContentView; + private boolean mReceivedUserLeave = false; private static final int TEARDOWN_GRACE_PERIOD_TIMEOUT_MILLIS = 300; public static final String ACTION_DATA_SCHEME = "cast"; @@ -72,6 +73,17 @@ public static final String ACTION_ACTIVITY_STOPPED = "com.google.android.apps.castshell.intent.action.ACTIVITY_STOPPED"; + /* + * Intended to be called from "onStop" to determine if this is a "legitimate" stop or not. + * When starting CastShellActivity from the TV in sleep mode, an extra onPause/onStop will be + * fired. + * Details: http://stackoverflow.com/questions/25369909/ + * We use onUserLeaveHint to determine if the onPause/onStop called because of user intent. + */ + private boolean isStopping() { + return mReceivedUserLeave; + } + @Override protected void onCreate(final Bundle savedInstanceState) { if (DEBUG) Log.d(TAG, "onCreate"); @@ -191,9 +203,10 @@ @Override protected void onStop() { if (DEBUG) Log.d(TAG, "onStop"); - - detachWebContentsIfAny(); - releaseStreamMuteIfNecessary(); + if (isStopping()) { + detachWebContentsIfAny(); + releaseStreamMuteIfNecessary(); + } super.onStop(); } @@ -221,6 +234,12 @@ } @Override + protected void onUserLeaveHint() { + if (DEBUG) Log.d(TAG, "onUserLeaveHint"); + mReceivedUserLeave = true; + } + + @Override public boolean dispatchKeyEvent(KeyEvent event) { if (DEBUG) Log.d(TAG, "dispatchKeyEvent"); int keyCode = event.getKeyCode(); @@ -249,6 +268,7 @@ } if (keyCode == KeyEvent.KEYCODE_BACK) { + mReceivedUserLeave = true; return super.dispatchKeyEvent(event); } return false;
diff --git a/chromecast/media/cma/backend/media_sink_default.cc b/chromecast/media/cma/backend/media_sink_default.cc index 61d58c6..1ab07d72 100644 --- a/chromecast/media/cma/backend/media_sink_default.cc +++ b/chromecast/media/cma/backend/media_sink_default.cc
@@ -64,8 +64,11 @@ // Those tests are wrong should be fixed. // TODO(alokp): Fix these issues when the next version of CMA backend is // scheduled to roll out. crbug.com/678394 - last_frame_pts_ = base::TimeDelta::FromMicroseconds(buffer->timestamp()); - time_interpolator_.SetUpperBound(last_frame_pts_); + auto timestamp = base::TimeDelta::FromMicroseconds(buffer->timestamp()); + if (timestamp != ::media::kNoTimestamp) { + last_frame_pts_ = timestamp; + time_interpolator_.SetUpperBound(last_frame_pts_); + } return MediaPipelineBackend::kBufferSuccess; }
diff --git a/components/autofill/core/browser/BUILD.gn b/components/autofill/core/browser/BUILD.gn index 5293b93c..87a22f6e 100644 --- a/components/autofill/core/browser/BUILD.gn +++ b/components/autofill/core/browser/BUILD.gn
@@ -183,6 +183,12 @@ ] } + if (!is_android) { + sources += [ + "ui/save_card_bubble_controller.h", + ] + } + configs += [ "//build/config:precompiled_headers" ] public_deps = [ @@ -252,6 +258,13 @@ "test_personal_data_manager.h", ] + if (!is_android) { + sources += [ + "ui/mock_save_card_bubble_controller.cc", + "ui/mock_save_card_bubble_controller.h", + ] + } + public_deps = [ ":browser", ]
diff --git a/components/autofill/core/browser/autofill_client.h b/components/autofill/core/browser/autofill_client.h index 1533f15..f6ff075 100644 --- a/components/autofill/core/browser/autofill_client.h +++ b/components/autofill/core/browser/autofill_client.h
@@ -49,6 +49,7 @@ class CreditCard; class FormStructure; class PersonalDataManager; +class SaveCardBubbleController; struct Suggestion; // A client interface that needs to be supplied to the Autofill component by the @@ -110,9 +111,13 @@ // Gets the RapporServiceImpl associated with the client (for metrics). virtual rappor::RapporServiceImpl* GetRapporServiceImpl() = 0; - // Gets the UKM service assiciated with this client (for metrics). + // Gets the UKM service associated with this client (for metrics). virtual ukm::UkmService* GetUkmService() = 0; + // Gets the SaveCardBubbleController instance associated with the client. + // May return nullptr if the save card bubble has not been shown yet. + virtual SaveCardBubbleController* GetSaveCardBubbleController() = 0; + // Causes the Autofill settings UI to be shown. virtual void ShowAutofillSettings() = 0; @@ -129,10 +134,12 @@ const base::Closure& callback) = 0; // Runs |callback| if the |card| should be uploaded to Payments. Displays the - // contents of |legal_message| to the user. + // contents of |legal_message| to the user. Display a CVC field in the bubble + // if |should_cvc_be_requested| is true. virtual void ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) = 0; // Will show an infobar to get user consent for Credit Card assistive filling.
diff --git a/components/autofill/core/browser/autofill_experiments.cc b/components/autofill/core/browser/autofill_experiments.cc index 8d5704d..ef37213e 100644 --- a/components/autofill/core/browser/autofill_experiments.cc +++ b/components/autofill/core/browser/autofill_experiments.cc
@@ -34,6 +34,8 @@ "AutofillCreditCardLastUsedDateDisplay", base::FEATURE_DISABLED_BY_DEFAULT}; const base::Feature kAutofillUkmLogging{"AutofillUkmLogging", base::FEATURE_DISABLED_BY_DEFAULT}; +const base::Feature kAutofillUpstreamRequestCvcIfMissing{ + "AutofillUpstreamRequestCvcIfMissing", base::FEATURE_DISABLED_BY_DEFAULT}; const char kCreditCardSigninPromoImpressionLimitParamKey[] = "impression_limit"; const char kAutofillCreditCardPopupBackgroundColorKey[] = "background_color"; const char kAutofillCreditCardPopupDividerColorKey[] = "dropdown_divider_color"; @@ -229,4 +231,12 @@ return base::FeatureList::IsEnabled(kAutofillUkmLogging); } +bool IsAutofillUpstreamRequestCvcIfMissingExperimentEnabled() { +#if defined(OS_ANDROID) + return false; +#else + return base::FeatureList::IsEnabled(kAutofillUpstreamRequestCvcIfMissing); +#endif +} + } // namespace autofill
diff --git a/components/autofill/core/browser/autofill_experiments.h b/components/autofill/core/browser/autofill_experiments.h index 70c3f41..ba03ac2 100644 --- a/components/autofill/core/browser/autofill_experiments.h +++ b/components/autofill/core/browser/autofill_experiments.h
@@ -29,6 +29,7 @@ extern const base::Feature kAutofillCreditCardPopupLayout; extern const base::Feature kAutofillCreditCardLastUsedDateDisplay; extern const base::Feature kAutofillUkmLogging; +extern const base::Feature kAutofillUpstreamRequestCvcIfMissing; extern const char kCreditCardSigninPromoImpressionLimitParamKey[]; extern const char kAutofillCreditCardPopupSettingsSuggestionValueKey[]; extern const char kAutofillCreditCardLastUsedDateShowExpirationDateKey[]; @@ -104,6 +105,10 @@ // Returns whether the feature to log UKMs is enabled. bool IsUkmLoggingEnabled(); +// Returns whether the experiment is enabled where Chrome Upstream requests CVC +// in the offer to save bubble if it was not detected during the checkout flow. +bool IsAutofillUpstreamRequestCvcIfMissingExperimentEnabled(); + } // namespace autofill #endif // COMPONENTS_AUTOFILL_CORE_BROWSER_AUTOFILL_EXPERIMENTS_H_
diff --git a/components/autofill/core/browser/autofill_manager.cc b/components/autofill/core/browser/autofill_manager.cc index a137044..933b7c5 100644 --- a/components/autofill/core/browser/autofill_manager.cc +++ b/components/autofill/core/browser/autofill_manager.cc
@@ -51,6 +51,7 @@ #include "components/autofill/core/browser/phone_number.h" #include "components/autofill/core/browser/phone_number_i18n.h" #include "components/autofill/core/browser/popup_item_ids.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" #include "components/autofill/core/browser/validation.h" #include "components/autofill/core/common/autofill_clock.h" #include "components/autofill/core/common/autofill_constants.h" @@ -217,16 +218,24 @@ AutofillDownloadManagerState enable_download_manager) : driver_(driver), client_(client), - payments_client_( - new payments::PaymentsClient(driver->GetURLRequestContext(), this)), + payments_client_(base::MakeUnique<payments::PaymentsClient>( + driver->GetURLRequestContext(), + this)), app_locale_(app_locale), personal_data_(client->GetPersonalDataManager()), autocomplete_history_manager_( - new AutocompleteHistoryManager(driver, client)), + base::MakeUnique<AutocompleteHistoryManager>(driver, client)), + form_interactions_ukm_logger_( + base::MakeUnique<AutofillMetrics::FormInteractionsUkmLogger>( + client->GetUkmService())), address_form_event_logger_( - new AutofillMetrics::FormEventLogger(false /* is_for_credit_card */)), + base::MakeUnique<AutofillMetrics::FormEventLogger>( + false /* is_for_credit_card */, + form_interactions_ukm_logger_.get())), credit_card_form_event_logger_( - new AutofillMetrics::FormEventLogger(true /* is_for_credit_card */)), + base::MakeUnique<AutofillMetrics::FormEventLogger>( + true /* is_for_credit_card */, + form_interactions_ukm_logger_.get())), has_logged_autofill_enabled_(false), has_logged_address_suggestions_count_(false), did_show_suggestions_(false), @@ -234,6 +243,7 @@ user_did_autofill_(false), user_did_edit_autofilled_field_(false), user_did_accept_upload_prompt_(false), + should_cvc_be_requested_(false), external_delegate_(NULL), test_delegate_(NULL), #if defined(OS_ANDROID) || defined(OS_IOS) @@ -493,7 +503,7 @@ if (!upload_form) return; - StartUploadProcess(std::move(upload_form), base::TimeTicks::Now(), false); + StartUploadProcess(std::move(upload_form), TimeTicks::Now(), false); } void AutofillManager::OnTextFieldDidChange(const FormData& form, @@ -512,6 +522,9 @@ UpdatePendingForm(form); + if (!user_did_type_ || autofill_field->is_autofilled) + form_interactions_ukm_logger_->LogTextFieldDidChange(*autofill_field); + if (!user_did_type_) { user_did_type_ = true; AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::USER_DID_TYPE); @@ -1033,13 +1046,16 @@ user_did_accept_upload_prompt_ = false; client_->ConfirmSaveCreditCardToCloud( upload_request_.card, std::move(legal_message), + should_cvc_be_requested_, base::Bind(&AutofillManager::OnUserDidAcceptUpload, weak_ptr_factory_.GetWeakPtr())); client_->LoadRiskData(base::Bind(&AutofillManager::OnDidGetUploadRiskData, weak_ptr_factory_.GetWeakPtr())); - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_OFFERED); - LogCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED); + AutofillMetrics::CardUploadDecisionMetric card_upload_decision_metric = + should_cvc_be_requested_ ? AutofillMetrics::UPLOAD_OFFERED_NO_CVC + : AutofillMetrics::UPLOAD_OFFERED; + AutofillMetrics::LogCardUploadDecisionMetric(card_upload_decision_metric); + LogCardUploadDecisionUkm(card_upload_decision_metric); } else { // If the upload details request failed, fall back to a local save. The // reasoning here is as follows: @@ -1099,6 +1115,13 @@ user_did_accept_upload_prompt_ = true; if (!upload_request_.risk_data.empty()) { upload_request_.app_locale = app_locale_; + // If the upload request does not have card CVC, populate it with the + // value provided by the user: + if (upload_request_.cvc.empty()) { + DCHECK(client_->GetSaveCardBubbleController()); + upload_request_.cvc = + client_->GetSaveCardBubbleController()->GetCvcEnteredByUser(); + } payments_client_->UploadCard(upload_request_); } } @@ -1107,6 +1130,13 @@ upload_request_.risk_data = risk_data; if (user_did_accept_upload_prompt_) { upload_request_.app_locale = app_locale_; + // If the upload request does not have card CVC, populate it with the + // value provided by the user: + if (upload_request_.cvc.empty()) { + DCHECK(client_->GetSaveCardBubbleController()); + upload_request_.cvc = + client_->GetSaveCardBubbleController()->GetCvcEnteredByUser(); + } payments_client_->UploadCard(upload_request_); } } @@ -1199,9 +1229,11 @@ // because if only one of the two is missing, it may be fixable. // Check for a CVC to determine whether we can prompt the user to upload - // their card. If no CVC is present, do nothing. We could fall back to a - // local save but we believe that sometimes offering upload and sometimes - // offering local save is a confusing user experience. + // their card. If no CVC is present and the experiment is off, do nothing. + // We could fall back to a local save but we believe that sometimes offering + // upload and sometimes offering local save is a confusing user experience. + // If no CVC and the experiment is on, request CVC from the user in the + // bubble and save using the provided value. for (const auto& field : submitted_form) { if (field->Type().GetStorableType() == CREDIT_CARD_VERIFICATION_CODE && IsValidCreditCardSecurityCode(field->value, @@ -1226,14 +1258,19 @@ // Both the CVC and address checks are done. Conform to the legacy order of // reporting on CVC then address. + should_cvc_be_requested_ = false; if (upload_request_.cvc.empty()) { - AutofillMetrics::LogCardUploadDecisionMetric( - AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); - LogCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); - pending_upload_request_url_ = GURL(); - CollectRapportSample(submitted_form.source_url(), - "Autofill.CardUploadNotOfferedNoCvc"); - return; + if (IsAutofillUpstreamRequestCvcIfMissingExperimentEnabled()) { + should_cvc_be_requested_ = true; + } else { + AutofillMetrics::LogCardUploadDecisionMetric( + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + LogCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + pending_upload_request_url_ = GURL(); + CollectRapportSample(submitted_form.source_url(), + "Autofill.CardUploadNotOfferedNoCvc"); + return; + } } if (!get_profiles_succeeded) { DCHECK(get_profiles_decision_metric != AutofillMetrics::UPLOAD_OFFERED); @@ -1378,11 +1415,10 @@ const TimeTicks& interaction_time, const TimeTicks& submission_time, bool observed_submission) { - submitted_form->LogQualityMetrics(load_time, interaction_time, - submission_time, - client_->GetRapporServiceImpl(), - did_show_suggestions_, observed_submission); - + submitted_form->LogQualityMetrics( + load_time, interaction_time, submission_time, + client_->GetRapporServiceImpl(), form_interactions_ukm_logger_.get(), + did_show_suggestions_, observed_submission); if (submitted_form->ShouldBeCrowdsourced()) UploadFormData(*submitted_form, observed_submission); } @@ -1416,10 +1452,12 @@ ProcessPendingFormForUpload(); DCHECK(!pending_form_data_); form_structures_.clear(); - address_form_event_logger_.reset( - new AutofillMetrics::FormEventLogger(false /* is_for_credit_card */)); - credit_card_form_event_logger_.reset( - new AutofillMetrics::FormEventLogger(true /* is_for_credit_card */)); + form_interactions_ukm_logger_.reset( + new AutofillMetrics::FormInteractionsUkmLogger(client_->GetUkmService())); + address_form_event_logger_.reset(new AutofillMetrics::FormEventLogger( + false /* is_for_credit_card */, form_interactions_ukm_logger_.get())); + credit_card_form_event_logger_.reset(new AutofillMetrics::FormEventLogger( + true /* is_for_credit_card */, form_interactions_ukm_logger_.get())); #if defined(OS_ANDROID) || defined(OS_IOS) autofill_assistant_.Reset(); #endif @@ -1443,16 +1481,24 @@ PersonalDataManager* personal_data) : driver_(driver), client_(client), - payments_client_( - new payments::PaymentsClient(driver->GetURLRequestContext(), this)), + payments_client_(base::MakeUnique<payments::PaymentsClient>( + driver->GetURLRequestContext(), + this)), app_locale_("en-US"), personal_data_(personal_data), autocomplete_history_manager_( - new AutocompleteHistoryManager(driver, client)), + base::MakeUnique<AutocompleteHistoryManager>(driver, client)), + form_interactions_ukm_logger_( + base::MakeUnique<AutofillMetrics::FormInteractionsUkmLogger>( + client->GetUkmService())), address_form_event_logger_( - new AutofillMetrics::FormEventLogger(false /* is_for_credit_card */)), + base::MakeUnique<AutofillMetrics::FormEventLogger>( + false /* is_for_credit_card */, + form_interactions_ukm_logger_.get())), credit_card_form_event_logger_( - new AutofillMetrics::FormEventLogger(true /* is_for_credit_card */)), + base::MakeUnique<AutofillMetrics::FormEventLogger>( + true /* is_for_credit_card */, + form_interactions_ukm_logger_.get())), has_logged_autofill_enabled_(false), has_logged_address_suggestions_count_(false), did_show_suggestions_(false), @@ -1483,34 +1529,34 @@ // Updating the FormEventLoggers for addresses and credit cards. { - bool is_server_data_available = false; - bool is_local_data_available = false; + size_t server_record_type_count = 0; + size_t local_record_type_count = 0; for (CreditCard* credit_card : credit_cards) { if (credit_card->record_type() == CreditCard::LOCAL_CARD) - is_local_data_available = true; + local_record_type_count++; else - is_server_data_available = true; + server_record_type_count++; } - credit_card_form_event_logger_->set_is_server_data_available( - is_server_data_available); - credit_card_form_event_logger_->set_is_local_data_available( - is_local_data_available); + credit_card_form_event_logger_->set_server_record_type_count( + server_record_type_count); + credit_card_form_event_logger_->set_local_record_type_count( + local_record_type_count); credit_card_form_event_logger_->set_is_context_secure( client_->IsContextSecure()); } { - bool is_server_data_available = false; - bool is_local_data_available = false; + size_t server_record_type_count = 0; + size_t local_record_type_count = 0; for (AutofillProfile* profile : profiles) { if (profile->record_type() == AutofillProfile::LOCAL_PROFILE) - is_local_data_available = true; + local_record_type_count++; else if (profile->record_type() == AutofillProfile::SERVER_PROFILE) - is_server_data_available = true; + server_record_type_count++; } - address_form_event_logger_->set_is_server_data_available( - is_server_data_available); - address_form_event_logger_->set_is_local_data_available( - is_local_data_available); + address_form_event_logger_->set_server_record_type_count( + server_record_type_count); + address_form_event_logger_->set_local_record_type_count( + local_record_type_count); } if (profiles.empty() && credit_cards.empty()) @@ -1679,7 +1725,8 @@ std::unique_ptr<FormStructure> AutofillManager::ValidateSubmittedForm( const FormData& form) { - std::unique_ptr<FormStructure> submitted_form(new FormStructure(form)); + std::unique_ptr<FormStructure> submitted_form( + base::MakeUnique<FormStructure>(form)); if (!ShouldUploadForm(*submitted_form)) return std::unique_ptr<FormStructure>(); @@ -1855,7 +1902,7 @@ std::vector<FormStructure*> non_queryable_forms; std::vector<FormStructure*> queryable_forms; for (const FormData& form : forms) { - const auto parse_form_start_time = base::TimeTicks::Now(); + const auto parse_form_start_time = TimeTicks::Now(); FormStructure* form_structure = nullptr; if (!ParseForm(form, &form_structure)) @@ -1869,7 +1916,7 @@ else non_queryable_forms.push_back(form_structure); - AutofillMetrics::LogParseFormTiming(base::TimeTicks::Now() - + AutofillMetrics::LogParseFormTiming(TimeTicks::Now() - parse_form_start_time); } @@ -1880,6 +1927,9 @@ if (!queryable_forms.empty() || !non_queryable_forms.empty()) { AutofillMetrics::LogUserHappinessMetric(AutofillMetrics::FORMS_LOADED); + // Setup the url for metrics that we will collect for this form. + form_interactions_ukm_logger_->OnFormsLoaded(forms[0].origin); + #if defined(OS_IOS) // Log this from same location as AutofillMetrics::FORMS_LOADED to ensure // that KeyboardAccessoryButtonsIOS and UserHappiness UMA metrics will be
diff --git a/components/autofill/core/browser/autofill_manager.h b/components/autofill/core/browser/autofill_manager.h index 90e7ecd..c8597275 100644 --- a/components/autofill/core/browser/autofill_manager.h +++ b/components/autofill/core/browser/autofill_manager.h
@@ -43,8 +43,6 @@ #define ENABLE_FORM_DEBUG_DUMP #endif -class GURL; - namespace gfx { class RectF; } @@ -270,6 +268,10 @@ return &form_structures_; } + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger() { + return form_interactions_ukm_logger_.get(); + } + // Exposed for testing. AutofillExternalDelegate* external_delegate() { return external_delegate_; @@ -510,6 +512,10 @@ // Handles single-field autocomplete form data. std::unique_ptr<AutocompleteHistoryManager> autocomplete_history_manager_; + // Utility for logging URL keyed metrics. + std::unique_ptr<AutofillMetrics::FormInteractionsUkmLogger> + form_interactions_ukm_logger_; + // Utilities for logging form events. std::unique_ptr<AutofillMetrics::FormEventLogger> address_form_event_logger_; std::unique_ptr<AutofillMetrics::FormEventLogger> @@ -555,6 +561,7 @@ payments::PaymentsClient::UploadRequestDetails upload_request_; bool user_did_accept_upload_prompt_; GURL pending_upload_request_url_; + bool should_cvc_be_requested_; #ifdef ENABLE_FORM_DEBUG_DUMP // The last few autofilled forms (key/value pairs) submitted, for debugging. @@ -595,6 +602,7 @@ FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSubmittedFormEvents); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressWillSubmitFormEvents); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AddressSuggestionsCount); + FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillFormSubmittedState); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, AutofillIsEnabledAtPageLoad); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardSelectedFormEvents); FRIEND_TEST_ALL_PREFIXES(AutofillMetricsTest, CreditCardFilledFormEvents);
diff --git a/components/autofill/core/browser/autofill_manager_unittest.cc b/components/autofill/core/browser/autofill_manager_unittest.cc index 34bdd02..e9d94a4 100644 --- a/components/autofill/core/browser/autofill_manager_unittest.cc +++ b/components/autofill/core/browser/autofill_manager_unittest.cc
@@ -1032,6 +1032,11 @@ scoped_feature_list_.InitAndEnableFeature(kAutofillUkmLogging); } + void EnableAutofillUpstreamRequestCvcIfMissingExperimentAndUkmLogging() { + scoped_feature_list_.InitWithFeatures( + {kAutofillUpstreamRequestCvcIfMissing, kAutofillUkmLogging}, {}); + } + void ExpectUniqueFillableFormParsedUkm() { ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); @@ -4842,6 +4847,145 @@ // TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. #if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NoCvcFieldOnForm \ + DISABLED_UploadCreditCard_NoCvcFieldOnForm +#else +#define MAYBE_UploadCreditCard_NoCvcFieldOnForm \ + UploadCreditCard_NoCvcFieldOnForm +#endif +TEST_F(AutofillManagerTest, MAYBE_UploadCreditCard_NoCvcFieldOnForm) { + EnableAutofillUpstreamRequestCvcIfMissingExperimentAndUkmLogging(); + autofill_manager_->set_credit_card_upload_enabled(true); + + // Remove the profiles that were created in the TestPersonalDataManager + // constructor because they would result in conflicting names that would + // prevent the upload. + personal_data_.ClearAutofillProfiles(); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. Note that CVC field is missing. + FormData credit_card_form; + credit_card_form.name = ASCIIToUTF16("MyForm"); + credit_card_form.origin = GURL("https://myform.com/form.html"); + credit_card_form.action = GURL("https://myform.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("Card Name", "cardname", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Expiration Month", "ccmonth", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Expiration Year", "ccyear", "", "text", &field); + credit_card_form.fields.push_back(field); + + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16("11"); + credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + + base::HistogramTester histogram_tester; + + // Upload should still happen as long as the user provides CVC in the bubble. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0); + FormSubmitted(credit_card_form); + EXPECT_TRUE(autofill_manager_->credit_card_was_uploaded()); + + // Verify that the correct histogram entry (and only that) was logged. + histogram_tester.ExpectUniqueSample("Autofill.CardUploadDecisionExpanded", + AutofillMetrics::UPLOAD_OFFERED_NO_CVC, + 1); + // Verify that the correct UKM was logged. + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_OFFERED_NO_CVC); +} + +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) +#define MAYBE_UploadCreditCard_NoCvcFieldOnFormExperimentOff \ + DISABLED_UploadCreditCard_NoCvcFieldOnFormExperimentOff +#else +#define MAYBE_UploadCreditCard_NoCvcFieldOnFormExperimentOff \ + UploadCreditCard_NoCvcFieldOnFormExperimentOff +#endif +TEST_F(AutofillManagerTest, + MAYBE_UploadCreditCard_NoCvcFieldOnFormExperimentOff) { + EnableUkmLogging(); + autofill_manager_->set_credit_card_upload_enabled(true); + + // Remove the profiles that were created in the TestPersonalDataManager + // constructor because they would result in conflicting names that would + // prevent the upload. + personal_data_.ClearAutofillProfiles(); + + // Create, fill and submit an address form in order to establish a recent + // profile which can be selected for the upload request. + FormData address_form; + test::CreateTestAddressFormData(&address_form); + FormsSeen(std::vector<FormData>(1, address_form)); + ManuallyFillAddressForm("Flo", "Master", "77401", "US", &address_form); + FormSubmitted(address_form); + + // Set up our credit card form data. Note that CVC field is missing. + FormData credit_card_form; + credit_card_form.name = ASCIIToUTF16("MyForm"); + credit_card_form.origin = GURL("https://myform.com/form.html"); + credit_card_form.action = GURL("https://myform.com/submit.html"); + + FormFieldData field; + test::CreateTestFormField("Card Name", "cardname", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Card Number", "cardnumber", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Expiration Month", "ccmonth", "", "text", &field); + credit_card_form.fields.push_back(field); + test::CreateTestFormField("Expiration Year", "ccyear", "", "text", &field); + credit_card_form.fields.push_back(field); + + FormsSeen(std::vector<FormData>(1, credit_card_form)); + + // Edit the data, and submit. + credit_card_form.fields[0].value = ASCIIToUTF16("Flo Master"); + credit_card_form.fields[1].value = ASCIIToUTF16("4111111111111111"); + credit_card_form.fields[2].value = ASCIIToUTF16("11"); + credit_card_form.fields[3].value = ASCIIToUTF16("2017"); + + base::HistogramTester histogram_tester; + + // Neither a local save nor an upload should happen in this case. + EXPECT_CALL(autofill_client_, ConfirmSaveCreditCardLocally(_, _)).Times(0); + FormSubmitted(credit_card_form); + EXPECT_FALSE(autofill_manager_->credit_card_was_uploaded()); + + // Verify that the correct histogram entry (and only that) was logged. + histogram_tester.ExpectUniqueSample( + "Autofill.CardUploadDecisionExpanded", + AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC, 1); + // Verify that the correct UKM was logged. + ExpectCardUploadDecisionUkm(AutofillMetrics::UPLOAD_NOT_OFFERED_NO_CVC); + + rappor::TestRapporServiceImpl* rappor_service = + autofill_client_.test_rappor_service(); + EXPECT_EQ(1, rappor_service->GetReportsCount()); + std::string sample; + rappor::RapporType type; + EXPECT_TRUE(rappor_service->GetRecordedSampleForMetric( + "Autofill.CardUploadNotOfferedNoCvc", &sample, &type)); + EXPECT_EQ("myform.com", sample); + EXPECT_EQ(rappor::ETLD_PLUS_ONE_RAPPOR_TYPE, type); +} + +// TODO(crbug.com/666704): Flaky on android_n5x_swarming_rel bot. +#if defined(OS_ANDROID) #define MAYBE_UploadCreditCard_NoProfileAvailable DISABLED_UploadCreditCard_NoProfileAvailable #else #define MAYBE_UploadCreditCard_NoProfileAvailable UploadCreditCard_NoProfileAvailable
diff --git a/components/autofill/core/browser/autofill_metrics.cc b/components/autofill/core/browser/autofill_metrics.cc index e813b183..7744239 100644 --- a/components/autofill/core/browser/autofill_metrics.cc +++ b/components/autofill/core/browser/autofill_metrics.cc
@@ -5,6 +5,8 @@ #include "components/autofill/core/browser/autofill_metrics.h" #include <algorithm> +#include <utility> +#include <vector> #include "base/logging.h" #include "base/metrics/histogram_macros.h" @@ -12,6 +14,7 @@ #include "base/metrics/user_metrics.h" #include "base/time/time.h" #include "components/autofill/core/browser/autofill_experiments.h" +#include "components/autofill/core/browser/autofill_field.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/form_structure.h" #include "components/autofill/core/common/form_data.h" @@ -22,6 +25,28 @@ const char kUKMCardUploadDecisionMetricName[] = "UploadDecision"; const char kUKMDeveloperEngagementEntryName[] = "Autofill.DeveloperEngagement"; const char kUKMDeveloperEngagementMetricName[] = "DeveloperEngagement"; +const char kUKMMillisecondsSinceFormLoadedMetricName[] = + "MillisecondsSinceFormLoaded"; +const char kUKMInteractedWithFormEntryName[] = "Autofill.InteractedWithForm"; +const char kUKMIsForCreditCardMetricName[] = "IsForCreditCard"; +const char kUKMLocalRecordTypeCountMetricName[] = "LocalRecordTypeCount"; +const char kUKMServerRecordTypeCountMetricName[] = "ServerRecordTypeCount"; +const char kUKMSuggestionsShownEntryName[] = "Autofill.SuggestionsShown"; +const char kUKMSelectedMaskedServerCardEntryName[] = + "Autofill.SelectedMaskedServerCard"; +const char kUKMSuggestionFilledEntryName[] = "Autofill.SuggestionFilled"; +const char kUKMRecordTypeMetricName[] = "RecordType"; +const char kUKMTextFieldDidChangeEntryName[] = "Autofill.TextFieldDidChange"; +const char kUKMFieldTypeGroupMetricName[] = "FieldTypeGroup"; +const char kUKMHeuristicTypeMetricName[] = "HeuristicType"; +const char kUKMServerTypeMetricName[] = "ServerType"; +const char kUKMHtmlFieldTypeMetricName[] = "HtmlFieldType"; +const char kUKMHtmlFieldModeMetricName[] = "HtmlFieldMode"; +const char kUKMIsAutofilledMetricName[] = "IsAutofilled"; +const char kUKMIsEmptyMetricName[] = "IsEmpty"; +const char kUKMFormSubmittedEntryName[] = "Autofill.AutofillFormSubmitted"; +const char kUKMAutofillFormSubmittedStateMetricName[] = + "AutofillFormSubmittedState"; } // namespace internal namespace autofill { @@ -616,7 +641,8 @@ // static void AutofillMetrics::LogAutofillFormSubmittedState( - AutofillFormSubmittedState state) { + AutofillFormSubmittedState state, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) { UMA_HISTOGRAM_ENUMERATION("Autofill.FormSubmittedState", state, AUTOFILL_FORM_SUBMITTED_STATE_ENUM_SIZE); @@ -650,6 +676,7 @@ NOTREACHED(); break; } + form_interactions_ukm_logger->LogFormSubmitted(state); } // static @@ -706,7 +733,7 @@ if (upload_decision >= AutofillMetrics::NUM_CARD_UPLOAD_DECISION_METRICS) return; - const std::map<std::string, int> metrics = { + const std::vector<std::pair<const char*, int>> metrics = { {internal::kUKMCardUploadDecisionMetricName, static_cast<int>(upload_decision)}}; LogUkm(ukm_service, url, internal::kUKMCardUploadDecisionEntryName, metrics); @@ -716,18 +743,22 @@ void AutofillMetrics::LogDeveloperEngagementUkm( ukm::UkmService* ukm_service, const GURL& url, - AutofillMetrics::DeveloperEngagementMetric metric) { - const std::map<std::string, int> form_structure_metrics = { - {internal::kUKMDeveloperEngagementMetricName, static_cast<int>(metric)}}; + std::vector<AutofillMetrics::DeveloperEngagementMetric> metrics) { + std::vector<std::pair<const char*, int>> form_structure_metrics; + for (const auto it : metrics) + form_structure_metrics.push_back( + {internal::kUKMDeveloperEngagementMetricName, static_cast<int>(it)}); + LogUkm(ukm_service, url, internal::kUKMDeveloperEngagementEntryName, form_structure_metrics); } // static -bool AutofillMetrics::LogUkm(ukm::UkmService* ukm_service, - const GURL& url, - const std::string& ukm_entry_name, - const std::map<std::string, int>& metrics) { +bool AutofillMetrics::LogUkm( + ukm::UkmService* ukm_service, + const GURL& url, + const std::string& ukm_entry_name, + const std::vector<std::pair<const char*, int>>& metrics) { if (!IsUkmLoggingEnabled() || !ukm_service || !url.is_valid() || metrics.empty()) { return false; @@ -739,16 +770,18 @@ ukm_service->GetEntryBuilder(source_id, ukm_entry_name.c_str()); for (auto it = metrics.begin(); it != metrics.end(); ++it) { - builder->AddMetric(it->first.c_str(), it->second); + builder->AddMetric(it->first, it->second); } return true; } -AutofillMetrics::FormEventLogger::FormEventLogger(bool is_for_credit_card) +AutofillMetrics::FormEventLogger::FormEventLogger( + bool is_for_credit_card, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger) : is_for_credit_card_(is_for_credit_card), - is_server_data_available_(false), - is_local_data_available_(false), + server_record_type_count_(0), + local_record_type_count_(0), is_context_secure_(false), has_logged_interacted_(false), has_logged_suggestions_shown_(false), @@ -757,11 +790,15 @@ has_logged_will_submit_(false), has_logged_submitted_(false), logged_suggestion_filled_was_server_data_(false), - logged_suggestion_filled_was_masked_server_card_(false) {} + logged_suggestion_filled_was_masked_server_card_(false), + form_interactions_ukm_logger_(form_interactions_ukm_logger) {} void AutofillMetrics::FormEventLogger::OnDidInteractWithAutofillableForm() { if (!has_logged_interacted_) { has_logged_interacted_ = true; + form_interactions_ukm_logger_->LogInteractedWithForm( + is_for_credit_card_, local_record_type_count_, + server_record_type_count_); Log(AutofillMetrics::FORM_EVENT_INTERACTED_ONCE); } } @@ -786,6 +823,8 @@ } void AutofillMetrics::FormEventLogger::OnDidShowSuggestions() { + form_interactions_ukm_logger_->LogSuggestionsShown(); + Log(AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN); if (!has_logged_suggestions_shown_) { has_logged_suggestions_shown_ = true; @@ -803,17 +842,22 @@ void AutofillMetrics::FormEventLogger::OnDidSelectMaskedServerCardSuggestion() { DCHECK(is_for_credit_card_); + form_interactions_ukm_logger_->LogSelectedMaskedServerCard(); + Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED); if (!has_logged_masked_server_card_suggestion_selected_) { has_logged_masked_server_card_suggestion_selected_ = true; - Log(AutofillMetrics - ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE); + Log(AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SELECTED_ONCE); } } void AutofillMetrics::FormEventLogger::OnDidFillSuggestion( const CreditCard& credit_card) { DCHECK(is_for_credit_card_); + form_interactions_ukm_logger_->LogDidFillSuggestion( + static_cast<int>(credit_card.record_type())); + if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) Log(AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED); else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD) @@ -829,8 +873,8 @@ logged_suggestion_filled_was_masked_server_card_ = credit_card.record_type() == CreditCard::MASKED_SERVER_CARD; if (credit_card.record_type() == CreditCard::MASKED_SERVER_CARD) { - Log(AutofillMetrics - ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE); + Log(AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE); } else if (credit_card.record_type() == CreditCard::FULL_SERVER_CARD) { Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE); } else { @@ -845,6 +889,9 @@ void AutofillMetrics::FormEventLogger::OnDidFillSuggestion( const AutofillProfile& profile) { DCHECK(!is_for_credit_card_); + form_interactions_ukm_logger_->LogDidFillSuggestion( + static_cast<int>(profile.record_type())); + if (profile.record_type() == AutofillProfile::SERVER_PROFILE) Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED); else @@ -855,8 +902,8 @@ logged_suggestion_filled_was_server_data_ = profile.record_type() == AutofillProfile::SERVER_PROFILE; Log(profile.record_type() == AutofillProfile::SERVER_PROFILE - ? AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE - : AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE); + ? AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_FILLED_ONCE + : AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_FILLED_ONCE); } base::RecordAction( @@ -904,8 +951,8 @@ if (!has_logged_suggestion_filled_) { Log(AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE); } else if (logged_suggestion_filled_was_masked_server_card_) { - Log(AutofillMetrics - ::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE); + Log(AutofillMetrics:: + FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_SUBMITTED_ONCE); } else if (logged_suggestion_filled_was_server_data_) { Log(AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE); } else { @@ -937,15 +984,154 @@ // Logging again in a different histogram for segmentation purposes. // TODO(waltercacau): Re-evaluate if we still need such fine grained // segmentation. http://crbug.com/454018 - if (!is_server_data_available_ && !is_local_data_available_) + if (server_record_type_count_ == 0 && local_record_type_count_ == 0) name += ".WithNoData"; - else if (is_server_data_available_ && !is_local_data_available_) + else if (server_record_type_count_ > 0 && local_record_type_count_ == 0) name += ".WithOnlyServerData"; - else if (!is_server_data_available_ && is_local_data_available_) + else if (server_record_type_count_ == 0 && local_record_type_count_ > 0) name += ".WithOnlyLocalData"; else name += ".WithBothServerAndLocalData"; LogUMAHistogramEnumeration(name, event, NUM_FORM_EVENTS); } +AutofillMetrics::FormInteractionsUkmLogger::FormInteractionsUkmLogger( + ukm::UkmService* ukm_service) + : ukm_service_(ukm_service) {} + +void AutofillMetrics::FormInteractionsUkmLogger::OnFormsLoaded( + const GURL& url) { + if (!IsUkmLoggingEnabled() || ukm_service_ == nullptr) + return; + + url_ = url; + form_loaded_timestamp_ = base::TimeTicks::Now(); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogInteractedWithForm( + bool is_for_credit_card, + size_t local_record_type_count, + size_t server_record_type_count) { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMInteractedWithFormEntryName); + builder->AddMetric(internal::kUKMIsForCreditCardMetricName, + is_for_credit_card); + builder->AddMetric(internal::kUKMLocalRecordTypeCountMetricName, + local_record_type_count); + builder->AddMetric(internal::kUKMServerRecordTypeCountMetricName, + server_record_type_count); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogSuggestionsShown() { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMSuggestionsShownEntryName); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogSelectedMaskedServerCard() { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMSelectedMaskedServerCardEntryName); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogDidFillSuggestion( + int record_type) { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMSuggestionFilledEntryName); + builder->AddMetric(internal::kUKMRecordTypeMetricName, record_type); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogTextFieldDidChange( + const AutofillField& field) { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMTextFieldDidChangeEntryName); + builder->AddMetric(internal::kUKMFieldTypeGroupMetricName, + static_cast<int>(field.Type().group())); + builder->AddMetric(internal::kUKMHeuristicTypeMetricName, + static_cast<int>(field.heuristic_type())); + builder->AddMetric(internal::kUKMServerTypeMetricName, + static_cast<int>(field.server_type())); + builder->AddMetric(internal::kUKMHtmlFieldTypeMetricName, + static_cast<int>(field.html_type())); + builder->AddMetric(internal::kUKMHtmlFieldModeMetricName, + static_cast<int>(field.html_mode())); + builder->AddMetric(internal::kUKMIsAutofilledMetricName, field.is_autofilled); + builder->AddMetric(internal::kUKMIsEmptyMetricName, field.IsEmpty()); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::LogFormSubmitted( + AutofillFormSubmittedState state) { + if (!CanLog()) + return; + + if (source_id_ == -1) + GetNewSourceID(); + + std::unique_ptr<ukm::UkmEntryBuilder> builder = ukm_service_->GetEntryBuilder( + source_id_, internal::kUKMFormSubmittedEntryName); + builder->AddMetric(internal::kUKMAutofillFormSubmittedStateMetricName, + static_cast<int>(state)); + builder->AddMetric(internal::kUKMMillisecondsSinceFormLoadedMetricName, + MillisecondsSinceFormLoaded()); +} + +void AutofillMetrics::FormInteractionsUkmLogger::UpdateSourceURL( + const GURL& url) { + url_ = url; + if (CanLog()) + ukm_service_->UpdateSourceURL(source_id_, url_); +} + +bool AutofillMetrics::FormInteractionsUkmLogger::CanLog() const { + return IsUkmLoggingEnabled() && ukm_service_ && url_.is_valid(); +} + +int64_t +AutofillMetrics::FormInteractionsUkmLogger::MillisecondsSinceFormLoaded() + const { + DCHECK(!form_loaded_timestamp_.is_null()); + return (base::TimeTicks::Now() - form_loaded_timestamp_).InMilliseconds(); +} + +void AutofillMetrics::FormInteractionsUkmLogger::GetNewSourceID() { + source_id_ = ukm_service_->GetNewSourceID(); + ukm_service_->UpdateSourceURL(source_id_, url_); +} + } // namespace autofill
diff --git a/components/autofill/core/browser/autofill_metrics.h b/components/autofill/core/browser/autofill_metrics.h index ace3362..58ab8d2 100644 --- a/components/autofill/core/browser/autofill_metrics.h +++ b/components/autofill/core/browser/autofill_metrics.h
@@ -7,18 +7,17 @@ #include <stddef.h> #include <string> +#include <utility> +#include <vector> #include "base/macros.h" +#include "base/time/time.h" #include "components/autofill/core/browser/autofill_client.h" #include "components/autofill/core/browser/autofill_profile.h" #include "components/autofill/core/browser/credit_card.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/common/form_field_data.h" -namespace base { -class TimeDelta; -} // namespace base - namespace ukm { class UkmService; } // namespace ukm @@ -29,10 +28,53 @@ extern const char kUKMCardUploadDecisionMetricName[]; extern const char kUKMDeveloperEngagementEntryName[]; extern const char kUKMDeveloperEngagementMetricName[]; + +// Each form interaction event has a separate |UkmEntry|. + +// The first form event |UkmEntry| contains metrics for metadata that apply +// to all subsequent events. +extern const char kUKMInteractedWithFormEntryName[]; +extern const char kUKMIsForCreditCardMetricName[]; +extern const char kUKMLocalRecordTypeCountMetricName[]; +extern const char kUKMServerRecordTypeCountMetricName[]; + +// |UkmEntry| when we show suggestions. +extern const char kUKMSuggestionsShownEntryName[]; + +// |UkmEntry| when user selects a masked server credit card. +extern const char kUKMSelectedMaskedServerCardEntryName[]; + +// Each |UkmEntry|, except the first interaction with the form, has a metric for +// time elapsed, in milliseconds, since we loaded the form. +extern const char kUKMMillisecondsSinceFormLoadedMetricName[]; + +// |FormEvent| for FORM_EVENT_*_SUGGESTION_FILLED in credit card forms include a +// |CreditCard| |record_type()| to indicate if the suggestion was for a local +// card, masked server card or full server card. Similarly, address/profile +// forms include a |AutofillProfile| |record_type()| to indicate if the +// profile was a local profile or server profile. +extern const char kUKMSuggestionFilledEntryName[]; +extern const char kUKMRecordTypeMetricName[]; + +// |UkmEntry| for user editing text field. Metrics contain field's attributes. +extern const char kUKMTextFieldDidChangeEntryName[]; +extern const char kUKMFieldTypeGroupMetricName[]; +extern const char kUKMHeuristicTypeMetricName[]; +extern const char kUKMServerTypeMetricName[]; +extern const char kUKMHtmlFieldTypeMetricName[]; +extern const char kUKMHtmlFieldModeMetricName[]; +extern const char kUKMIsAutofilledMetricName[]; +extern const char kUKMIsEmptyMetricName[]; + +// |UkmEntry| for |AutofillFormSubmittedState|. +extern const char kUKMFormSubmittedEntryName[]; +extern const char kUKMAutofillFormSubmittedStateMetricName[]; } // namespace internal namespace autofill { +class AutofillField; + class AutofillMetrics { public: enum AutofillProfileAction { @@ -82,6 +124,9 @@ // were otherwise valid nor whether we would have been able to get upload // details. UPLOAD_NOT_OFFERED_CONFLICTING_NAMES, + // No CVC was detected, but valid addresses and names were. Upload is still + // possible if the user manually enters CVC, so upload was offered. + UPLOAD_OFFERED_NO_CVC, NUM_CARD_UPLOAD_DECISION_METRICS, }; @@ -566,6 +611,39 @@ NUM_CONVERTED_ADDRESS_CONVERSION_TYPES }; + // Utility to log URL keyed form interaction events. + class FormInteractionsUkmLogger { + public: + explicit FormInteractionsUkmLogger(ukm::UkmService* ukm_service); + + const GURL& url() const { return url_; } + + void OnFormsLoaded(const GURL& url); + void LogInteractedWithForm(bool is_for_credit_card, + size_t local_record_type_count, + size_t server_record_type_count); + void LogSuggestionsShown(); + void LogSelectedMaskedServerCard(); + void LogDidFillSuggestion(int record_type); + void LogTextFieldDidChange(const AutofillField& field); + void LogFormSubmitted(AutofillFormSubmittedState state); + + // We initialize |url_| with the form's URL when we log the first form + // interaction. Later, we may update |url_| with the |source_url()| for the + // submitted form. + void UpdateSourceURL(const GURL& url); + + private: + bool CanLog() const; + int64_t MillisecondsSinceFormLoaded() const; + void GetNewSourceID(); + + ukm::UkmService* ukm_service_; // Weak reference. + int32_t source_id_ = -1; + GURL url_; + base::TimeTicks form_loaded_timestamp_; + }; + static void LogCardUploadDecisionMetric(CardUploadDecisionMetric metric); static void LogCreditCardInfoBarMetric(InfoBarMetric metric, bool is_uploading); @@ -692,7 +770,9 @@ // This should be called at each form submission to indicate the autofilled // state of the form. - static void LogAutofillFormSubmittedState(AutofillFormSubmittedState state); + static void LogAutofillFormSubmittedState( + AutofillFormSubmittedState state, + FormInteractionsUkmLogger* form_interactions_ukm_logger); // This should be called when determining the heuristic types for a form's // fields. @@ -731,27 +811,28 @@ static void LogDeveloperEngagementUkm( ukm::UkmService* ukm_service, const GURL& url, - AutofillMetrics::DeveloperEngagementMetric metric); + std::vector<AutofillMetrics::DeveloperEngagementMetric> metrics); // Logs the the |ukm_entry_name| with the specified |url| and the specified // |metrics|. Returns whether the ukm was sucessfully logged. static bool LogUkm(ukm::UkmService* ukm_service, const GURL& url, const std::string& ukm_entry_name, - const std::map<std::string, int>& metrics); + const std::vector<std::pair<const char*, int>>& metrics); - // Utility to autofill form events in the relevant histograms depending on + // Utility to log autofill form events in the relevant histograms depending on // the presence of server and/or local data. class FormEventLogger { public: - FormEventLogger(bool is_for_credit_card); + FormEventLogger(bool is_for_credit_card, + FormInteractionsUkmLogger* form_interactions_ukm_logger); - inline void set_is_server_data_available(bool is_server_data_available) { - is_server_data_available_ = is_server_data_available; + inline void set_server_record_type_count(size_t server_record_type_count) { + server_record_type_count_ = server_record_type_count; } - inline void set_is_local_data_available(bool is_local_data_available) { - is_local_data_available_ = is_local_data_available; + inline void set_local_record_type_count(size_t local_record_type_count) { + local_record_type_count_ = local_record_type_count; } inline void set_is_context_secure(bool is_context_secure) { @@ -780,8 +861,8 @@ void Log(FormEvent event) const; bool is_for_credit_card_; - bool is_server_data_available_; - bool is_local_data_available_; + size_t server_record_type_count_; + size_t local_record_type_count_; bool is_context_secure_; bool has_logged_interacted_; bool has_logged_suggestions_shown_; @@ -794,6 +875,9 @@ // The last field that was polled for suggestions. FormFieldData last_polled_field_; + + FormInteractionsUkmLogger* + form_interactions_ukm_logger_; // Weak reference. }; private:
diff --git a/components/autofill/core/browser/autofill_metrics_unittest.cc b/components/autofill/core/browser/autofill_metrics_unittest.cc index 56b76bc..3372ee3 100644 --- a/components/autofill/core/browser/autofill_metrics_unittest.cc +++ b/components/autofill/core/browser/autofill_metrics_unittest.cc
@@ -7,6 +7,7 @@ #include <stddef.h> #include <memory> +#include <utility> #include <vector> #include "base/feature_list.h" @@ -54,7 +55,8 @@ using base::TimeTicks; using rappor::TestRapporServiceImpl; using ::testing::ElementsAre; -using ::testing::UnorderedElementsAre; +using ::testing::Matcher; +using ::testing::UnorderedPointwise; namespace autofill { namespace { @@ -264,6 +266,8 @@ base::MakeUnique<TestFormStructure>(empty_form); form_structure->SetFieldTypes(heuristic_types, server_types); form_structures()->push_back(std::move(form_structure)); + + form_interactions_ukm_logger()->OnFormsLoaded(form.origin); } // Calls AutofillManager::OnWillSubmitForm and waits for it to complete. @@ -288,9 +292,9 @@ void RunRunLoop() { run_loop_->Run(); } void UploadFormDataAsyncCallback(const FormStructure* submitted_form, - const base::TimeTicks& load_time, - const base::TimeTicks& interaction_time, - const base::TimeTicks& submission_time, + const TimeTicks& load_time, + const TimeTicks& interaction_time, + const TimeTicks& submission_time, bool observed_submission) override { run_loop_->Quit(); @@ -317,6 +321,83 @@ return nullptr; } +MATCHER(CompareMetrics, "") { + const ukm::Entry_Metric& lhs = ::testing::get<0>(arg); + const std::pair<const char*, int64_t>& rhs = ::testing::get<1>(arg); + return lhs.metric_hash() == base::HashMetricName(rhs.first) && + lhs.value() == rhs.second; +} + +void VerifyDeveloperEngagementUkm( + const FormData& form, + const ukm::TestUkmService* ukm_service, + const std::vector<int64_t>& expected_metric_values) { + const ukm::UkmEntry* entry = ukm_service->GetEntryForEntryName( + internal::kUKMDeveloperEngagementEntryName); + ASSERT_NE(nullptr, entry); + ukm::Entry entry_proto; + entry->PopulateProto(&entry_proto); + + const ukm::UkmSource* source = + ukm_service->GetSourceForSourceId(entry_proto.source_id()); + ASSERT_NE(nullptr, source); + EXPECT_EQ(form.origin, source->url()); + + std::vector<std::pair<const char*, int64_t>> expected_metrics; + for (const auto it : expected_metric_values) + expected_metrics.push_back( + {internal::kUKMDeveloperEngagementMetricName, it}); + + EXPECT_THAT(entry_proto.metrics(), + UnorderedPointwise(CompareMetrics(), expected_metrics)); +} + +MATCHER(CompareMetricsIgnoringMillisecondsSinceFormLoaded, "") { + const ukm::Entry_Metric& lhs = ::testing::get<0>(arg); + const std::pair<const char*, int64_t>& rhs = ::testing::get<1>(arg); + return lhs.metric_hash() == base::HashMetricName(rhs.first) && + (lhs.value() == rhs.second || + (lhs.value() > 0 && + rhs.first == internal::kUKMMillisecondsSinceFormLoadedMetricName)); +} + +void VerifyFormInteractionUkm( + const FormData& form, + const ukm::TestUkmService* ukm_service, + const char* event_name, + const std::vector<std::vector<std::pair<const char*, int64_t>>>& + expected_metrics) { + size_t expected_metrics_index = 0; + for (size_t i = 0; i < ukm_service->entries_count(); ++i) { + const ukm::UkmEntry* entry = ukm_service->GetEntry(i); + if (entry->event_hash() != base::HashMetricName(event_name)) + continue; + + ukm::Entry entry_proto; + entry->PopulateProto(&entry_proto); + + const ukm::UkmSource* source = + ukm_service->GetSourceForSourceId(entry_proto.source_id()); + ASSERT_NE(nullptr, source); + EXPECT_EQ(form.origin, source->url()); + + ASSERT_LT(expected_metrics_index, expected_metrics.size()); + EXPECT_THAT( + entry_proto.metrics(), + UnorderedPointwise(CompareMetricsIgnoringMillisecondsSinceFormLoaded(), + expected_metrics[expected_metrics_index++])); + } +} + +void VerifySubmitFormUkm(const FormData& form, + const ukm::TestUkmService* ukm_service, + AutofillMetrics::AutofillFormSubmittedState state) { + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMFormSubmittedEntryName, + {{{internal::kUKMAutofillFormSubmittedStateMetricName, state}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); +} + } // namespace // This is defined in the autofill_metrics.cc implementation file. @@ -394,6 +475,7 @@ account_tracker_.reset(); signin_client_.reset(); test::ReenableSystemServices(); + autofill_client_.GetTestUkmService()->Purge(); } void AutofillMetricsTest::EnableWalletSync() { @@ -694,7 +776,7 @@ // Simulate a OnFormsSeen() call that should trigger the recording. std::vector<FormData> forms; forms.push_back(form); - autofill_manager_->OnFormsSeen(forms, base::TimeTicks::Now()); + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); // Because these metrics are related to timing, it is not possible to know in // advance which bucket the sample will fall into, so we just need to make @@ -1545,6 +1627,11 @@ // fields is logged. histogram_tester.ExpectUniqueSample( "Autofill.NumberOfEditedAutofilledFieldsAtSubmission", 2, 1); + + // UKM must not be logged unless enabled. + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + EXPECT_EQ(0U, ukm_service->sources_count()); + EXPECT_EQ(0U, ukm_service->entries_count()); } // Verify that when resetting the autofill manager (such as during a @@ -1721,7 +1808,7 @@ // Ensure no metrics are logged when loading a non-fillable form. { - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); autofill_manager_->Reset(); EXPECT_EQ(0U, ukm_service->sources_count()); @@ -1732,30 +1819,17 @@ test::CreateTestFormField("Phone", "phone", "", "text", &field); forms.back().fields.push_back(field); - // Expect the "form parsed without field type hints" metric to be logged. + // Expect the "form parsed without field type hints" metric and the + // "form loaded" form interaction event to be logged. { - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); autofill_manager_->Reset(); - ASSERT_EQ(1U, ukm_service->sources_count()); - const ukm::UkmSource* source = - ukm_service->GetSourceForUrl(form.origin.spec().c_str()); - ASSERT_NE(nullptr, source); - ASSERT_EQ(1U, ukm_service->entries_count()); - const ukm::UkmEntry* entry = ukm_service->GetEntry(0); - EXPECT_EQ(source->id(), entry->source_id()); - - ukm::Entry entry_proto; - entry->PopulateProto(&entry_proto); - EXPECT_EQ(source->id(), entry_proto.source_id()); - EXPECT_EQ(base::HashMetricName(internal::kUKMDeveloperEngagementEntryName), - entry_proto.event_hash()); - const ukm::Entry_Metric* metric = FindMetric( - internal::kUKMDeveloperEngagementMetricName, entry_proto.metrics()); - ASSERT_NE(nullptr, metric); - EXPECT_EQ(AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS, - metric->value()); + ASSERT_EQ(1U, ukm_service->sources_count()); + VerifyDeveloperEngagementUkm( + form, ukm_service, + {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); } } @@ -1798,30 +1872,17 @@ field.autocomplete_attribute = "address-line1"; forms.back().fields.push_back(field); - // Expect the "form parsed with field type hints" metric to be logged. + // Expect the "form parsed without field type hints" metric and the + // "form loaded" form interaction event to be logged. { - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); autofill_manager_->Reset(); - ASSERT_EQ(1U, ukm_service->sources_count()); - const ukm::UkmSource* source = - ukm_service->GetSourceForUrl(form.origin.spec().c_str()); - ASSERT_NE(nullptr, source); - ASSERT_EQ(1U, ukm_service->entries_count()); - const ukm::UkmEntry* entry = ukm_service->GetEntry(0); - EXPECT_EQ(source->id(), entry->source_id()); - - ukm::Entry entry_proto; - entry->PopulateProto(&entry_proto); - EXPECT_EQ(source->id(), entry_proto.source_id()); - EXPECT_EQ(base::HashMetricName(internal::kUKMDeveloperEngagementEntryName), - entry_proto.event_hash()); - const ukm::Entry_Metric* metric = FindMetric( - internal::kUKMDeveloperEngagementMetricName, entry_proto.metrics()); - ASSERT_NE(nullptr, metric); - EXPECT_EQ(AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, - metric->value()); + ASSERT_EQ(1U, ukm_service->sources_count()); + VerifyDeveloperEngagementUkm( + form, ukm_service, + {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS}); } } @@ -1847,29 +1908,32 @@ std::vector<FormData> forms(1, form); - // Expect the "upi-vpa hint" metric to be logged. + // Expect the "upi-vpa hint" metric to be logged and the "form loaded" form + // interaction event to be logged. { - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); autofill_manager_->Reset(); - ASSERT_EQ(1U, ukm_service->sources_count()); - const ukm::UkmSource* source = - ukm_service->GetSourceForUrl(form.origin.spec().c_str()); - ASSERT_NE(nullptr, source); - ASSERT_EQ(1U, ukm_service->entries_count()); - const ukm::UkmEntry* entry = ukm_service->GetEntry(0); - EXPECT_EQ(source->id(), entry->source_id()); + ASSERT_EQ(1U, ukm_service->sources_count()); + VerifyDeveloperEngagementUkm(form, ukm_service, + {AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT}); + ukm_service->Purge(); + } - ukm::Entry entry_proto; - entry->PopulateProto(&entry_proto); - EXPECT_EQ(source->id(), entry_proto.source_id()); - EXPECT_EQ(base::HashMetricName(internal::kUKMDeveloperEngagementEntryName), - entry_proto.event_hash()); - const ukm::Entry_Metric* metric = FindMetric( - internal::kUKMDeveloperEngagementMetricName, entry_proto.metrics()); - ASSERT_NE(nullptr, metric); - EXPECT_EQ(AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT, metric->value()); + // Add another field with an author-specified field type to the form. + test::CreateTestFormField("", "", "", "text", &field); + field.autocomplete_attribute = "address-line1"; + forms.back().fields.push_back(field); + + { + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); + autofill_manager_->Reset(); + + VerifyDeveloperEngagementUkm( + form, ukm_service, + {AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS, + AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT}); } } @@ -2052,6 +2116,9 @@ // Test that the credit card checkout flow user actions are correctly logged. TEST_F(AutofillMetricsTest, CreditCardCheckoutFlowUserActions) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + personal_data_->RecreateCreditCards( true /* include_local_credit_card */, false /* include_masked_server_credit_card */, @@ -2127,10 +2194,30 @@ EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); } + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + // Expect 2 |FORM_EVENT_LOCAL_SUGGESTION_FILLED| events. First, from + // call to |external_delegate_->DidAcceptSuggestion|. Second, from call to + // |autofill_manager_->FillOrPreviewForm|. + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + // Expect |NON_FILLABLE_FORM_OR_NEW_DATA| in |AutofillFormSubmittedState| + // because |field.value| is empty in |DeterminePossibleFieldTypesForUpload|. + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } // Test that the profile checkout flow user actions are correctly logged. TEST_F(AutofillMetricsTest, ProfileCheckoutFlowUserActions) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + // Create a profile. personal_data_->RecreateProfile(); @@ -2178,7 +2265,7 @@ std::string guid("00000000-0000-0000-0000-000000000001"); // local profile. external_delegate_->DidAcceptSuggestion( ASCIIToUTF16("Test"), - autofill_manager_->MakeFrontendID(guid, std::string()), 0); + autofill_manager_->MakeFrontendID(std::string(), guid), 0); EXPECT_EQ(1, user_action_tester.GetActionCount("Autofill_SelectedSuggestion")); } @@ -2204,6 +2291,23 @@ EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); } + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + // Expect 2 |FORM_EVENT_LOCAL_SUGGESTION_FILLED| events. First, from + // call to |external_delegate_->DidAcceptSuggestion|. Second, from call to + // |autofill_manager_->FillOrPreviewForm|. + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + // Expect |NON_FILLABLE_FORM_OR_NEW_DATA| in |AutofillFormSubmittedState| + // because |field.value| is empty in |DeterminePossibleFieldTypesForUpload|. + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } // Tests that the Autofill_PolledCreditCardSuggestions user action is only @@ -2493,6 +2597,11 @@ "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_SUGGESTIONS_SHOWN_ONCE, 0); } + + // UKM must not be logged unless enabled. + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + EXPECT_EQ(0U, ukm_service->sources_count()); + EXPECT_EQ(0U, ukm_service->entries_count()); } // Test that we log selected form event for credit cards. @@ -2625,6 +2734,7 @@ autofill_manager_->MakeFrontendID(guid, std::string())); autofill_manager_->OnDidGetRealPan(AutofillClient::SUCCESS, "6011000990139424"); + autofill_manager_->SubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1); @@ -2758,6 +2868,9 @@ // Test that we log submitted form events for credit cards. TEST_F(AutofillMetricsTest, CreditCardSubmittedFormEvents) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + EnableWalletSync(); // Creating all kinds of cards. personal_data_->RecreateCreditCards( @@ -2797,10 +2910,15 @@ histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); + + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2815,10 +2933,18 @@ histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 1); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2836,10 +2962,19 @@ histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_LOCAL_SUGGESTION_SUBMITTED_ONCE, 1); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, CreditCard::LOCAL_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2858,10 +2993,19 @@ histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_SERVER_SUGGESTION_SUBMITTED_ONCE, 1); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, CreditCard::FULL_SERVER_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2874,6 +3018,7 @@ autofill_manager_->MakeFrontendID(guid, std::string())); autofill_manager_->OnDidGetRealPan(AutofillClient::SUCCESS, "6011000990139424"); + autofill_manager_->SubmitForm(form, TimeTicks::Now()); histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED, 1); @@ -2881,8 +3026,22 @@ "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_FILLED_ONCE, 1); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, CreditCard::MASKED_SERVER_CARD}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSelectedMaskedServerCardEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } + // Reset the autofill manager state and purge UKM logs. + autofill_manager_->Reset(); + ukm_service->Purge(); + // Recreating cards as the previous test should have upgraded the masked // card to a full card. personal_data_->RecreateCreditCards( @@ -2899,7 +3058,24 @@ base::HistogramTester histogram_tester; autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); autofill_manager_->SubmitForm(form, TimeTicks::Now()); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMFormSubmittedEntryName, + {{{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + autofill_manager_->SubmitForm(form, TimeTicks::Now()); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMFormSubmittedEntryName, + {{{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + histogram_tester.ExpectBucketCount( "Autofill.FormEvents.CreditCard", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -2936,8 +3112,10 @@ 0); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -2980,6 +3158,12 @@ AutofillMetrics:: FORM_EVENT_MASKED_SERVER_CARD_SUGGESTION_WILL_SUBMIT_ONCE, 0); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } } @@ -3401,6 +3585,9 @@ // Test that we log submitted form events for address. TEST_F(AutofillMetricsTest, AddressSubmittedFormEvents) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + EnableWalletSync(); // Create a profile. personal_data_->RecreateProfile(); @@ -3437,10 +3624,15 @@ histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_SUBMITTED_ONCE, 1); + + VerifySubmitFormUkm(form, ukm_service, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); } - // Reset the autofill manager state. + // Reset the autofill manager state and purge UKM logs. autofill_manager_->Reset(); + ukm_service->Purge(); + autofill_manager_->AddSeenForm(form, field_types, field_types); { @@ -3488,6 +3680,7 @@ autofill_manager_->OnQueryFormFieldAutofill(0, form, field, gfx::RectF()); autofill_manager_->SubmitForm(form, TimeTicks::Now()); autofill_manager_->SubmitForm(form, TimeTicks::Now()); + histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_NO_SUGGESTION_WILL_SUBMIT_ONCE, 1); @@ -3524,6 +3717,7 @@ base::HistogramTester histogram_tester; autofill_manager_->DidShowSuggestions(true /* is_new_popup */, form, field); autofill_manager_->SubmitForm(form, TimeTicks::Now()); + histogram_tester.ExpectBucketCount( "Autofill.FormEvents.Address", AutofillMetrics::FORM_EVENT_SUGGESTION_SHOWN_WILL_SUBMIT_ONCE, 0); @@ -3894,7 +4088,6 @@ } } - // Test that we log that Autofill is enabled when filling a form. TEST_F(AutofillMetricsTest, AutofillIsEnabledAtPageLoad) { base::HistogramTester histogram_tester; @@ -3933,6 +4126,9 @@ // Verify that we correctly log the submitted form's state. TEST_F(AutofillMetricsTest, AutofillFormSubmittedState) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + // Start with a form with insufficiently many fields. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -3953,10 +4149,16 @@ // Expect no notifications when the form is first seen. { base::HistogramTester histogram_tester; - autofill_manager_->OnFormsSeen(forms, TimeTicks()); + autofill_manager_->OnFormsSeen(forms, TimeTicks::Now()); histogram_tester.ExpectTotalCount("Autofill.FormSubmittedState", 0); } + std::vector<std::vector<std::pair<const char*, int64_t>>> + expected_form_submission_ukm_metrics = { + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}; + // No data entered in the form. { base::HistogramTester histogram_tester; @@ -3967,6 +4169,17 @@ AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); + + // Expect an entry for |DeveloperEngagement| and an entry for form + // interactions. Both entries are for the same URL. + ASSERT_EQ(2U, ukm_service->entries_count()); + ASSERT_EQ(2U, ukm_service->sources_count()); + VerifyDeveloperEngagementUkm( + form, ukm_service, + {AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Non fillable form. @@ -3983,6 +4196,14 @@ AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Fill in the third field. @@ -4000,6 +4221,15 @@ 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_FilledNone_SuggestionsNotShown")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics:: + FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Autofilled none with suggestions shown. @@ -4013,6 +4243,17 @@ AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_FilledNone_SuggestionsShown")); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Mark one of the fields as autofilled. @@ -4029,6 +4270,14 @@ AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_FilledSome")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Mark all of the fillable fields as autofilled. @@ -4046,6 +4295,14 @@ AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_FilledAll")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } // Clear out the third field's value. @@ -4062,12 +4319,23 @@ AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA, 1); EXPECT_EQ(1, user_action_tester.GetActionCount( "Autofill_FormSubmitted_NonFillable")); + + expected_form_submission_ukm_metrics.push_back( + {{internal::kUKMAutofillFormSubmittedStateMetricName, + AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}); + VerifyFormInteractionUkm(form, ukm_service, + internal::kUKMFormSubmittedEntryName, + expected_form_submission_ukm_metrics); } } // Verify that we correctly log user happiness metrics dealing with form // interaction. TEST_F(AutofillMetricsTest, UserHappinessFormInteraction) { + EnableUkmLogging(); + ukm::TestUkmService* ukm_service = autofill_client_.GetTestUkmService(); + // Load a fillable form. FormData form; form.name = ASCIIToUTF16("TestForm"); @@ -4167,6 +4435,50 @@ "Autofill.UserHappiness", AutofillMetrics::USER_DID_EDIT_AUTOFILLED_FIELD, 1); } + + autofill_manager_->Reset(); + + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMInteractedWithFormEntryName, + {{{internal::kUKMIsForCreditCardMetricName, false}, + {internal::kUKMLocalRecordTypeCountMetricName, 0}, + {internal::kUKMServerRecordTypeCountMetricName, 0}}}); + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionsShownEntryName, + {{{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMSuggestionFilledEntryName, + {{{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMRecordTypeMetricName, AutofillProfile::LOCAL_PROFILE}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); + VerifyFormInteractionUkm( + form, ukm_service, internal::kUKMTextFieldDidChangeEntryName, + {{{internal::kUKMFieldTypeGroupMetricName, NAME}, + {internal::kUKMHeuristicTypeMetricName, NAME_FULL}, + {internal::kUKMServerTypeMetricName, NO_SERVER_DATA}, + {internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED}, + {internal::kUKMHtmlFieldModeMetricName, HTML_MODE_NONE}, + {internal::kUKMIsAutofilledMetricName, false}, + {internal::kUKMIsEmptyMetricName, true}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMFieldTypeGroupMetricName, NAME}, + {internal::kUKMHeuristicTypeMetricName, NAME_FULL}, + {internal::kUKMServerTypeMetricName, NO_SERVER_DATA}, + {internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED}, + {internal::kUKMHtmlFieldModeMetricName, HTML_MODE_NONE}, + {internal::kUKMIsAutofilledMetricName, true}, + {internal::kUKMIsEmptyMetricName, true}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}, + {{internal::kUKMFieldTypeGroupMetricName, EMAIL}, + {internal::kUKMHeuristicTypeMetricName, EMAIL_ADDRESS}, + {internal::kUKMServerTypeMetricName, NO_SERVER_DATA}, + {internal::kUKMHtmlFieldTypeMetricName, HTML_TYPE_UNSPECIFIED}, + {internal::kUKMHtmlFieldModeMetricName, HTML_MODE_NONE}, + {internal::kUKMIsAutofilledMetricName, true}, + {internal::kUKMIsEmptyMetricName, true}, + {internal::kUKMMillisecondsSinceFormLoadedMetricName, 0}}}); } // Verify that we correctly log metrics tracking the duration of form fill. @@ -4744,7 +5056,7 @@ ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); int upload_decision = 1; - std::map<std::string, int> metrics = { + std::vector<std::pair<const char*, int>> metrics = { {internal::kUKMCardUploadDecisionMetricName, upload_decision}}; EXPECT_TRUE(AutofillMetrics::LogUkm( @@ -4785,7 +5097,7 @@ ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); int form_structure_metric = 1; - std::map<std::string, int> metrics = { + std::vector<std::pair<const char*, int>> metrics = { {internal::kUKMDeveloperEngagementMetricName, form_structure_metric}}; EXPECT_TRUE(AutofillMetrics::LogUkm( @@ -4825,7 +5137,7 @@ EnableUkmLogging(); ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url(""); - std::map<std::string, int> metrics = {{"metric", 1}}; + std::vector<std::pair<const char*, int>> metrics = {{"metric", 1}}; EXPECT_FALSE(AutofillMetrics::LogUkm( ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics)); @@ -4837,7 +5149,7 @@ EnableUkmLogging(); ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); - std::map<std::string, int> metrics; + std::vector<std::pair<const char*, int>> metrics; EXPECT_FALSE(AutofillMetrics::LogUkm( ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics)); @@ -4849,7 +5161,7 @@ EnableUkmLogging(); ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); - std::map<std::string, int> metrics = {{"metric", 1}}; + std::vector<std::pair<const char*, int>> metrics = {{"metric", 1}}; EXPECT_FALSE(AutofillMetrics::LogUkm(nullptr, url, "test_ukm", metrics)); ASSERT_EQ(0U, ukm_service_test_harness.test_ukm_service()->sources_count()); @@ -4859,7 +5171,7 @@ TEST_F(AutofillMetricsTest, RecordCardUploadDecisionMetric_FeatureDisabled) { ukm::UkmServiceTestingHarness ukm_service_test_harness; GURL url("https://www.google.com"); - std::map<std::string, int> metrics = {{"metric", 1}}; + std::vector<std::pair<const char*, int>> metrics = {{"metric", 1}}; EXPECT_FALSE(AutofillMetrics::LogUkm( ukm_service_test_harness.test_ukm_service(), url, "test_ukm", metrics));
diff --git a/components/autofill/core/browser/form_structure.cc b/components/autofill/core/browser/form_structure.cc index aaa1c82d..9bff7e2 100644 --- a/components/autofill/core/browser/form_structure.cc +++ b/components/autofill/core/browser/form_structure.cc
@@ -9,6 +9,7 @@ #include <algorithm> #include <map> #include <utility> +#include <vector> #include "base/command_line.h" #include "base/i18n/case_conversion.h" @@ -364,23 +365,25 @@ UpdateAutofillCount(); IdentifySections(has_author_specified_sections_); + std::vector<AutofillMetrics::DeveloperEngagementMetric> metrics; if (IsAutofillable()) { - const auto metric = + AutofillMetrics::DeveloperEngagementMetric metric = has_author_specified_types_ ? AutofillMetrics::FILLABLE_FORM_PARSED_WITH_TYPE_HINTS : AutofillMetrics::FILLABLE_FORM_PARSED_WITHOUT_TYPE_HINTS; + metrics.push_back(metric); AutofillMetrics::LogDeveloperEngagementMetric(metric); - AutofillMetrics::LogDeveloperEngagementUkm(ukm_service, source_url(), - metric); } if (has_author_specified_upi_vpa_hint_) { AutofillMetrics::LogDeveloperEngagementMetric( AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT); - AutofillMetrics::LogDeveloperEngagementUkm( - ukm_service, source_url(), AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT); + metrics.push_back(AutofillMetrics::FORM_CONTAINS_UPI_VPA_HINT); } + AutofillMetrics::LogDeveloperEngagementUkm(ukm_service, source_url(), + metrics); + AutofillMetrics::LogDetermineHeuristicTypesTiming( base::TimeTicks::Now() - determine_heuristic_types_start_time); } @@ -682,12 +685,14 @@ form_signature_ = cached_form.form_signature_; } -void FormStructure::LogQualityMetrics(const base::TimeTicks& load_time, - const base::TimeTicks& interaction_time, - const base::TimeTicks& submission_time, - rappor::RapporServiceImpl* rappor_service, - bool did_show_suggestions, - bool observed_submission) const { +void FormStructure::LogQualityMetrics( + const base::TimeTicks& load_time, + const base::TimeTicks& interaction_time, + const base::TimeTicks& submission_time, + rappor::RapporServiceImpl* rappor_service, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, + bool did_show_suggestions, + bool observed_submission) const { size_t num_detected_field_types = 0; size_t num_server_mismatches = 0; size_t num_heuristic_mismatches = 0; @@ -824,24 +829,20 @@ // We log "submission" and duration metrics if we are here after observing a // submission event. if (observed_submission) { + AutofillMetrics::AutofillFormSubmittedState state; if (num_detected_field_types < kRequiredFieldsForPredictionRoutines) { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA); + state = AutofillMetrics::NON_FILLABLE_FORM_OR_NEW_DATA; } else { if (did_autofill_all_possible_fields) { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL); + state = AutofillMetrics::FILLABLE_FORM_AUTOFILLED_ALL; } else if (did_autofill_some_possible_fields) { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME); + state = AutofillMetrics::FILLABLE_FORM_AUTOFILLED_SOME; } else if (!did_show_suggestions) { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics:: - FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS); + state = AutofillMetrics:: + FILLABLE_FORM_AUTOFILLED_NONE_DID_NOT_SHOW_SUGGESTIONS; } else { - AutofillMetrics::LogAutofillFormSubmittedState( - AutofillMetrics:: - FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS); + state = + AutofillMetrics::FILLABLE_FORM_AUTOFILLED_NONE_DID_SHOW_SUGGESTIONS; } // Log some RAPPOR metrics for problematic cases. @@ -888,6 +889,10 @@ } } } + if (form_interactions_ukm_logger->url() != source_url()) + form_interactions_ukm_logger->UpdateSourceURL(source_url()); + AutofillMetrics::LogAutofillFormSubmittedState( + state, form_interactions_ukm_logger); } }
diff --git a/components/autofill/core/browser/form_structure.h b/components/autofill/core/browser/form_structure.h index 2890f67..5365ec1 100644 --- a/components/autofill/core/browser/form_structure.h +++ b/components/autofill/core/browser/form_structure.h
@@ -18,6 +18,7 @@ #include "base/strings/string16.h" #include "base/strings/string_piece.h" #include "components/autofill/core/browser/autofill_field.h" +#include "components/autofill/core/browser/autofill_metrics.h" #include "components/autofill/core/browser/autofill_type.h" #include "components/autofill/core/browser/field_types.h" #include "components/autofill/core/browser/proto/server.pb.h" @@ -132,12 +133,16 @@ // indicates whether this method is called as a result of observing a // submission event (otherwise, it may be that an upload was triggered after // a form was unfocused or a navigation occurred). - void LogQualityMetrics(const base::TimeTicks& load_time, - const base::TimeTicks& interaction_time, - const base::TimeTicks& submission_time, - rappor::RapporServiceImpl* rappor_service, - bool did_show_suggestions, - bool observed_submission) const; + // TODO(sebsg): We log more than quality metrics. Maybe rename or split + // function? + void LogQualityMetrics( + const base::TimeTicks& load_time, + const base::TimeTicks& interaction_time, + const base::TimeTicks& submission_time, + rappor::RapporServiceImpl* rappor_service, + AutofillMetrics::FormInteractionsUkmLogger* form_interactions_ukm_logger, + bool did_show_suggestions, + bool observed_submission) const; // Log the quality of the heuristics and server predictions for this form // structure, if autocomplete attributes are present on the fields (they are
diff --git a/components/autofill/core/browser/test_autofill_client.cc b/components/autofill/core/browser/test_autofill_client.cc index 753edd5..1c67ac6 100644 --- a/components/autofill/core/browser/test_autofill_client.cc +++ b/components/autofill/core/browser/test_autofill_client.cc
@@ -3,6 +3,9 @@ // found in the LICENSE file. #include "components/autofill/core/browser/test_autofill_client.h" +#if !defined(OS_ANDROID) +#include "components/autofill/core/browser/ui/mock_save_card_bubble_controller.h" +#endif #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" @@ -12,6 +15,9 @@ : token_service_(new FakeOAuth2TokenService()), identity_provider_(new FakeIdentityProvider(token_service_.get())), rappor_service_(new rappor::TestRapporServiceImpl()), +#if !defined(OS_ANDROID) + save_card_bubble_controller_(new MockSaveCardBubbleController()), +#endif form_origin_(GURL("https://example.test")) {} TestAutofillClient::~TestAutofillClient() { @@ -45,6 +51,14 @@ return ukm_service_test_harness_.test_ukm_service(); } +SaveCardBubbleController* TestAutofillClient::GetSaveCardBubbleController() { +#if defined(OS_ANDROID) + return nullptr; +#else + return save_card_bubble_controller_.get(); +#endif +} + void TestAutofillClient::ShowAutofillSettings() { } @@ -65,6 +79,7 @@ void TestAutofillClient::ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) { callback.Run(); }
diff --git a/components/autofill/core/browser/test_autofill_client.h b/components/autofill/core/browser/test_autofill_client.h index 7bd1c96..a140c772 100644 --- a/components/autofill/core/browser/test_autofill_client.h +++ b/components/autofill/core/browser/test_autofill_client.h
@@ -36,6 +36,7 @@ IdentityProvider* GetIdentityProvider() override; rappor::RapporServiceImpl* GetRapporServiceImpl() override; ukm::UkmService* GetUkmService() override; + SaveCardBubbleController* GetSaveCardBubbleController() override; void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, UnmaskCardReason reason, @@ -46,6 +47,7 @@ void ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) override; void ConfirmCreditCardFillAssist(const CreditCard& card, const base::Closure& callback) override; @@ -97,6 +99,9 @@ std::unique_ptr<FakeIdentityProvider> identity_provider_; std::unique_ptr<rappor::TestRapporServiceImpl> rappor_service_; ukm::UkmServiceTestingHarness ukm_service_test_harness_; +#if !defined(OS_ANDROID) + std::unique_ptr<SaveCardBubbleController> save_card_bubble_controller_; +#endif GURL form_origin_; DISALLOW_COPY_AND_ASSIGN(TestAutofillClient);
diff --git a/components/autofill/core/browser/ui/mock_save_card_bubble_controller.cc b/components/autofill/core/browser/ui/mock_save_card_bubble_controller.cc new file mode 100644 index 0000000..b0c8d787 --- /dev/null +++ b/components/autofill/core/browser/ui/mock_save_card_bubble_controller.cc
@@ -0,0 +1,22 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/autofill/core/browser/ui/mock_save_card_bubble_controller.h" + +namespace autofill { + +MockSaveCardBubbleController::MockSaveCardBubbleController() {} + +MockSaveCardBubbleController::~MockSaveCardBubbleController() {} + +base::string16 MockSaveCardBubbleController::GetCvcEnteredByUser() const { + return cvc_entered_by_user_; +} + +void MockSaveCardBubbleController::OnSaveButton(const base::string16& cvc) { + if (!cvc.empty()) + cvc_entered_by_user_ = cvc; +} + +} // namespace autofill
diff --git a/components/autofill/core/browser/ui/mock_save_card_bubble_controller.h b/components/autofill/core/browser/ui/mock_save_card_bubble_controller.h new file mode 100644 index 0000000..ce040ae --- /dev/null +++ b/components/autofill/core/browser/ui/mock_save_card_bubble_controller.h
@@ -0,0 +1,42 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOCK_SAVE_CARD_BUBBLE_CONTROLLER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOCK_SAVE_CARD_BUBBLE_CONTROLLER_H_ + +#include "components/autofill/core/browser/credit_card.h" +#include "components/autofill/core/browser/ui/save_card_bubble_controller.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace autofill { + +class MockSaveCardBubbleController : public SaveCardBubbleController { + public: + MockSaveCardBubbleController(); + ~MockSaveCardBubbleController() override; + + MOCK_CONST_METHOD0(GetWindowTitle, base::string16()); + MOCK_CONST_METHOD0(GetExplanatoryMessage, base::string16()); + MOCK_CONST_METHOD0(GetCard, const CreditCard()); + MOCK_CONST_METHOD0(GetCvcImageResourceId, int()); + MOCK_CONST_METHOD0(ShouldRequestCvcFromUser, bool()); + MOCK_METHOD0(OnCancelButton, void()); + MOCK_METHOD0(OnLearnMoreClicked, void()); + MOCK_METHOD1(OnLegalMessageLinkClicked, void(const GURL& url)); + MOCK_METHOD0(OnBubbleClosed, void()); + MOCK_CONST_METHOD0(GetLegalMessageLines, const LegalMessageLines&()); + MOCK_CONST_METHOD1(InputCvcIsValid, bool(const base::string16& input_text)); + + base::string16 GetCvcEnteredByUser() const override; + void OnSaveButton(const base::string16& cvc = base::string16()) override; + + private: + base::string16 cvc_entered_by_user_; + + DISALLOW_COPY_AND_ASSIGN(MockSaveCardBubbleController); +}; + +} // namespace autofill + +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_MOCK_SAVE_CARD_BUBBLE_CONTROLLER_H_
diff --git a/chrome/browser/ui/autofill/save_card_bubble_controller.h b/components/autofill/core/browser/ui/save_card_bubble_controller.h similarity index 61% rename from chrome/browser/ui/autofill/save_card_bubble_controller.h rename to components/autofill/core/browser/ui/save_card_bubble_controller.h index 6f74eb3..f01cac3 100644 --- a/chrome/browser/ui/autofill/save_card_bubble_controller.h +++ b/components/autofill/core/browser/ui/save_card_bubble_controller.h
@@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROME_BROWSER_UI_AUTOFILL_SAVE_CARD_BUBBLE_CONTROLLER_H_ -#define CHROME_BROWSER_UI_AUTOFILL_SAVE_CARD_BUBBLE_CONTROLLER_H_ +#ifndef COMPONENTS_AUTOFILL_CORE_BROWSER_UI_SAVE_CARD_BUBBLE_CONTROLLER_H_ +#define COMPONENTS_AUTOFILL_CORE_BROWSER_UI_SAVE_CARD_BUBBLE_CONTROLLER_H_ #include <memory> #include <vector> @@ -21,6 +21,9 @@ // Interface that exposes controller functionality to SaveCardBubbleView. class SaveCardBubbleController { public: + SaveCardBubbleController() {} + virtual ~SaveCardBubbleController() {} + // Returns the title that should be displayed in the bubble. virtual base::string16 GetWindowTitle() const = 0; @@ -31,8 +34,20 @@ // Returns the card that will be uploaded if the user accepts. virtual const CreditCard GetCard() const = 0; + // Returns the CVC image icon resource ID. + virtual int GetCvcImageResourceId() const = 0; + + // Returns whether the dialog should include a field requesting the card's CVC + // from the user. + virtual bool ShouldRequestCvcFromUser() const = 0; + + // Returns the CVC provided by the user in the save card bubble. + virtual base::string16 GetCvcEnteredByUser() const = 0; + // Interaction. - virtual void OnSaveButton() = 0; + // OnSaveButton takes in a string value representing the CVC entered by the + // user if it was requested, or an empty string otherwise. + virtual void OnSaveButton(const base::string16& cvc) = 0; virtual void OnCancelButton() = 0; virtual void OnLearnMoreClicked() = 0; virtual void OnLegalMessageLinkClicked(const GURL& url) = 0; @@ -43,13 +58,13 @@ // Returns empty vector if no legal message should be shown. virtual const LegalMessageLines& GetLegalMessageLines() const = 0; - protected: - SaveCardBubbleController() {} - virtual ~SaveCardBubbleController() {} + // Utilities. + virtual bool InputCvcIsValid(const base::string16& input_text) const = 0; + private: DISALLOW_COPY_AND_ASSIGN(SaveCardBubbleController); }; } // namespace autofill -#endif // CHROME_BROWSER_UI_AUTOFILL_SAVE_CARD_BUBBLE_CONTROLLER_H_ +#endif // COMPONENTS_AUTOFILL_CORE_BROWSER_UI_SAVE_CARD_BUBBLE_CONTROLLER_H_
diff --git a/components/autofill_strings.grdp b/components/autofill_strings.grdp index 73c48b7..2ab82162 100644 --- a/components/autofill_strings.grdp +++ b/components/autofill_strings.grdp
@@ -210,6 +210,9 @@ <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_UPLOAD_EXPLANATION" desc="Explanation of the effect of the Autofill save card prompt when the card is to be saved by uploading it to Google Payments. The prompt can be either a bubble or an infobar."> Pay quickly on sites and apps across devices using cards you have saved with Google. </message> + <message name="IDS_AUTOFILL_SAVE_CARD_PROMPT_ENTER_CVC" desc="Text displayed in the Autofill save credit card prompt explaining that the card needs additional CVC information in order to be saved to Google Payments."> + Please verify your CVC + </message> <!-- Autofill credit card suggestion popup --> <message name="IDS_AUTOFILL_CREDIT_CARD_EXPIRATION_DATE_ABBR" desc="Abbreviated label for credit card expiration date. [CHAR-LIMIT=32]">
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc index eb267fb1..f5a610b1 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.cc
@@ -287,6 +287,12 @@ return OK; } +DataStore::Status TestDataStore::RecreateDB() { + map_.clear(); + + return OK; +} + DataReductionProxyTestContext::Builder::Builder() : params_flags_(DataReductionProxyParams::kPromoAllowed), params_definitions_(TestDataReductionProxyParams::HAS_EVERYTHING),
diff --git a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h index e42557f3..a6eb403 100644 --- a/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h +++ b/components/data_reduction_proxy/core/browser/data_reduction_proxy_test_utils.h
@@ -261,6 +261,8 @@ DataStore::Status Delete(base::StringPiece key) override; + DataStore::Status RecreateDB() override; + std::map<std::string, std::string>* map() { return &map_; } private:
diff --git a/components/data_reduction_proxy/core/browser/data_store.cc b/components/data_reduction_proxy/core/browser/data_store.cc index eacf50c..782a194 100644 --- a/components/data_reduction_proxy/core/browser/data_store.cc +++ b/components/data_reduction_proxy/core/browser/data_store.cc
@@ -28,4 +28,8 @@ return DataStore::Status::OK; } +DataStore::Status DataStore::RecreateDB() { + return DataStore::Status::OK; +} + } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_store.h b/components/data_reduction_proxy/core/browser/data_store.h index 1c254c2..f275da7 100644 --- a/components/data_reduction_proxy/core/browser/data_store.h +++ b/components/data_reduction_proxy/core/browser/data_store.h
@@ -36,6 +36,9 @@ virtual Status Delete(base::StringPiece key); + // Deletes the LevelDB and recreates it. + virtual Status RecreateDB(); + private: DISALLOW_COPY_AND_ASSIGN(DataStore); };
diff --git a/components/data_reduction_proxy/core/browser/data_store_impl.cc b/components/data_reduction_proxy/core/browser/data_store_impl.cc index 76903be..3c7c180f 100644 --- a/components/data_reduction_proxy/core/browser/data_store_impl.cc +++ b/components/data_reduction_proxy/core/browser/data_store_impl.cc
@@ -117,7 +117,9 @@ leveldb::Options options; options.create_if_missing = true; options.paranoid_checks = true; - options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue; + // Deletes to buckets not found are stored in the log. Use a new log so that + // these log entries are deleted. + options.reuse_logs = false; std::string db_name = profile_path_.Append(kDBName).AsUTF8Unsafe(); leveldb::DB* dbptr = nullptr; Status status = @@ -144,14 +146,13 @@ return status; } -void DataStoreImpl::RecreateDB() { +DataStore::Status DataStoreImpl::RecreateDB() { DCHECK(sequence_checker_.CalledOnValidSequence()); - LOG(WARNING) << "Deleting corrupt Data Reduction Proxy LevelDB"; db_.reset(nullptr); base::DeleteFile(profile_path_.Append(kDBName), true); - OpenDB(); + return OpenDB(); } } // namespace data_reduction_proxy
diff --git a/components/data_reduction_proxy/core/browser/data_store_impl.h b/components/data_reduction_proxy/core/browser/data_store_impl.h index b7aeca2..950de079 100644 --- a/components/data_reduction_proxy/core/browser/data_store_impl.h +++ b/components/data_reduction_proxy/core/browser/data_store_impl.h
@@ -36,14 +36,14 @@ Status Delete(base::StringPiece key) override; + // Deletes the LevelDB and recreates it. This method is called if any DB call + // returns a |CORRUPTED| status or the database is cleared. + Status RecreateDB() override; + private: // Opens the underlying LevelDB for read and write. Status OpenDB(); - // Deletes the LevelDB and recreates it. This method is called if any DB call - // returns a |CORRUPTED| status. - void RecreateDB(); - // The underlying LevelDB used by this implementation. std::unique_ptr<leveldb::DB> db_;
diff --git a/components/data_reduction_proxy/core/browser/data_usage_store.cc b/components/data_reduction_proxy/core/browser/data_usage_store.cc index fb89dd0..41f5fb6 100644 --- a/components/data_reduction_proxy/core/browser/data_usage_store.cc +++ b/components/data_reduction_proxy/core/browser/data_usage_store.cc
@@ -152,10 +152,16 @@ } void DataUsageStore::DeleteHistoricalDataUsage() { - for (int i = 0; i < kNumDataUsageBuckets; ++i) - db_->Delete(DbKeyForBucketIndex(i)); + std::string current_index_string; + DataStore::Status index_read_status = + db_->Get(kCurrentBucketIndexKey, ¤t_index_string); - db_->Delete(kCurrentBucketIndexKey); + // If the index doesn't exist, then no buckets have been written and the + // data usage doesn't need to be deleted. + if (index_read_status != DataStore::Status::OK) + return; + + db_->RecreateDB(); } void DataUsageStore::DeleteBrowsingHistory(const base::Time& start,
diff --git a/components/history/core/browser/BUILD.gn b/components/history/core/browser/BUILD.gn index e60183b..b288937 100644 --- a/components/history/core/browser/BUILD.gn +++ b/components/history/core/browser/BUILD.gn
@@ -192,7 +192,6 @@ "history_backend_db_unittest.cc", "history_backend_unittest.cc", "history_database_unittest.cc", - "history_model_worker_unittest.cc", "history_querying_unittest.cc", "history_service_unittest.cc", "history_types_unittest.cc",
diff --git a/components/history/core/browser/history_model_worker.cc b/components/history/core/browser/history_model_worker.cc index ad3f991..72b843d 100644 --- a/components/history/core/browser/history_model_worker.cc +++ b/components/history/core/browser/history_model_worker.cc
@@ -6,19 +6,30 @@ #include <utility> -#include "base/memory/ptr_util.h" +#include "base/synchronization/waitable_event.h" #include "components/history/core/browser/history_db_task.h" #include "components/history/core/browser/history_service.h" +#include "components/sync/base/scoped_event_signal.h" namespace browser_sync { class WorkerTask : public history::HistoryDBTask { public: - WorkerTask(base::OnceClosure work) : work_(std::move(work)) {} + WorkerTask(const syncer::WorkCallback& work, + syncer::ScopedEventSignal scoped_event_signal, + syncer::SyncerError* error) + : work_(work), + scoped_event_signal_(std::move(scoped_event_signal)), + error_(error) {} bool RunOnDBThread(history::HistoryBackend* backend, history::HistoryDatabase* db) override { - std::move(work_).Run(); + // Signal the completion event at the end of this scope. + auto scoped_event_signal = std::move(scoped_event_signal_); + + // Run the task. + *error_ = work_.Run(); + return true; } @@ -26,12 +37,34 @@ // any code asynchronously on the main thread after completion. void DoneRunOnMainThread() override {} - private: - // A OnceClosure is deleted right after it runs. This is important to unblock - // DoWorkAndWaitUntilDone() right after the task runs. - base::OnceClosure work_; + protected: + ~WorkerTask() override { + // The event in |scoped_event_signal_| is signaled at the end of this + // scope if this is destroyed before RunOnDBThread runs. + } - DISALLOW_COPY_AND_ASSIGN(WorkerTask); + syncer::WorkCallback work_; + syncer::ScopedEventSignal scoped_event_signal_; + syncer::SyncerError* error_; +}; + +class AddDBThreadObserverTask : public history::HistoryDBTask { + public: + explicit AddDBThreadObserverTask(base::Closure register_callback) + : register_callback_(register_callback) {} + + bool RunOnDBThread(history::HistoryBackend* backend, + history::HistoryDatabase* db) override { + register_callback_.Run(); + return true; + } + + void DoneRunOnMainThread() override {} + + private: + ~AddDBThreadObserverTask() override {} + + base::Closure register_callback_; }; namespace { @@ -40,11 +73,18 @@ // thread. void PostWorkerTask( const base::WeakPtr<history::HistoryService>& history_service, - base::OnceClosure work, - base::CancelableTaskTracker* cancelable_tracker) { + const syncer::WorkCallback& work, + syncer::ScopedEventSignal scoped_event_signal, + base::CancelableTaskTracker* cancelable_tracker, + syncer::SyncerError* error) { if (history_service.get()) { - history_service->ScheduleDBTask( - base::MakeUnique<WorkerTask>(std::move(work)), cancelable_tracker); + std::unique_ptr<history::HistoryDBTask> task( + new WorkerTask(work, std::move(scoped_event_signal), error)); + history_service->ScheduleDBTask(std::move(task), cancelable_tracker); + } else { + *error = syncer::CANNOT_DO_WORK; + // The event in |scoped_event_signal| is signaled at the end of this + // scope. } } @@ -59,6 +99,27 @@ cancelable_tracker_.reset(new base::CancelableTaskTracker); } +syncer::SyncerError HistoryModelWorker::DoWorkAndWaitUntilDoneImpl( + const syncer::WorkCallback& work) { + syncer::SyncerError error = syncer::UNSET; + + // Signaled after the task runs or when it is abandoned. + base::WaitableEvent work_done_or_abandoned( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + if (ui_thread_->PostTask(FROM_HERE, + base::Bind(&PostWorkerTask, history_service_, work, + base::Passed(syncer::ScopedEventSignal( + &work_done_or_abandoned)), + cancelable_tracker_.get(), &error))) { + work_done_or_abandoned.Wait(); + } else { + error = syncer::CANNOT_DO_WORK; + } + return error; +} + syncer::ModelSafeGroup HistoryModelWorker::GetModelSafeGroup() { return syncer::GROUP_HISTORY; } @@ -77,10 +138,4 @@ ui_thread_->DeleteSoon(FROM_HERE, cancelable_tracker_.release()); } -void HistoryModelWorker::ScheduleWork(base::OnceClosure work) { - ui_thread_->PostTask(FROM_HERE, base::Bind(&PostWorkerTask, history_service_, - base::Passed(std::move(work)), - cancelable_tracker_.get())); -} - } // namespace browser_sync
diff --git a/components/history/core/browser/history_model_worker.h b/components/history/core/browser/history_model_worker.h index f72b00af..6421c64 100644 --- a/components/history/core/browser/history_model_worker.h +++ b/components/history/core/browser/history_model_worker.h
@@ -32,11 +32,13 @@ syncer::ModelSafeGroup GetModelSafeGroup() override; bool IsOnModelThread() override; + protected: + syncer::SyncerError DoWorkAndWaitUntilDoneImpl( + const syncer::WorkCallback& work) override; + private: ~HistoryModelWorker() override; - void ScheduleWork(base::OnceClosure work) override; - const base::WeakPtr<history::HistoryService> history_service_; // A reference to the UI thread's task runner.
diff --git a/components/history/core/browser/history_model_worker_unittest.cc b/components/history/core/browser/history_model_worker_unittest.cc deleted file mode 100644 index 1877558..0000000 --- a/components/history/core/browser/history_model_worker_unittest.cc +++ /dev/null
@@ -1,227 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "components/history/core/browser/history_model_worker.h" - -#include <memory> -#include <utility> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/memory/ptr_util.h" -#include "base/single_thread_task_runner.h" -#include "base/synchronization/atomic_flag.h" -#include "base/test/test_simple_task_runner.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" -#include "components/history/core/browser/history_db_task.h" -#include "components/history/core/browser/history_service.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace browser_sync { -namespace { - -class HistoryServiceMock : public history::HistoryService { - public: - HistoryServiceMock(scoped_refptr<base::SingleThreadTaskRunner> history_thread) - : history_thread_(std::move(history_thread)) {} - - base::CancelableTaskTracker::TaskId ScheduleDBTask( - std::unique_ptr<history::HistoryDBTask> task, - base::CancelableTaskTracker* tracker) override { - history::HistoryDBTask* task_raw = task.get(); - history_thread_->PostTaskAndReply( - FROM_HERE, - base::Bind(base::IgnoreResult(&history::HistoryDBTask::RunOnDBThread), - base::Unretained(task_raw), nullptr, nullptr), - base::Bind(&history::HistoryDBTask::DoneRunOnMainThread, - base::Passed(std::move(task)))); - return base::CancelableTaskTracker::kBadTaskId; // Unused. - } - - private: - const scoped_refptr<base::SingleThreadTaskRunner> history_thread_; - - DISALLOW_COPY_AND_ASSIGN(HistoryServiceMock); -}; - -syncer::WorkCallback ClosureToWorkCallback(base::Closure work) { - return base::Bind( - [](base::Closure work) { - work.Run(); - return syncer::SYNCER_OK; - }, - std::move(work)); -} - -class HistoryModelWorkerTest : public testing::Test { - public: - HistoryModelWorkerTest() - : sync_thread_("SyncThreadForTest"), - history_service_(history_thread_), - history_service_factory_(&history_service_) { - sync_thread_.Start(); - worker_ = new HistoryModelWorker(history_service_factory_.GetWeakPtr(), - ui_thread_); - } - - ~HistoryModelWorkerTest() override { - // HistoryModelWorker posts a cleanup task to the UI thread in its - // destructor. Run it to prevent a leak. - worker_ = nullptr; - ui_thread_->RunUntilIdle(); - } - - protected: - void DoWorkAndWaitUntilDoneOnSyncThread(base::Closure work) { - sync_thread_.task_runner()->PostTask( - FROM_HERE, - base::Bind( - base::IgnoreResult(&HistoryModelWorker::DoWorkAndWaitUntilDone), - worker_, base::Passed(ClosureToWorkCallback(work)))); - sync_thread_.task_runner()->PostTask( - FROM_HERE, base::Bind(&base::AtomicFlag::Set, - base::Unretained(&sync_thread_unblocked_))); - } - - const scoped_refptr<base::TestSimpleTaskRunner> ui_thread_ = - new base::TestSimpleTaskRunner(); - scoped_refptr<base::TestSimpleTaskRunner> history_thread_ = - new base::TestSimpleTaskRunner(); - base::AtomicFlag sync_thread_unblocked_; - base::Thread sync_thread_; - HistoryServiceMock history_service_; - scoped_refptr<HistoryModelWorker> worker_; - - private: - base::WeakPtrFactory<HistoryServiceMock> history_service_factory_; - - DISALLOW_COPY_AND_ASSIGN(HistoryModelWorkerTest); -}; - -} // namespace - -TEST_F(HistoryModelWorkerTest, DoWorkAndWaitUntilDone) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](bool* did_work) { *did_work = true; }, base::Unretained(&did_work))); - - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to the UI thread and run it. Expect this task - // to post another task to the history DB thread and run it. - while (!ui_thread_->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - ui_thread_->RunUntilIdle(); - EXPECT_TRUE(history_thread_->HasPendingTask()); - history_thread_->RunUntilIdle(); - - EXPECT_TRUE(did_work); - - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - -TEST_F(HistoryModelWorkerTest, DoWorkAndWaitUntilDoneRequestStopBeforeRunWork) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](bool* did_work) { *did_work = true; }, base::Unretained(&did_work))); - - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to the UI thread and run it. - while (!ui_thread_->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - ui_thread_->RunUntilIdle(); - - // Stop the worker. - worker_->RequestStop(); - - // The WorkCallback should not run on the history DB thread. - EXPECT_TRUE(history_thread_->HasPendingTask()); - history_thread_->RunUntilIdle(); - EXPECT_FALSE(did_work); - - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - -TEST_F(HistoryModelWorkerTest, - DoWorkAndWaitUntilDoneRequestStopBeforeUITaskRun) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](bool* did_work) { *did_work = true; }, base::Unretained(&did_work))); - - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to the UI thread. - while (!ui_thread_->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - - // Stop the worker. - worker_->RequestStop(); - - // Stopping the worker should unblock the sync thread. - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - -TEST_F(HistoryModelWorkerTest, DoWorkAndWaitUntilDoneDeleteWorkBeforeRun) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](bool* did_work) { *did_work = true; }, base::Unretained(&did_work))); - - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to the UI thread. Delete it before it can run. - while (!ui_thread_->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - ui_thread_->ClearPendingTasks(); - - EXPECT_FALSE(did_work); - - // Deleting the task should have unblocked the sync thread. - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - -TEST_F(HistoryModelWorkerTest, DoWorkAndWaitUntilDoneRequestStopDuringRunWork) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](scoped_refptr<HistoryModelWorker> worker, - base::AtomicFlag* sync_thread_unblocked, bool* did_work) { - worker->RequestStop(); - base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); - - // The sync thread should not be unblocked while a WorkCallback is - // running. - EXPECT_FALSE(sync_thread_unblocked->IsSet()); - - *did_work = true; - }, - worker_, base::Unretained(&sync_thread_unblocked_), - base::Unretained(&did_work))); - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to the UI thread and run it. - while (!ui_thread_->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - ui_thread_->RunUntilIdle(); - - // Expect a task to be posted to the history DB thread. Run it. - EXPECT_TRUE(history_thread_->HasPendingTask()); - history_thread_->RunUntilIdle(); - EXPECT_TRUE(did_work); - - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - -} // namespace browser_sync
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc index 82af8ed..4b5a0e1 100644 --- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc +++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.cc
@@ -145,6 +145,96 @@ return base::TimeDelta::FromSecondsD(value_hours * 3600.0); } +void ReportTimeUntilFirstSoftTrigger(UserClassifier::UserClass user_class, + base::TimeDelta time_until_first_trigger) { + switch (user_class) { + case UserClassifier::UserClass::RARE_NTP_USER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger.RareNTPUser", + time_until_first_trigger, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + case UserClassifier::UserClass::ACTIVE_NTP_USER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger." + "ActiveNTPUser", + time_until_first_trigger, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger." + "ActiveSuggestionsConsumer", + time_until_first_trigger, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + } +} + +void ReportTimeUntilSoftFetch(UserClassifier::UserClass user_class, + base::TimeDelta time_until_soft_fetch) { + switch (user_class) { + case UserClassifier::UserClass::RARE_NTP_USER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilSoftFetch." + "RareNTPUser", + time_until_soft_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + case UserClassifier::UserClass::ACTIVE_NTP_USER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilSoftFetch." + "ActiveNTPUser", + time_until_soft_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilSoftFetch." + "ActiveSuggestionsConsumer", + time_until_soft_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + } +} + +void ReportTimeUntilPersistentFetch( + UserClassifier::UserClass user_class, + base::TimeDelta time_until_persistent_fetch) { + switch (user_class) { + case UserClassifier::UserClass::RARE_NTP_USER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilPersistentFetch." + "RareNTPUser", + time_until_persistent_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + case UserClassifier::UserClass::ACTIVE_NTP_USER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilPersistentFetch." + "ActiveNTPUser", + time_until_persistent_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + case UserClassifier::UserClass::ACTIVE_SUGGESTIONS_CONSUMER: + UMA_HISTOGRAM_CUSTOM_TIMES( + "NewTabPage.ContentSuggestions.TimeUntilPersistentFetch." + "ActiveSuggestionsConsumer", + time_until_persistent_fetch, base::TimeDelta::FromSeconds(1), + base::TimeDelta::FromDays(7), + /*bucket_count=*/50); + break; + } +} + } // namespace class EulaState : public web_resource::EulaAcceptedNotifier::Observer { @@ -248,6 +338,7 @@ profile_prefs, RequestThrottler::RequestType:: CONTENT_SUGGESTION_FETCHER_ACTIVE_SUGGESTIONS_CONSUMER), + time_until_first_trigger_reported_(false), eula_state_(base::MakeUnique<EulaState>(local_state_prefs, this)), profile_prefs_(profile_prefs), clock_(std::move(clock)), @@ -440,8 +531,17 @@ return; } - if (trigger != TriggerType::PERSISTENT_SCHEDULER_WAKE_UP && - !ShouldRefetchInTheBackgroundNow()) { + bool is_soft = trigger != TriggerType::PERSISTENT_SCHEDULER_WAKE_UP; + const base::Time last_fetch_attempt_time = base::Time::FromInternalValue( + profile_prefs_->GetInt64(prefs::kSnippetLastFetchAttempt)); + + if (is_soft && !time_until_first_trigger_reported_) { + time_until_first_trigger_reported_ = true; + ReportTimeUntilFirstSoftTrigger(user_classifier_->GetUserClass(), + clock_->Now() - last_fetch_attempt_time); + } + + if (is_soft && !ShouldRefetchInTheBackgroundNow(last_fetch_attempt_time)) { return; } @@ -449,6 +549,14 @@ return; } + if (is_soft) { + ReportTimeUntilSoftFetch(user_classifier_->GetUserClass(), + clock_->Now() - last_fetch_attempt_time); + } else { + ReportTimeUntilPersistentFetch(user_classifier_->GetUserClass(), + clock_->Now() - last_fetch_attempt_time); + } + UMA_HISTOGRAM_ENUMERATION( "NewTabPage.ContentSuggestions.BackgroundFetchTrigger", static_cast<int>(trigger), static_cast<int>(TriggerType::COUNT)); @@ -459,10 +567,8 @@ base::Unretained(this))); } -bool RemoteSuggestionsSchedulerImpl::ShouldRefetchInTheBackgroundNow() { - const base::Time last_fetch_attempt_time = base::Time::FromInternalValue( - profile_prefs_->GetInt64(prefs::kSnippetLastFetchAttempt)); - +bool RemoteSuggestionsSchedulerImpl::ShouldRefetchInTheBackgroundNow( + base::Time last_fetch_attempt_time) { // If we have no persistent scheduler to ask, err on the side of caution. bool wifi = false; if (persistent_scheduler_) { @@ -523,6 +629,7 @@ void RemoteSuggestionsSchedulerImpl::OnFetchCompleted(Status fetch_status) { profile_prefs_->SetInt64(prefs::kSnippetLastFetchAttempt, clock_->Now().ToInternalValue()); + time_until_first_trigger_reported_ = false; // Reschedule after a fetch. The persistent schedule is applied only after a // successful fetch. After a failed fetch, we want to keep the previous
diff --git a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h index 7fe94d6..9921f51 100644 --- a/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h +++ b/components/ntp_snippets/remote/remote_suggestions_scheduler_impl.h
@@ -93,7 +93,7 @@ // Checks whether it is time to perform a soft background fetch, according to // |schedule|. - bool ShouldRefetchInTheBackgroundNow(); + bool ShouldRefetchInTheBackgroundNow(base::Time last_fetch_attempt_time); // Returns whether background fetching (for the given |trigger|) is disabled. bool BackgroundFetchesDisabled(TriggerType trigger) const; @@ -144,6 +144,9 @@ RequestThrottler request_throttler_active_ntp_user_; RequestThrottler request_throttler_active_suggestions_consumer_; + // To make sure we only report the first trigger to UMA. + bool time_until_first_trigger_reported_; + // We should not fetch in background before EULA gets accepted. std::unique_ptr<EulaState> eula_state_;
diff --git a/components/open_from_clipboard/clipboard_recent_content_generic.cc b/components/open_from_clipboard/clipboard_recent_content_generic.cc index 87c6542..15ffa42 100644 --- a/components/open_from_clipboard/clipboard_recent_content_generic.cc +++ b/components/open_from_clipboard/clipboard_recent_content_generic.cc
@@ -10,17 +10,12 @@ ClipboardRecentContentGeneric::ClipboardRecentContentGeneric() {} bool ClipboardRecentContentGeneric::GetRecentURLFromClipboard(GURL* url) { - ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); - base::Time last_modified_time = clipboard->GetClipboardLastModifiedTime(); - if (!last_modified_time_to_suppress_.is_null() && - (last_modified_time == last_modified_time_to_suppress_)) - return false; - if (GetClipboardContentAge() > MaximumAgeOfClipboard()) return false; // Get and clean up the clipboard before processing. std::string gurl_string; + ui::Clipboard* clipboard = ui::Clipboard::GetForCurrentThread(); clipboard->ReadAsciiText(ui::CLIPBOARD_TYPE_COPY_PASTE, &gurl_string); base::TrimWhitespaceASCII(gurl_string, base::TrimPositions::TRIM_ALL, &gurl_string); @@ -53,7 +48,7 @@ base::TimeDelta ClipboardRecentContentGeneric::GetClipboardContentAge() const { const base::Time last_modified_time = - ui::Clipboard::GetForCurrentThread()->GetClipboardLastModifiedTime(); + ui::Clipboard::GetForCurrentThread()->GetLastModifiedTime(); const base::Time now = base::Time::Now(); // In case of system clock change, assume the last modified time is now. // (Don't return a negative age, i.e., a time in the future.) @@ -64,9 +59,7 @@ void ClipboardRecentContentGeneric::SuppressClipboardContent() { // User cleared the user data. The pasteboard entry must be removed from the - // omnibox list. Do this by suppressing all clipboard content with the - // current clipboard content's time. Then we only suggest the clipboard - // content later if the time changed. - last_modified_time_to_suppress_ = - ui::Clipboard::GetForCurrentThread()->GetClipboardLastModifiedTime(); + // omnibox list. Do this by pretending the current clipboard is ancient, + // not recent. + ui::Clipboard::GetForCurrentThread()->ClearLastModifiedTime(); }
diff --git a/components/open_from_clipboard/clipboard_recent_content_generic.h b/components/open_from_clipboard/clipboard_recent_content_generic.h index 60024dc..c0b8704 100644 --- a/components/open_from_clipboard/clipboard_recent_content_generic.h +++ b/components/open_from_clipboard/clipboard_recent_content_generic.h
@@ -27,11 +27,6 @@ void SuppressClipboardContent() override; private: - // When told to suppress clipboard content, store the time of the last - // modified clipboard and suppress suggestions that correspond to that time. - // Set to base::Time() if not suppressing clipboard content. - base::Time last_modified_time_to_suppress_; - DISALLOW_COPY_AND_ASSIGN(ClipboardRecentContentGeneric); };
diff --git a/components/open_from_clipboard/clipboard_recent_content_generic_unittest.cc b/components/open_from_clipboard/clipboard_recent_content_generic_unittest.cc index 1101c5e..83b9ac9 100644 --- a/components/open_from_clipboard/clipboard_recent_content_generic_unittest.cc +++ b/components/open_from_clipboard/clipboard_recent_content_generic_unittest.cc
@@ -65,8 +65,8 @@ for (size_t i = 0; i < arraysize(test_data); ++i) { test_clipboard_->WriteText(test_data[i].clipboard.data(), test_data[i].clipboard.length()); - test_clipboard_->SetClipboardLastModifiedTime( - now - base::TimeDelta::FromSeconds(10)); + test_clipboard_->SetLastModifiedTime(now - + base::TimeDelta::FromSeconds(10)); GURL url; EXPECT_EQ(test_data[i].expected_get_recent_url_value, recent_content.GetRecentURLFromClipboard(&url)) @@ -79,13 +79,11 @@ base::Time now = base::Time::Now(); std::string text = "http://example.com/"; test_clipboard_->WriteText(text.data(), text.length()); - test_clipboard_->SetClipboardLastModifiedTime( - now - base::TimeDelta::FromSeconds(10)); + test_clipboard_->SetLastModifiedTime(now - base::TimeDelta::FromSeconds(10)); GURL url; EXPECT_TRUE(recent_content.GetRecentURLFromClipboard(&url)); // If the last modified time is days ago, the URL shouldn't be suggested. - test_clipboard_->SetClipboardLastModifiedTime(now - - base::TimeDelta::FromDays(2)); + test_clipboard_->SetLastModifiedTime(now - base::TimeDelta::FromDays(2)); EXPECT_FALSE(recent_content.GetRecentURLFromClipboard(&url)); } @@ -94,8 +92,7 @@ base::Time now = base::Time::Now(); std::string text = " whether URL or not should not matter here."; test_clipboard_->WriteText(text.data(), text.length()); - test_clipboard_->SetClipboardLastModifiedTime( - now - base::TimeDelta::FromSeconds(32)); + test_clipboard_->SetLastModifiedTime(now - base::TimeDelta::FromSeconds(32)); base::TimeDelta age = recent_content.GetClipboardContentAge(); // It's possible the GetClipboardContentAge() took some time, so allow a // little slop (5 seconds) in this comparison; don't check for equality. @@ -109,8 +106,7 @@ base::Time now = base::Time::Now(); std::string text = "http://example.com/"; test_clipboard_->WriteText(text.data(), text.length()); - test_clipboard_->SetClipboardLastModifiedTime( - now - base::TimeDelta::FromSeconds(10)); + test_clipboard_->SetLastModifiedTime(now - base::TimeDelta::FromSeconds(10)); GURL url; EXPECT_TRUE(recent_content.GetRecentURLFromClipboard(&url)); @@ -121,6 +117,6 @@ // If the clipboard changes, even if to the same thing again, the content // should be suggested again. test_clipboard_->WriteText(text.data(), text.length()); - test_clipboard_->SetClipboardLastModifiedTime(now); + test_clipboard_->SetLastModifiedTime(now); EXPECT_TRUE(recent_content.GetRecentURLFromClipboard(&url)); }
diff --git a/components/password_manager/sync/browser/password_model_worker.cc b/components/password_manager/sync/browser/password_model_worker.cc index 997a0b1c..81fb889b 100644 --- a/components/password_manager/sync/browser/password_model_worker.cc +++ b/components/password_manager/sync/browser/password_model_worker.cc
@@ -4,18 +4,60 @@ #include "components/password_manager/sync/browser/password_model_worker.h" -#include <utility> - +#include "base/bind.h" +#include "base/callback.h" +#include "base/synchronization/waitable_event.h" #include "components/password_manager/core/browser/password_store.h" +#include "components/sync/base/scoped_event_signal.h" namespace browser_sync { +namespace { + +void CallDoWorkAndSignalEvent(const syncer::WorkCallback& work, + syncer::ScopedEventSignal scoped_event_signal, + syncer::SyncerError* error_info) { + *error_info = work.Run(); + // The event in |scoped_event_signal| is signaled at the end of this scope. +} + +} // namespace + PasswordModelWorker::PasswordModelWorker( const scoped_refptr<password_manager::PasswordStore>& password_store) : password_store_(password_store) { DCHECK(password_store.get()); } +syncer::SyncerError PasswordModelWorker::DoWorkAndWaitUntilDoneImpl( + const syncer::WorkCallback& work) { + syncer::SyncerError error = syncer::UNSET; + + // Signaled when the task is deleted, i.e. after it runs or when it is + // abandoned. + base::WaitableEvent work_done_or_abandoned( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + bool scheduled = false; + { + base::AutoLock lock(password_store_lock_); + if (!password_store_.get()) + return syncer::CANNOT_DO_WORK; + + scheduled = password_store_->ScheduleTask(base::Bind( + &CallDoWorkAndSignalEvent, work, + base::Passed(syncer::ScopedEventSignal(&work_done_or_abandoned)), + &error)); + } + + if (scheduled) + work_done_or_abandoned.Wait(); + else + error = syncer::CANNOT_DO_WORK; + return error; +} + syncer::ModelSafeGroup PasswordModelWorker::GetModelSafeGroup() { return syncer::GROUP_PASSWORD; } @@ -29,15 +71,6 @@ PasswordModelWorker::~PasswordModelWorker() {} -void PasswordModelWorker::ScheduleWork(base::OnceClosure work) { - base::AutoLock lock(password_store_lock_); - if (password_store_) { - password_store_->ScheduleTask( - base::Bind([](base::OnceClosure work) { std::move(work).Run(); }, - base::Passed(std::move(work)))); - } -} - void PasswordModelWorker::RequestStop() { ModelSafeWorker::RequestStop();
diff --git a/components/password_manager/sync/browser/password_model_worker.h b/components/password_manager/sync/browser/password_model_worker.h index 08405739..eb45efc 100644 --- a/components/password_manager/sync/browser/password_model_worker.h +++ b/components/password_manager/sync/browser/password_model_worker.h
@@ -28,11 +28,13 @@ bool IsOnModelThread() override; void RequestStop() override; + protected: + syncer::SyncerError DoWorkAndWaitUntilDoneImpl( + const syncer::WorkCallback& work) override; + private: ~PasswordModelWorker() override; - void ScheduleWork(base::OnceClosure work) override; - // |password_store_| is used on password thread but released on UI thread. // Protected by |password_store_lock_|. base::Lock password_store_lock_;
diff --git a/components/payments/content/payment_request_spec.cc b/components/payments/content/payment_request_spec.cc index bec17e2..6fbfc5f6 100644 --- a/components/payments/content/payment_request_spec.cc +++ b/components/payments/content/payment_request_spec.cc
@@ -127,6 +127,13 @@ return formatter->formatted_currency_code(); } +void PaymentRequestSpec::StartWaitingForUpdateWith( + PaymentRequestSpec::UpdateReason reason) { + for (auto& observer : observers_) { + observer.OnStartUpdating(reason); + } +} + void PaymentRequestSpec::PopulateValidatedMethodData( const std::vector<mojom::PaymentMethodDataPtr>& method_data_mojom) { if (method_data_mojom.empty()) {
diff --git a/components/payments/content/payment_request_spec.h b/components/payments/content/payment_request_spec.h index 39bbda8..de0187a 100644 --- a/components/payments/content/payment_request_spec.h +++ b/components/payments/content/payment_request_spec.h
@@ -25,6 +25,14 @@ // certain occasions by the merchant (see API). class PaymentRequestSpec : public PaymentOptionsProvider { public: + // This enum represents which bit of information was changed to trigger an + // update roundtrip with the website. + enum class UpdateReason { + NONE, + SHIPPING_OPTION, + SHIPPING_ADDRESS, + }; + // Any class call add itself as Observer via AddObserver() and receive // notification about spec events. class Observer { @@ -32,6 +40,11 @@ // Called when the provided spec (details, options, method_data) is invalid. virtual void OnInvalidSpecProvided() = 0; + // Called when the website is notified that the user selected shipping + // options or a shipping address. This will be followed by a call to + // OnSpecUpdated or the PaymentRequest being aborted due to a timeout. + virtual void OnStartUpdating(UpdateReason reason) {} + // Called when the provided spec has changed. virtual void OnSpecUpdated() = 0; @@ -87,6 +100,8 @@ const mojom::PaymentDetails& details() const { return *details_.get(); } + void StartWaitingForUpdateWith(UpdateReason reason); + private: friend class PaymentRequestDialogView; void add_observer_for_testing(Observer* observer_for_testing) {
diff --git a/components/payments/content/payment_request_state.cc b/components/payments/content/payment_request_state.cc index 9f009bf..c3f1526 100644 --- a/components/payments/content/payment_request_state.cc +++ b/components/payments/content/payment_request_state.cc
@@ -100,6 +100,8 @@ void PaymentRequestState::SetSelectedShippingOption( const std::string& shipping_option_id) { + spec_->StartWaitingForUpdateWith( + PaymentRequestSpec::UpdateReason::SHIPPING_OPTION); // This will inform the merchant and will lead to them calling updateWith with // new PaymentDetails. delegate_->OnShippingOptionIdSelected(shipping_option_id); @@ -107,6 +109,8 @@ void PaymentRequestState::SetSelectedShippingProfile( autofill::AutofillProfile* profile) { + spec_->StartWaitingForUpdateWith( + PaymentRequestSpec::UpdateReason::SHIPPING_ADDRESS); selected_shipping_profile_ = profile; UpdateIsReadyToPayAndNotifyObservers(); delegate_->OnShippingAddressSelected(
diff --git a/components/payments/core/journey_logger.h b/components/payments/core/journey_logger.h index 85c850e..c3330eb 100644 --- a/components/payments/core/journey_logger.h +++ b/components/payments/core/journey_logger.h
@@ -65,7 +65,8 @@ EVENT_SHOWN = 1 << 0, EVENT_PAY_CLICKED = 1 << 1, EVENT_RECEIVED_INSTRUMENT_DETAILS = 1 << 2, - EVENT_MAX = 8, + EVENT_SKIPPED_SHOW = 1 << 3, + EVENT_MAX = 16, }; // Used to mesure the impact of the CanMakePayment return value on whether the
diff --git a/components/search_engines/template_url_service.cc b/components/search_engines/template_url_service.cc index ab0ba57..a4db5e1 100644 --- a/components/search_engines/template_url_service.cc +++ b/components/search_engines/template_url_service.cc
@@ -367,12 +367,12 @@ } bool TemplateURLService::IsPrepopulatedOrCreatedByPolicy( - const TemplateURL* t_url) { + const TemplateURL* t_url) const { return (t_url->prepopulate_id() > 0 || t_url->created_by_policy()) && t_url->SupportsReplacement(search_terms_data()); } -bool TemplateURLService::ShowInDefaultList(const TemplateURL* t_url) { +bool TemplateURLService::ShowInDefaultList(const TemplateURL* t_url) const { return t_url == default_search_provider_ || IsPrepopulatedOrCreatedByPolicy(t_url); } @@ -570,7 +570,7 @@ NotifyObservers(); } -bool TemplateURLService::CanMakeDefault(const TemplateURL* url) { +bool TemplateURLService::CanMakeDefault(const TemplateURL* url) const { return ((default_search_provider_source_ == DefaultSearchManager::FROM_USER) || (default_search_provider_source_ == @@ -621,7 +621,7 @@ default_provider->IsSearchURL(url, search_terms_data()); } -bool TemplateURLService::IsExtensionControlledDefaultSearch() { +bool TemplateURLService::IsExtensionControlledDefaultSearch() const { return default_search_provider_source_ == DefaultSearchManager::FROM_EXTENSION; } @@ -850,8 +850,11 @@ base::string16 TemplateURLService::GetKeywordShortName( const base::string16& keyword, - bool* is_omnibox_api_extension_keyword) { - const TemplateURL* template_url = GetTemplateURLForKeyword(keyword); + bool* is_omnibox_api_extension_keyword) const { + // TODO(jeffschiller): Make GetTemplateURLForKeyword const and remove the + // const_cast. + const TemplateURL* template_url = + const_cast<TemplateURLService*>(this)->GetTemplateURLForKeyword(keyword); // TODO(sky): Once LocationBarView adds a listener to the TemplateURLService // to track changes to the model, this should become a DCHECK. @@ -1629,7 +1632,7 @@ } bool TemplateURLService::CanAddAutogeneratedKeywordForHost( - const std::string& host) { + const std::string& host) const { const TemplateURLSet* urls = provider_map_->GetURLsForHost(host); if (!urls) return true; @@ -1640,7 +1643,7 @@ return true; } -bool TemplateURLService::CanReplace(const TemplateURL* t_url) { +bool TemplateURLService::CanReplace(const TemplateURL* t_url) const { return !ShowInDefaultList(t_url) && t_url->safe_for_autoreplace(); } @@ -2230,10 +2233,14 @@ return keyword_candidate; } -bool TemplateURLService::IsLocalTemplateURLBetter(const TemplateURL* local_turl, - const TemplateURL* sync_turl, - bool prefer_local_default) { - DCHECK(GetTemplateURLForGUID(local_turl->sync_guid())); +bool TemplateURLService::IsLocalTemplateURLBetter( + const TemplateURL* local_turl, + const TemplateURL* sync_turl, + bool prefer_local_default) const { + // TODO(jeffschiller): Make GetTemplateURLForKeyword const and remove the + // const_cast. + DCHECK(const_cast<TemplateURLService*>(this)->GetTemplateURLForGUID( + local_turl->sync_guid())); return local_turl->last_modified() > sync_turl->last_modified() || local_turl->created_by_policy() || (prefer_local_default && local_turl == GetDefaultSearchProvider());
diff --git a/components/search_engines/template_url_service.h b/components/search_engines/template_url_service.h index b6bb73ba7..4187327 100644 --- a/components/search_engines/template_url_service.h +++ b/components/search_engines/template_url_service.h
@@ -135,14 +135,14 @@ // Returns whether the engine is a "pre-existing" engine, either from the // prepopulate list or created by policy. - bool IsPrepopulatedOrCreatedByPolicy(const TemplateURL* template_url); + bool IsPrepopulatedOrCreatedByPolicy(const TemplateURL* template_url) const; // Returns whether |template_url| should be shown in the list of engines // most likely to be selected as a default engine. This is meant to highlight // the current default, as well as the other most likely choices of default // engine, separately from a full list of all TemplateURLs (which might be // very long). - bool ShowInDefaultList(const TemplateURL* template_url); + bool ShowInDefaultList(const TemplateURL* template_url) const; // Adds to |matches| all TemplateURLs whose keywords begin with |prefix|, // sorted shortest-keyword-first. If |supports_replacement_only| is true, only @@ -248,7 +248,7 @@ // Return true if the given |url| can be made the default. This returns false // regardless of |url| if the default search provider is managed by policy or // controlled by an extension. - bool CanMakeDefault(const TemplateURL* url); + bool CanMakeDefault(const TemplateURL* url) const; // Set the default search provider. |url| may be null. // This will assert if the default search is managed; the UI should not be @@ -274,7 +274,7 @@ } // Returns true if the default search provider is controlled by an extension. - bool IsExtensionControlledDefaultSearch(); + bool IsExtensionControlledDefaultSearch() const; // Returns the default search specified in the prepopulated data, if it // exists. If not, returns first URL in |template_urls_|, or NULL if that's @@ -330,8 +330,9 @@ // Returns the locale-direction-adjusted short name for the given keyword. // Also sets the out param to indicate whether the keyword belongs to an // Omnibox extension. - base::string16 GetKeywordShortName(const base::string16& keyword, - bool* is_omnibox_api_extension_keyword); + base::string16 GetKeywordShortName( + const base::string16& keyword, + bool* is_omnibox_api_extension_keyword) const; // Called by the history service when a URL is visited. void OnHistoryURLVisited(const URLVisitedDetails& details); @@ -543,13 +544,13 @@ // Returns false if there is a TemplateURL that has a search url with the // specified host and that TemplateURL has been manually modified. - bool CanAddAutogeneratedKeywordForHost(const std::string& host); + bool CanAddAutogeneratedKeywordForHost(const std::string& host) const; // Returns true if the TemplateURL is replaceable. This doesn't look at the // uniqueness of the keyword or host and is intended to be called after those // checks have been done. This returns true if the TemplateURL doesn't appear // in the default list and is marked as safe_for_autoreplace. - bool CanReplace(const TemplateURL* t_url); + bool CanReplace(const TemplateURL* t_url) const; // Like GetTemplateURLForKeyword(), but ignores extension-provided keywords. TemplateURL* FindNonExtensionTemplateURLForKeyword( @@ -661,7 +662,7 @@ // search provider bool IsLocalTemplateURLBetter(const TemplateURL* local_turl, const TemplateURL* sync_turl, - bool prefer_local_default = true); + bool prefer_local_default = true) const; // Given two synced TemplateURLs with a conflicting keyword, one of which // needs to be added to or updated in the local model (|unapplied_sync_turl|)
diff --git a/components/sync/BUILD.gn b/components/sync/BUILD.gn index 3967025..1f81eb0 100644 --- a/components/sync/BUILD.gn +++ b/components/sync/BUILD.gn
@@ -61,6 +61,8 @@ "base/proto_value_ptr.h", "base/report_unrecoverable_error.cc", "base/report_unrecoverable_error.h", + "base/scoped_event_signal.cc", + "base/scoped_event_signal.h", "base/stop_source.h", "base/sync_features.cc", "base/sync_features.h", @@ -842,6 +844,7 @@ "base/ordinal_unittest.cc", "base/proto_value_ptr_unittest.cc", "base/protobuf_unittest.cc", + "base/scoped_event_signal_unittest.cc", "base/sync_prefs_unittest.cc", "base/system_encryptor_unittest.cc", "base/unique_position_unittest.cc", @@ -933,6 +936,7 @@ "syncable/model_type_unittest.cc", "syncable/nigori_util_unittest.cc", "syncable/parent_child_index_unittest.cc", + "syncable/syncable_delete_journal_unittest.cc", "syncable/syncable_enum_conversions_unittest.cc", "syncable/syncable_id_unittest.cc", "syncable/syncable_unittest.cc",
diff --git a/components/sync/base/scoped_event_signal.cc b/components/sync/base/scoped_event_signal.cc new file mode 100644 index 0000000..daaaa1d --- /dev/null +++ b/components/sync/base/scoped_event_signal.cc
@@ -0,0 +1,34 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sync/base/scoped_event_signal.h" + +#include "base/logging.h" +#include "base/synchronization/waitable_event.h" + +namespace syncer { + +ScopedEventSignal::ScopedEventSignal(base::WaitableEvent* event) + : event_(event) { + DCHECK(event_); +} + +ScopedEventSignal::ScopedEventSignal(ScopedEventSignal&& other) + : event_(other.event_) { + other.event_ = nullptr; +} + +ScopedEventSignal& ScopedEventSignal::operator=(ScopedEventSignal&& other) { + DCHECK(!event_); + event_ = other.event_; + other.event_ = nullptr; + return *this; +} + +ScopedEventSignal::~ScopedEventSignal() { + if (event_) + event_->Signal(); +} + +} // namespace syncer
diff --git a/components/sync/base/scoped_event_signal.h b/components/sync/base/scoped_event_signal.h new file mode 100644 index 0000000..d8df104 --- /dev/null +++ b/components/sync/base/scoped_event_signal.h
@@ -0,0 +1,40 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_SYNC_BASE_SCOPED_EVENT_SIGNAL_H_ +#define COMPONENTS_SYNC_BASE_SCOPED_EVENT_SIGNAL_H_ + +#include "base/macros.h" + +namespace base { +class WaitableEvent; +} + +namespace syncer { + +// An object which signals a WaitableEvent when it is deleted. Used to wait for +// a task to run or be abandoned. +class ScopedEventSignal { + public: + // |event| will be signaled in the destructor. + explicit ScopedEventSignal(base::WaitableEvent* event); + + // This ScopedEventSignal will signal |other|'s WaitableEvent in its + // destructor. |other| will not signal anything in its destructor. The + // assignment operator cannot be used if this ScopedEventSignal's + // WaitableEvent hasn't been moved to another ScopedEventSignal. + ScopedEventSignal(ScopedEventSignal&& other); + ScopedEventSignal& operator=(ScopedEventSignal&& other); + + ~ScopedEventSignal(); + + private: + base::WaitableEvent* event_; + + DISALLOW_COPY_AND_ASSIGN(ScopedEventSignal); +}; + +} // namespace syncer + +#endif // COMPONENTS_SYNC_BASE_SCOPED_EVENT_SIGNAL_H_
diff --git a/components/sync/base/scoped_event_signal_unittest.cc b/components/sync/base/scoped_event_signal_unittest.cc new file mode 100644 index 0000000..fe65b4b9 --- /dev/null +++ b/components/sync/base/scoped_event_signal_unittest.cc
@@ -0,0 +1,84 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sync/base/scoped_event_signal.h" + +#include <memory> +#include <utility> + +#include "base/synchronization/waitable_event.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { + +TEST(ScopedEventSignalTest, SignalAtEndOfScope) { + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + { + ScopedEventSignal scoped_event_signal(&event); + EXPECT_FALSE(event.IsSignaled()); + } + + EXPECT_TRUE(event.IsSignaled()); +} + +TEST(ScopedEventSignalTest, MoveConstructor) { + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + { + ScopedEventSignal scoped_event_signal(&event); + EXPECT_FALSE(event.IsSignaled()); + + { + ScopedEventSignal other_scoped_event_signal( + std::move(scoped_event_signal)); + EXPECT_FALSE(event.IsSignaled()); + } + + // |event| is signaled when |other_scoped_event_signal| is destroyed. + EXPECT_TRUE(event.IsSignaled()); + + event.Reset(); + } + + // |event| is not signaled when |scoped_signal_event| is destroyed. + EXPECT_FALSE(event.IsSignaled()); +} + +TEST(ScopedEventSignalTest, MoveAssignOperator) { + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + { + ScopedEventSignal scoped_event_signal_a(&event); + EXPECT_FALSE(event.IsSignaled()); + + { + base::WaitableEvent other_event( + base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED); + ScopedEventSignal scoped_event_signal_b(&other_event); + + // Move |scoped_event_signal_b| to |scoped_event_signal_c| because the + // assignment operator cannot be used on a ScopedEventSignal which is + // already associated with an event. + ScopedEventSignal scoped_event_signal_c(std::move(scoped_event_signal_b)); + + scoped_event_signal_b = std::move(scoped_event_signal_a); + EXPECT_FALSE(event.IsSignaled()); + } + + // |event| is signaled when |scoped_event_signal_b| is destroyed. + EXPECT_TRUE(event.IsSignaled()); + + event.Reset(); + } + + // |event| is not signaled when |scoped_signal_event_a| is destroyed. + EXPECT_FALSE(event.IsSignaled()); +} + +} // namespace syncer
diff --git a/components/sync/engine/browser_thread_model_worker.cc b/components/sync/engine/browser_thread_model_worker.cc index a6d92c5..3b29d85 100644 --- a/components/sync/engine/browser_thread_model_worker.cc +++ b/components/sync/engine/browser_thread_model_worker.cc
@@ -4,22 +4,45 @@ #include "components/sync/engine/browser_thread_model_worker.h" -#include <utility> +#include "base/bind.h" +#include "base/callback.h" +#include "base/synchronization/waitable_event.h" + +using base::SingleThreadTaskRunner; namespace syncer { BrowserThreadModelWorker::BrowserThreadModelWorker( - const scoped_refptr<base::SingleThreadTaskRunner>& runner, + const scoped_refptr<SingleThreadTaskRunner>& runner, ModelSafeGroup group) : runner_(runner), group_(group) {} -void BrowserThreadModelWorker::ScheduleWork(base::OnceClosure work) { +SyncerError BrowserThreadModelWorker::DoWorkAndWaitUntilDoneImpl( + const WorkCallback& work) { + SyncerError error = UNSET; if (runner_->BelongsToCurrentThread()) { DLOG(WARNING) << "Already on thread " << runner_; - std::move(work).Run(); - } else { - runner_->PostTask(FROM_HERE, std::move(work)); + return work.Run(); } + + // Signaled when the task is deleted, i.e. after it runs or when it is + // abandoned. + base::WaitableEvent work_done_or_abandoned( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); + + if (!runner_->PostTask( + FROM_HERE, + base::Bind( + &BrowserThreadModelWorker::CallDoWorkAndSignalTask, this, work, + base::Passed(syncer::ScopedEventSignal(&work_done_or_abandoned)), + &error))) { + DLOG(WARNING) << "Failed to post task to runner " << runner_; + error = CANNOT_DO_WORK; + return error; + } + work_done_or_abandoned.Wait(); + return error; } ModelSafeGroup BrowserThreadModelWorker::GetModelSafeGroup() { @@ -32,4 +55,14 @@ BrowserThreadModelWorker::~BrowserThreadModelWorker() {} +void BrowserThreadModelWorker::CallDoWorkAndSignalTask( + const WorkCallback& work, + syncer::ScopedEventSignal scoped_event_signal, + SyncerError* error) { + DCHECK(runner_->BelongsToCurrentThread()); + if (!IsStopped()) + *error = work.Run(); + // The event in |scoped_event_signal| is signaled at the end of this scope. +} + } // namespace syncer
diff --git a/components/sync/engine/browser_thread_model_worker.h b/components/sync/engine/browser_thread_model_worker.h index 32e63b28..3a121f66 100644 --- a/components/sync/engine/browser_thread_model_worker.h +++ b/components/sync/engine/browser_thread_model_worker.h
@@ -8,6 +8,8 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/single_thread_task_runner.h" +#include "components/sync/base/scoped_event_signal.h" +#include "components/sync/base/syncer_error.h" #include "components/sync/engine/model_safe_worker.h" namespace syncer { @@ -26,11 +28,16 @@ ModelSafeGroup GetModelSafeGroup() override; bool IsOnModelThread() override; - private: + protected: ~BrowserThreadModelWorker() override; - void ScheduleWork(base::OnceClosure work) override; + SyncerError DoWorkAndWaitUntilDoneImpl(const WorkCallback& work) override; + void CallDoWorkAndSignalTask(const WorkCallback& work, + syncer::ScopedEventSignal scoped_event_signal, + SyncerError* error); + + private: scoped_refptr<base::SingleThreadTaskRunner> runner_; ModelSafeGroup group_;
diff --git a/components/sync/engine/browser_thread_model_worker_unittest.cc b/components/sync/engine/browser_thread_model_worker_unittest.cc index 98900e6..f4417e9 100644 --- a/components/sync/engine/browser_thread_model_worker_unittest.cc +++ b/components/sync/engine/browser_thread_model_worker_unittest.cc
@@ -39,10 +39,11 @@ // DoWork hasn't executed within action_timeout(). void ScheduleWork() { // We wait until the callback is done. So it is safe to use unretained. + WorkCallback c = base::Bind(&SyncBrowserThreadModelWorkerTest::DoWork, + base::Unretained(this)); timer()->Start(FROM_HERE, TestTimeouts::action_timeout(), this, &SyncBrowserThreadModelWorkerTest::Timeout); - worker()->DoWorkAndWaitUntilDone(base::BindOnce( - &SyncBrowserThreadModelWorkerTest::DoWork, base::Unretained(this))); + worker()->DoWorkAndWaitUntilDone(c); } // This is the work that will be scheduled to be done on the DB thread.
diff --git a/components/sync/engine/model_safe_worker.cc b/components/sync/engine/model_safe_worker.cc index 7735716..530f2065 100644 --- a/components/sync/engine/model_safe_worker.cc +++ b/components/sync/engine/model_safe_worker.cc
@@ -4,9 +4,6 @@ #include "components/sync/engine/model_safe_worker.h" -#include <utility> - -#include "base/bind.h" #include "base/json/json_writer.h" #include "base/values.h" @@ -72,86 +69,24 @@ } } -ModelSafeWorker::ModelSafeWorker() - : work_done_or_abandoned_(base::WaitableEvent::ResetPolicy::AUTOMATIC, - base::WaitableEvent::InitialState::NOT_SIGNALED) { -} +ModelSafeWorker::ModelSafeWorker() {} ModelSafeWorker::~ModelSafeWorker() {} void ModelSafeWorker::RequestStop() { - base::AutoLock auto_lock(lock_); - - // Set stop flag to prevent any *further* WorkCallback from starting to run - // (note that one may alreay be running). - stopped_ = true; - - // If no work is running, unblock DoWorkAndWaitUntilDone(). If work is - // running, it is unsafe to return from DoWorkAndWaitUntilDone(). - // ScopedSignalWorkDoneOrAbandoned will take care of signaling the event when - // the work is done. - if (!is_work_running_) - work_done_or_abandoned_.Signal(); + // Set stop flag. This prevents any *further* tasks from being posted to + // worker threads (see DoWorkAndWaitUntilDone below), but note that one may + // already be posted. + stopped_.Set(); } -SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(WorkCallback work) { - { - // It is important to check |stopped_| and reset |work_done_or_abandoned_| - // atomically to prevent this race: - // - // Thread Action - // Sync Sees that |stopped_| is false. - // UI Calls RequestStop(). Signals |work_done_or_abandoned_|. - // Sync Resets |work_done_or_abandoned_|. - // Waits on |work_done_or_abandoned_| forever since the task may not - // run after RequestStop() is called. - base::AutoLock auto_lock(lock_); - if (stopped_) - return CANNOT_DO_WORK; - DCHECK(!is_work_running_); - work_done_or_abandoned_.Reset(); - } - - SyncerError error = UNSET; - bool did_run = false; - ScheduleWork(base::BindOnce( - &ModelSafeWorker::DoWork, this, base::Passed(std::move(work)), - base::Passed(base::ScopedClosureRunner(base::Bind( - [](scoped_refptr<ModelSafeWorker> worker) { - worker->work_done_or_abandoned_.Signal(); - }, - make_scoped_refptr(this)))), - base::Unretained(&error), base::Unretained(&did_run))); - - // Unblocked when the task runs or is deleted or when RequestStop() is called - // before the task starts running. - work_done_or_abandoned_.Wait(); - - return did_run ? error : CANNOT_DO_WORK; +SyncerError ModelSafeWorker::DoWorkAndWaitUntilDone(const WorkCallback& work) { + if (stopped_.IsSet()) + return CANNOT_DO_WORK; + return DoWorkAndWaitUntilDoneImpl(work); } -void ModelSafeWorker::DoWork(WorkCallback work, - base::ScopedClosureRunner scoped_closure_runner, - SyncerError* error, - bool* did_run) { - { - base::AutoLock auto_lock(lock_); - if (stopped_) - return; - - // Set |is_work_running_| to make sure that DoWorkAndWaitUntilDone() doesn't - // return while |work| is running. - DCHECK(!is_work_running_); - is_work_running_ = true; - } - - *error = std::move(work).Run(); - *did_run = true; - - { - base::AutoLock auto_lock(lock_); - DCHECK(is_work_running_); - is_work_running_ = false; - } +bool ModelSafeWorker::IsStopped() { + return stopped_.IsSet(); } } // namespace syncer
diff --git a/components/sync/engine/model_safe_worker.h b/components/sync/engine/model_safe_worker.h index 620297fb..1565aa2 100644 --- a/components/sync/engine/model_safe_worker.h +++ b/components/sync/engine/model_safe_worker.h
@@ -9,12 +9,10 @@ #include <memory> #include <string> -#include "base/callback.h" -#include "base/callback_helpers.h" +#include "base/callback_forward.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" -#include "base/synchronization/waitable_event.h" +#include "base/synchronization/atomic_flag.h" #include "components/sync/base/model_type.h" #include "components/sync/base/syncer_error.h" @@ -24,7 +22,7 @@ namespace syncer { -using WorkCallback = base::OnceCallback<enum SyncerError(void)>; +using WorkCallback = base::Callback<enum SyncerError(void)>; enum ModelSafeGroup { GROUP_PASSIVE = 0, // Models that are just "passively" being synced; e.g. @@ -56,14 +54,17 @@ // a thread and does actual work on that thread. class ModelSafeWorker : public base::RefCountedThreadSafe<ModelSafeWorker> { public: - // If not stopped, calls ScheduleWork() to schedule |work| and waits until it - // is done or abandoned. Otherwise, returns CANNOT_DO_WORK. - SyncerError DoWorkAndWaitUntilDone(WorkCallback work); + // If not stopped, call DoWorkAndWaitUntilDoneImpl() to do work. Otherwise + // return CANNOT_DO_WORK. + SyncerError DoWorkAndWaitUntilDone(const WorkCallback& work); // Soft stop worker by setting stopped_ flag. Called when sync is disabled // or browser is shutting down. Called on UI loop. virtual void RequestStop(); + // Return true if the worker was stopped. Thread safe. + bool IsStopped(); + virtual ModelSafeGroup GetModelSafeGroup() = 0; // Returns true if called on the thread this worker works on. @@ -73,31 +74,16 @@ ModelSafeWorker(); virtual ~ModelSafeWorker(); + // Any time the Syncer performs model modifications (e.g employing a + // WriteTransaction), it should be done by this method to ensure it is done + // from a model-safe thread. + virtual SyncerError DoWorkAndWaitUntilDoneImpl(const WorkCallback& work) = 0; + private: friend class base::RefCountedThreadSafe<ModelSafeWorker>; - // Schedules |work| on the appropriate thread. - virtual void ScheduleWork(base::OnceClosure work) = 0; - - void DoWork(WorkCallback work, - base::ScopedClosureRunner scoped_closure_runner, - SyncerError* error, - bool* did_run); - - // Synchronizes access to all members. - base::Lock lock_; - - // Signaled when DoWorkAndWaitUntilDone() can return, either because the work - // is done, the work has been abandoned or RequestStop() was called while no - // work was running. Reset at the beginning of DoWorkAndWaitUntilDone(). - base::WaitableEvent work_done_or_abandoned_; - - // Whether a WorkCallback is currently running. - bool is_work_running_ = false; - - // Whether the worker was stopped. No WorkCallback can start running when this - // is true. - bool stopped_ = false; + // Whether the worker should do more work. Set when sync is disabled. + base::AtomicFlag stopped_; DISALLOW_COPY_AND_ASSIGN(ModelSafeWorker); };
diff --git a/components/sync/engine/model_safe_worker_unittest.cc b/components/sync/engine/model_safe_worker_unittest.cc index cb6cedd..6340dca 100644 --- a/components/sync/engine/model_safe_worker_unittest.cc +++ b/components/sync/engine/model_safe_worker_unittest.cc
@@ -4,84 +4,13 @@ #include "components/sync/engine/model_safe_worker.h" -#include <utility> - -#include "base/bind.h" -#include "base/macros.h" -#include "base/synchronization/atomic_flag.h" -#include "base/test/test_simple_task_runner.h" -#include "base/test/test_timeouts.h" -#include "base/threading/platform_thread.h" -#include "base/threading/thread.h" #include "base/values.h" #include "testing/gtest/include/gtest/gtest.h" namespace syncer { namespace { -syncer::WorkCallback ClosureToWorkCallback(base::Closure work) { - return base::Bind( - [](base::Closure work) { - work.Run(); - return syncer::SYNCER_OK; - }, - std::move(work)); -} - -class MockModelSafeWorker : public ModelSafeWorker { - public: - MockModelSafeWorker() = default; - - void ScheduleWork(base::OnceClosure work) override { - task_runner_->PostTask(FROM_HERE, std::move(work)); - } - - ModelSafeGroup GetModelSafeGroup() override { return GROUP_PASSIVE; } - - bool IsOnModelThread() override { - return task_runner_->BelongsToCurrentThread(); - } - - scoped_refptr<base::TestSimpleTaskRunner> task_runner() const { - return task_runner_; - } - - private: - friend class base::RefCountedThreadSafe<MockModelSafeWorker>; - - ~MockModelSafeWorker() override = default; - - const scoped_refptr<base::TestSimpleTaskRunner> task_runner_ = - new base::TestSimpleTaskRunner(); - - DISALLOW_COPY_AND_ASSIGN(MockModelSafeWorker); -}; - -class ModelSafeWorkerTest : public ::testing::Test { - protected: - ModelSafeWorkerTest() : sync_thread_("SyncThreadForTest") { - sync_thread_.Start(); - } - - void DoWorkAndWaitUntilDoneOnSyncThread(base::Closure work) { - sync_thread_.task_runner()->PostTask( - FROM_HERE, - base::Bind(base::IgnoreResult(&ModelSafeWorker::DoWorkAndWaitUntilDone), - worker_, base::Passed(ClosureToWorkCallback(work)))); - sync_thread_.task_runner()->PostTask( - FROM_HERE, base::Bind(&base::AtomicFlag::Set, - base::Unretained(&sync_thread_unblocked_))); - } - - base::AtomicFlag sync_thread_unblocked_; - base::Thread sync_thread_; - const scoped_refptr<MockModelSafeWorker> worker_ = new MockModelSafeWorker(); - - private: - DISALLOW_COPY_AND_ASSIGN(ModelSafeWorkerTest); -}; - -} // namespace +class ModelSafeWorkerTest : public ::testing::Test {}; TEST_F(ModelSafeWorkerTest, ModelSafeRoutingInfoToValue) { ModelSafeRoutingInfo routing_info; @@ -120,95 +49,5 @@ EXPECT_EQ(expected_types, GetRoutingInfoTypes(routing_info)); } -TEST_F(ModelSafeWorkerTest, DoWorkAndWaitUntilDone) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](bool* did_work) { *did_work = true; }, base::Unretained(&did_work))); - - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to |worker_|'s TaskRunner and run it. - while (!worker_->task_runner()->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - worker_->task_runner()->RunUntilIdle(); - - EXPECT_TRUE(did_work); - - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - -TEST_F(ModelSafeWorkerTest, DoWorkAndWaitUntilDoneRequestStopBeforeRunWork) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](bool* did_work) { *did_work = true; }, base::Unretained(&did_work))); - - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to |worker_|'s TaskRunner. - while (!worker_->task_runner()->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - - // Stop the worker. - worker_->RequestStop(); - - // The WorkCallback should not run. - worker_->task_runner()->RunUntilIdle(); - EXPECT_FALSE(did_work); - - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - -TEST_F(ModelSafeWorkerTest, DoWorkAndWaitUntilDoneDeleteWorkBeforeRun) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](bool* did_work) { *did_work = true; }, base::Unretained(&did_work))); - - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to |worker_|'s TaskRunner and delete it. - while (!worker_->task_runner()->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - worker_->task_runner()->ClearPendingTasks(); - - EXPECT_FALSE(did_work); - - // Deleting the task should have unblocked the sync thread. - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - -TEST_F(ModelSafeWorkerTest, DoWorkAndWaitUntilDoneRequestStopDuringRunWork) { - bool did_work = false; - DoWorkAndWaitUntilDoneOnSyncThread(base::Bind( - [](scoped_refptr<ModelSafeWorker> worker, - base::AtomicFlag* sync_thread_unblocked, bool* did_work) { - worker->RequestStop(); - base::PlatformThread::Sleep(TestTimeouts::tiny_timeout()); - - // The sync thread should not be unblocked while a WorkCallback is - // running. - EXPECT_FALSE(sync_thread_unblocked->IsSet()); - - *did_work = true; - }, - worker_, base::Unretained(&sync_thread_unblocked_), - base::Unretained(&did_work))); - EXPECT_FALSE(did_work); - EXPECT_FALSE(sync_thread_unblocked_.IsSet()); - - // Wait for a task to be posted to |worker_|'s TaskRunner and run it. - while (!worker_->task_runner()->HasPendingTask()) - base::PlatformThread::YieldCurrentThread(); - worker_->task_runner()->RunUntilIdle(); - - EXPECT_TRUE(did_work); - sync_thread_.Stop(); - EXPECT_TRUE(sync_thread_unblocked_.IsSet()); -} - +} // namespace } // namespace syncer
diff --git a/components/sync/engine/passive_model_worker.cc b/components/sync/engine/passive_model_worker.cc index f8ba2d0..6a4f766 100644 --- a/components/sync/engine/passive_model_worker.cc +++ b/components/sync/engine/passive_model_worker.cc
@@ -4,7 +4,7 @@ #include "components/sync/engine/passive_model_worker.h" -#include <utility> +#include "base/callback.h" namespace syncer { @@ -12,8 +12,10 @@ PassiveModelWorker::~PassiveModelWorker() {} -void PassiveModelWorker::ScheduleWork(base::OnceClosure work) { - std::move(work).Run(); +SyncerError PassiveModelWorker::DoWorkAndWaitUntilDoneImpl( + const WorkCallback& work) { + // Simply do the work on the current thread. + return work.Run(); } ModelSafeGroup PassiveModelWorker::GetModelSafeGroup() {
diff --git a/components/sync/engine/passive_model_worker.h b/components/sync/engine/passive_model_worker.h index 11725df7..ac744de 100644 --- a/components/sync/engine/passive_model_worker.h +++ b/components/sync/engine/passive_model_worker.h
@@ -6,6 +6,7 @@ #define COMPONENTS_SYNC_ENGINE_PASSIVE_MODEL_WORKER_H_ #include "base/macros.h" +#include "components/sync/base/syncer_error.h" #include "components/sync/engine/model_safe_worker.h" namespace syncer { @@ -21,11 +22,12 @@ ModelSafeGroup GetModelSafeGroup() override; bool IsOnModelThread() override; + protected: + SyncerError DoWorkAndWaitUntilDoneImpl(const WorkCallback& work) override; + private: ~PassiveModelWorker() override; - void ScheduleWork(base::OnceClosure work) override; - DISALLOW_COPY_AND_ASSIGN(PassiveModelWorker); };
diff --git a/components/sync/engine/ui_model_worker.cc b/components/sync/engine/ui_model_worker.cc index 08cc1a25..77fe9c77 100644 --- a/components/sync/engine/ui_model_worker.cc +++ b/components/sync/engine/ui_model_worker.cc
@@ -6,11 +6,90 @@ #include <utility> +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "components/sync/base/scoped_event_signal.h" + namespace syncer { +namespace { + +class ScopedEventSignalWithWorker { + public: + ScopedEventSignalWithWorker(scoped_refptr<UIModelWorker> ui_model_worker, + base::WaitableEvent* event) + : ui_model_worker_(std::move(ui_model_worker)), + scoped_event_signal_(event) {} + + ScopedEventSignalWithWorker(ScopedEventSignalWithWorker&&) = default; + ScopedEventSignalWithWorker& operator=(ScopedEventSignalWithWorker&&) = + default; + + bool IsStopped() const { return ui_model_worker_->IsStopped(); } + + private: + // This reference prevents the event in |scoped_event_signal_| from being + // signaled after being deleted. + scoped_refptr<UIModelWorker> ui_model_worker_; + + ScopedEventSignal scoped_event_signal_; + + DISALLOW_COPY_AND_ASSIGN(ScopedEventSignalWithWorker); +}; + +void CallDoWorkAndSignalEvent( + const WorkCallback& work, + ScopedEventSignalWithWorker scoped_event_signal_with_worker, + SyncerError* error_info) { + if (!scoped_event_signal_with_worker.IsStopped()) + *error_info = work.Run(); + // The event in |scoped_event_signal_with_worker| is signaled at the end of + // this scope. +} + +} // namespace + UIModelWorker::UIModelWorker( scoped_refptr<base::SingleThreadTaskRunner> ui_thread) - : ui_thread_(std::move(ui_thread)) {} + : ui_thread_(std::move(ui_thread)), + work_done_or_abandoned_(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED), + stop_requested_(base::WaitableEvent::ResetPolicy::MANUAL, + base::WaitableEvent::InitialState::NOT_SIGNALED) { + sequence_checker_.DetachFromSequence(); +} + +SyncerError UIModelWorker::DoWorkAndWaitUntilDoneImpl( + const WorkCallback& work) { + DCHECK(sequence_checker_.CalledOnValidSequence()); + DCHECK(!ui_thread_->BelongsToCurrentThread()); + + SyncerError error_info; + work_done_or_abandoned_.Reset(); + + if (!ui_thread_->PostTask( + FROM_HERE, + base::Bind(&CallDoWorkAndSignalEvent, work, + base::Passed(syncer::ScopedEventSignalWithWorker( + this, &work_done_or_abandoned_)), + &error_info))) { + DLOG(WARNING) << "Could not post work to UI loop."; + error_info = CANNOT_DO_WORK; + return error_info; + } + + base::WaitableEvent* events[] = {&work_done_or_abandoned_, &stop_requested_}; + base::WaitableEvent::WaitMany(events, arraysize(events)); + + return error_info; +} + +void UIModelWorker::RequestStop() { + DCHECK(ui_thread_->BelongsToCurrentThread()); + ModelSafeWorker::RequestStop(); + stop_requested_.Signal(); +} ModelSafeGroup UIModelWorker::GetModelSafeGroup() { return GROUP_UI; @@ -22,8 +101,4 @@ UIModelWorker::~UIModelWorker() {} -void UIModelWorker::ScheduleWork(base::OnceClosure work) { - ui_thread_->PostTask(FROM_HERE, std::move(work)); -} - } // namespace syncer
diff --git a/components/sync/engine/ui_model_worker.h b/components/sync/engine/ui_model_worker.h index bff75a8..2306a39c 100644 --- a/components/sync/engine/ui_model_worker.h +++ b/components/sync/engine/ui_model_worker.h
@@ -7,7 +7,9 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/sequence_checker.h" #include "base/single_thread_task_runner.h" +#include "base/synchronization/waitable_event.h" #include "components/sync/engine/model_safe_worker.h" namespace syncer { @@ -20,17 +22,33 @@ explicit UIModelWorker(scoped_refptr<base::SingleThreadTaskRunner> ui_thread); // ModelSafeWorker implementation. + void RequestStop() override; ModelSafeGroup GetModelSafeGroup() override; bool IsOnModelThread() override; + protected: + SyncerError DoWorkAndWaitUntilDoneImpl(const WorkCallback& work) override; + private: ~UIModelWorker() override; - void ScheduleWork(base::OnceClosure work) override; - // A reference to the UI thread's task runner. const scoped_refptr<base::SingleThreadTaskRunner> ui_thread_; + // Signaled when a task posted by DoWorkAndWaitUntilDoneImpl() is deleted, + // i.e. after it runs or when it is abandoned. Reset at the beginning of every + // DoWorkAndWaitUntilDoneImpl() call. + base::WaitableEvent work_done_or_abandoned_; + + // Signaled from RequestStop(). When this is signaled, + // DoWorkAndWaitUntilDoneImpl() returns immediately. This is needed to prevent + // the UI thread from joining the sync thread while it is waiting for a + // WorkCallback to run on the UI thread. See crbug.com/663600. + base::WaitableEvent stop_requested_; + + // Verifies that calls to DoWorkAndWaitUntilDoneImpl() are sequenced. + base::SequenceChecker sequence_checker_; + DISALLOW_COPY_AND_ASSIGN(UIModelWorker); };
diff --git a/components/sync/engine/ui_model_worker_unittest.cc b/components/sync/engine/ui_model_worker_unittest.cc index ab27452..9411a67a 100644 --- a/components/sync/engine/ui_model_worker_unittest.cc +++ b/components/sync/engine/ui_model_worker_unittest.cc
@@ -52,7 +52,7 @@ sync_thread_.task_runner()->PostTask( FROM_HERE, base::Bind(base::IgnoreResult(&UIModelWorker::DoWorkAndWaitUntilDone), - worker_, base::Passed(ClosureToWorkCallback(work)))); + worker_, ClosureToWorkCallback(work))); } protected:
diff --git a/components/sync/engine_impl/directory_update_handler.cc b/components/sync/engine_impl/directory_update_handler.cc index 7b71cf6..495346c 100644 --- a/components/sync/engine_impl/directory_update_handler.cc +++ b/components/sync/engine_impl/directory_update_handler.cc
@@ -6,7 +6,6 @@ #include <stdint.h> -#include <utility> #include <vector> #include "base/memory/ptr_util.h" @@ -128,7 +127,7 @@ // We wait until the callback is executed. We can safely use // Unretained. base::Unretained(this), base::Unretained(status)); - worker_->DoWorkAndWaitUntilDone(std::move(c)); + worker_->DoWorkAndWaitUntilDone(c); debug_info_emitter_->EmitUpdateCountersUpdate(); debug_info_emitter_->EmitStatusCountersUpdate();
diff --git a/components/sync/syncable/directory_backing_store.cc b/components/sync/syncable/directory_backing_store.cc index d2e3246..3714cd90 100644 --- a/components/sync/syncable/directory_backing_store.cc +++ b/components/sync/syncable/directory_backing_store.cc
@@ -717,13 +717,11 @@ while (s.Step()) { int total_entry_copies; - std::unique_ptr<EntryKernel> kernel_ptr = - UnpackEntry(&s, &total_entry_copies); + std::unique_ptr<EntryKernel> kernel = UnpackEntry(&s, &total_entry_copies); // A null kernel is evidence of external data corruption. - if (!kernel_ptr) + if (!kernel) return false; - EntryKernel* kernel = kernel_ptr.get(); - (*delete_journals)[kernel] = std::move(kernel_ptr); + DeleteJournal::AddEntryToJournalIndex(delete_journals, std::move(kernel)); } return s.Succeeded(); }
diff --git a/components/sync/syncable/syncable_delete_journal.cc b/components/sync/syncable/syncable_delete_journal.cc index a5b426a..e43f695c 100644 --- a/components/sync/syncable/syncable_delete_journal.cc +++ b/components/sync/syncable/syncable_delete_journal.cc
@@ -45,10 +45,9 @@ if (entry.ref(SERVER_IS_DEL)) { if (it == delete_journals_.end()) { // New delete. - std::unique_ptr<EntryKernel> t_ptr = base::MakeUnique<EntryKernel>(entry); - EntryKernel* t = t_ptr.get(); - delete_journals_to_purge_.erase(t->ref(META_HANDLE)); - delete_journals_[t] = std::move(t_ptr); + auto entry_copy = base::MakeUnique<EntryKernel>(entry); + delete_journals_to_purge_.erase(entry_copy->ref(META_HANDLE)); + AddEntryToJournalIndex(&delete_journals_, std::move(entry_copy)); } } else { // Undelete. This could happen in two cases: @@ -124,10 +123,8 @@ for (auto& entry : entries) { needle.put(ID, entry->ref(ID)); if (delete_journals_.find(&needle) == delete_journals_.end()) { - std::unique_ptr<EntryKernel> t_ptr = - base::MakeUnique<EntryKernel>(*entry); - EntryKernel* t = t_ptr.get(); - delete_journals_[t] = std::move(t_ptr); + auto entry_copy = base::MakeUnique<EntryKernel>(*entry); + AddEntryToJournalIndex(&delete_journals_, std::move(entry_copy)); } delete_journals_to_purge_.erase(entry->ref(META_HANDLE)); } @@ -143,5 +140,13 @@ } } +// static +void DeleteJournal::AddEntryToJournalIndex(JournalIndex* journal_index, + std::unique_ptr<EntryKernel> entry) { + EntryKernel* key = entry.get(); + if (journal_index->find(key) == journal_index->end()) + (*journal_index)[key] = std::move(entry); +} + } // namespace syncable } // namespace syncer
diff --git a/components/sync/syncable/syncable_delete_journal.h b/components/sync/syncable/syncable_delete_journal.h index da2b4c1..6d907d8 100644 --- a/components/sync/syncable/syncable_delete_journal.h +++ b/components/sync/syncable/syncable_delete_journal.h
@@ -92,6 +92,10 @@ // Return true if delete journals of |type| are maintained. static bool IsDeleteJournalEnabled(ModelType type); + // Adds entry to JournalIndex if it doesn't already exist. + static void AddEntryToJournalIndex(JournalIndex* journal_index, + std::unique_ptr<EntryKernel> entry); + private: FRIEND_TEST_ALL_PREFIXES(SyncableDirectoryTest, ManageDeleteJournals);
diff --git a/components/sync/syncable/syncable_delete_journal_unittest.cc b/components/sync/syncable/syncable_delete_journal_unittest.cc new file mode 100644 index 0000000..e7ad444 --- /dev/null +++ b/components/sync/syncable/syncable_delete_journal_unittest.cc
@@ -0,0 +1,41 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/sync/syncable/syncable_delete_journal.h" + +#include <utility> + +#include "base/memory/ptr_util.h" +#include "components/sync/syncable/entry_kernel.h" +#include "components/sync/syncable/syncable_id.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace syncer { +namespace syncable { + +// Tests that when adding entries to JournalIndex key pointer always matches +// value object. +TEST(SyncableDeleteJournal, AddEntryToJournalIndex_KeyMatchesValue) { + JournalIndex journal_index; + std::unique_ptr<EntryKernel> entry; + + // Add two entries with the same id. Verify that only one entry is stored in + // JournalIndex and that entry's key matches the value object. + entry = base::MakeUnique<EntryKernel>(); + entry->put(ID, Id::CreateFromServerId("id1")); + DeleteJournal::AddEntryToJournalIndex(&journal_index, std::move(entry)); + EXPECT_EQ(1U, journal_index.size()); + + entry = base::MakeUnique<EntryKernel>(); + entry->put(ID, Id::CreateFromServerId("id1")); + DeleteJournal::AddEntryToJournalIndex(&journal_index, std::move(entry)); + EXPECT_EQ(1U, journal_index.size()); + + for (auto it = journal_index.begin(); it != journal_index.end(); ++it) { + EXPECT_TRUE(it->first == it->second.get()); + } +} + +} // namespace syncable +} // namespace syncer
diff --git a/components/sync/test/engine/fake_model_worker.cc b/components/sync/test/engine/fake_model_worker.cc index 966e0a4..c696005 100644 --- a/components/sync/test/engine/fake_model_worker.cc +++ b/components/sync/test/engine/fake_model_worker.cc
@@ -4,7 +4,7 @@ #include "components/sync/test/engine/fake_model_worker.h" -#include <utility> +#include "base/callback.h" namespace syncer { @@ -18,10 +18,11 @@ DCHECK(thread_checker_.CalledOnValidThread()); } -void FakeModelWorker::ScheduleWork(base::OnceClosure work) { +SyncerError FakeModelWorker::DoWorkAndWaitUntilDoneImpl( + const WorkCallback& work) { DCHECK(thread_checker_.CalledOnValidThread()); // Simply do the work on the current thread. - std::move(work).Run(); + return work.Run(); } ModelSafeGroup FakeModelWorker::GetModelSafeGroup() {
diff --git a/components/sync/test/engine/fake_model_worker.h b/components/sync/test/engine/fake_model_worker.h index ccc922d..55ab4a9 100644 --- a/components/sync/test/engine/fake_model_worker.h +++ b/components/sync/test/engine/fake_model_worker.h
@@ -22,11 +22,12 @@ ModelSafeGroup GetModelSafeGroup() override; bool IsOnModelThread() override; + protected: + SyncerError DoWorkAndWaitUntilDoneImpl(const WorkCallback& work) override; + private: ~FakeModelWorker() override; - void ScheduleWork(base::OnceClosure work) override; - const ModelSafeGroup group_; base::ThreadChecker thread_checker_;
diff --git a/components/ukm/test_ukm_service.cc b/components/ukm/test_ukm_service.cc index e3fcef86..824fa25 100644 --- a/components/ukm/test_ukm_service.cc +++ b/components/ukm/test_ukm_service.cc
@@ -4,6 +4,9 @@ #include "components/ukm/test_ukm_service.h" +#include "base/logging.h" +#include "base/metrics/metrics_hashes.h" +#include "components/ukm/ukm_entry.h" #include "components/ukm/ukm_source.h" namespace ukm { @@ -33,15 +36,38 @@ } const UkmSource* TestUkmService::GetSourceForUrl(const char* url) const { + const UkmSource* source = nullptr; for (const auto& kv : sources_for_testing()) { - if (kv.second->url() == url) - return kv.second.get(); + if (kv.second->url() == url) { + DCHECK_EQ(nullptr, source); + source = kv.second.get(); + } } - return nullptr; + return source; +} + +const UkmSource* TestUkmService::GetSourceForSourceId(int32_t source_id) const { + const UkmSource* source = nullptr; + for (const auto& kv : sources_for_testing()) { + if (kv.second->id() == source_id) { + DCHECK_EQ(nullptr, source); + source = kv.second.get(); + } + } + return source; } const UkmEntry* TestUkmService::GetEntry(size_t entry_num) const { return entries_for_testing()[entry_num].get(); } +const UkmEntry* TestUkmService::GetEntryForEntryName( + const char* entry_name) const { + for (const auto& it : entries_for_testing()) { + if (it->event_hash() == base::HashMetricName(entry_name)) + return it.get(); + } + return nullptr; +} + } // namespace ukm
diff --git a/components/ukm/test_ukm_service.h b/components/ukm/test_ukm_service.h index e3ffbf9..55d5920 100644 --- a/components/ukm/test_ukm_service.h +++ b/components/ukm/test_ukm_service.h
@@ -24,9 +24,11 @@ size_t sources_count() const { return sources_for_testing().size(); } const std::map<int32_t, std::unique_ptr<UkmSource>>& GetSources() const; const UkmSource* GetSourceForUrl(const char* url) const; + const UkmSource* GetSourceForSourceId(int32_t source_id) const; size_t entries_count() const { return entries_for_testing().size(); } const UkmEntry* GetEntry(size_t entry_num) const; + const UkmEntry* GetEntryForEntryName(const char* entry_name) const; private: metrics::TestMetricsServiceClient test_metrics_service_client_;
diff --git a/content/app/strings/content_strings.grd b/content/app/strings/content_strings.grd index 6596fd3e..8b521fc 100644 --- a/content/app/strings/content_strings.grd +++ b/content/app/strings/content_strings.grd
@@ -517,7 +517,7 @@ tree item </message> </if> - + <message name="IDS_AX_AM_PM_FIELD_TEXT" desc="Accessible description of the AM/PM field in a date/time control"> AM/PM </message> @@ -872,6 +872,12 @@ <message name="IDS_MEDIA_OVERFLOW_MENU_DOWNLOAD" desc="Media controls overflow menu item label for a download button."> Download </message> + <message name="IDS_MEDIA_REMOTING_DISABLE_TEXT" desc="Media remoting disable button label."> + Play on both screens + </message> + <message name="IDS_MEDIA_REMOTING_CAST_TEXT" desc="Media remoting cast video text message."> + Now casting to your TV + </message> <message name="IDS_MEDIA_TRACKS_NO_LABEL" desc="Menu item label for a text track that has no name specified. The number represents the track number in the list of tracks."> Track <ph name="NUMBER">$1<ex>1</ex></ph> </message>
diff --git a/content/browser/devtools/protocol/network_handler.cc b/content/browser/devtools/protocol/network_handler.cc index 94eb1e88..700f10f 100644 --- a/content/browser/devtools/protocol/network_handler.cc +++ b/content/browser/devtools/protocol/network_handler.cc
@@ -696,9 +696,11 @@ return; if (completion_status.error_code != net::OK) { frontend_->LoadingFailed( - request_id, base::TimeTicks::Now().ToInternalValue() / - static_cast<double>(base::Time::kMicrosecondsPerSecond), - Page::ResourceTypeEnum::Other, "Navigation Preload Error", + request_id, + base::TimeTicks::Now().ToInternalValue() / + static_cast<double>(base::Time::kMicrosecondsPerSecond), + Page::ResourceTypeEnum::Other, + net::ErrorToString(completion_status.error_code), completion_status.error_code == net::Error::ERR_ABORTED); } frontend_->LoadingFinished(
diff --git a/content/browser/loader/navigation_url_loader_network_service.cc b/content/browser/loader/navigation_url_loader_network_service.cc index c503716..2ebed65 100644 --- a/content/browser/loader/navigation_url_loader_network_service.cc +++ b/content/browser/loader/navigation_url_loader_network_service.cc
@@ -41,6 +41,7 @@ auto new_request = base::MakeUnique<ResourceRequest>(); new_request->method = "GET"; new_request->url = request_info->common_params.url; + new_request->first_party_for_cookies = request_info->first_party_for_cookies; new_request->priority = net::HIGHEST; mojom::URLLoaderClientPtr url_loader_client_ptr;
diff --git a/content/browser/renderer_host/render_view_host_impl.cc b/content/browser/renderer_host/render_view_host_impl.cc index 2e5c40c..1de98c2 100644 --- a/content/browser/renderer_host/render_view_host_impl.cc +++ b/content/browser/renderer_host/render_view_host_impl.cc
@@ -548,15 +548,6 @@ prefs.background_video_track_optimization_enabled = base::FeatureList::IsEnabled(media::kBackgroundVideoTrackOptimization); - // TODO(avayvod, asvitkine): Query the value directly when it is available in - // the renderer process. See https://crbug.com/681160. - prefs.max_keyframe_distance_to_disable_background_video = - base::TimeDelta::FromMilliseconds( - variations::GetVariationParamByFeatureAsInt( - media::kBackgroundVideoTrackOptimization, - "max_keyframe_distance_ms", - base::TimeDelta::FromSeconds(10).InMilliseconds())); - // TODO(servolk, asvitkine): Query the value directly when it is available in // the renderer process. See https://crbug.com/681160. prefs.enable_instant_source_buffer_gc =
diff --git a/content/child/blink_platform_impl.cc b/content/child/blink_platform_impl.cc index 0787909..74044ea 100644 --- a/content/child/blink_platform_impl.cc +++ b/content/child/blink_platform_impl.cc
@@ -212,6 +212,10 @@ return IDS_FORM_INPUT_ALT; case WebLocalizedString::kMissingPluginText: return IDS_PLUGIN_INITIALIZATION_ERROR; + case WebLocalizedString::kMediaRemotingDisableText: + return IDS_MEDIA_REMOTING_DISABLE_TEXT; + case WebLocalizedString::kMediaRemotingCastText: + return IDS_MEDIA_REMOTING_CAST_TEXT; case WebLocalizedString::kMultipleFileUploadText: return IDS_FORM_FILE_MULTIPLE_UPLOAD; case WebLocalizedString::kOtherColorLabel: @@ -523,6 +527,8 @@ ui::SCALE_FACTOR_100P, false}, {"mediaplayerOverlayPlay", IDR_MEDIAPLAYER_OVERLAY_PLAY_BUTTON, ui::SCALE_FACTOR_100P, false}, + {"mediaRemotingCastIcon", IDR_MEDIA_REMOTING_CAST_ICON, + ui::SCALE_FACTOR_100P, false}, {"mediaplayerTrackSelectionCheckmark", IDR_MEDIAPLAYER_TRACKSELECTION_CHECKMARK, ui::SCALE_FACTOR_100P, false}, {"mediaplayerClosedCaptionsIcon", IDR_MEDIAPLAYER_CLOSEDCAPTIONS_ICON,
diff --git a/content/network/network_context.cc b/content/network/network_context.cc index 5637a659..fcca28c 100644 --- a/content/network/network_context.cc +++ b/content/network/network_context.cc
@@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "content/network/url_loader_impl.h" #include "content/public/common/content_client.h" #include "net/dns/host_resolver.h" @@ -13,6 +14,7 @@ #include "net/log/net_log_util.h" #include "net/log/write_to_file_net_log_observer.h" #include "net/proxy/proxy_service.h" +#include "net/url_request/data_protocol_handler.h" #include "net/url_request/url_request_context.h" #include "net/url_request/url_request_context_builder.h" @@ -58,6 +60,10 @@ builder.EnableHttpCache(cache_params); builder.set_file_enabled(true); + + builder.SetProtocolHandler(url::kDataScheme, + base::MakeUnique<net::DataProtocolHandler>()); + return builder.Build(); }
diff --git a/content/network/url_loader_impl.cc b/content/network/url_loader_impl.cc index a29cec4..b2fd442 100644 --- a/content/network/url_loader_impl.cc +++ b/content/network/url_loader_impl.cc
@@ -6,6 +6,7 @@ #include "base/task_scheduler/post_task.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" #include "content/network/net_adapters.h" #include "content/network/network_context.h" #include "content/public/common/referrer.h" @@ -63,6 +64,11 @@ response->head.effective_connection_type = net::EFFECTIVE_CONNECTION_TYPE_UNKNOWN; + + request->GetLoadTimingInfo(&response->head.load_timing); + + response->head.request_start = request->creation_time(); + response->head.response_start = base::TimeTicks::Now(); } // A subclass of net::UploadBytesElementReader which owns @@ -226,6 +232,7 @@ scoped_refptr<ResourceResponse> response = new ResourceResponse(); PopulateResourceResponse(url_request_.get(), response.get()); + response->head.encoded_data_length = url_request_->GetTotalReceivedBytes(); url_loader_client_->OnReceiveRedirect(redirect_info, response->head); } @@ -237,6 +244,7 @@ scoped_refptr<ResourceResponse> response = new ResourceResponse(); PopulateResourceResponse(url_request_.get(), response.get()); + response->head.encoded_data_length = url_request_->raw_header_size(); mojom::DownloadedTempFilePtr downloaded_file_ptr; url_loader_client_->OnReceiveResponse(response->head,
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContextSelectionClient.java b/content/public/android/java/src/org/chromium/content/browser/ContextSelectionClient.java index 63d5a616..6a8bcea1 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContextSelectionClient.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContextSelectionClient.java
@@ -137,11 +137,11 @@ switch (callbackData) { case SUGGEST_AND_CLASSIFY: - mProvider.sendSuggestAndClassifyRequest(text, start, end); + mProvider.sendSuggestAndClassifyRequest(text, start, end, null); break; case CLASSIFY: - mProvider.sendClassifyRequest(text, start, end); + mProvider.sendClassifyRequest(text, start, end, null); break; default:
diff --git a/content/public/android/java/src/org/chromium/content/browser/ContextSelectionProvider.java b/content/public/android/java/src/org/chromium/content/browser/ContextSelectionProvider.java index 50890fe..0f6dc07 100644 --- a/content/public/android/java/src/org/chromium/content/browser/ContextSelectionProvider.java +++ b/content/public/android/java/src/org/chromium/content/browser/ContextSelectionProvider.java
@@ -10,6 +10,8 @@ import org.chromium.base.annotations.SuppressFBWarnings; +import java.util.Locale; + /** * The interface that controls contextual text selection. */ @@ -78,7 +80,8 @@ * @param end The index pointing to the first character that comes after * the selected text inside the textual context. */ - public void sendSuggestAndClassifyRequest(CharSequence text, int start, int end); + public void sendSuggestAndClassifyRequest( + CharSequence text, int start, int end, Locale[] locales); /** * Sends asynchronous request to obtain the selection and analyze its type. @@ -87,7 +90,7 @@ * @param end The index pointing to the first character that comes after * the selected text inside the textual context. */ - public void sendClassifyRequest(CharSequence text, int start, int end); + public void sendClassifyRequest(CharSequence text, int start, int end, Locale[] locales); /** * Cancel all asynchronous requests.
diff --git a/content/public/common/common_param_traits_macros.h b/content/public/common/common_param_traits_macros.h index cb9519dd..3dd2e75 100644 --- a/content/public/common/common_param_traits_macros.h +++ b/content/public/common/common_param_traits_macros.h
@@ -248,7 +248,6 @@ IPC_STRUCT_TRAITS_MEMBER(default_maximum_page_scale_factor) IPC_STRUCT_TRAITS_MEMBER(hide_download_ui) IPC_STRUCT_TRAITS_MEMBER(background_video_track_optimization_enabled) - IPC_STRUCT_TRAITS_MEMBER(max_keyframe_distance_to_disable_background_video) IPC_STRUCT_TRAITS_MEMBER(enable_instant_source_buffer_gc) IPC_STRUCT_TRAITS_MEMBER(presentation_receiver) IPC_STRUCT_TRAITS_MEMBER(media_controls_enabled)
diff --git a/content/public/common/web_preferences.cc b/content/public/common/web_preferences.cc index 78312c4..72f7bd8 100644 --- a/content/public/common/web_preferences.cc +++ b/content/public/common/web_preferences.cc
@@ -225,8 +225,6 @@ #endif hide_download_ui(false), background_video_track_optimization_enabled(false), - max_keyframe_distance_to_disable_background_video( - base::TimeDelta::FromSeconds(10)), enable_instant_source_buffer_gc(false), presentation_receiver(false), media_controls_enabled(true),
diff --git a/content/public/common/web_preferences.h b/content/public/common/web_preferences.h index e2e6c5f0..ae9756c 100644 --- a/content/public/common/web_preferences.h +++ b/content/public/common/web_preferences.h
@@ -266,13 +266,6 @@ // If enabled, disabled video track when the video is in the background. bool background_video_track_optimization_enabled; - // If background video track optimization is enabled, don't disable video - // track for videos with the average keyframe distance greater than this - // value. - // TODO(avayvod, asvitkine): Query the value directly when it is available in - // the renderer process. See https://crbug.com/681160. - base::TimeDelta max_keyframe_distance_to_disable_background_video; - // When memory pressure based garbage collection is enabled for MSE, the // |enable_instant_source_buffer_gc| flag controls whether the GC is done // immediately on memory pressure notification or during the next SourceBuffer
diff --git a/content/renderer/mojo/blink_connector_js_wrapper.cc b/content/renderer/mojo/blink_connector_js_wrapper.cc index e7fb6f51..abf402c 100644 --- a/content/renderer/mojo/blink_connector_js_wrapper.cc +++ b/content/renderer/mojo/blink_connector_js_wrapper.cc
@@ -24,7 +24,7 @@ // static gin::Handle<BlinkConnectorJsWrapper> BlinkConnectorJsWrapper::Create( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, service_manager::Connector* connector) { return gin::CreateHandle( isolate, @@ -73,7 +73,7 @@ BlinkConnectorJsWrapper::BlinkConnectorJsWrapper( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, base::WeakPtr<service_manager::Connector> connector) : isolate_(isolate), context_(isolate, context), @@ -90,7 +90,7 @@ return; v8::HandleScope handle_scope(isolate_); - v8::Handle<v8::Context> context = context_.Get(isolate_); + v8::Local<v8::Context> context = context_.Get(isolate_); v8::Context::Scope context_scope(context); v8::Local<v8::Value> argv[] = { gin::ConvertToV8(isolate_, mojo::Handle(pipe.release().value()))};
diff --git a/content/renderer/mojo/blink_connector_js_wrapper.h b/content/renderer/mojo/blink_connector_js_wrapper.h index 54f53c2..34fe942e6 100644 --- a/content/renderer/mojo/blink_connector_js_wrapper.h +++ b/content/renderer/mojo/blink_connector_js_wrapper.h
@@ -29,7 +29,7 @@ ~BlinkConnectorJsWrapper() override; static gin::Handle<BlinkConnectorJsWrapper> Create( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, service_manager::Connector* remote_interfaces); // gin::Wrappable<BlinkConnectorJsWrapper> overrides. @@ -52,7 +52,7 @@ v8::Persistent<v8::Function, v8::CopyablePersistentTraits<v8::Function>>; BlinkConnectorJsWrapper(v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, base::WeakPtr<service_manager::Connector> connector); void CallJsFactory(const ScopedJsFactory& factory,
diff --git a/content/renderer/mojo/interface_provider_js_wrapper.cc b/content/renderer/mojo/interface_provider_js_wrapper.cc index 03c3dfd..352ee01 100644 --- a/content/renderer/mojo/interface_provider_js_wrapper.cc +++ b/content/renderer/mojo/interface_provider_js_wrapper.cc
@@ -28,7 +28,7 @@ // static gin::Handle<InterfaceProviderJsWrapper> InterfaceProviderJsWrapper::Create( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, service_manager::Connector* connector) { return gin::CreateHandle( isolate, new InterfaceProviderJsWrapper(isolate, context, @@ -38,7 +38,7 @@ // static gin::Handle<InterfaceProviderJsWrapper> InterfaceProviderJsWrapper::Create( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, service_manager::InterfaceProvider* remote_interfaces) { return gin::CreateHandle( isolate, @@ -104,7 +104,7 @@ InterfaceProviderJsWrapper::InterfaceProviderJsWrapper( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, base::WeakPtr<service_manager::Connector> connector) : isolate_(isolate), context_(isolate, context), @@ -116,7 +116,7 @@ InterfaceProviderJsWrapper::InterfaceProviderJsWrapper( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, base::WeakPtr<service_manager::InterfaceProvider> remote_interfaces) : isolate_(isolate), context_(isolate, context), @@ -133,7 +133,7 @@ return; v8::HandleScope handle_scope(isolate_); - v8::Handle<v8::Context> context = context_.Get(isolate_); + v8::Local<v8::Context> context = context_.Get(isolate_); v8::Context::Scope context_scope(context); v8::Local<v8::Value> argv[] = { gin::ConvertToV8(isolate_, mojo::Handle(pipe.release().value()))};
diff --git a/content/renderer/mojo/interface_provider_js_wrapper.h b/content/renderer/mojo/interface_provider_js_wrapper.h index ec18bfc1..b6dd37a 100644 --- a/content/renderer/mojo/interface_provider_js_wrapper.h +++ b/content/renderer/mojo/interface_provider_js_wrapper.h
@@ -30,11 +30,11 @@ ~InterfaceProviderJsWrapper() override; static gin::Handle<InterfaceProviderJsWrapper> Create( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, service_manager::Connector* connector); static gin::Handle<InterfaceProviderJsWrapper> Create( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, service_manager::InterfaceProvider* remote_interfaces); // gin::Wrappable<InterfaceProviderJsWrapper> overrides. @@ -58,11 +58,11 @@ InterfaceProviderJsWrapper( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, base::WeakPtr<service_manager::Connector> connector); InterfaceProviderJsWrapper( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, base::WeakPtr<service_manager::InterfaceProvider> remote_interfaces); void CallJsFactory(const ScopedJsFactory& factory,
diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 676a081..6922b94 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc
@@ -24,6 +24,7 @@ #include "base/memory/shared_memory.h" #include "base/memory/weak_ptr.h" #include "base/metrics/field_trial.h" +#include "base/metrics/field_trial_params.h" #include "base/metrics/histogram_macros.h" #include "base/process/process.h" #include "base/stl_util.h" @@ -2875,6 +2876,16 @@ base::WeakPtr<media::MediaObserver> media_observer = nullptr; #endif + base::TimeDelta max_keyframe_distance_to_disable_background_video = + base::TimeDelta::FromMilliseconds(base::GetFieldTrialParamByFeatureAsInt( + media::kBackgroundVideoTrackOptimization, "max_keyframe_distance_ms", + base::TimeDelta::FromSeconds(10).InMilliseconds())); + base::TimeDelta max_keyframe_distance_to_disable_background_video_mse = + base::TimeDelta::FromMilliseconds(base::GetFieldTrialParamByFeatureAsInt( + media::kBackgroundVideoTrackOptimization, + "max_keyframe_distance_media_source_ms", + base::TimeDelta::FromSeconds(10).InMilliseconds())); + media::WebMediaPlayerParams params( base::Bind(&ContentRendererClient::DeferMediaLoad, base::Unretained(GetContentClient()->renderer()), @@ -2887,9 +2898,8 @@ base::Bind(&v8::Isolate::AdjustAmountOfExternalAllocatedMemory, base::Unretained(blink::MainThreadIsolate())), initial_cdm, media_surface_manager_, media_observer, - // TODO(avayvod, asvitkine): Query the value directly when it is available - // in the renderer process. See https://crbug.com/681160. - GetWebkitPreferences().max_keyframe_distance_to_disable_background_video, + max_keyframe_distance_to_disable_background_video, + max_keyframe_distance_to_disable_background_video_mse, GetWebkitPreferences().enable_instant_source_buffer_gc, GetContentClient()->renderer()->AllowMediaSuspend(), embedded_media_experience_enabled);
diff --git a/content/shell/renderer/layout_test/interface_registry_js_wrapper.cc b/content/shell/renderer/layout_test/interface_registry_js_wrapper.cc index 7f2a054..3a1458f 100644 --- a/content/shell/renderer/layout_test/interface_registry_js_wrapper.cc +++ b/content/shell/renderer/layout_test/interface_registry_js_wrapper.cc
@@ -24,7 +24,7 @@ // static gin::Handle<InterfaceRegistryJsWrapper> InterfaceRegistryJsWrapper::Create( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, service_manager::InterfaceRegistry* interface_registry) { return gin::CreateHandle( isolate, new InterfaceRegistryJsWrapper( @@ -52,7 +52,7 @@ InterfaceRegistryJsWrapper::InterfaceRegistryJsWrapper( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, base::WeakPtr<service_manager::InterfaceRegistry> interface_registry) : isolate_(isolate), context_(isolate, context),
diff --git a/content/shell/renderer/layout_test/interface_registry_js_wrapper.h b/content/shell/renderer/layout_test/interface_registry_js_wrapper.h index 1330d56..e562316c 100644 --- a/content/shell/renderer/layout_test/interface_registry_js_wrapper.h +++ b/content/shell/renderer/layout_test/interface_registry_js_wrapper.h
@@ -28,7 +28,7 @@ public: static gin::Handle<InterfaceRegistryJsWrapper> Create( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, service_manager::InterfaceRegistry* interface_registry); // gin::Wrappable<InterfaceRegistryJsWrapper> overrides. @@ -48,7 +48,7 @@ InterfaceRegistryJsWrapper( v8::Isolate* isolate, - v8::Handle<v8::Context> context, + v8::Local<v8::Context> context, base::WeakPtr<service_manager::InterfaceRegistry> interface_registry); ~InterfaceRegistryJsWrapper() override;
diff --git a/device/vr/vr_display_impl.cc b/device/vr/vr_display_impl.cc index ddbdbb5..46542389 100644 --- a/device/vr/vr_display_impl.cc +++ b/device/vr/vr_display_impl.cc
@@ -59,7 +59,9 @@ void VRDisplayImpl::RequestPresent(bool secure_origin, mojom::VRSubmitFrameClientPtr submit_client, const RequestPresentCallback& callback) { - if (!device_->IsAccessAllowed(this)) { + // TODO(mthiesse): Re-enable insecure origin support once webVR content + // warnings are fixed. crbug.com/704937 + if (!device_->IsAccessAllowed(this) || !secure_origin) { callback.Run(false); return; }
diff --git a/docs/README.md b/docs/README.md index c203f4ed..b0d26a2 100644 --- a/docs/README.md +++ b/docs/README.md
@@ -1,17 +1,22 @@ # Chromium docs This directory contains chromium project documentation in -[Gitiles-flavored Markdown]. +[Gitiles-flavored Markdown](https://gerrit.googlesource.com/gitiles/+/master/Documentation/markdown.md). It is automatically [rendered by Gitiles](https://chromium.googlesource.com/chromium/src/+/master/docs/). +If you add new documents, please also add a link to them in the Document Index +below. + +[TOC] + ## Style guide -Markdown documents must follow the [style guide]. +Markdown documents must follow the [style guide](https://github.com/google/styleguide/tree/gh-pages/docguide). ## Previewing changes -You can preview your local changes using [md_browser]: +You can preview your local changes using [md_browser](../tools/md_browser/): ```bash # in chromium checkout @@ -26,6 +31,280 @@ ./tools/md_browser/md_browser.py ``` -[Gitiles-flavored Markdown]: https://gerrit.googlesource.com/gitiles/+/master/Documentation/markdown.md -[style guide]: https://github.com/google/styleguide/tree/gh-pages/docguide -[md_browser]: ../tools/md_browser/ +## Document Index + +### Checking Out and Building +* [Linux Build Instructions](linux_build_instructions.md) - Linux +* [Mac Build Instructions](mac_build_instructions.md) - MacOS +* [Windows Build Instructions](windows_build_instructions.md) - Windows +* [Android Build Instructions](android_build_instructions.md) - Android target + (on a Linux host) +* [Cast Build Instructions](linux_cast_build_instructions.md) - Cast target + (on a Linux host) +* [Cast for Android Build Instructions](android_cast_build_instructions.md) - + Cast for Android (on a Linux host) +* [iOS Build Instructions](ios/build_instructions.md) - iOS target (on a MacOS + host) +* [Linux Chromium ARM Recipes](linux_chromium_arm.md) - Recipes for building + Chromium for ARM on Linux. +* [Common Build Tasks](common_build_tasks.md) - Recipes for slightly more + advanced build tasks +* [Chrome Component Build](component_build.md) - Faster builds using more + libraries +* [Using the BuildRunner](using_build_runner.md) - Scripts that extract build + stops from builders and runs them locally on a slave +* [Cr User Manual](cr_user_manual.md) - Manual for `cr`, a tool that tries to + hide some of the tools used for working on Chromium behind an abstraction + layer + +### Integrated Development Environment (IDE) Set Up Guides +* [Android Studio](android_studio.md) - Android Studio for Android builds +* [Eclipse for Android](eclipse.md) - Eclipse for Android +* [Eclipse for Linux](linux_eclipse_dev.md) - Eclipse for other platforms + (This guide was written for Linux, but is probably usable on Windows/MacOS + as well) +* [Qt Creator](qtcreator.md) - Using Qt Creator as an IDE or GUI debugger +* [Setting up Visual Studio Code](vscode.md) - Visual Studio Code +* [EMACS Notes](emacs.md) - EMACS commands/styles/tool integrations +* [Atom](atom.md) - Atom multi-platform code editor + +### Git +* [Git Cookbook](git_cookbook.md) - A collection of git recipes for common + tasks +* [Git Tips](git_tips.md) - More git tips + +### Clang +* [Clang Compiler](clang.md) - General information on the clang compiler, used + by default on Mac and Linux +* [Clang Tool Refactoring](clang_tool_refactoring.md) - Leveraging clang tools + to perform refactorings that are AST-aware +* [The Clang Static Analyzer](clang_static_analyzer.md) - How to enable static + analysis at build time +* [Writing Clang Plugins](writing_clang_plugins.md) - Don't write a clang + plugin, but if you do, read this +* [Updating Clang](updating_clang.md) - Updating the version of Clang used to + build +* [Using clang-format on Chromium C++ Code](clang_format.md) - Various ways to + invoke clang-format on C++ code +* [Clang Tidy](clang_tidy.md) - Support for the `clang-tidy` tool in Chromium +* [Updating Clang Format Binaries](updating_clang_format_binaries.md) - How up + update the clang-format binaries that come with a checkout of Chromium + +### General Development +* [Code Reviews](code_reviews.md) - Code review requirements and guidelines +* [Closure Compilation](closure_compilation.md) - The _Closure_ JavaScript + compiler +* [Callback<> and Bind()](callback.md) - All about Callbacks, Closures, and + Bind(). +* [Views Platform Styling](ui/views/platform_style.md) - How views are styled + to fit in different native platforms +* [Tab Helpers](tab_helpers.md) - Using WebContents/WebContentsObserver to add + features to browser tabs. +* [Adding third_party Libraries](adding_to_third_party.md) - How to get code + into third_party/ +* [Graphical Debugging Aid for Chromium Views](graphical_debugging_aid_chromium_views.md) - + Visualizing view trees during debugging +* [Bitmap Pipeline](bitmap_pipeline.md) - How bitmaps are moved from the + renderer to the screen. +* [base::Optional](optional.md) - How to use `base::Optional` in C++ code. +* [Using the Origin Trials Framework](origin_trials_integration.md) - A + framework for conditionally enabling experimental APIs for testing. +* [`SharedModelTypeProcessor` in Unified Sync and Storage](sync/uss/shared_model_type_processor.md) - + Notes on the central data structure used in Chrome Sync. +* [Chrome Sync's Model API](sync/model_api.md) - Data models used for syncing + information across devices using Chrome Sync. +* [Ozone Overview](ozone_overview.md) - Ozone is an abstraction layer between + the window system and low level input and graphics. +* [Optimizing Chrome Web UIs](optimizing_web_uis.md) - Notes on making webuis + more performant +* [ES6 Support in Chromium](es6_chromium.md) - Implementation of ECMAScript6 + features in Chromium + +### Testing +* [Running and Debugging Layout Tests](testing/layout_tests.md) +* [Writing Layout Tests](testing/writing_layout_tests.md) - Layout Tests using + `content_shell` +* [Layout Test Expectations and Baselines](testing/layout_test_expectations.md) - + Setting expected results of layout tests. +* [Layout Tests Tips](testing/layout_tests_tips.md) - Best practices for Layout + Tests +* [Layout Tests with Manual Fallback](testing/layout_tests_with_manual_fallback.md) - + Writing tests that simulate manual interventions +* [Extending the Layout Test Framework](how_to_extend_layout_test_framework.md) +* [Fixing Layout Test Flakiness](testing/identifying_tests_that_depend_on_order.md) - + Diagnosing and fixing layout test flakiness due to ordering dependencies. +* [Running Layout Tests using `content_shell`](testing/layout_tests_in_content_shell.md) - + Running layout tests by hand. +* [Testing Browser Dialogs](testing/test_browser_dialog.md) - Using + TestBrowserDialog +* [Web Platform Tests](testing/web_platform_tests.md) - Shared tests across + browser vendors +* [Using Breakpad with `content_shell`](testing/using_breakpad_with_content_shell.md) - + Capture stack traces on layout test crashes without an attached debugger +* [Test Descriptions](test_descriptions.md) - Unit test targets that can be + built, with associated desciptions. +* [IPC Fuzzer](ipc_fuzzer.md) - Fuzz testing of Chromium IPC interfaces. + +### Misc Linux-Specific Docs +* [Linux Proxy Config](linux_proxy_config.md) - Network proxy sources on Linux +* [Debugging SSL on Linux](linux_debugging_ssl.md) - Tips on debugging SSL + code in Linux +* [Linux Cert Managment](linux_cert_management.md) - Managing X.509 + Certificates in Linux +* [Tips for Debugging on Linux](linux_debugging.md) +* [Linux GTK Theme Integration](linux_gtk_theme_integration.md) - Having + Chrome match the GTK+ theme. +* [Gtk vs ViewsGtk](gtk_vs_views_gtk.md) - Notes on when to use Gtk vs + ViewsGtk +* [Browser Plugins on Linux](linux_plugins.md) - A collection of links to + information on how browser plugins work on Linux +* [Linux Crash Dumping](linux_crash_dumping.md) - How Breakpad uploads crash + reports to Google crash servers. +* [Linux Minidump to Core](linux_minidump_to_core.md) - How to convert a + Breakpad-generated minidump files to a core file readable by most debuggersx +* [Linux Sandbox IPC](linux_sandbox_ipc.md) - The lower level UPC system used + to route requests from the bottom of the call stack up into the browser. +* [Linux Dev Build as Default Browser](linux_dev_build_as_default_browser.md) - + How to configure a Dev build of Chrome as the default browser in Linux. +* [Linux Chromium Packages](linux_chromium_packages.md) - Packages of Chromium + browser (not Chrome) provided by some Linux distributions. +* [`seccomp` Sandbox Crash Dumping](seccomp_sandbox_crash_dumping.md) - Notes + on crash dumping a process running in a seccomp sandbox. +* [Linux Password Storage](linux_password_storage.md) - Keychain integrations + between Chromium and Linux. +* [Linux Sublime Development](linux_sublime_dev.md) - Using Sublime as an IDE + for Chromium development on Linux. +* [Building and Debugging GTK](linux_building_debug_gtk.md) - Building + Chromium against GTK using lower optimization levels and/or more debugging + symbols. +* [Debugging GTK](linux_debugging_gtk.md) - Using the GTK Debug packages and + related tools. +* [Chroot Notes](using_a_linux_chroot.md) - Setting up a chroot to work around + libfreetype differences in some versions of Linux. +* [Linux Sandboxing](linux_sandboxing.md) - The Linux multi-process model to + isolate browser components with different privileges. +* [Zygote Process](linux_zygote.md) - How the Linux Zygote process, used to + spawn new processes, works. +* [Running Layout Tests on Linux](layout_tests_linux.md) - Linux-specific + instructions for running layout tests. +* [Linux Sysroot Images](linux_sysroot.md) - How builds use libraries on Linux +* [`msttcorefonts` on Mandriva](mandriva_msttcorefonts.md) - Getting fonts + needed to build Chrome that are not available for Mandriva +* [Linux Hardware Video Decoding](linux_hw_video_decode.md) - Enabling + hardware video decode codepaths on Linux + +### Misc MacOS-Specific Docs +* [Using CCache on Mac](ccache_mac.md) - Speed up builds on Mac using ccache + with clang/ninja +* [Cocoa tips and tricks](cocoa_tips_and_tricks.md) - A collection of idioms + used when writing Cocoa views and controllers +* [MacViews Release Plan](ui/views/macviews_release.md) + +### Misc Windows-Specific Docs +* [Handling cygwin rebaseall failures](cygwin_dll_remapping_failure.md) +* [Hacking on ANGLE in Chromium](angle_in_chromium.md) - OpenGL ES 2.0 built + on top of DirectX +* [Retrieveing Code Analysis Warnings](retrieving_code_analysis_warnings.md) - + How to retrieve and summarize the warnings generated by Microsoft VC++'s + `/analyze` compile option. +* [Windows Split DLLs](windows_split_dll.md) - Splitting `chrome.dll` into + multiple dlls to work around toolchain limitations on Windows. + +### Misc Android-Specific Docs +* [Google Play Services in Chrome for Android](google_play_services.md) +* [Accessing C++ Enums in Java](android_accessing_cpp_enums_in_java.md) - How + to use C++-defined enums in Java code +* [Profiling Content Shell on Android](profiling_content_shell_on_android.md) - + Setting up profiling for `content_shell` on Android +* [Working Remotely with Android](working_remotely_with_android.md) - Building + on a remote machine for an Android device connected to your local machine +* [Using FindBugs for Android](use_find_bugs_for_android.md) - Using the open + source static analysis tool findbugs on the Java code. +* [Android Test Instructions](android_test_instructions.md) - Running a build + on an Android device or emulator. +* [Android Debugging](android_debugging_instructions.md) - Tools and tips for + how to debug Java and/or C/C++ code running on Android. +* [Android Logging](android_logging.md) - How Chrome's logging API works with + `android.util.Log` on Android, and usage guidelines. +* [Chromoting Android Hacking](chromoting_android_hacking.md) - Viewing the + logs and debugging the Chrome Remote Desktop Android client. + +### Misc iOS-Specific Docs +* [Continuous Build and Test Infrastructure for Chromium for iOS](ios/infra.md) +* [Opening links in Chrome for iOS](ios/opening_links.md) - How to have your + iOS app open links in Chrome. +* [User Agent in Chrome for iOS](ios/user_agent.md) - Notes on User Agent + strings using Chrome for iOS. + +### Media +* [Audio Focus Handling](media/audio_focus.md) - How multiple MediaSession + audio streams interact +* [Autoplay of HTMLMediaElements](media/autoplay.md) - How HTMLMediaElements + are autoplayed. +* [Piranha Plant](piranha_plant.md) - Future architecture of MediaStreams + +### Accessibility +* [Accessibility Overview](accessibility/overview.md) - Overview of + accessibility concerns and approaches in Chromium. +* [Accessibility Tests](accessibility/tests.md) - Where to find + accessibility-related tests in the codebase. +* [ChromeVox on Chrome OS](accessibility/chromevox.md) - Enabling spoken + feedback (ChromeVox) on Chrome OS. +* [ChromeVox on Desktop Linux](accessibility/chromevox_on_desktop_linux.md) - + Enabling spoken feedback (ChromeVox) on desktop Linux. +* [BRLTTY in Chrome OS](accessibility/brltty.md) - Chrome OS integration with + BRLTTY to support refreshable braille displays +* [PATTS on Chrome OS](accessibility/patts.md) - Notes on the PATTS speech + sythesis engine used on Chrome OS +* [VoiceOver](ios/voiceover.md) - Using Apple's VoiceOver feature with + Chromium on iOS. + +### Memory Infrastructure Timeline Profiling (MemoryInfra) +* [Overview](memory-infra/README.md) +* [GPU Profiling](memory-infra/probe-gpu.md) +* [Adding Tracing to a Component](memory-infra/adding_memory_infra_tracing.md) +* [Enabling Startup Tracing](memory-infra/memory_infra_startup_tracing.md) +* [Memory Usage in CC](memory-infra/probe-cc.md) +* [Memory Benchmarks](memory-infra/memory_benchmarks.md) +* [Heap Profiling](memory-infra/heap_profiler.md) +* [Heap Profiling Internals](memory-infra/heap_profiler_internals.md) + +### Misc +* [Useful URLs](useful_urls.md) - A collection of links to various tools and + dashboards +* [ERC IRC](erc_irc.md) - Using ERC in EMACS to access IRC +* [Kiosk Mode](kiosk_mode.md) - Simulating kiosk mode. +* [User Handle Mapping](user_handle_mapping.md) - Names of developers across + Chromium/IRC/Google +* [Documentation Best Practices](documentation_best_practices.md) +* [Documentation Guidelines](documentation_guidelines.md) +* [Shift-Based Development](shift_based_development.md) - An experiment in + handing off development of coordinated work between developers in different + time zones. +* [Chromium Browser vs Google Chrome](chromium_browser_vs_google_chrome.md) - + What's the difference between _Chromium Browser_ and _Google Chrome_? +* [Proxy Auto Config using WPAD](proxy_auto_config.md) - How WPAD servers are + used to automatically set proxy settings. +* [Installing Chromium OS on VMWare](installation_at_vmware.md) - How to + install Chromium OS on VMWare. + +### Probably Obsolete +* [Old ChromeOS build instructions](old_chromeos_build_instructions.md) +* [TPM Quick Reference](tpm_quick_ref.md) - Trusted Platform Module notes. +* [System Hardening Features](system_hardening_features.md) - A list of + current and planned Chrome OS security features. +* [Browser View Resizer](browser_view_resizer.md) - Design doc for making + browser window resizing easier on Windows. +* [WebView Policies](webview_policies.md) +* [Linux Profiling](linux_profiling.md) - How to profile Chromium on Linux +* [Linux Graphics Pipeline](linux_graphics_pipeline.md) +* [Linux `SUID` Sandbox](linux_suid_sandbox.md) - Sandboxing renderers using a + SUID binary on Linux +* [Linux `SUID` Sandbox Development](linux_suid_sandbox_development.md) - + Development on the above system. +* [Linux PID Namespace Support](linux_pid_namespace_support.md) +* [Vanilla msysgit workflow](vanilla_msysgit_workflow.md) - A workflow for + using mostly vanilla git on Windows. +* [Old Chromoting Build Instructions](old_chromoting_build_instructions.md) +* [Old Options](chrome_settings.md) - Pre-Material Design chrome://settings + notes.
diff --git a/extensions/browser/api/networking_private/networking_private_api.cc b/extensions/browser/api/networking_private/networking_private_api.cc index e99b5fb..eee7356 100644 --- a/extensions/browser/api/networking_private/networking_private_api.cc +++ b/extensions/browser/api/networking_private/networking_private_api.cc
@@ -656,6 +656,11 @@ ExtensionFunction::ResponseAction NetworkingPrivateStartActivateFunction::Run() { + if (!HasPrivateNetworkingAccess(extension(), source_context_type(), + source_url())) { + return RespondNow(Error(kPrivateOnlyError)); + } + std::unique_ptr<private_api::StartActivate::Params> params = private_api::StartActivate::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params); @@ -958,6 +963,11 @@ ExtensionFunction::ResponseAction NetworkingPrivateUnlockCellularSimFunction::Run() { + if (!HasPrivateNetworkingAccess(extension(), source_context_type(), + source_url())) { + return RespondNow(Error(kPrivateOnlyError)); + } + std::unique_ptr<private_api::UnlockCellularSim::Params> params = private_api::UnlockCellularSim::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params); @@ -992,6 +1002,11 @@ ExtensionFunction::ResponseAction NetworkingPrivateSetCellularSimStateFunction::Run() { + if (!HasPrivateNetworkingAccess(extension(), source_context_type(), + source_url())) { + return RespondNow(Error(kPrivateOnlyError)); + } + std::unique_ptr<private_api::SetCellularSimState::Params> params = private_api::SetCellularSimState::Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(params);
diff --git a/extensions/common/api/_permission_features.json b/extensions/common/api/_permission_features.json index 411f8cd..80c5489a 100644 --- a/extensions/common/api/_permission_features.json +++ b/extensions/common/api/_permission_features.json
@@ -300,7 +300,7 @@ "extension_types": ["extension", "platform_app"] }, "networking.onc": [{ - "channel": "dev", + "channel": "stable", "extension_types": ["platform_app"], "platforms": ["chromeos"], "session_types": ["kiosk.autolaunched"]
diff --git a/extensions/common/api/networking_onc.idl b/extensions/common/api/networking_onc.idl new file mode 100644 index 0000000..6e37363 --- /dev/null +++ b/extensions/common/api/networking_onc.idl
@@ -0,0 +1,1014 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// <p> +// The <code>chrome.networking.onc</code> API is used for configuring +// network connections (Cellular, Ethernet, VPN, WiFi or WiMAX). +// This API is available in Chrome OS kiosk sessions. +// </p> +// <p> +// Network connection configurations are specified following +// <a href="https://chromium.googlesource.com/chromium/src/+/master/components/onc/docs/onc_spec.md"> +// Open Network Configuration (ONC)</a> specification. +// </p> +// <p> +// <b>NOTE</b>: Most dictionary properties and enum values use UpperCamelCase +// to match the ONC specification instead of the JavaScript lowerCamelCase +// convention. +// </p> +namespace networking.onc { + enum ActivationStateType { + Activated, Activating, NotActivated, PartiallyActivated + }; + + enum CaptivePortalStatus { + Unknown, Offline, Online, Portal, ProxyAuthRequired + }; + + enum ConnectionStateType { + Connected, Connecting, NotConnected + }; + + enum DeviceStateType { + // Device is available but not initialized. + Uninitialized, + // Device is initialized but not enabled. + Disabled, + // Enabled state has been requested but has not completed. + Enabling, + // Device is enabled. + Enabled, + // Device is prohibited. + Prohibited + }; + + enum IPConfigType { + DHCP, Static + }; + + enum NetworkType { + All, Cellular, Ethernet, VPN, Wireless, WiFi, WiMAX + }; + + enum ProxySettingsType { + Direct, Manual, PAC, WPAD + }; + + enum ClientCertificateType { + Ref, Pattern + }; + + dictionary ManagedBoolean { + // The active value currently used by the network configuration manager + // (e.g. Shill). + boolean? Active; + // The source from which the effective property value was determined. + DOMString? Effective; + // The property value provided by the user policy. + boolean? UserPolicy; + // The property value provided by the device policy. + boolean? DevicePolicy; + // The property value set by the logged in user. Only provided if + // |UserEditable| is <code>true</code>. + boolean? UserSetting; + // The value set for all users of the device. Only provided if + // |DeviceEditiable| is <code>true</code>. + boolean? SharedSetting; + // Whether a UserPolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? UserEditable; + // Whether a DevicePolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? DeviceEditable; + }; + + dictionary ManagedLong { + // The active value currently used by the network configuration manager + // (e.g. Shill). + long? Active; + // The source from which the effective property value was determined. + DOMString? Effective; + // The property value provided by the user policy. + long? UserPolicy; + // The property value provided by the device policy. + long? DevicePolicy; + // The property value set by the logged in user. Only provided if + // |UserEditable| is <code>true</code>. + long? UserSetting; + // The value set for all users of the device. Only provided if + // |DeviceEditiable| is <code>true</code>. + long? SharedSetting; + // Whether a UserPolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? UserEditable; + // Whether a DevicePolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? DeviceEditable; + }; + + dictionary ManagedDOMString { + // The active value currently used by the network configuration manager + // (e.g. Shill). + DOMString? Active; + // The source from which the effective property value was determined. + DOMString? Effective; + // The property value provided by the user policy. + DOMString? UserPolicy; + // The property value provided by the device policy. + DOMString? DevicePolicy; + // The property value set by the logged in user. Only provided if + // |UserEditable| is <code>true</code>. + DOMString? UserSetting; + // The value set for all users of the device. Only provided if + // |DeviceEditiable| is <code>true</code>. + DOMString? SharedSetting; + // Whether a UserPolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? UserEditable; + // Whether a DevicePolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? DeviceEditable; + }; + + dictionary ManagedDOMStringList { + // The active value currently used by the network configuration manager + // (e.g. Shill). + DOMString[]? Active; + // The source from which the effective property value was determined. + DOMString? Effective; + // The property value provided by the user policy. + DOMString[]? UserPolicy; + // The property value provided by the device policy. + DOMString[]? DevicePolicy; + // The property value set by the logged in user. Only provided if + // |UserEditable| is <code>true</code>. + DOMString[]? UserSetting; + // The value set for all users of the device. Only provided if + // |DeviceEditiable| is <code>true</code>. + DOMString[]? SharedSetting; + // Whether a UserPolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? UserEditable; + // Whether a DevicePolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? DeviceEditable; + }; + + dictionary ManagedIPConfigType { + // The active value currently used by the network configuration manager + // (e.g. Shill). + IPConfigType? Active; + // The source from which the effective property value was determined. + DOMString? Effective; + // The property value provided by the user policy. + IPConfigType? UserPolicy; + // The property value provided by the device policy. + IPConfigType? DevicePolicy; + // The property value set by the logged in user. Only provided if + // |UserEditable| is <code>true</code>. + IPConfigType? UserSetting; + // The value set for all users of the device. Only provided if + // |DeviceEditiable| is <code>true</code>. + IPConfigType? SharedSetting; + // Whether a UserPolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? UserEditable; + // Whether a DevicePolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? DeviceEditable; + }; + + dictionary ManagedProxySettingsType { + // The active value currently used by the network configuration manager + // (e.g. Shill). + ProxySettingsType? Active; + // The source from which the effective property value was determined. + DOMString? Effective; + // The property value provided by the user policy. + ProxySettingsType? UserPolicy; + // The property value provided by the device policy. + ProxySettingsType? DevicePolicy; + // The property value set by the logged in user. Only provided if + // |UserEditable| is <code>true</code>. + ProxySettingsType? UserSetting; + // The value set for all users of the device. Only provided if + // |DeviceEditiable| is <code>true</code>. + ProxySettingsType? SharedSetting; + // Whether a UserPolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? UserEditable; + // Whether a DevicePolicy for the property exists and allows the property to + // be edited (i.e. the policy set recommended property value). + // Defaults to <code>false</code>. + boolean? DeviceEditable; + }; + + // Sub-dictionary types. + + dictionary CellularProviderProperties { + // The operator name. + DOMString Name; + // Cellular network ID as a simple concatenation of the network's + // MCC (Mobile Country Code) and MNC (Mobile Network Code). + DOMString Code; + // The two-letter country code. + DOMString? Country; + }; + + dictionary IssuerSubjectPattern { + // If set, the value against which to match the certificate subject's + // common name. + DOMString? CommonName; + // If set, the value against which to match the certificate subject's + // common location. + DOMString? Locality; + // If set, the value against which to match the certificate subject's + // organizations. At least one organization should match the value. + DOMString? Organization; + // If set, the value against which to match the certificate subject's + // organizational units. At least one organizational unit should match the + // value. + DOMString? OrganizationalUnit; + }; + + dictionary CertificatePattern { + // List of URIs to which the user can be directed in case no certificates + // that match this pattern are found. + DOMString[]? EnrollmentURI; + // If set, pattern against which X.509 issuer settings should be matched. + IssuerSubjectPattern? Issuer; + // List of certificate issuer CA certificates. A certificate must be signed + // by one of them in order to match this pattern. + DOMString[]? IssuerCARef; + // If set, pattern against which X.509 subject settings should be matched. + IssuerSubjectPattern? Subject; + }; + + dictionary EAPProperties { + DOMString? AnonymousIdentity; + CertificatePattern? ClientCertPattern; + DOMString? ClientCertRef; + ClientCertificateType ClientCertType; + DOMString? Identity; + DOMString? Inner; + DOMString Outer; + DOMString? Password; + boolean? SaveCredentials; + DOMString[]? ServerCARefs; + boolean? UseProactiveKeyCaching; + boolean? UseSystemCAs; + }; + + dictionary FoundNetworkProperties { + // Network availability. + DOMString Status; + // Network ID. + DOMString NetworkId; + // Access technology used by the network. + DOMString Technology; + // The network operator's short-format name. + DOMString? ShortName; + // The network operator's long-format name. + DOMString? LongName; + }; + + dictionary IPConfigProperties { + // Gateway address used for the IP configuration. + DOMString? Gateway; + // The IP address for a connection. Can be IPv4 or IPv6 address, depending + // on value of <code>Type</code>. + DOMString? IPAddress; + // Array of addresses used for name servers. + DOMString[]? NameServers; + // The routing prefix. + long? RoutingPrefix; + // The IP configuration type. Can be <code>IPv4</code> or <code>IPv6</code>. + DOMString? Type; + // The URL for WEb Proxy Auto-Discovery, as reported over DHCP. + DOMString? WebProxyAutoDiscoveryUrl; + }; + + dictionary ManagedIPConfigProperties { + // See $(ref:IPConfigProperties.Gateway). + ManagedDOMString? Gateway; + // See $(ref:IPConfigProperties.IPAddress). + ManagedDOMString? IPAddress; + // See $(ref:IPConfigProperties.NameServers). + ManagedDOMStringList? NameServers; + // See $(ref:IPConfigProperties.RoutingPrefix). + ManagedLong? RoutingPrefix; + // See $(ref:IPConfigProperties.Type). + ManagedDOMString? Type; + // See $(ref:IPConfigProperties.WebProxyAutoDiscoveryUrl). + ManagedDOMString? WebProxyAutoDiscoveryUrl; + }; + + dictionary PaymentPortal { + // The HTTP method to use for the payment portal. + DOMString Method; + // The post data to send to the payment portal. Ignored unless + // <code>Method</code> is <code>POST</code>. + DOMString? PostData; + // The payment portal URL. + DOMString? Url; + }; + + dictionary ProxyLocation { + // The proxy IP address host. + DOMString Host; + // The port to use for the proxy. + long Port; + }; + + dictionary ManagedProxyLocation { + // See $(ref:ProxyLocation.Host). + ManagedDOMString Host; + // See $(ref:ProxyLocation.Port). + ManagedLong Port; + }; + + [noinline_doc] dictionary ManualProxySettings { + // Settings for HTTP proxy. + ProxyLocation? HTTPProxy; + // Settings for secure HTTP proxy. + ProxyLocation? SecureHTTPProxy; + // Settings for FTP proxy. + ProxyLocation? FTPProxy; + // Settings for SOCKS proxy. + ProxyLocation? SOCKS; + }; + + dictionary ManagedManualProxySettings { + // See $(ref:ManualProxySettings.HTTPProxy). + ManagedProxyLocation? HTTPProxy; + // See $(ref:ManualProxySettings.SecureHTTPProxy). + ManagedProxyLocation? SecureHTTPProxy; + // See $(ref:ManualProxySettings.FTPProxy). + ManagedProxyLocation? FTPProxy; + // See $(ref:ManualProxySettings.SOCKS). + ManagedProxyLocation? SOCKS; + }; + + [noinline_doc] dictionary ProxySettings { + // The type of proxy settings. + ProxySettingsType Type; + // Manual proxy settings - used only for <code>Manual</code> proxy settings. + ManualProxySettings? Manual; + // Domains and hosts for which manual proxy settings are excluded. + DOMString[]? ExcludeDomains; + // URL for proxy auto-configuration file. + DOMString? PAC; + }; + + dictionary ManagedProxySettings { + // See $(ref:ProxySettings.Type). + ManagedProxySettingsType Type; + // See $(ref:ProxySettings.Manual). + ManagedManualProxySettings? Manual; + // See $(ref:ProxySettings.ExcludeDomains). + ManagedDOMStringList? ExcludeDomains; + // See $(ref:ProxySettings.PAC). + ManagedDOMString? PAC; + }; + + dictionary SIMLockStatus { + // The status of SIM lock - possible values are <code>'sim-pin'</code>, + // <code>'sim-puk'</code> and <code>''</code>. + DOMString LockType; + // Whether SIM lock is enabled. + boolean LockEnabled; + // Number of PIN lock tries allowed before PUK is required to unlock the + // SIM. + long? RetriesLeft; + }; + + dictionary ThirdPartyVPNProperties { + // ID of the third-party VPN provider extension. + DOMString ExtensionID; + // The VPN provider name. + DOMString? ProviderName; + }; + + dictionary ManagedThirdPartyVPNProperties { + // See $(ref:ThirdPartyVPNProperties.ExtensionID). + ManagedDOMString ExtensionID; + // See $(ref:ThirdPartyVPNProperties.ProviderName). + DOMString? ProviderName; + }; + + // Network type dictionary types. + + [noinline_doc] dictionary CellularProperties { + // Whether the cellular network should be connected automatically (when + // in range). + boolean? AutoConnect; + // The cellular network activation type. + DOMString? ActivationType; + // Carrier account activation state. + ActivationStateType? ActivationState; + // Whether roaming is allowed for the network. + boolean? AllowRoaming; + // The name of the carrier for which the cellular device is configured. + DOMString? Carrier; + // Cellular device technology family - <code>CDMA</code> or + // <code>GSM</code>. + DOMString? Family; + // The firmware revision loaded in the cellular modem. + DOMString? FirmwareRevision; + // The list of networks found during the most recent network scan. + FoundNetworkProperties[]? FoundNetworks; + // The cellular modem hardware revision. + DOMString? HardwareRevision; + // Information about the operator that issued the SIM card currently + // installed in the modem. + CellularProviderProperties? HomeProvider; + // The cellular modem manufacturer. + DOMString? Manufacturer; + // The cellular modem model ID. + DOMString? ModelID; + // If the modem is registered on a network, the network technology + // currently in use. + DOMString? NetworkTechnology; + // Online payment portal a user can use to sign-up for or modify a mobile + // data plan. + PaymentPortal? PaymentPortal; + // The revision of the Preferred Roaming List loaded in the modem. + long? PRLVersion; + // The roaming state of the cellular modem on the current network. + DOMString? RoamingState; + // Information about the operator on whose network the modem is currently + // registered. + CellularProviderProperties? ServingOperator; + // The state of SIM lock for GSM family networks. + SIMLockStatus? SIMLockStatus; + // Whether a SIM card is present. + boolean? SIMPresent; + // The current network signal strength. + long? SignalStrength; + // Whether the cellular network supports scanning. + boolean? SupportNetworkScan; + // A list of supported carriers. + DOMString[]? SupportedCarriers; + }; + + dictionary ManagedCellularProperties { + // See $(ref:CellularProperties.AutoConnect). + ManagedBoolean? AutoConnect; + // See $(ref:CellularProperties.ActivationType). + DOMString? ActivationType; + // See $(ref:CellularProperties.ActivationState). + ActivationStateType? ActivationState; + // See $(ref:CellularProperties.AllowRoaming). + boolean? AllowRoaming; + // See $(ref:CellularProperties.Carrier). + ManagedDOMString? Carrier; + // See $(ref:CellularProperties.Family). + DOMString? Family; + // See $(ref:CellularProperties.FirmwareRevision). + DOMString? FirmwareRevision; + // See $(ref:CellularProperties.FoundNetworks). + FoundNetworkProperties[]? FoundNetworks; + // See $(ref:CellularProperties.HardwareRevision). + DOMString? HardwareRevision; + // See $(ref:CellularProperties.HomeProvider). + CellularProviderProperties[]? HomeProvider; + // See $(ref:CellularProperties.Manufacturer). + DOMString? Manufacturer; + // See $(ref:CellularProperties.ModelID). + DOMString? ModelID; + // See $(ref:CellularProperties.NetworkTechnology). + DOMString? NetworkTechnology; + // See $(ref:CellularProperties.PaymentPortal). + PaymentPortal? PaymentPortal; + // See $(ref:CellularProperties.PRLVersion). + long? PRLVersion; + // See $(ref:CellularProperties.RoamingState). + DOMString? RoamingState; + // See $(ref:CellularProperties.ServingOperator). + CellularProviderProperties? ServingOperator; + // See $(ref:CellularProperties.SIMLockStatus). + SIMLockStatus? SIMLockStatus; + // See $(ref:CellularProperties.SIMPresent). + boolean? SIMPresent; + // See $(ref:CellularProperties.SignalStrength). + long? SignalStrength; + // See $(ref:CellularProperties.SupportNetworkScan). + boolean? SupportNetworkScan; + // See $(ref:CellularProperties.SupportedCarriers). + DOMString[]? SupportedCarriers; + }; + + dictionary CellularStateProperties { + // See $(ref:CellularProperties.ActivationState). + ActivationStateType? ActivationState; + // See $(ref:CellularProperties.NetworkTechnology). + DOMString? NetworkTechnology; + // See $(ref:CellularProperties.RoamingState). + DOMString? RoamingState; + // See $(ref:CellularProperties.SIMPresent). + boolean? SIMPresent; + // See $(ref:CellularProperties.SignalStrength). + long? SignalStrength; + }; + + dictionary EthernetProperties { + // Whether the Ethernet network should be connected automatically. + boolean? AutoConnect; + // The authentication used by the Ethernet network. Possible values are + // <code>None</code> and <code>8021X</code>. + DOMString? Authentication; + // Network's EAP settings. Required for 8021X authentication. + EAPProperties? EAP; + }; + + dictionary ManagedEthernetProperties { + // See $(ref:EthernetProperties.AutoConnect). + ManagedBoolean? AutoConnect; + // See $(ref:EthernetProperties.Authentication). + ManagedDOMString? Authentication; + }; + + dictionary EthernetStateProperties { + // See $(ref:EthernetProperties.Authentication). + DOMString Authentication; + }; + + dictionary VPNProperties { + // Whether the VPN network should be connected automatically. + boolean? AutoConnect; + // The VPN host. + DOMString? Host; + // The VPN type. + DOMString? Type; + }; + + dictionary ManagedVPNProperties { + // See $(ref:VPNProperties.AutoConnect). + ManagedBoolean? AutoConnect; + // See $(ref:VPNProperties.Host). + ManagedDOMString? Host; + // See $(ref:VPNProperties.Type). + ManagedDOMString Type; + }; + + dictionary VPNStateProperties { + // See $(ref:VPNProperties.Type). + DOMString Type; + }; + + [noinline_doc] dictionary WiFiProperties { + // Whether ARP polling of default gateway is allowed. Defaults to true. + boolean? AllowGatewayARPPolling; + // Whether the WiFi network should be connected automatically when in range. + boolean? AutoConnect; + // The BSSID of the associated access point.. + DOMString? BSSID; + // The network EAP properties. Required for <code>WEP-8021X</code> and + // <code>WPA-EAP</code> networks. + EAPProperties? EAP; + // The WiFi service operating frequency in MHz. For connected networks, the + // current frequency on which the network is connected. Otherwise, the + // frequency of the best available BSS. + long? Frequency; + // Contains all operating frequency recently seen for the WiFi network. + long[]? FrequencyList; + // HEX-encoded copy of the network SSID. + DOMString? HexSSID; + // Whether the network SSID will be broadcast. + boolean? HiddenSSID; + // Signal-to-noise value (in dB) below which roaming to a new network + // should be attempted. + long? RoamThreshold; + // The network SSID. + DOMString? SSID; + // The network security type. + DOMString? Security; + // The network signal strength. + long? SignalStrength; + }; + + dictionary ManagedWiFiProperties { + // See $(ref:WiFiProperties.AllowGatewayARPPolling). + ManagedBoolean? AllowGatewayARPPolling; + // See $(ref:WiFiProperties.AutoConnect). + ManagedBoolean? AutoConnect; + // See $(ref:WiFiProperties.BSSID). + DOMString? BSSID; + // See $(ref:WiFiProperties.Frequency). + long? Frequency; + // See $(ref:WiFiProperties.FrequencyList). + long[]? FrequencyList; + // See $(ref:WiFiProperties.HexSSID). + ManagedDOMString? HexSSID; + // See $(ref:WiFiProperties.HiddenSSID). + ManagedBoolean? HiddenSSID; + // See $(ref:WiFiProperties.RoamThreshold). + ManagedLong? RoamThreshold; + // See $(ref:WiFiProperties.SSID). + ManagedDOMString? SSID; + // See $(ref:WiFiProperties.Security). + ManagedDOMString Security; + // See $(ref:WiFiProperties.SignalStrength). + long? SignalStrength; + }; + + dictionary WiFiStateProperties { + // See $(ref:WiFiProperties.BSSID). + DOMString? BSSID; + // See $(ref:WiFiProperties.Frequency). + long? Frequency; + // See $(ref:WiFiProperties.Security). + DOMString Security; + // See $(ref:WiFiProperties.SignalStrength). + long? SignalStrength; + }; + + dictionary WiMAXProperties { + // Whether the network should be connected automatically. + boolean? AutoConnect; + // The network EAP properties. + EAPProperties? EAP; + // The network signal strength. + long? SignalStrength; + }; + + dictionary ManagedWiMAXProperties { + // See $(ref:WiMAXProperties.AutoConnect). + ManagedBoolean? AutoConnect; + // See $(ref:WiMAXProperties.SignalStrength). + long? SignalStrength; + }; + + dictionary WiMAXStateProperties { + // See $(ref:WiMAXProperties.SignalStrength). + long? SignalStrength; + }; + + dictionary NetworkConfigProperties { + // See $(ref:NetworkProperties.Cellular). + CellularProperties? Cellular; + // See $(ref:NetworkProperties.Ethernet). + EthernetProperties? Ethernet; + // See $(ref:NetworkProperties.GUID). + DOMString? GUID; + // See $(ref:NetworkProperties.IPAddressConfigType). + IPConfigType? IPAddressConfigType; + // See $(ref:NetworkProperties.Name). + DOMString? Name; + // See $(ref:NetworkProperties.NameServersConfigType). + IPConfigType? NameServersConfigType; + // See $(ref:NetworkProperties.Priority). + long? Priority; + // See $(ref:NetworkProperties.Type). + NetworkType? Type; + // See $(ref:NetworkProperties.VPN). + VPNProperties? VPN; + // See $(ref:NetworkProperties.WiFi). + WiFiProperties? WiFi; + // See $(ref:NetworkProperties.WiMAX). + WiMAXProperties? WiMAX; + }; + + [noinline_doc] + dictionary NetworkProperties { + // For cellular networks, cellular network properties. + CellularProperties? Cellular; + // Whether the network is connectable. + boolean? Connectable; + // The network's current connection state. + ConnectionStateType? ConnectionState; + // The last recorded network error state. + DOMString? ErrorState; + // For Ethernet networks, the Ethernet network properties. + EthernetProperties? Ethernet; + // The network GUID. + DOMString GUID; + // The network's IP address configuration type. + IPConfigType? IPAddressConfigType; + // The network's IP configuration. + IPConfigProperties[]? IPConfigs; + // The network's MAC address. + DOMString? MacAddress; + // A user friendly network name. + DOMString? Name; + // The IP configuration type for the name servers used by the network. + IPConfigType? NameServersConfigType; + // The network priority. + long? Priority; + // The network's proxy settings. + ProxySettings? ProxySettings; + // For a connected network, whether the network connectivity to the + // Internet is limited, e.g. if the network is behind a portal, or a + // cellular network is not activated. + boolean? RestrictedConnectivity; + // The network's static IP configuration. + IPConfigProperties? StaticIPConfig; + // IP configuration that was received from the DHCP server before applying + // static IP configuration. + IPConfigProperties? SavedIPConfig; + // Indicates whether and how the network is configured. + DOMString? Source; + // The network type. + NetworkType Type; + // For VPN networks, the network VPN properties. + VPNProperties? VPN; + // For WiFi networks, the network WiFi properties. + WiFiProperties? WiFi; + // For WiMAX networks, the network WiMAX properties. + WiMAXProperties? WiMAX; + }; + + [noinline_doc] + dictionary ManagedProperties { + // See $(ref:NetworkProperties.Cellular). + ManagedCellularProperties? Cellular; + // See $(ref:NetworkProperties.Connectable). + boolean? Connectable; + // See $(ref:NetworkProperties.ConnectionState). + ConnectionStateType? ConnectionState; + // See $(ref:NetworkProperties.ErrorState). + DOMString? ErrorState; + // See $(ref:NetworkProperties.Ethernet). + ManagedEthernetProperties? Ethernet; + // See $(ref:NetworkProperties.GUID). + DOMString GUID; + // See $(ref:NetworkProperties.IPAddressConfigType). + ManagedIPConfigType? IPAddressConfigType; + // See $(ref:NetworkProperties.IPConfigs). + IPConfigProperties[]? IPConfigs; + // See $(ref:NetworkProperties.MacAddress). + DOMString? MacAddress; + // See $(ref:NetworkProperties.Name). + ManagedDOMString? Name; + // See $(ref:NetworkProperties.NameServersConfigType). + ManagedIPConfigType? NameServersConfigType; + // See $(ref:NetworkProperties.Priority). + ManagedLong? Priority; + // See $(ref:NetworkProperties.ProxySettings). + ManagedProxySettings? ProxySettings; + // See $(ref:NetworkProperties.RestrictedConnectivity). + boolean? RestrictedConnectivity; + // See $(ref:NetworkProperties.StaticIPConfig). + ManagedIPConfigProperties? StaticIPConfig; + // See $(ref:NetworkProperties.SavedIPConfig). + IPConfigProperties? SavedIPConfig; + // See $(ref:NetworkProperties.Source). + DOMString? Source; + // See $(ref:NetworkProperties.Type). + NetworkType Type; + // See $(ref:NetworkProperties.VPN). + ManagedVPNProperties? VPN; + // See $(ref:NetworkProperties.WiFi). + ManagedWiFiProperties? WiFi; + // See $(ref:NetworkProperties.WiMAX). + ManagedWiMAXProperties? WiMAX; + }; + + dictionary NetworkStateProperties { + // See $(ref:NetworkProperties.Cellular). + CellularStateProperties? Cellular; + // See $(ref:NetworkProperties.Connectable). + boolean? Connectable; + // See $(ref:NetworkProperties.ConnectionState). + ConnectionStateType? ConnectionState; + // See $(ref:NetworkProperties.Ethernet). + EthernetStateProperties? Ethernet; + // See $(ref:NetworkProperties.ErrorState). + DOMString? ErrorState; + // See $(ref:NetworkProperties.GUID). + DOMString GUID; + // See $(ref:NetworkProperties.Name). + DOMString? Name; + // See $(ref:NetworkProperties.Priority). + long? Priority; + // See $(ref:NetworkProperties.Source). + DOMString? Source; + // See $(ref:NetworkProperties.Type). + NetworkType Type; + // See $(ref:NetworkProperties.VPN). + VPNStateProperties? VPN; + // See $(ref:NetworkProperties.WiFi). + WiFiStateProperties? WiFi; + // See $(ref:NetworkProperties.WiMAX). + WiMAXStateProperties? WiMAX; + }; + + dictionary DeviceStateProperties { + // Set if the device is enabled. True if the device is currently scanning. + boolean? Scanning; + + // Set to the SIM lock type if the device type is Cellular and the device + // is locked. + DOMString? SimLockType; + + // Set to the SIM present state if the device type is Cellular. + boolean? SimPresent; + + // The current state of the device. + DeviceStateType State; + + // The network type associated with the device (Cellular, Ethernet, WiFi, or + // WiMAX). + NetworkType Type; + }; + + dictionary NetworkFilter { + // The type of networks to return. + NetworkType networkType; + + // If true, only include visible (physically connected or in-range) + // networks. Defaults to 'false'. + boolean? visible; + + // If true, only include configured (saved) networks. Defaults to 'false'. + boolean? configured; + + // Maximum number of networks to return. Defaults to 1000 if unspecified. + // Use 0 for no limit. + long? limit; + }; + + dictionary GlobalPolicy { + // If true, only policy networks may auto connect. Defaults to false. + boolean? AllowOnlyPolicyNetworksToAutoconnect; + + // If true, only policy networks may be connected to and no new networks may + // be added or configured. Defaults to false. + boolean? AllowOnlyPolicyNetworksToConnect; + }; + + callback VoidCallback = void(); + callback BooleanCallback = void(boolean result); + callback StringCallback = void(DOMString result); + callback GetPropertiesCallback = void(NetworkProperties result); + callback GetManagedPropertiesCallback = void(ManagedProperties result); + callback GetStatePropertiesCallback = void(NetworkStateProperties result); + callback GetNetworksCallback = void(NetworkStateProperties[] result); + callback GetDeviceStatesCallback = void(DeviceStateProperties[] result); + callback GetEnabledNetworkTypesCallback = void(NetworkType[] result); + callback CaptivePortalStatusCallback = void(CaptivePortalStatus result); + callback GetGlobalPolicyCallback = void(GlobalPolicy result); + + interface Functions { + // Gets all the properties of the network with id networkGuid. Includes all + // properties of the network (read-only and read/write values). + // |networkGuid|: The GUID of the network to get properties for. + // |callback|: Called with the network properties when received. + static void getProperties(DOMString networkGuid, + GetPropertiesCallback callback); + + // Gets the merged properties of the network with id networkGuid from the + // sources: User settings, shared settings, user policy, device policy and + // the currently active settings. + // |networkGuid|: The GUID of the network to get properties for. + // |callback|: Called with the managed network properties when received. + static void getManagedProperties(DOMString networkGuid, + GetManagedPropertiesCallback callback); + + // Gets the cached read-only properties of the network with id networkGuid. + // This is meant to be a higher performance function than + // $(ref:getProperties), which requires a round trip to query the networking + // subsystem. The following properties are returned for all networks: GUID, + // Type, Name, WiFi.Security. Additional properties are provided for visible + // networks: ConnectionState, ErrorState, WiFi.SignalStrength, + // Cellular.NetworkTechnology, Cellular.ActivationState, + // Cellular.RoamingState. + // |networkGuid|: The GUID of the network to get properties for. + // |callback|: Called immediately with the network state properties. + static void getState(DOMString networkGuid, + GetStatePropertiesCallback callback); + + // Sets the properties of the network with id networkGuid. + // <b> + // In kiosk sessions, calling this method on a shared network will fail. + // </b> + // |networkGuid|: The GUID of the network to set properties for. + // |properties|: The properties to set. + // |callback|: Called when the operation has completed. + static void setProperties(DOMString networkGuid, + NetworkConfigProperties properties, + optional VoidCallback callback); + + // Creates a new network configuration from properties. If a matching + // configured network already exists, this will fail. Otherwise returns the + // GUID of the new network. + // |shared|: <p> + // If <code>true</code>, share this network configuration with + // other users. + // </p> + // <p> + // <b>This option is exposed only to Chrome's Web UI.</b> + // When called by apps, <code>false</code> is the only allowed value. + // </p> + // |properties|: The properties to configure the new network with. + // |callback|: Called with the GUID for the new network configuration once + // the network has been created. + static void createNetwork(boolean shared, + NetworkConfigProperties properties, + optional StringCallback callback); + + // <p> + // Forgets a network configuration by clearing any configured properties + // for the network with GUID <code>networkGuid</code>. This may also + // include any other networks with matching identifiers (e.g. WiFi SSID + // and Security). If no such configuration exists, an error will be set + // and the operation will fail. + // </p> + // <p> + // <b>In kiosk sessions, this method will not be able to forget shared + // network configurations.</b> + // </p> + // |networkGuid|: The GUID of the network to forget. + // |callback|: Called when the operation has completed. + static void forgetNetwork(DOMString networkGuid, + optional VoidCallback callback); + + // Returns a list of network objects with the same properties provided by + // $(ref:getState). A filter is provided to specify the + // type of networks returned and to limit the number of networks. Networks + // are ordered by the system based on their priority, with connected or + // connecting networks listed first. + // |filter|: Describes which networks to return. + // |callback|: Called with a dictionary of networks and their state + // properties when received. + static void getNetworks(NetworkFilter filter, + GetNetworksCallback callback); + + // Returns states of available networking devices. + // |callback|: Called with a list of devices and their state. + static void getDeviceStates(GetDeviceStatesCallback callback); + + // Enables any devices matching the specified network type. Note, the type + // might represent multiple network types (e.g. 'Wireless'). + // |networkType|: The type of network to enable. + static void enableNetworkType(NetworkType networkType); + + // Disables any devices matching the specified network type. See note for + // $(ref:enableNetworkType). + // |networkType|: The type of network to disable. + static void disableNetworkType(NetworkType networkType); + + // Requests that the networking subsystem scan for new networks and + // update the list returned by $(ref:getNetworks). This is only a + // request: the network subsystem can choose to ignore it. If the list + // is updated, then the $(ref:onNetworkListChanged) event will be fired. + static void requestNetworkScan(); + + // Starts a connection to the network with networkGuid. + // |networkGuid|: The GUID of the network to connect to. + // |callback|: Called when the connect request has been sent. Note: the + // connection may not have completed. Observe $(ref:onNetworksChanged) + // to be notified when a network state changes. + static void startConnect(DOMString networkGuid, + optional VoidCallback callback); + + // Starts a disconnect from the network with networkGuid. + // |networkGuid|: The GUID of the network to disconnect from. + // |callback|: Called when the disconnect request has been sent. See note + // for $(ref:startConnect). + static void startDisconnect(DOMString networkGuid, + optional VoidCallback callback); + + // Returns captive portal status for the network matching 'networkGuid'. + // |networkGuid|: The GUID of the network to get captive portal status for. + // |callback|: A callback function that returns the results of the query for + // network captive portal status. + static void getCaptivePortalStatus(DOMString networkGuid, + CaptivePortalStatusCallback callback); + + // Gets the global policy properties. These properties are not expected to + // change during a session. + static void getGlobalPolicy(GetGlobalPolicyCallback callback); + }; + + interface Events { + // Fired when the properties change on any of the networks. Sends a list of + // GUIDs for networks whose properties have changed. + static void onNetworksChanged(DOMString[] changes); + + // Fired when the list of networks has changed. Sends a complete list of + // GUIDs for all the current networks. + static void onNetworkListChanged(DOMString[] changes); + + // Fired when the list of devices has changed or any device state properties + // have changed. + static void onDeviceStateListChanged(); + + // Fired when a portal detection for a network completes. Sends the GUID of + // the network and the corresponding captive portal status. + static void onPortalDetectionCompleted(DOMString networkGuid, + CaptivePortalStatus status); + }; +};
diff --git a/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc b/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc index 080019e..77440ee 100644 --- a/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc +++ b/extensions/renderer/guest_view/guest_view_internal_custom_bindings.cc
@@ -48,7 +48,7 @@ namespace { -content::RenderFrame* GetRenderFrame(v8::Handle<v8::Value> value) { +content::RenderFrame* GetRenderFrame(v8::Local<v8::Value> value) { v8::Local<v8::Context> context = v8::Local<v8::Object>::Cast(value)->CreationContext(); if (context.IsEmpty()) @@ -368,8 +368,8 @@ if (map_entry == view_map.end()) return; - auto return_object = v8::Handle<v8::Object>::New(args.GetIsolate(), - *map_entry->second); + auto return_object = + v8::Local<v8::Object>::New(args.GetIsolate(), *map_entry->second); args.GetReturnValue().Set(return_object); }
diff --git a/extensions/renderer/wake_event_page.cc b/extensions/renderer/wake_event_page.cc index 2118929..a33f45a 100644 --- a/extensions/renderer/wake_event_page.cc +++ b/extensions/renderer/wake_event_page.cc
@@ -115,7 +115,7 @@ v8::Isolate* isolate = context->isolate(); v8::EscapableHandleScope handle_scope(isolate); - v8::Handle<v8::Context> v8_context = context->v8_context(); + v8::Local<v8::Context> v8_context = context->v8_context(); v8::Context::Scope context_scope(v8_context); // Cache the imported function as a hidden property on the global object of
diff --git a/gpu/command_buffer/common/capabilities.h b/gpu/command_buffer/common/capabilities.h index 29d025f7..41ab8ef 100644 --- a/gpu/command_buffer/common/capabilities.h +++ b/gpu/command_buffer/common/capabilities.h
@@ -168,6 +168,10 @@ // work around this. See https://crbug.com/449150 for an example. bool emulate_rgb_buffer_with_rgba = false; + // When true, is safe to convert a canvas from software to accelerated. + // See https://crbug.com/710029. + bool software_to_accelerated_canvas_upgrade = true; + int major_version = 2; int minor_version = 0; };
diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc index cc1c913..b34e690 100644 --- a/gpu/command_buffer/service/gles2_cmd_decoder.cc +++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -3812,6 +3812,8 @@ kGpuFeatureStatusEnabled; caps.disable_webgl_rgb_multisampling_usage = workarounds().disable_webgl_rgb_multisampling_usage; + caps.software_to_accelerated_canvas_upgrade = + !workarounds().disable_software_to_accelerated_canvas_upgrade; caps.emulate_rgb_buffer_with_rgba = workarounds().disable_gl_rgb_format;
diff --git a/gpu/config/gpu_driver_bug_list.json b/gpu/config/gpu_driver_bug_list.json index 16c85a86..fe5d306c0b 100644 --- a/gpu/config/gpu_driver_bug_list.json +++ b/gpu/config/gpu_driver_bug_list.json
@@ -1,6 +1,6 @@ { "name": "gpu driver bug list", - "version": "10.2", + "version": "10.3", "entries": [ { "id": 1, @@ -2350,6 +2350,18 @@ "features": [ "disallow_large_instanced_draw" ] + }, + { + "id": 222, + "description": "Software to Accelerated canvas update breaks Linux AMD", + "cr_bugs": [710029], + "os": { + "type": "linux" + }, + "vendor_id": "0x1002", + "features": [ + "disable_software_to_accelerated_canvas_upgrade" + ] } ], "comment": [
diff --git a/gpu/config/gpu_driver_bug_workaround_type.h b/gpu/config/gpu_driver_bug_workaround_type.h index 043acc33..2fb5625 100644 --- a/gpu/config/gpu_driver_bug_workaround_type.h +++ b/gpu/config/gpu_driver_bug_workaround_type.h
@@ -209,6 +209,8 @@ use_gpu_driver_workaround_for_testing) \ GPU_OP(DISALLOW_LARGE_INSTANCED_DRAW, \ disallow_large_instanced_draw) \ + GPU_OP(DISABLE_SOFTWARE_TO_ACCELERATED_CANVAS_UPGRADE, \ + disable_software_to_accelerated_canvas_upgrade) \ // clang-format on namespace gpu {
diff --git a/gpu/ipc/common/gpu_command_buffer_traits_multi.h b/gpu/ipc/common/gpu_command_buffer_traits_multi.h index 87a3eb1..fe721e65 100644 --- a/gpu/ipc/common/gpu_command_buffer_traits_multi.h +++ b/gpu/ipc/common/gpu_command_buffer_traits_multi.h
@@ -127,6 +127,7 @@ IPC_STRUCT_TRAITS_MEMBER(gpu_rasterization) IPC_STRUCT_TRAITS_MEMBER(chromium_image_rgb_emulation) IPC_STRUCT_TRAITS_MEMBER(emulate_rgb_buffer_with_rgba) + IPC_STRUCT_TRAITS_MEMBER(software_to_accelerated_canvas_upgrade) IPC_STRUCT_TRAITS_MEMBER(dc_layers) IPC_STRUCT_TRAITS_MEMBER(major_version)
diff --git a/gpu/ipc/service/direct_composition_surface_win.cc b/gpu/ipc/service/direct_composition_surface_win.cc index f67183c..0f4e40e5 100644 --- a/gpu/ipc/service/direct_composition_surface_win.cc +++ b/gpu/ipc/service/direct_composition_surface_win.cc
@@ -473,7 +473,28 @@ swap_chain_scale_x_ = bounds_rect.width() * 1.0f / swap_chain_size.width(); swap_chain_scale_y_ = bounds_rect.height() * 1.0f / swap_chain_size.height(); - swap_chain_->Present(first_present ? 0 : 1, 0); + if (first_present) { + swap_chain_->Present(0, 0); + + // DirectComposition can display black for a swapchain between the first + // and second time it's presented to - maybe the first Present can get + // lost somehow and it shows the wrong buffer. In that case copy the + // buffers so both have the correct contents, which seems to help. The + // first Present() after this needs to have SyncInterval > 0, or else the + // workaround doesn't help. + base::win::ScopedComPtr<ID3D11Texture2D> dest_texture; + HRESULT hr = + swap_chain_->GetBuffer(0, IID_PPV_ARGS(dest_texture.Receive())); + DCHECK(SUCCEEDED(hr)); + base::win::ScopedComPtr<ID3D11Texture2D> src_texture; + hr = swap_chain_->GetBuffer(1, IID_PPV_ARGS(src_texture.Receive())); + DCHECK(SUCCEEDED(hr)); + base::win::ScopedComPtr<ID3D11DeviceContext> context; + d3d11_device_->GetImmediateContext(context.Receive()); + context->CopyResource(dest_texture.get(), src_texture.get()); + } + + swap_chain_->Present(1, 0); frames_since_color_space_change_++;
diff --git a/gpu/ipc/service/direct_composition_surface_win_unittest.cc b/gpu/ipc/service/direct_composition_surface_win_unittest.cc index fcc34d8..8e6485b 100644 --- a/gpu/ipc/service/direct_composition_surface_win_unittest.cc +++ b/gpu/ipc/service/direct_composition_surface_win_unittest.cc
@@ -326,7 +326,10 @@ UINT last_present_count = 0; EXPECT_TRUE(SUCCEEDED(swap_chain->GetLastPresentCount(&last_present_count))); - EXPECT_EQ(1u, last_present_count); + + // One present is normal, and a second present because it's the first frame + // and the other buffer needs to be drawn to. + EXPECT_EQ(2u, last_present_count); surface->ScheduleDCLayer(params); EXPECT_EQ(gfx::SwapResult::SWAP_ACK, surface->SwapBuffers()); @@ -337,7 +340,7 @@ // It's the same image, so it should have the same swapchain. EXPECT_TRUE(SUCCEEDED(swap_chain->GetLastPresentCount(&last_present_count))); - EXPECT_EQ(1u, last_present_count); + EXPECT_EQ(2u, last_present_count); // The size of the swapchain changed, so it should be recreated. ui::DCRendererLayerParams params2(false, gfx::Rect(), 1, gfx::Transform(),
diff --git a/ios/chrome/app/strings/ios_strings.grd b/ios/chrome/app/strings/ios_strings.grd index b715db6..b53e4328f 100644 --- a/ios/chrome/app/strings/ios_strings.grd +++ b/ios/chrome/app/strings/ios_strings.grd
@@ -629,6 +629,9 @@ <message name="IDS_IOS_FACETIME_BUTTON" desc="Text in the confirmation dialog button that will initiate a FaceTime call for the presented number. [Length: 10em] [iOS only]"> FaceTime </message> + <message name="IDS_IOS_PHONE_CALL_BUTTON" desc="Text in the confirmation dialog button that will initiate a phone call for the presented number. [Length: 10em] [iOS only]"> + Call + </message> <message name="IDS_IOS_FIRSTRUN_ACCOUNT_CONSISTENCY_SKIP_BUTTON" desc="Title of the button to skip the selection of an account on First Run when account consistency is enabled. [Length: 10em] [iOS only]"> No thanks </message>
diff --git a/ios/chrome/browser/ui/autofill/autofill_client_ios.h b/ios/chrome/browser/ui/autofill/autofill_client_ios.h index 410cc187..458a95d 100644 --- a/ios/chrome/browser/ui/autofill/autofill_client_ios.h +++ b/ios/chrome/browser/ui/autofill/autofill_client_ios.h
@@ -60,6 +60,7 @@ IdentityProvider* GetIdentityProvider() override; rappor::RapporServiceImpl* GetRapporServiceImpl() override; ukm::UkmService* GetUkmService() override; + SaveCardBubbleController* GetSaveCardBubbleController() override; void ShowAutofillSettings() override; void ShowUnmaskPrompt(const CreditCard& card, UnmaskCardReason reason, @@ -70,6 +71,7 @@ void ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) override; void ConfirmCreditCardFillAssist(const CreditCard& card, const base::Closure& callback) override;
diff --git a/ios/chrome/browser/ui/autofill/autofill_client_ios.mm b/ios/chrome/browser/ui/autofill/autofill_client_ios.mm index 7ffcb0fa..377e7e74f 100644 --- a/ios/chrome/browser/ui/autofill/autofill_client_ios.mm +++ b/ios/chrome/browser/ui/autofill/autofill_client_ios.mm
@@ -84,6 +84,10 @@ return GetApplicationContext()->GetUkmService(); } +SaveCardBubbleController* AutofillClientIOS::GetSaveCardBubbleController() { + return nullptr; +} + void AutofillClientIOS::ShowAutofillSettings() { NOTREACHED(); } @@ -120,6 +124,7 @@ void AutofillClientIOS::ConfirmSaveCreditCardToCloud( const CreditCard& card, std::unique_ptr<base::DictionaryValue> legal_message, + bool should_cvc_be_requested, const base::Closure& callback) { infobar_manager_->AddInfoBar(CreateSaveCardInfoBarMobile( base::MakeUnique<AutofillSaveCardInfoBarDelegateMobile>(
diff --git a/media/base/media_observer.h b/media/base/media_observer.h index f5b1fb6..0181b1d 100644 --- a/media/base/media_observer.h +++ b/media/base/media_observer.h
@@ -16,9 +16,10 @@ virtual ~MediaObserverClient() {} // Requests to restart the media pipeline and create a new renderer as soon as - // possible. |disable_pipeline_auto_suspend| indicates whether to disable - // any optimizations that might suspend the media pipeline. - virtual void SwitchRenderer(bool disable_pipeline_auto_suspend) = 0; + // possible. |is_rendered_remotely| indicates whether the media is rendered + // remotely. When it is true, all the optimizations that might suspend the + // media pipeline should be disabled. + virtual void SwitchRenderer(bool is_rendered_remotely) = 0; // Requests to activate monitoring changes on viewport intersection. virtual void ActivateViewportIntersectionMonitoring(bool activate) = 0;
diff --git a/media/base/media_switches.cc b/media/base/media_switches.cc index 28e749dc..03b4c76 100644 --- a/media/base/media_switches.cc +++ b/media/base/media_switches.cc
@@ -173,6 +173,10 @@ const base::Feature kBackgroundVideoTrackOptimization{ "BackgroundVideoTrackOptimization", base::FEATURE_DISABLED_BY_DEFAULT}; +// Let video without audio be paused when it is playing in the background. +const base::Feature kBackgroundVideoPauseOptimization{ + "BackgroundVideoPauseOptimization", base::FEATURE_DISABLED_BY_DEFAULT}; + // Make MSE garbage collection algorithm more aggressive when we are under // moderate or critical memory pressure. This will relieve memory pressure by // releasing stale data from MSE buffers.
diff --git a/media/base/media_switches.h b/media/base/media_switches.h index 89b4a03a..b2234d1a 100644 --- a/media/base/media_switches.h +++ b/media/base/media_switches.h
@@ -95,6 +95,7 @@ MEDIA_EXPORT extern const base::Feature kVideoBlitColorAccuracy; MEDIA_EXPORT extern const base::Feature kExternalClearKeyForTesting; MEDIA_EXPORT extern const base::Feature kBackgroundVideoTrackOptimization; +MEDIA_EXPORT extern const base::Feature kBackgroundVideoPauseOptimization; MEDIA_EXPORT extern const base::Feature kMemoryPressureBasedSourceBufferGC; #if defined(OS_ANDROID)
diff --git a/media/blink/webmediaplayer_impl.cc b/media/blink/webmediaplayer_impl.cc index 7c10f9f..309441e 100644 --- a/media/blink/webmediaplayer_impl.cc +++ b/media/blink/webmediaplayer_impl.cc
@@ -130,6 +130,10 @@ return base::FeatureList::IsEnabled(kBackgroundVideoTrackOptimization); } +bool IsBackgroundVideoPauseOptimizationEnabled() { + return base::FeatureList::IsEnabled(kBackgroundVideoPauseOptimization); +} + bool IsNetworkStateError(blink::WebMediaPlayer::NetworkState state) { bool result = state == blink::WebMediaPlayer::kNetworkStateFormatError || state == blink::WebMediaPlayer::kNetworkStateNetworkError || @@ -237,6 +241,8 @@ observer_(params.media_observer()), max_keyframe_distance_to_disable_background_video_( params.max_keyframe_distance_to_disable_background_video()), + max_keyframe_distance_to_disable_background_video_mse_( + params.max_keyframe_distance_to_disable_background_video_mse()), enable_instant_source_buffer_gc_( params.enable_instant_source_buffer_gc()), embedded_media_experience_enabled_( @@ -2199,7 +2205,7 @@ // Otherwise only pause if the optimization is on and it's a video-only // optimization candidate. - return IsBackgroundVideoTrackOptimizationEnabled() && !HasAudio() && + return IsBackgroundVideoPauseOptimizationEnabled() && !HasAudio() && IsBackgroundOptimizationCandidate(); } @@ -2233,13 +2239,16 @@ // Videos shorter than the maximum allowed keyframe distance can be optimized. base::TimeDelta duration = GetPipelineMediaDuration(); - if (duration < max_keyframe_distance_to_disable_background_video_) + base::TimeDelta max_keyframe_distance = + (load_type_ == kLoadTypeMediaSource) + ? max_keyframe_distance_to_disable_background_video_mse_ + : max_keyframe_distance_to_disable_background_video_; + if (duration < max_keyframe_distance) return true; // Otherwise, only optimize videos with shorter average keyframe distance. PipelineStatistics stats = GetPipelineStatistics(); - return stats.video_keyframe_distance_average < - max_keyframe_distance_to_disable_background_video_; + return stats.video_keyframe_distance_average < max_keyframe_distance; } void WebMediaPlayerImpl::UpdateBackgroundVideoOptimizationState() { @@ -2334,10 +2343,16 @@ time_to_first_frame); } } -void WebMediaPlayerImpl::SwitchRenderer(bool disable_pipeline_auto_suspend) { +void WebMediaPlayerImpl::SwitchRenderer(bool is_rendered_remotely) { DCHECK(main_task_runner_->BelongsToCurrentThread()); - disable_pipeline_auto_suspend_ = disable_pipeline_auto_suspend; + disable_pipeline_auto_suspend_ = is_rendered_remotely; ScheduleRestart(); + if (client_) { + if (is_rendered_remotely) + client_->MediaRemotingStarted(); + else + client_->MediaRemotingStopped(); + } } void WebMediaPlayerImpl::RecordUnderflowDuration(base::TimeDelta duration) {
diff --git a/media/blink/webmediaplayer_impl.h b/media/blink/webmediaplayer_impl.h index 8e1a16b5..5be40d1fd 100644 --- a/media/blink/webmediaplayer_impl.h +++ b/media/blink/webmediaplayer_impl.h
@@ -223,7 +223,7 @@ #endif // MediaObserverClient implementation. - void SwitchRenderer(bool disable_pipeline_auto_suspend) override; + void SwitchRenderer(bool is_rendered_remotely) override; void ActivateViewportIntersectionMonitoring(bool activate) override; // Called from WebMediaPlayerCast. @@ -681,9 +681,13 @@ base::WeakPtr<MediaObserver> observer_; // The maximum video keyframe distance that allows triggering background - // playback optimizations. + // playback optimizations (non-MSE). base::TimeDelta max_keyframe_distance_to_disable_background_video_; + // The maximum video keyframe distance that allows triggering background + // playback optimizations (MSE). + base::TimeDelta max_keyframe_distance_to_disable_background_video_mse_; + // When MSE memory pressure based garbage collection is enabled, the // |enable_instant_source_buffer_gc| controls whether the GC is done // immediately on memory pressure notification or during the next SourceBuffer
diff --git a/media/blink/webmediaplayer_impl_unittest.cc b/media/blink/webmediaplayer_impl_unittest.cc index 493dfb8..0d4e6f8 100644 --- a/media/blink/webmediaplayer_impl_unittest.cc +++ b/media/blink/webmediaplayer_impl_unittest.cc
@@ -28,6 +28,7 @@ #include "media/renderers/default_renderer_factory.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/WebKit/public/platform/WebMediaPlayer.h" #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h" #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" #include "third_party/WebKit/public/platform/WebSize.h" @@ -44,6 +45,12 @@ namespace media { +// Specify different values for testing. +const base::TimeDelta kMaxKeyframeDistanceToDisableBackgroundVideo = + base::TimeDelta::FromSeconds(5); +const base::TimeDelta kMaxKeyframeDistanceToDisableBackgroundVideoMSE = + base::TimeDelta::FromSeconds(10); + int64_t OnAdjustAllocatedMemory(int64_t delta) { return 0; } @@ -215,7 +222,9 @@ media_thread_.task_runner(), message_loop_.task_runner(), message_loop_.task_runner(), WebMediaPlayerParams::Context3DCB(), base::Bind(&OnAdjustAllocatedMemory), nullptr, nullptr, nullptr, - base::TimeDelta::FromSeconds(10), false, allow_suspend, false)); + kMaxKeyframeDistanceToDisableBackgroundVideo, + kMaxKeyframeDistanceToDisableBackgroundVideoMSE, false, + allow_suspend, false)); } ~WebMediaPlayerImplTest() override { @@ -331,6 +340,10 @@ wmpi_->SetSuspendState(is_suspended); } + void SetLoadType(blink::WebMediaPlayer::LoadType load_type) { + wmpi_->load_type_ = load_type; + } + // "Renderer" thread. base::MessageLoop message_loop_; @@ -796,7 +809,7 @@ class WebMediaPlayerImplBackgroundBehaviorTest : public WebMediaPlayerImplTest, public ::testing::WithParamInterface< - std::tuple<bool, bool, int, int, bool>> { + std::tuple<bool, bool, int, int, bool, bool, bool>> { public: // Indices of the tuple parameters. static const int kIsMediaSuspendEnabled = 0; @@ -804,6 +817,8 @@ static const int kDurationSec = 2; static const int kAverageKeyframeDistanceSec = 3; static const int kIsResumeBackgroundVideoEnabled = 4; + static const int kIsMediaSource = 5; + static const int kIsBackgroundPauseEnabled = 6; void SetUp() override { WebMediaPlayerImplTest::SetUp(); @@ -818,6 +833,16 @@ disabled_features += kBackgroundVideoTrackOptimization.name; } + if (IsBackgroundPauseOn()) { + if (!enabled_features.empty()) + enabled_features += ","; + enabled_features += kBackgroundVideoPauseOptimization.name; + } else { + if (!disabled_features.empty()) + disabled_features += ","; + disabled_features += kBackgroundVideoPauseOptimization.name; + } + if (IsResumeBackgroundVideoEnabled()) { if (!enabled_features.empty()) enabled_features += ","; @@ -831,6 +856,9 @@ feature_list_.InitFromCommandLine(enabled_features, disabled_features); InitializeWebMediaPlayerImpl(true); + bool is_media_source = std::get<kIsMediaSource>(GetParam()); + SetLoadType(is_media_source ? blink::WebMediaPlayer::kLoadTypeMediaSource + : blink::WebMediaPlayer::kLoadTypeURL); SetVideoKeyframeDistanceAverage( base::TimeDelta::FromSeconds(GetAverageKeyframeDistanceSec())); SetDuration(base::TimeDelta::FromSeconds(GetDurationSec())); @@ -859,12 +887,24 @@ return std::get<kIsResumeBackgroundVideoEnabled>(GetParam()); } + bool IsBackgroundPauseOn() { + return std::get<kIsBackgroundPauseEnabled>(GetParam()); + } + int GetDurationSec() const { return std::get<kDurationSec>(GetParam()); } int GetAverageKeyframeDistanceSec() const { return std::get<kAverageKeyframeDistanceSec>(GetParam()); } + int GetMaxKeyframeDistanceSec() const { + base::TimeDelta max_keyframe_distance = + std::get<kIsMediaSource>(GetParam()) + ? kMaxKeyframeDistanceToDisableBackgroundVideoMSE + : kMaxKeyframeDistanceToDisableBackgroundVideo; + return max_keyframe_distance.InSeconds(); + } + bool IsAndroid() { #if defined(OS_ANDROID) return true; @@ -907,14 +947,14 @@ // There's no optimization criteria for video only on Android. bool matches_requirements = IsAndroid() || - ((GetDurationSec() < GetAverageKeyframeDistanceSec()) || - (GetAverageKeyframeDistanceSec() < 10)); + ((GetDurationSec() < GetMaxKeyframeDistanceSec()) || + (GetAverageKeyframeDistanceSec() < GetMaxKeyframeDistanceSec())); EXPECT_EQ(matches_requirements, IsBackgroundOptimizationCandidate()); // Video is always paused when suspension is on and only if matches the // optimization criteria if the optimization is on. - bool should_pause = IsMediaSuspendOn() || - (IsBackgroundOptimizationOn() && matches_requirements); + bool should_pause = + IsMediaSuspendOn() || (IsBackgroundPauseOn() && matches_requirements); EXPECT_EQ(should_pause, ShouldPauseVideoWhenHidden()); } @@ -923,15 +963,15 @@ // Optimization requirements are the same for all platforms. bool matches_requirements = - (GetDurationSec() < GetAverageKeyframeDistanceSec()) || - (GetAverageKeyframeDistanceSec() < 10); + (GetDurationSec() < GetMaxKeyframeDistanceSec()) || + (GetAverageKeyframeDistanceSec() < GetMaxKeyframeDistanceSec()); EXPECT_EQ(matches_requirements, IsBackgroundOptimizationCandidate()); EXPECT_EQ(IsBackgroundOptimizationOn() && matches_requirements, ShouldDisableVideoWhenHidden()); - // Only pause audible videos on Android if both media suspend and resume - // background videos is on. On Desktop + // Only pause audible videos if both media suspend and resume background + // videos is on. Both are on by default on Android and off on desktop. EXPECT_EQ(IsMediaSuspendOn() && IsResumeBackgroundVideoEnabled(), ShouldPauseVideoWhenHidden()); } @@ -942,6 +982,8 @@ ::testing::Bool(), ::testing::Values(5, 300), ::testing::Values(5, 100), + ::testing::Bool(), + ::testing::Bool(), ::testing::Bool())); } // namespace media
diff --git a/media/blink/webmediaplayer_params.cc b/media/blink/webmediaplayer_params.cc index a89f693..ce635b6 100644 --- a/media/blink/webmediaplayer_params.cc +++ b/media/blink/webmediaplayer_params.cc
@@ -24,6 +24,7 @@ SurfaceManager* surface_manager, base::WeakPtr<MediaObserver> media_observer, base::TimeDelta max_keyframe_distance_to_disable_background_video, + base::TimeDelta max_keyframe_distance_to_disable_background_video_mse, bool enable_instant_source_buffer_gc, bool allow_suspend, bool embedded_media_experience_enabled) @@ -40,6 +41,8 @@ media_observer_(media_observer), max_keyframe_distance_to_disable_background_video_( max_keyframe_distance_to_disable_background_video), + max_keyframe_distance_to_disable_background_video_mse_( + max_keyframe_distance_to_disable_background_video_mse), enable_instant_source_buffer_gc_(enable_instant_source_buffer_gc), allow_suspend_(allow_suspend), embedded_media_experience_enabled_(embedded_media_experience_enabled) {}
diff --git a/media/blink/webmediaplayer_params.h b/media/blink/webmediaplayer_params.h index b16ee3a..2da0d23 100644 --- a/media/blink/webmediaplayer_params.h +++ b/media/blink/webmediaplayer_params.h
@@ -60,6 +60,7 @@ SurfaceManager* surface_manager, base::WeakPtr<MediaObserver> media_observer, base::TimeDelta max_keyframe_distance_to_disable_background_video, + base::TimeDelta max_keyframe_distance_to_disable_background_video_mse, bool enable_instant_source_buffer_gc, bool allow_suspend, bool embedded_media_experience_enabled); @@ -110,6 +111,11 @@ return max_keyframe_distance_to_disable_background_video_; } + base::TimeDelta max_keyframe_distance_to_disable_background_video_mse() + const { + return max_keyframe_distance_to_disable_background_video_mse_; + } + bool enable_instant_source_buffer_gc() const { return enable_instant_source_buffer_gc_; } @@ -134,6 +140,7 @@ SurfaceManager* surface_manager_; base::WeakPtr<MediaObserver> media_observer_; base::TimeDelta max_keyframe_distance_to_disable_background_video_; + base::TimeDelta max_keyframe_distance_to_disable_background_video_mse_; bool enable_instant_source_buffer_gc_; const bool allow_suspend_; const bool embedded_media_experience_enabled_;
diff --git a/mojo/edk/js/core.cc b/mojo/edk/js/core.cc index baccc4c..4b557db 100644 --- a/mojo/edk/js/core.cc +++ b/mojo/edk/js/core.cc
@@ -81,7 +81,7 @@ MojoHandle handle1 = MOJO_HANDLE_INVALID; MojoResult result = MOJO_RESULT_OK; - v8::Handle<v8::Value> options_value = args.PeekNext(); + v8::Local<v8::Value> options_value = args.PeekNext(); if (options_value.IsEmpty() || options_value->IsNull() || options_value->IsUndefined()) { result = MojoCreateMessagePipe(NULL, &handle0, &handle1); @@ -144,7 +144,7 @@ return dictionary; } - v8::Handle<v8::ArrayBuffer> array_buffer = + v8::Local<v8::ArrayBuffer> array_buffer = v8::ArrayBuffer::New(args.isolate(), num_bytes); std::vector<mojo::Handle> handles(num_handles); @@ -178,7 +178,7 @@ MojoHandle consumer_handle = MOJO_HANDLE_INVALID; MojoResult result = MOJO_RESULT_OK; - v8::Handle<v8::Value> options_value = args.PeekNext(); + v8::Local<v8::Value> options_value = args.PeekNext(); if (options_value.IsEmpty() || options_value->IsNull() || options_value->IsUndefined()) { result = MojoCreateDataPipe(NULL, &producer_handle, &consumer_handle); @@ -234,7 +234,7 @@ return dictionary; } - v8::Handle<v8::ArrayBuffer> array_buffer = + v8::Local<v8::ArrayBuffer> array_buffer = v8::ArrayBuffer::New(args.isolate(), num_bytes); gin::ArrayBuffer buffer; ConvertFromV8(args.isolate(), array_buffer, &buffer); @@ -257,12 +257,12 @@ // and the buffer will contain whatever was read before the error occurred. // The drainData data pipe handle argument is closed automatically. -v8::Handle<v8::Value> DoDrainData(gin::Arguments* args, - gin::Handle<HandleWrapper> handle) { +v8::Local<v8::Value> DoDrainData(gin::Arguments* args, + gin::Handle<HandleWrapper> handle) { return (new DrainData(args->isolate(), handle->release()))->GetPromise(); } -bool IsHandle(gin::Arguments* args, v8::Handle<v8::Value> val) { +bool IsHandle(gin::Arguments* args, v8::Local<v8::Value> val) { gin::Handle<mojo::edk::js::HandleWrapper> ignore_handle; return gin::Converter<gin::Handle<mojo::edk::js::HandleWrapper>>::FromV8( args->isolate(), val, &ignore_handle); @@ -332,7 +332,7 @@ return dictionary; } - v8::Handle<v8::ArrayBuffer> array_buffer = + v8::Local<v8::ArrayBuffer> array_buffer = v8::ArrayBuffer::New(args.isolate(), data, num_bytes); dictionary.Set("result", result); @@ -342,7 +342,7 @@ } MojoResult UnmapBuffer(const gin::Arguments& args, - const v8::Handle<v8::ArrayBuffer>& buffer) { + const v8::Local<v8::ArrayBuffer>& buffer) { // Buffer must be external, created by MapBuffer if (!buffer->IsExternal()) return MOJO_RESULT_INVALID_ARGUMENT;
diff --git a/mojo/edk/js/drain_data.cc b/mojo/edk/js/drain_data.cc index 334ced32..00c10a4 100644 --- a/mojo/edk/js/drain_data.cc +++ b/mojo/edk/js/drain_data.cc
@@ -24,15 +24,15 @@ : isolate_(isolate), handle_(DataPipeConsumerHandle(handle.value())), handle_watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC) { - v8::Handle<v8::Context> context(isolate_->GetCurrentContext()); + v8::Local<v8::Context> context(isolate_->GetCurrentContext()); runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); WaitForData(); } -v8::Handle<v8::Value> DrainData::GetPromise() { +v8::Local<v8::Value> DrainData::GetPromise() { CHECK(resolver_.IsEmpty()); - v8::Handle<v8::Promise::Resolver> resolver( + v8::Local<v8::Promise::Resolver> resolver( v8::Promise::Resolver::New(isolate_)); resolver_.Reset(isolate_, resolver); return resolver->GetPromise(); @@ -86,7 +86,7 @@ // Create a total_bytes length ArrayBuffer return value. gin::Runner::Scope scope(runner_.get()); - v8::Handle<v8::ArrayBuffer> array_buffer = + v8::Local<v8::ArrayBuffer> array_buffer = v8::ArrayBuffer::New(isolate_, total_bytes); gin::ArrayBuffer buffer; ConvertFromV8(isolate_, array_buffer, &buffer); @@ -108,13 +108,13 @@ // that was read before either an error occurred or the remote pipe handle // was closed. The latter is indicated by MOJO_RESULT_FAILED_PRECONDITION. - v8::Handle<v8::Promise::Resolver> resolver( + v8::Local<v8::Promise::Resolver> resolver( v8::Local<v8::Promise::Resolver>::New(isolate_, resolver_)); gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate_); dictionary.Set("result", result); dictionary.Set("buffer", array_buffer); - v8::Handle<v8::Value> settled_value(ConvertToV8(isolate_, dictionary)); + v8::Local<v8::Value> settled_value(ConvertToV8(isolate_, dictionary)); if (result == MOJO_RESULT_FAILED_PRECONDITION) resolver->Resolve(settled_value);
diff --git a/mojo/edk/js/drain_data.h b/mojo/edk/js/drain_data.h index 42da90f..42734893d 100644 --- a/mojo/edk/js/drain_data.h +++ b/mojo/edk/js/drain_data.h
@@ -31,7 +31,7 @@ // Returns a Promise that will be settled when no more data can be read. // Should be called just once on a newly allocated DrainData object. - v8::Handle<v8::Value> GetPromise(); + v8::Local<v8::Value> GetPromise(); private: ~DrainData();
diff --git a/mojo/edk/js/handle.cc b/mojo/edk/js/handle.cc index 7da8e9f..b120f96 100644 --- a/mojo/edk/js/handle.cc +++ b/mojo/edk/js/handle.cc
@@ -47,15 +47,15 @@ namespace gin { -v8::Handle<v8::Value> Converter<mojo::Handle>::ToV8(v8::Isolate* isolate, - const mojo::Handle& val) { +v8::Local<v8::Value> Converter<mojo::Handle>::ToV8(v8::Isolate* isolate, + const mojo::Handle& val) { if (!val.is_valid()) return v8::Null(isolate); return mojo::edk::js::HandleWrapper::Create(isolate, val.value()).ToV8(); } bool Converter<mojo::Handle>::FromV8(v8::Isolate* isolate, - v8::Handle<v8::Value> val, + v8::Local<v8::Value> val, mojo::Handle* out) { if (val->IsNull()) { *out = mojo::Handle(); @@ -71,13 +71,14 @@ return true; } -v8::Handle<v8::Value> Converter<mojo::MessagePipeHandle>::ToV8( - v8::Isolate* isolate, mojo::MessagePipeHandle val) { +v8::Local<v8::Value> Converter<mojo::MessagePipeHandle>::ToV8( + v8::Isolate* isolate, + mojo::MessagePipeHandle val) { return Converter<mojo::Handle>::ToV8(isolate, val); } bool Converter<mojo::MessagePipeHandle>::FromV8(v8::Isolate* isolate, - v8::Handle<v8::Value> val, + v8::Local<v8::Value> val, mojo::MessagePipeHandle* out) { return Converter<mojo::Handle>::FromV8(isolate, val, out); }
diff --git a/mojo/edk/js/handle.h b/mojo/edk/js/handle.h index 60652ed..af5cc1a 100644 --- a/mojo/edk/js/handle.h +++ b/mojo/edk/js/handle.h
@@ -59,18 +59,19 @@ // TODO(mpcomplete): define converters for all Handle subtypes. template <> struct MOJO_JS_EXPORT Converter<mojo::Handle> { - static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, - const mojo::Handle& val); - static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, + static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, + const mojo::Handle& val); + static bool FromV8(v8::Isolate* isolate, + v8::Local<v8::Value> val, mojo::Handle* out); }; template <> struct MOJO_JS_EXPORT Converter<mojo::MessagePipeHandle> { - static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, - mojo::MessagePipeHandle val); + static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, + mojo::MessagePipeHandle val); static bool FromV8(v8::Isolate* isolate, - v8::Handle<v8::Value> val, + v8::Local<v8::Value> val, mojo::MessagePipeHandle* out); }; @@ -78,14 +79,14 @@ // converting |null| to a wrapper for an empty mojo::Handle. template <> struct MOJO_JS_EXPORT Converter<gin::Handle<mojo::edk::js::HandleWrapper>> { - static v8::Handle<v8::Value> ToV8( + static v8::Local<v8::Value> ToV8( v8::Isolate* isolate, const gin::Handle<mojo::edk::js::HandleWrapper>& val) { return val.ToV8(); } static bool FromV8(v8::Isolate* isolate, - v8::Handle<v8::Value> val, + v8::Local<v8::Value> val, gin::Handle<mojo::edk::js::HandleWrapper>* out) { if (val->IsNull()) { *out = mojo::edk::js::HandleWrapper::Create(isolate, MOJO_HANDLE_INVALID);
diff --git a/mojo/edk/js/mojo_runner_delegate.cc b/mojo/edk/js/mojo_runner_delegate.cc index dda0b2c3..5d001f9 100644 --- a/mojo/edk/js/mojo_runner_delegate.cc +++ b/mojo/edk/js/mojo_runner_delegate.cc
@@ -34,13 +34,12 @@ void StartCallback(base::WeakPtr<gin::Runner> runner, MojoHandle pipe, - v8::Handle<v8::Value> module) { + v8::Local<v8::Value> module) { v8::Isolate* isolate = runner->GetContextHolder()->isolate(); - v8::Handle<v8::Function> start; + v8::Local<v8::Function> start; CHECK(gin::ConvertFromV8(isolate, module, &start)); - v8::Handle<v8::Value> args[] = { - gin::ConvertToV8(isolate, Handle(pipe)) }; + v8::Local<v8::Value> args[] = {gin::ConvertToV8(isolate, Handle(pipe))}; runner->Call(start, runner->global(), 1, args); }
diff --git a/mojo/edk/js/support.cc b/mojo/edk/js/support.cc index 404cb9b7..21ce0d0d 100644 --- a/mojo/edk/js/support.cc +++ b/mojo/edk/js/support.cc
@@ -25,7 +25,7 @@ WaitingCallback* AsyncWait(const gin::Arguments& args, gin::Handle<HandleWrapper> handle, MojoHandleSignals signals, - v8::Handle<v8::Function> callback) { + v8::Local<v8::Function> callback) { return WaitingCallback::Create( args.isolate(), callback, handle, signals, true /* one_shot */).get(); } @@ -37,7 +37,7 @@ WaitingCallback* Watch(const gin::Arguments& args, gin::Handle<HandleWrapper> handle, MojoHandleSignals signals, - v8::Handle<v8::Function> callback) { + v8::Local<v8::Function> callback) { return WaitingCallback::Create( args.isolate(), callback, handle, signals, false /* one_shot */).get(); }
diff --git a/mojo/edk/js/waiting_callback.cc b/mojo/edk/js/waiting_callback.cc index 98501236..d9c920a 100644 --- a/mojo/edk/js/waiting_callback.cc +++ b/mojo/edk/js/waiting_callback.cc
@@ -14,7 +14,7 @@ namespace { -v8::Handle<v8::Private> GetHiddenPropertyName(v8::Isolate* isolate) { +v8::Local<v8::Private> GetHiddenPropertyName(v8::Isolate* isolate) { return v8::Private::ForApi( isolate, gin::StringToV8(isolate, "::mojo::js::WaitingCallback")); } @@ -26,7 +26,7 @@ // static gin::Handle<WaitingCallback> WaitingCallback::Create( v8::Isolate* isolate, - v8::Handle<v8::Function> callback, + v8::Local<v8::Function> callback, gin::Handle<HandleWrapper> handle_wrapper, MojoHandleSignals signals, bool one_shot) { @@ -50,12 +50,12 @@ } WaitingCallback::WaitingCallback(v8::Isolate* isolate, - v8::Handle<v8::Function> callback, + v8::Local<v8::Function> callback, bool one_shot) : one_shot_(one_shot), watcher_(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC), weak_factory_(this) { - v8::Handle<v8::Context> context = isolate->GetCurrentContext(); + v8::Local<v8::Context> context = isolate->GetCurrentContext(); runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); v8::Maybe<bool> result = GetWrapper(isolate).ToLocalChecked()->SetPrivate( context, GetHiddenPropertyName(isolate), callback);
diff --git a/mojo/edk/js/waiting_callback.h b/mojo/edk/js/waiting_callback.h index f97b389a..03518fe 100644 --- a/mojo/edk/js/waiting_callback.h +++ b/mojo/edk/js/waiting_callback.h
@@ -29,7 +29,7 @@ // WaitingCallback is explicitly cancelled. static gin::Handle<WaitingCallback> Create( v8::Isolate* isolate, - v8::Handle<v8::Function> callback, + v8::Local<v8::Function> callback, gin::Handle<HandleWrapper> handle_wrapper, MojoHandleSignals signals, bool one_shot); @@ -41,7 +41,7 @@ private: WaitingCallback(v8::Isolate* isolate, - v8::Handle<v8::Function> callback, + v8::Local<v8::Function> callback, bool one_shot); ~WaitingCallback() override;
diff --git a/net/dns/host_resolver.cc b/net/dns/host_resolver.cc index 656e7a8b..1f6dcf9 100644 --- a/net/dns/host_resolver.cc +++ b/net/dns/host_resolver.cc
@@ -132,14 +132,6 @@ const PersistCallback& persist_callback, std::unique_ptr<const base::Value> old_data) {} -void HostResolver::SetDefaultAddressFamily(AddressFamily address_family) { - NOTREACHED(); -} - -AddressFamily HostResolver::GetDefaultAddressFamily() const { - return ADDRESS_FAMILY_UNSPECIFIED; -} - void HostResolver::SetNoIPv6OnWifi(bool no_ipv6_on_wifi) { NOTREACHED(); }
diff --git a/net/dns/host_resolver.h b/net/dns/host_resolver.h index ba507870..f909e7b0 100644 --- a/net/dns/host_resolver.h +++ b/net/dns/host_resolver.h
@@ -213,14 +213,6 @@ const PersistCallback& persist_callback, std::unique_ptr<const base::Value> old_data); - // Sets the default AddressFamily to use when requests have left it - // unspecified. For example, this could be used to restrict resolution - // results to AF_INET by passing in ADDRESS_FAMILY_IPV4, or to - // AF_INET6 by passing in ADDRESS_FAMILY_IPV6. See http://crbug.com/696569 for - // why this option is necessary. - virtual void SetDefaultAddressFamily(AddressFamily address_family); - virtual AddressFamily GetDefaultAddressFamily() const; - // Sets the HostResolver to assume that IPv6 is unreachable when on a wifi // connection. See https://crbug.com/696569 for further context. virtual void SetNoIPv6OnWifi(bool no_ipv6_on_wifi);
diff --git a/net/dns/host_resolver_impl.cc b/net/dns/host_resolver_impl.cc index cb4d66f2..fd4c065 100644 --- a/net/dns/host_resolver_impl.cc +++ b/net/dns/host_resolver_impl.cc
@@ -2046,7 +2046,6 @@ net_log_(net_log), received_dns_config_(false), num_dns_failures_(0), - default_address_family_(ADDRESS_FAMILY_UNSPECIFIED), assume_ipv6_failure_on_wifi_(false), use_local_ipv6_(false), last_ipv6_probe_result_(true), @@ -2223,15 +2222,6 @@ return rv; } -void HostResolverImpl::SetDefaultAddressFamily(AddressFamily address_family) { - DCHECK(CalledOnValidThread()); - default_address_family_ = address_family; -} - -AddressFamily HostResolverImpl::GetDefaultAddressFamily() const { - return default_address_family_; -} - void HostResolverImpl::SetNoIPv6OnWifi(bool no_ipv6_on_wifi) { DCHECK(CalledOnValidThread()); assume_ipv6_failure_on_wifi_ = no_ipv6_on_wifi; @@ -2253,11 +2243,6 @@ *net_error = OK; AddressFamily family = GetAddressFamily(*ip_address); - if (family == ADDRESS_FAMILY_IPV6 && - default_address_family_ == ADDRESS_FAMILY_IPV4) { - // Don't return IPv6 addresses if default address family is set to IPv4. - *net_error = ERR_NAME_NOT_RESOLVED; - } if (key.address_family != ADDRESS_FAMILY_UNSPECIFIED && key.address_family != family) { // Don't return IPv6 addresses for IPv4 queries, and vice versa. @@ -2400,9 +2385,6 @@ info.host_resolver_flags() | additional_resolver_flags_; AddressFamily effective_address_family = info.address_family(); - if (info.address_family() == ADDRESS_FAMILY_UNSPECIFIED) - effective_address_family = default_address_family_; - if (effective_address_family == ADDRESS_FAMILY_UNSPECIFIED && // When resolving IPv4 literals, there's no need to probe for IPv6. // When resolving IPv6 literals, there's no benefit to artificially
diff --git a/net/dns/host_resolver_impl.h b/net/dns/host_resolver_impl.h index 65b5c0b..f78d39e 100644 --- a/net/dns/host_resolver_impl.h +++ b/net/dns/host_resolver_impl.h
@@ -158,9 +158,6 @@ const PersistCallback& persist_callback, std::unique_ptr<const base::Value> old_data) override; - void SetDefaultAddressFamily(AddressFamily address_family) override; - AddressFamily GetDefaultAddressFamily() const override; - void SetNoIPv6OnWifi(bool no_ipv6_on_wifi) override; bool GetNoIPv6OnWifi() override; @@ -356,10 +353,6 @@ // Number of consecutive failures of DnsTask, counted when fallback succeeds. unsigned num_dns_failures_; - // Address family to use when the request doesn't specify one. See - // http://crbug.com/696569 for why the option is needed. - AddressFamily default_address_family_; - // True if IPv6 should not be attempted when on a WiFi connection. See // https://crbug.com/696569 for further context. bool assume_ipv6_failure_on_wifi_;
diff --git a/net/dns/host_resolver_impl_unittest.cc b/net/dns/host_resolver_impl_unittest.cc index c6a9426..c33515e 100644 --- a/net/dns/host_resolver_impl_unittest.cc +++ b/net/dns/host_resolver_impl_unittest.cc
@@ -2606,98 +2606,4 @@ EXPECT_EQ(1, count2); } -// Tests that after changing the default AddressFamily to IPV4, requests -// with UNSPECIFIED address family map to IPV4. -TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv4) { - CreateSerialResolver(); // To guarantee order of resolutions. - - proc_->AddRule("h1", ADDRESS_FAMILY_IPV4, "1.0.0.1"); - proc_->AddRule("h1", ADDRESS_FAMILY_IPV6, "::2"); - - resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV4); - - CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_UNSPECIFIED); - CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV4); - CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV6); - - // Start all of the requests. - for (size_t i = 0; i < requests_.size(); ++i) { - EXPECT_EQ(ERR_IO_PENDING, requests_[i]->Resolve()) << i; - } - - proc_->SignalMultiple(requests_.size()); - - // Wait for all the requests to complete. - for (size_t i = 0u; i < requests_.size(); ++i) { - EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i; - } - - // Since the requests all had the same priority and we limited the thread - // count to 1, they should have completed in the same order as they were - // requested. Moreover, request0 and request1 will have been serviced by - // the same job. - - MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList(); - ASSERT_EQ(2u, capture_list.size()); - - EXPECT_EQ("h1", capture_list[0].hostname); - EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[0].address_family); - - EXPECT_EQ("h1", capture_list[1].hostname); - EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[1].address_family); - - // Now check that the correct resolved IP addresses were returned. - EXPECT_TRUE(requests_[0]->HasOneAddress("1.0.0.1", 80)); - EXPECT_TRUE(requests_[1]->HasOneAddress("1.0.0.1", 80)); - EXPECT_TRUE(requests_[2]->HasOneAddress("::2", 80)); -} - -// This is the exact same test as SetDefaultAddressFamily_IPv4, except the -// default family is set to IPv6 and the family of requests is flipped where -// specified. -TEST_F(HostResolverImplTest, SetDefaultAddressFamily_IPv6) { - CreateSerialResolver(); // To guarantee order of resolutions. - - // Don't use IPv6 replacements here since some systems don't support it. - proc_->AddRule("h1", ADDRESS_FAMILY_IPV4, "1.0.0.1"); - proc_->AddRule("h1", ADDRESS_FAMILY_IPV6, "::2"); - - resolver_->SetDefaultAddressFamily(ADDRESS_FAMILY_IPV6); - - CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_UNSPECIFIED); - CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV6); - CreateRequest("h1", 80, MEDIUM, ADDRESS_FAMILY_IPV4); - - // Start all of the requests. - for (size_t i = 0; i < requests_.size(); ++i) { - EXPECT_EQ(ERR_IO_PENDING, requests_[i]->Resolve()) << i; - } - - proc_->SignalMultiple(requests_.size()); - - // Wait for all the requests to complete. - for (size_t i = 0u; i < requests_.size(); ++i) { - EXPECT_EQ(OK, requests_[i]->WaitForResult()) << i; - } - - // Since the requests all had the same priority and we limited the thread - // count to 1, they should have completed in the same order as they were - // requested. Moreover, request0 and request1 will have been serviced by - // the same job. - - MockHostResolverProc::CaptureList capture_list = proc_->GetCaptureList(); - ASSERT_EQ(2u, capture_list.size()); - - EXPECT_EQ("h1", capture_list[0].hostname); - EXPECT_EQ(ADDRESS_FAMILY_IPV6, capture_list[0].address_family); - - EXPECT_EQ("h1", capture_list[1].hostname); - EXPECT_EQ(ADDRESS_FAMILY_IPV4, capture_list[1].address_family); - - // Now check that the correct resolved IP addresses were returned. - EXPECT_TRUE(requests_[0]->HasOneAddress("::2", 80)); - EXPECT_TRUE(requests_[1]->HasOneAddress("::2", 80)); - EXPECT_TRUE(requests_[2]->HasOneAddress("1.0.0.1", 80)); -} - } // namespace net
diff --git a/net/dns/mapped_host_resolver.cc b/net/dns/mapped_host_resolver.cc index 0003fbb..dc14b650 100644 --- a/net/dns/mapped_host_resolver.cc +++ b/net/dns/mapped_host_resolver.cc
@@ -56,14 +56,6 @@ return impl_->GetDnsConfigAsValue(); } -void MappedHostResolver::SetDefaultAddressFamily(AddressFamily address_family) { - impl_->SetDefaultAddressFamily(address_family); -} - -AddressFamily MappedHostResolver::GetDefaultAddressFamily() const { - return impl_->GetDefaultAddressFamily(); -} - void MappedHostResolver::SetNoIPv6OnWifi(bool no_ipv6_on_wifi) { impl_->SetNoIPv6OnWifi(no_ipv6_on_wifi); }
diff --git a/net/dns/mapped_host_resolver.h b/net/dns/mapped_host_resolver.h index 5c76ce9..5ae459c 100644 --- a/net/dns/mapped_host_resolver.h +++ b/net/dns/mapped_host_resolver.h
@@ -57,8 +57,6 @@ void SetDnsClientEnabled(bool enabled) override; HostCache* GetHostCache() override; std::unique_ptr<base::Value> GetDnsConfigAsValue() const override; - void SetDefaultAddressFamily(AddressFamily address_family) override; - AddressFamily GetDefaultAddressFamily() const override; void SetNoIPv6OnWifi(bool no_ipv6_on_wifi) override; bool GetNoIPv6OnWifi() override;
diff --git a/printing/pdf_metafile_skia.cc b/printing/pdf_metafile_skia.cc index 12b24ad..54c1cf35 100644 --- a/printing/pdf_metafile_skia.cc +++ b/printing/pdf_metafile_skia.cc
@@ -12,9 +12,9 @@ #include "base/files/file.h" #include "base/memory/ptr_util.h" #include "base/time/time.h" +#include "cc/paint/paint_canvas.h" #include "cc/paint/paint_record.h" #include "cc/paint/paint_recorder.h" -#include "cc/paint/skia_paint_canvas.h" #include "printing/print_settings.h" #include "third_party/skia/include/core/SkDocument.h" #include "third_party/skia/include/core/SkStream.h"
diff --git a/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/idbobjectstore-index-finished.html b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/idbobjectstore-index-finished.html new file mode 100644 index 0000000..75677cf --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/idbobjectstore-index-finished.html
@@ -0,0 +1,26 @@ +<!doctype html> +<meta charset=utf-8> +<title>IndexedDB: IDBObjectStore index() when transaction is finished</title> +<link rel="help" href="https://w3c.github.io/IndexedDB/#dom-idbobjectstore-index"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support.js"></script> +<script> + +indexeddb_test( + (t, db) => { + const store = db.createObjectStore('store'); + store.createIndex('index', 'key_path'); + }, + (t, db) => { + const tx = db.transaction('store'); + const store = tx.objectStore('store'); + tx.abort(); + assert_throws('InvalidStateError', () => store.index('index'), + 'index() should throw if transaction is finished'); + t.done(); + }, + 'IDBObjectStore index() behavior when transaction is finished' +); + +</script>
diff --git a/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/idbtransaction-objectStore-finished.html b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/idbtransaction-objectStore-finished.html new file mode 100644 index 0000000..5e363ea --- /dev/null +++ b/third_party/WebKit/LayoutTests/external/wpt/IndexedDB/idbtransaction-objectStore-finished.html
@@ -0,0 +1,24 @@ +<!doctype html> +<meta charset=utf-8> +<title>IndexedDB: IDBTransaction objectStore() when transaction is finished</title> +<link rel="help" href="https://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="support.js"></script> +<script> + +indexeddb_test( + (t, db) => { + db.createObjectStore('store'); + }, + (t, db) => { + const tx = db.transaction('store'); + tx.abort(); + assert_throws('InvalidStateError', () => tx.objectStore('store'), + 'objectStore() should throw if transaction is finished'); + t.done(); + }, + 'IDBTransaction objectStore() behavior when transaction is finished' +); + +</script>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-invalid-args-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-invalid-args-expected.txt index 09b0bad..4fa88d53 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-invalid-args-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-invalid-args-expected.txt
@@ -5,10 +5,10 @@ PASS Rejected as expected: undefined PASS reason instanceof Error is true -TypeError: Failed to execute 'createImageBitmap' on 'Window': The provided value is not of type '(HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or Blob or ImageData or ImageBitmap or OffscreenCanvas)' +TypeError: Failed to execute 'createImageBitmap' on 'Window': The provided value is not of type '(HTMLImageElement or HTMLVideoElement or HTMLCanvasElement or Blob or ImageData or ImageBitmap or OffscreenCanvas)' PASS Rejected as expected: null PASS reason instanceof Error is true -TypeError: Failed to execute 'createImageBitmap' on 'Window': The provided value is not of type '(HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or Blob or ImageData or ImageBitmap or OffscreenCanvas)' +TypeError: Failed to execute 'createImageBitmap' on 'Window': The provided value is not of type '(HTMLImageElement or HTMLVideoElement or HTMLCanvasElement or Blob or ImageData or ImageBitmap or OffscreenCanvas)' PASS Rejected as expected: empty image PASS reason instanceof Error is true InvalidStateError: Failed to execute 'createImageBitmap' on 'Window': No image can be retrieved from the provided element.
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-invalid-args-in-workers-expected.txt b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-invalid-args-in-workers-expected.txt index dcf97bd7..eddad15 100644 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-invalid-args-in-workers-expected.txt +++ b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-invalid-args-in-workers-expected.txt
@@ -6,7 +6,7 @@ Starting worker: ./resources/canvas-createImageBitmap-invalid-args-in-workers.js PASS [Worker] Rejected as expected: null PASS [Worker] reason instanceof Error is true -[Worker] TypeError: Failed to execute 'createImageBitmap' on 'WorkerGlobalScope': The provided value is not of type '(HTMLImageElement or SVGImageElement or HTMLVideoElement or HTMLCanvasElement or Blob or ImageData or ImageBitmap or OffscreenCanvas)' +[Worker] TypeError: Failed to execute 'createImageBitmap' on 'WorkerGlobalScope': The provided value is not of type '(HTMLImageElement or HTMLVideoElement or HTMLCanvasElement or Blob or ImageData or ImageBitmap or OffscreenCanvas)' PASS [Worker] Rejected as expected: invalid area PASS [Worker] reason instanceof Error is true [Worker] IndexSizeError: Failed to execute 'createImageBitmap' on 'WorkerGlobalScope': The source height provided is 0.
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-svg-image-expected.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-svg-image-expected.html deleted file mode 100644 index a18476a..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-svg-image-expected.html +++ /dev/null
@@ -1,27 +0,0 @@ -<!doctype html> -<style> -svg { - border: 1px solid black; -} -#img4 { - display: none; -} -</style> -<svg id="s1" width="20px" height="20px"> - <image id="img1" href="resources/pattern.png" x="0" y="0" width="20px" height="20px" /> -</svg> -<svg id="s2" width="20px" height="20px"> - <image id="img1" href="resources/pattern.png" x="10" y="10" width="20px" height="20px" /> -</svg> -<svg id="s3" width="20px" height="20px"> - <image id="img3" href="resources/pattern.png" x="0" y="0" width="20px" height="20px" /> -</svg> -<svg id="s4" width="20px" height="20px"> - <image id="img4" href="invalid" x="0" y="0" width="20px" height="20px" /> -</svg> -<svg id="s5" width="20px" height="20px"> -</svg> -<svg id="s6" width="20px" height="20px"> - <image id="img1" href="resources/pattern.png" x="0" y="0" width="10px" height="10px" /> -</svg> -</svg>
diff --git a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-svg-image.html b/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-svg-image.html deleted file mode 100644 index 6780ef2..0000000 --- a/third_party/WebKit/LayoutTests/fast/canvas/canvas-createImageBitmap-svg-image.html +++ /dev/null
@@ -1,44 +0,0 @@ -<!doctype html> -<style> -svg { - display: none; -} -canvas { - border: 1px solid black; -} -</style> - -<svg id="s1" width="200px" height="20px"> - <image id="img1" href="resources/pattern.png" x="0" y="0" width="20px" height="20px" /> - <image id="img2" href="resources/pattern.png" x="20" y="0" width="40px" height="40px" /> - <image id="img3" href="resources/pattern.png" x="40" y="0" width="40px" height="40px" /> - <image id="img4" href="invalid" x="60" y="0" width="20px" height="20px" /> - <image id="img5" href="resources/pattern.png" x="80" y="0" width="20px" height="20px" /> -</svg> - -<canvas id="c1" width="20" height="20"></canvas> -<canvas id="c2" width="20" height="20"></canvas> -<canvas id="c3" width="20" height="20"></canvas> -<canvas id="c4" width="20" height="20"></canvas> -<canvas id="c5" width="20" height="20"></canvas> -<canvas id="c6" width="20" height="20"></canvas> - -<script> -var draw = function(target, img, x, y, opts) { - document.getElementById(img).addEventListener("load", function() { - createImageBitmap(this, opts).then((ib) => { - document.getElementById(target).getContext("2d").drawImage( - ib, x || 0, y || 0); - }); - }); -} - -draw("c1", "img1"); -draw("c2", "img2", 10, 10); -draw("c3", "img3"); -draw("c4", "img4"); -document.getElementById("c5").getContext("2d").drawImage( - document.getElementById("img5"), 0, 0); -draw("c6", "img2", 0, 0, {resizeWidth: 10, resizeHeight: 10}); - -</script>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/layers-in-multicol-expected.html b/third_party/WebKit/LayoutTests/fast/multicol/layers-in-multicol-expected.html new file mode 100644 index 0000000..97d5761f --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/multicol/layers-in-multicol-expected.html
@@ -0,0 +1,87 @@ +<!DOCTYPE html> +<style> + .multicol { + width: 300px; + height: 100px; + line-height: 20px; + border: 5px solid maroon; + } + .column { + width: 100px; + float: left; + } + .multicol[dir="rtl"] > .column { + float: right; + } + .block { + display: inline-block; + width: 1em; + height: 10px; + background-color: green; + } + .opacity { + opacity: 0.5; + color: green; + } + .relative { + position: relative; + top: -4px; + color: green; + } +</style> +<p> + Test layers which are fully contained within a single column. +</p> +LTR: +<div class="multicol"> + <div class="column"> + line1<br> + line2<br> + line3<br> + line4<br> + line5<br> + </div> + <div class="column"> + line6<br> + <div class="block"></div> line7<br> + line8<br> + <span class="relative">relative9</span><br> + line10<br> + </div> + <div class="column"> + line11<br> + line12<br> + <!-- The extra inner span below forces the creation of a transparency layer in Skia to work + around optimizations that would cause blending differences between the test and the + expectation. --> + <span class="opacity">opacity<span>13</span></span><br> + line14 + </div> +</div> + +RTL: +<div class="multicol" dir="rtl"> + <div class="column"> + line1<br> + line2<br> + line3<br> + line4<br> + line5<br> + </div> + <div class="column"> + line6<br> + <div class="block"></div> line7<br> + line8<br> + <span class="relative">relative9</span><br> + line10<br> + </div> + <div class="column"> + line11<br> + line12<br> + <!-- The extra inner span below forces the creation of a transparency layer in Skia to work + around optimizations that would cause blending differences between the test and the + expectation. --> + <span class="opacity">opacity<span>13</span></span><br> + line14 + </div> +</div>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/layers-split-across-columns-expected.html b/third_party/WebKit/LayoutTests/fast/multicol/layers-split-across-columns-expected.html new file mode 100644 index 0000000..8bbd920 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/multicol/layers-split-across-columns-expected.html
@@ -0,0 +1,90 @@ +<!DOCTYPE html> +<style> + .container { + margin-right: 4px; + position: absolute; + } + .multicol { + width: 110px; + height: 150px; + border: 5px solid black; + } + .multicol > div { + float: left; + width: 50px; + height: 50px; + } + + .row1_left { background-color: black; } + .row1_right { background-color: #0000b0; } + .row2_left { background-color: #0000f0; } + .row2_right { background-color: #000090; } + .row3_left { background-color: #0000d0; } + .row3_right { background-color: black; } + + .row1_right, + .row2_right, + .row3_right { + margin-left: 10px; + } + + #opacity .row1_right, + #opacity .row2_left, + #opacity .row2_right, + #opacity .row3_left { + opacity: 0.99; + } + + .pos1 { left: 10px; top: 10px; } + .pos2 { left: 150px; top: 10px; } + .pos3 { left: 10px; top: 200px; } + .pos4 { left: 150px; top: 200px; } + +</style> +<div class="container pos1"> + Overflow: + <div class="multicol"> + <div class="row1_left"></div> + <div class="row1_right"></div> + <div class="row2_left"></div> + <div class="row2_right"></div> + <div class="row3_left"></div> + <div class="row3_right"></div> + </div> +</div> +<div class="container pos2"> + Transforms: + <div class="multicol"> + <div class="row1_left"></div> + <div class="row1_right"></div> + <div class="row2_left"></div> + <div class="row2_right"></div> + <div class="row3_left"></div> + <div class="row3_right"></div> + </div> +</div> +<div class="container pos3"> + Relative Pos.: + <div class="multicol"> + <div class="row1_left"></div> + <div class="row1_right"></div> + <div class="row2_left"></div> + <div class="row2_right"></div> + <div class="row3_left"></div> + <div class="row3_right"></div> + </div> +</div> +<div class="container pos4" id="opacity"> + Opacity: + <div class="multicol"> + <div class="row1_left"></div> + <!-- The extra s below force the creation of transparency layers in Skia to work + around optimizations that would cause blending differences between the test and the + expectation. --> + <div class="row1_right"> </div> + <div class="row2_left"> </div> + <div class="row2_right"> </div> + <div class="row3_left"> </div> + <div class="row3_right"></div> + </div> +</div>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.html b/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.html new file mode 100644 index 0000000..0b67873 --- /dev/null +++ b/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.html
@@ -0,0 +1,6 @@ +<div style="position:relative; width:420px;border:2px solid black; height:200px"> +<!-- The extra below forces the creation of a transparency layer in Skia to work around + optimizations that would cause blending differences between the test and the expectation. --> +<div style="opacity:0.5; position:absolute;width:200px;height:100px;background-color:green;right:0;top:0"> </div> +</div> +</div>
diff --git a/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.png b/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.png deleted file mode 100644 index 1ebb6e8..0000000 --- a/third_party/WebKit/LayoutTests/fast/multicol/transform-inside-opacity-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector/service-workers/service-workers-navigation-preload-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector/service-workers/service-workers-navigation-preload-expected.txt index 16d3794c..2ee62a6 100644 --- a/third_party/WebKit/LayoutTests/http/tests/inspector/service-workers/service-workers-navigation-preload-expected.txt +++ b/third_party/WebKit/LayoutTests/http/tests/inspector/service-workers/service-workers-navigation-preload-expected.txt
@@ -20,7 +20,7 @@ timing available: true requestHeaders['Service-Worker-Navigation-Preload']: hello onRequestFinished: - localizedFailDescription: Navigation Preload Error + localizedFailDescription: net::ERR_INVALID_CHUNKED_ENCODING The iframe loaded. ----------------- Loading another iframe.
diff --git a/third_party/WebKit/LayoutTests/paint/invalidation/column-float-under-stacked-inline-expected.png b/third_party/WebKit/LayoutTests/paint/invalidation/column-float-under-stacked-inline-expected.png index c6cabcbb..a92e6a8 100644 --- a/third_party/WebKit/LayoutTests/paint/invalidation/column-float-under-stacked-inline-expected.png +++ b/third_party/WebKit/LayoutTests/paint/invalidation/column-float-under-stacked-inline-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-in-multicol-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-in-multicol-expected.png deleted file mode 100644 index 44fc4a4..0000000 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-in-multicol-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-split-across-columns-expected.png b/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-split-across-columns-expected.png deleted file mode 100644 index 27fbf58..0000000 --- a/third_party/WebKit/LayoutTests/platform/linux/fast/multicol/layers-split-across-columns-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index 5e1fff8..b024e71 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/linux/transforms/2d/hindi-rotated-expected.png index 6f98684..8d5f6fd3 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/transforms/2d/hindi-rotated-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/transforms/2d/hindi-rotated-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index 5e1fff8..b024e71 100644 --- a/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/linux/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/transforms/2d/hindi-rotated-expected.png index 0acea10..3a881330 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.10/transforms/2d/hindi-rotated-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.10/transforms/2d/hindi-rotated-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index 991aaf7a..07e019c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/2d/hindi-rotated-expected.png index 84b89da..11b5d71 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/2d/hindi-rotated-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/2d/hindi-rotated-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/transformed-focused-text-input-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/transformed-focused-text-input-expected.png index 1693941..f409f9e 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/transformed-focused-text-input-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/transforms/transformed-focused-text-input-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index 991aaf7a..07e019c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac-mac10.9/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-in-multicol-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-in-multicol-expected.png deleted file mode 100644 index 9243efa..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-in-multicol-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-split-across-columns-expected.png b/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-split-across-columns-expected.png deleted file mode 100644 index 86cf379..0000000 --- a/third_party/WebKit/LayoutTests/platform/mac/fast/multicol/layers-split-across-columns-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index af00c64c0..56d7e380c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png index 31dd704..ab426ac 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/as-background-image/background-image-preserveaspectRatio-support-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/as-image/img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/as-image/img-preserveAspectRatio-support-1-expected.png index 98ce8c5..7e1243a1 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/as-image/img-preserveAspectRatio-support-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/as-image/img-preserveAspectRatio-support-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png index 556507c..8938c8b 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/svg/zoom/page/zoom-img-preserveAspectRatio-support-1-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/2d/hindi-rotated-expected.png index 70f55312..497919fc 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/transforms/2d/hindi-rotated-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/2d/hindi-rotated-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-focused-text-input-expected.png b/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-focused-text-input-expected.png index c57f417b..38e6870 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-focused-text-input-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/transforms/transformed-focused-text-input-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index af00c64c0..56d7e380c 100644 --- a/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/mac/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-in-multicol-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-in-multicol-expected.png deleted file mode 100644 index 62b98ea..0000000 --- a/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-in-multicol-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-split-across-columns-expected.png b/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-split-across-columns-expected.png deleted file mode 100644 index 608b610..0000000 --- a/third_party/WebKit/LayoutTests/platform/win/fast/multicol/layers-split-across-columns-expected.png +++ /dev/null Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index 6eb1bf6..8829276ef 100644 --- a/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/win/transforms/2d/hindi-rotated-expected.png index 5b597ed..6837fed 100644 --- a/third_party/WebKit/LayoutTests/platform/win/transforms/2d/hindi-rotated-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/transforms/2d/hindi-rotated-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index 6eb1bf6..8829276ef 100644 --- a/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index 27dad6f..bbb8278 100644 --- a/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win7/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/transforms/2d/hindi-rotated-expected.png b/third_party/WebKit/LayoutTests/platform/win7/transforms/2d/hindi-rotated-expected.png index 855bfdf8..7e9778db 100644 --- a/third_party/WebKit/LayoutTests/platform/win7/transforms/2d/hindi-rotated-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win7/transforms/2d/hindi-rotated-expected.png Binary files differ
diff --git a/third_party/WebKit/LayoutTests/platform/win7/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png b/third_party/WebKit/LayoutTests/platform/win7/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png index 27dad6f..bbb8278 100644 --- a/third_party/WebKit/LayoutTests/platform/win7/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png +++ b/third_party/WebKit/LayoutTests/platform/win7/virtual/disable-spinvalidation/paint/invalidation/svg/scrolling-embedded-svg-file-image-repaint-problem-expected.png Binary files differ
diff --git a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md index 4aff4bd..986f8f2 100644 --- a/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md +++ b/third_party/WebKit/Source/bindings/IDLExtendedAttributes.md
@@ -811,12 +811,12 @@ You can write custom bindings as V8XXX::namedPropertyQuery(...) and V8XXX::namedPropertyEnumerator(...) in Source/bindings/v8/custom/V8XXXCustom.cpp: ```c++ -v8::Handle<v8::Integer> V8XXX::namedPropertyQuery(v8::Local<v8::String> name, const v8::AccessorInfo& info) +v8::Local<v8::Integer> V8XXX::namedPropertyQuery(v8::Local<v8::String> name, const v8::AccessorInfo& info) { ...; } -v8::Handle<v8::Array> V8XXX::namedPropertyEnumerator(const v8::AccessorInfo& info) +v8::Local<v8::Array> V8XXX::namedPropertyEnumerator(const v8::AccessorInfo& info) { ...; } @@ -841,7 +841,7 @@ You can write custom `V8XXX::callAsFunctionCallback(...)` in Source/bindings/v8/custom/V8XXXCustom.cpp: ```c++ -v8::Handle<v8::Value> V8XXX::callAsFunctionCallback(const v8::Arguments& args) +v8::Local<v8::Value> V8XXX::callAsFunctionCallback(const v8::Arguments& args) { ...; } @@ -1411,7 +1411,7 @@ Then you can write custom bindings in Source/bindings/v8/custom/V8XXXConstructorCustom.cpp: ```c++ -v8::Handle<v8::Value> V8XXX::constructorCallback(const v8::Arguments& args) +v8::Local<v8::Value> V8XXX::constructorCallback(const v8::Arguments& args) { ...; }
diff --git a/third_party/WebKit/Source/bindings/core/v8/BUILD.gn b/third_party/WebKit/Source/bindings/core/v8/BUILD.gn index 9686fc9..086d637 100644 --- a/third_party/WebKit/Source/bindings/core/v8/BUILD.gn +++ b/third_party/WebKit/Source/bindings/core/v8/BUILD.gn
@@ -204,8 +204,8 @@ "$bindings_core_v8_output_dir/FloatOrStringElementRecord.h", "$bindings_core_v8_output_dir/HTMLElementOrLong.cpp", "$bindings_core_v8_output_dir/HTMLElementOrLong.h", - "$bindings_core_v8_output_dir/HTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas.cpp", - "$bindings_core_v8_output_dir/HTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas.h", + "$bindings_core_v8_output_dir/HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas.cpp", + "$bindings_core_v8_output_dir/HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas.h", "$bindings_core_v8_output_dir/HTMLOptionElementOrHTMLOptGroupElement.cpp", "$bindings_core_v8_output_dir/HTMLOptionElementOrHTMLOptGroupElement.h", "$bindings_core_v8_output_dir/HTMLScriptElementOrSVGScriptElement.cpp",
diff --git a/third_party/WebKit/Source/bindings/core/v8/ScopedPersistent.h b/third_party/WebKit/Source/bindings/core/v8/ScopedPersistent.h index 3d5872a..51ffd6e 100644 --- a/third_party/WebKit/Source/bindings/core/v8/ScopedPersistent.h +++ b/third_party/WebKit/Source/bindings/core/v8/ScopedPersistent.h
@@ -85,7 +85,7 @@ handle_.Reset(isolate, handle); } - // Note: This is clear in the std::unique_ptr sense, not the v8::Handle sense. + // Note: This is clear in the std::unique_ptr sense, not the v8::Local sense. void Clear() { handle_.Reset(); } bool operator==(const ScopedPersistent<T>& other) {
diff --git a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h index 0594bc6..60b8cf6 100644 --- a/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h +++ b/third_party/WebKit/Source/core/css/CSSPrimitiveValueMappings.h
@@ -475,6 +475,9 @@ case kCapsLockIndicatorPart: value_id_ = CSSValueCapsLockIndicator; break; + case kMediaRemotingCastIconPart: + value_id_ = CSSValueInternalMediaRemotingCastIcon; + break; } }
diff --git a/third_party/WebKit/Source/core/css/CSSValueKeywords.json5 b/third_party/WebKit/Source/core/css/CSSValueKeywords.json5 index 6dfe0a6..e340a8fe 100644 --- a/third_party/WebKit/Source/core/css/CSSValueKeywords.json5 +++ b/third_party/WebKit/Source/core/css/CSSValueKeywords.json5
@@ -668,6 +668,7 @@ "-internal-media-subtitles-icon", "-internal-media-overflow-button", "-internal-media-download-button", + "-internal-media-remoting-cast-icon", "menulist", "menulist-button", "menulist-text",
diff --git a/third_party/WebKit/Source/core/css/mediaControls.css b/third_party/WebKit/Source/core/css/mediaControls.css index f13cb3d..dd4a094 100644 --- a/third_party/WebKit/Source/core/css/mediaControls.css +++ b/third_party/WebKit/Source/core/css/mediaControls.css
@@ -145,6 +145,87 @@ padding: 0; } +/* TODO(xjz): Move media remoting elements to a separate css file. */ +video::-internal-media-remoting-interstitial { + width: inherit; + height: inherit; + position: relative; + direction: ltr; + display: flex; + flex-direction: column; + font-family: Segoe, "Helvetica Neue", Roboto, Arial, Helvetica, sans-serif; + justify-content: flex-end; + align-items: center; + font-size: 28px; + background-color: black; + transition: opacity .2s cubic-bezier (0.4, 0.0, 0.2, 1); +} + +video::-internal-media-remoting-background-image { + display: flex; + position: absolute; + margin: 0; + top: 0px; + left: 0px; + width: 100%; + height: 100%; + border: none; + border-width: 0px; + background-color: transparent; + padding: 0; + filter: grayscale(100%) blur(5px); +} + +video::-internal-media-remoting-cast-icon { + -webkit-appearance: -internal-media-remoting-cast-icon; + display: flex; + position: absolute; + margin: 0px; + border-width: 0px; + background-color: transparent; + height: 36px; + width: 44px; + padding: 0px; + left: calc(50% - 22px); + top: calc(50% - 60px); +} + +video::-internal-media-remoting-cast-text-message { + display: inline; + position: absolute; + top: calc(50% - 10px); + border: none; + color: #FFFFFF; + opacity: 54% + width: 100%; + text-wrap: none; + text-align: center; + background-color: transparent; + font-size: 13pt; + font-face: Roboto-Regular, Sans-serif, Segoe, Serif, Helvetica; + padding: 0px; + margin: 0px; +} + +video::-internal-media-remoting-disable-button { + display: flex; + position: absolute; + top: calc(50% + 55pt); + left: calc(50% - 102px); + height: 28pt; + border: 2pt solid rgba(255,255,255,.54); + border-radius: 2pt; + background-color: transparent; + color: #FFFFFF; + margin: 0px; + padding: 8pt 16pt 0pt 16pt; + text-wrap: none; + font-size: 13pt; + font-face: Roboto-Medium, Sans-serif, Segoe, Serif, Helvetica; + opacity: 54% + transition: border .5s ease-out; +} + video::-internal-media-controls-overlay-cast-button { -webkit-appearance: -internal-media-overlay-cast-off-button; display: flex;
diff --git a/third_party/WebKit/Source/core/dom/BUILD.gn b/third_party/WebKit/Source/core/dom/BUILD.gn index 13a8eb86..c23c3a3d 100644 --- a/third_party/WebKit/Source/core/dom/BUILD.gn +++ b/third_party/WebKit/Source/core/dom/BUILD.gn
@@ -197,6 +197,8 @@ "Modulator.h", "ModuleMap.cpp", "ModuleMap.h", + "ModulePendingScript.cpp", + "ModulePendingScript.h", "ModuleScript.cpp", "ModuleScript.h", "MutationCallback.h",
diff --git a/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp b/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp index 2ff5068..047b3647 100644 --- a/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp +++ b/third_party/WebKit/Source/core/dom/ClassicPendingScript.cpp
@@ -229,7 +229,7 @@ return GetResource()->WasCanceled(); } -KURL ClassicPendingScript::Url() const { +KURL ClassicPendingScript::UrlForClassicScript() const { return GetResource()->Url(); }
diff --git a/third_party/WebKit/Source/core/dom/ClassicPendingScript.h b/third_party/WebKit/Source/core/dom/ClassicPendingScript.h index 13a7519b..153eeb61 100644 --- a/third_party/WebKit/Source/core/dom/ClassicPendingScript.h +++ b/third_party/WebKit/Source/core/dom/ClassicPendingScript.h
@@ -49,11 +49,11 @@ ClassicScript* GetSource(const KURL& document_url, bool& error_occurred) const override; bool IsReady() const override; - KURL Url() const override; bool IsExternal() const override { return GetResource(); } bool ErrorOccurred() const override; bool WasCanceled() const override; void StartStreamingIfPossible(Document*, ScriptStreamer::Type) override; + KURL UrlForClassicScript() const override; void RemoveFromMemoryCache() override; void DisposeInternal() override;
diff --git a/third_party/WebKit/Source/core/dom/ClassicScript.cpp b/third_party/WebKit/Source/core/dom/ClassicScript.cpp index 9898325..a4b726bb 100644 --- a/third_party/WebKit/Source/core/dom/ClassicScript.cpp +++ b/third_party/WebKit/Source/core/dom/ClassicScript.cpp
@@ -51,10 +51,6 @@ visitor->Trace(script_source_code_); } -DEFINE_TRACE_WRAPPERS(ClassicScript) { - Script::TraceWrappers(visitor); -} - bool ClassicScript::IsEmpty() const { return GetScriptSourceCode().IsEmpty(); }
diff --git a/third_party/WebKit/Source/core/dom/ClassicScript.h b/third_party/WebKit/Source/core/dom/ClassicScript.h index b8c36533..887214d 100644 --- a/third_party/WebKit/Source/core/dom/ClassicScript.h +++ b/third_party/WebKit/Source/core/dom/ClassicScript.h
@@ -18,7 +18,6 @@ } DECLARE_TRACE(); - DECLARE_TRACE_WRAPPERS(); const ScriptSourceCode& GetScriptSourceCode() const { return script_source_code_;
diff --git a/third_party/WebKit/Source/core/dom/ElementRareData.h b/third_party/WebKit/Source/core/dom/ElementRareData.h index ee04b86..9422f5c8 100644 --- a/third_party/WebKit/Source/core/dom/ElementRareData.h +++ b/third_party/WebKit/Source/core/dom/ElementRareData.h
@@ -44,15 +44,14 @@ namespace blink { -class LayoutObject; class CompositorProxiedPropertySet; class ResizeObservation; class ResizeObserver; class ElementRareData : public NodeRareData { public: - static ElementRareData* Create(LayoutObject* layout_object) { - return new ElementRareData(layout_object); + static ElementRareData* Create(NodeLayoutData* node_layout_data) { + return new ElementRareData(node_layout_data); } ~ElementRareData(); @@ -226,17 +225,16 @@ Member<AccessibleNode> accessible_node_; - explicit ElementRareData(LayoutObject*); + explicit ElementRareData(NodeLayoutData*); }; - DEFINE_TRAIT_FOR_TRACE_WRAPPERS(ElementRareData); inline LayoutSize DefaultMinimumSizeForResizing() { return LayoutSize(LayoutUnit::Max(), LayoutUnit::Max()); } -inline ElementRareData::ElementRareData(LayoutObject* layout_object) - : NodeRareData(layout_object), +inline ElementRareData::ElementRareData(NodeLayoutData* node_layout_data) + : NodeRareData(node_layout_data), minimum_size_for_resizing_(DefaultMinimumSizeForResizing()), class_list_(nullptr) { is_element_rare_data_ = true;
diff --git a/third_party/WebKit/Source/core/dom/Modulator.h b/third_party/WebKit/Source/core/dom/Modulator.h index ef95e0b..ad1d582 100644 --- a/third_party/WebKit/Source/core/dom/Modulator.h +++ b/third_party/WebKit/Source/core/dom/Modulator.h
@@ -34,6 +34,13 @@ virtual void NotifyModuleLoadFinished(ModuleScript*) = 0; }; +// A ModuleTreeClient is notified when a module script and its whole descendent +// tree load is complete. +class ModuleTreeClient : public GarbageCollectedMixin { + public: + virtual void NotifyModuleTreeLoadFinished(ModuleScript*) = 0; +}; + // spec: "top-level module fetch flag" // https://html.spec.whatwg.org/multipage/webappapis.html#fetching-scripts-is-top-level enum class ModuleGraphLevel { kTopLevelModuleFetch, kDependentModuleFetch };
diff --git a/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp b/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp new file mode 100644 index 0000000..f0184b1 --- /dev/null +++ b/third_party/WebKit/Source/core/dom/ModulePendingScript.cpp
@@ -0,0 +1,82 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/dom/ModulePendingScript.h" + +#include "core/dom/ScriptLoader.h" +#include "core/frame/LocalFrame.h" + +namespace blink { + +ModulePendingScriptTreeClient::ModulePendingScriptTreeClient() + : module_script_(nullptr), pending_script_(nullptr) {} + +void ModulePendingScriptTreeClient::SetPendingScript( + ModulePendingScript* pending_script) { + DCHECK(!pending_script_); + pending_script_ = pending_script; + + if (finished_) { + pending_script_->NotifyModuleTreeLoadFinished(); + } +} + +void ModulePendingScriptTreeClient::NotifyModuleTreeLoadFinished( + ModuleScript* module_script) { + DCHECK(!(module_script && module_script->InstantiationState() == + ModuleInstantiationState::kUninstantiated)); + DCHECK(!finished_); + finished_ = true; + module_script_ = module_script; + + if (pending_script_) + pending_script_->NotifyModuleTreeLoadFinished(); +} + +DEFINE_TRACE(ModulePendingScriptTreeClient) { + visitor->Trace(module_script_); + visitor->Trace(pending_script_); + ModuleTreeClient::Trace(visitor); +} + +ModulePendingScript::ModulePendingScript(ScriptElementBase* element, + ModulePendingScriptTreeClient* client) + : PendingScript(element, TextPosition()), module_tree_client_(client) { + CHECK(this->GetElement()); + DCHECK(module_tree_client_); + client->SetPendingScript(this); +} + +ModulePendingScript::~ModulePendingScript() {} + +void ModulePendingScript::DisposeInternal() { + module_tree_client_ = nullptr; +} + +DEFINE_TRACE(ModulePendingScript) { + visitor->Trace(module_tree_client_); + PendingScript::Trace(visitor); +} + +void ModulePendingScript::NotifyModuleTreeLoadFinished() { + CHECK(!IsReady()); + ready_ = true; + + if (Client()) + Client()->PendingScriptFinished(this); +} + +Script* ModulePendingScript::GetSource(const KURL& document_url, + bool& error_occurred) const { + CHECK(IsReady()); + error_occurred = ErrorOccurred(); + return GetModuleScript(); +} + +bool ModulePendingScript::ErrorOccurred() const { + CHECK(IsReady()); + return !GetModuleScript(); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/dom/ModulePendingScript.h b/third_party/WebKit/Source/core/dom/ModulePendingScript.h new file mode 100644 index 0000000..62ecccb --- /dev/null +++ b/third_party/WebKit/Source/core/dom/ModulePendingScript.h
@@ -0,0 +1,98 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef ModulePendingScript_h +#define ModulePendingScript_h + +#include "core/dom/Modulator.h" +#include "core/dom/ModuleScript.h" +#include "core/dom/PendingScript.h" + +namespace blink { + +class ModulePendingScript; + +// ModulePendingScriptTreeClient is used to connect from Modulator::FetchTree() +// to ModulePendingScript. Because ModulePendingScript is created after +// Modulator::FetchTree() is called, ModulePendingScriptTreeClient is +// registered as ModuleTreeClient to FetchTree() first, and later +// ModulePendingScript is supplied to ModulePendingScriptTreeClient via +// SetPendingScript() and is notified of module tree load finish. +class ModulePendingScriptTreeClient final + : public GarbageCollectedFinalized<ModulePendingScriptTreeClient>, + public ModuleTreeClient { + USING_GARBAGE_COLLECTED_MIXIN(ModulePendingScriptTreeClient); + + public: + static ModulePendingScriptTreeClient* Create() { + return new ModulePendingScriptTreeClient(); + } + virtual ~ModulePendingScriptTreeClient() = default; + + void SetPendingScript(ModulePendingScript* client); + + ModuleScript* GetModuleScript() const { return module_script_; } + + DECLARE_TRACE(); + + private: + ModulePendingScriptTreeClient(); + + // Implements ModuleTreeClient + void NotifyModuleTreeLoadFinished(ModuleScript*) override; + + bool finished_ = false; + Member<ModuleScript> module_script_; + Member<ModulePendingScript> pending_script_; +}; + +// PendingScript for a module script +// https://html.spec.whatwg.org/#module-script. +class CORE_EXPORT ModulePendingScript : public PendingScript { + public: + static ModulePendingScript* Create(ScriptElementBase* element, + ModulePendingScriptTreeClient* client) { + return new ModulePendingScript(element, client); + } + + ~ModulePendingScript() override; + + void NotifyModuleTreeLoadFinished(); + + ModuleScript* GetModuleScript() const { + return module_tree_client_->GetModuleScript(); + } + + DECLARE_TRACE(); + + private: + ModulePendingScript(ScriptElementBase*, ModulePendingScriptTreeClient*); + + // PendingScript + ScriptType GetScriptType() const override { return ScriptType::kModule; } + Script* GetSource(const KURL& document_url, + bool& error_occurred) const override; + bool IsReady() const override { return ready_; } + bool IsExternal() const override { return true; } + bool ErrorOccurred() const override; + bool WasCanceled() const override { return false; } + + void StartStreamingIfPossible(Document*, ScriptStreamer::Type) override {} + KURL UrlForClassicScript() const override { + NOTREACHED(); + return KURL(); + } + void RemoveFromMemoryCache() override { NOTREACHED(); } + + void DisposeInternal() override; + + void CheckState() const override {} + + Member<ModulePendingScriptTreeClient> module_tree_client_; + bool ready_ = false; +}; + +} // namespace blink + +#endif // ModulePendingScript_h
diff --git a/third_party/WebKit/Source/core/dom/ModuleScript.cpp b/third_party/WebKit/Source/core/dom/ModuleScript.cpp index ced261f0..28fa847d 100644 --- a/third_party/WebKit/Source/core/dom/ModuleScript.cpp +++ b/third_party/WebKit/Source/core/dom/ModuleScript.cpp
@@ -24,7 +24,6 @@ Script::Trace(visitor); } DEFINE_TRACE_WRAPPERS(ModuleScript) { - Script::TraceWrappers(visitor); visitor->TraceWrappers(instantiation_error_); }
diff --git a/third_party/WebKit/Source/core/dom/ModuleScript.h b/third_party/WebKit/Source/core/dom/ModuleScript.h index 6624f605..dbe6db9 100644 --- a/third_party/WebKit/Source/core/dom/ModuleScript.h +++ b/third_party/WebKit/Source/core/dom/ModuleScript.h
@@ -27,7 +27,7 @@ // ModuleScript is a model object for the "module script" spec concept. // https://html.spec.whatwg.org/multipage/webappapis.html#module-script -class CORE_EXPORT ModuleScript final : public Script { +class CORE_EXPORT ModuleScript final : public Script, public TraceWrapperBase { public: static ModuleScript* Create( ScriptModule record, @@ -58,7 +58,7 @@ const String& Nonce() const { return nonce_; } DECLARE_TRACE(); - DECLARE_VIRTUAL_TRACE_WRAPPERS(); + DECLARE_TRACE_WRAPPERS(); private: ModuleScript(ScriptModule record, @@ -96,6 +96,13 @@ ModuleInstantiationState::kUninstantiated; // https://html.spec.whatwg.org/multipage/webappapis.html#concept-module-script-instantiation-error + // + // |instantiation_error_| is TraceWrappers()ed and kept alive via the path of + // v8::Context -> PerContextData -> Modulator/ModulatorImpl + // -> ModuleMap -> ModuleMap::Entry -> ModuleScript -> instantiation_error_. + // All the classes/references on the path above should be + // TraceWrapperBase/TraceWrapperMember<>/etc., + // but other references to those classes can be normal Member<>. TraceWrapperV8Reference<v8::Value> instantiation_error_; // https://html.spec.whatwg.org/multipage/webappapis.html#concept-module-script-nonce
diff --git a/third_party/WebKit/Source/core/dom/Node.cpp b/third_party/WebKit/Source/core/dom/Node.cpp index e7d6f09..a61464b 100644 --- a/third_party/WebKit/Source/core/dom/Node.cpp +++ b/third_party/WebKit/Source/core/dom/Node.cpp
@@ -295,9 +295,8 @@ } Node::~Node() { - // With Oilpan, the rare data finalizer also asserts for - // this condition (we cannot directly access it here.) - CHECK(HasRareData() || !GetLayoutObject()); + if (!HasRareData() && !data_.node_layout_data_->IsSharedEmptyData()) + delete data_.node_layout_data_; InstanceCounters::DecrementNodeCounter(); } @@ -311,9 +310,9 @@ return *RareData(); if (IsElementNode()) - data_.rare_data_ = ElementRareData::Create(data_.layout_object_); + data_.rare_data_ = ElementRareData::Create(data_.node_layout_data_); else - data_.rare_data_ = NodeRareData::Create(data_.layout_object_); + data_.rare_data_ = NodeRareData::Create(data_.node_layout_data_); DCHECK(data_.rare_data_); SetFlag(kHasRareDataFlag); @@ -589,6 +588,30 @@ : nullptr; } +void Node::SetLayoutObject(LayoutObject* layout_object) { + NodeLayoutData* node_layout_data = HasRareData() + ? data_.rare_data_->GetNodeLayoutData() + : data_.node_layout_data_; + + // Already pointing to a non empty NodeLayoutData so just set the pointer to + // the new LayoutObject. + if (!node_layout_data->IsSharedEmptyData()) { + node_layout_data->SetLayoutObject(layout_object); + return; + } + + if (!layout_object) + return; + + // Swap the NodeLayoutData to point to a new NodeLayoutData instead of the + // static SharedEmptyData instance. + node_layout_data = new NodeLayoutData(layout_object); + if (HasRareData()) + data_.rare_data_->SetNodeLayoutData(node_layout_data); + else + data_.node_layout_data_ = node_layout_data; +} + LayoutBoxModelObject* Node::GetLayoutBoxModelObject() const { LayoutObject* layout_object = this->GetLayoutObject(); return layout_object && layout_object->IsBoxModelObject()
diff --git a/third_party/WebKit/Source/core/dom/Node.h b/third_party/WebKit/Source/core/dom/Node.h index c3f294d8..369b8c5 100644 --- a/third_party/WebKit/Source/core/dom/Node.h +++ b/third_party/WebKit/Source/core/dom/Node.h
@@ -57,6 +57,7 @@ class NodeList; class NodeListsNodeData; class NodeOrString; +class NodeLayoutData; class NodeRareData; class QualifiedName; class RegisteredEventListener; @@ -98,21 +99,45 @@ kChained, }; -class NodeRareDataBase { +class NodeLayoutData { public: + explicit NodeLayoutData(LayoutObject* layout_object) + : layout_object_(layout_object) {} + ~NodeLayoutData() { CHECK(!layout_object_); } + LayoutObject* GetLayoutObject() const { return layout_object_; } void SetLayoutObject(LayoutObject* layout_object) { + DCHECK_NE(&SharedEmptyData(), this); layout_object_ = layout_object; } + static NodeLayoutData& SharedEmptyData() { + DEFINE_STATIC_LOCAL(NodeLayoutData, shared_empty_data, (nullptr)); + return shared_empty_data; + } + bool IsSharedEmptyData() { return this == &SharedEmptyData(); } + + private: + LayoutObject* layout_object_; +}; + +class NodeRareDataBase { + public: + NodeLayoutData* GetNodeLayoutData() const { return node_layout_data_; } + void SetNodeLayoutData(NodeLayoutData* node_layout_data) { + DCHECK(node_layout_data); + node_layout_data_ = node_layout_data; + } protected: - NodeRareDataBase(LayoutObject* layout_object) - : layout_object_(layout_object) {} + NodeRareDataBase(NodeLayoutData* node_layout_data) + : node_layout_data_(node_layout_data) {} + ~NodeRareDataBase() { + if (node_layout_data_ && !node_layout_data_->IsSharedEmptyData()) + delete node_layout_data_; + } protected: - // LayoutObjects are fully owned by their DOM node. See LayoutObject's - // LIFETIME documentation section. - LayoutObject* layout_object_; + NodeLayoutData* node_layout_data_; }; class Node; @@ -287,6 +312,7 @@ virtual bool IsAttributeNode() const { return false; } virtual bool IsCharacterDataNode() const { return false; } virtual bool IsFrameOwnerElement() const { return false; } + virtual bool IsMediaRemotingInterstitial() const { return false; } bool IsStyledElement() const; @@ -568,16 +594,11 @@ // Note that if a Node has a layoutObject, it's parentNode is guaranteed to // have one as well. LayoutObject* GetLayoutObject() const { - return HasRareData() ? data_.rare_data_->GetLayoutObject() - : data_.layout_object_; + return HasRareData() + ? data_.rare_data_->GetNodeLayoutData()->GetLayoutObject() + : data_.node_layout_data_->GetLayoutObject(); } - void SetLayoutObject(LayoutObject* layout_object) { - if (HasRareData()) - data_.rare_data_->SetLayoutObject(layout_object); - else - data_.layout_object_ = layout_object; - } - + void SetLayoutObject(LayoutObject*); // Use these two methods with caution. LayoutBox* GetLayoutBox() const; LayoutBoxModelObject* GetLayoutBoxModelObject() const; @@ -930,10 +951,10 @@ Member<Node> next_; // When a node has rare data we move the layoutObject into the rare data. union DataUnion { - DataUnion() : layout_object_(nullptr) {} + DataUnion() : node_layout_data_(&NodeLayoutData::SharedEmptyData()) {} // LayoutObjects are fully owned by their DOM node. See LayoutObject's // LIFETIME documentation section. - LayoutObject* layout_object_; + NodeLayoutData* node_layout_data_; NodeRareDataBase* rare_data_; } data_; };
diff --git a/third_party/WebKit/Source/core/dom/NodeRareData.cpp b/third_party/WebKit/Source/core/dom/NodeRareData.cpp index 221b9ca..46b358d 100644 --- a/third_party/WebKit/Source/core/dom/NodeRareData.cpp +++ b/third_party/WebKit/Source/core/dom/NodeRareData.cpp
@@ -77,7 +77,6 @@ } void NodeRareData::FinalizeGarbageCollectedObject() { - CHECK(!GetLayoutObject()); if (is_element_rare_data_) static_cast<ElementRareData*>(this)->~ElementRareData(); else
diff --git a/third_party/WebKit/Source/core/dom/NodeRareData.h b/third_party/WebKit/Source/core/dom/NodeRareData.h index f33fbe6..0e50069f 100644 --- a/third_party/WebKit/Source/core/dom/NodeRareData.h +++ b/third_party/WebKit/Source/core/dom/NodeRareData.h
@@ -98,8 +98,8 @@ WTF_MAKE_NONCOPYABLE(NodeRareData); public: - static NodeRareData* Create(LayoutObject* layout_object) { - return new NodeRareData(layout_object); + static NodeRareData* Create(NodeLayoutData* node_layout_data) { + return new NodeRareData(node_layout_data); } void ClearNodeLists() { node_lists_.Clear(); } @@ -163,12 +163,14 @@ DECLARE_TRACE_WRAPPERS_AFTER_DISPATCH(); protected: - explicit NodeRareData(LayoutObject* layout_object) - : NodeRareDataBase(layout_object), + explicit NodeRareData(NodeLayoutData* node_layout_data) + : NodeRareDataBase(node_layout_data), connected_frame_count_(0), element_flags_(0), restyle_flags_(0), - is_element_rare_data_(false) {} + is_element_rare_data_(false) { + CHECK_NE(node_layout_data, nullptr); + } private: Member<NodeListsNodeData> node_lists_;
diff --git a/third_party/WebKit/Source/core/dom/PendingScript.h b/third_party/WebKit/Source/core/dom/PendingScript.h index f0175f5..6886b9bb 100644 --- a/third_party/WebKit/Source/core/dom/PendingScript.h +++ b/third_party/WebKit/Source/core/dom/PendingScript.h
@@ -88,14 +88,14 @@ // https://html.spec.whatwg.org/#the-script-is-ready virtual bool IsReady() const = 0; - virtual KURL Url() const = 0; virtual bool IsExternal() const = 0; virtual bool ErrorOccurred() const = 0; virtual bool WasCanceled() const = 0; virtual void StartStreamingIfPossible(Document*, ScriptStreamer::Type) = 0; - // Used for document.write() intervention. - // Has effects only for classic scripts. + // The following two methods are used for document.write() intervention and + // have effects only for classic scripts. + virtual KURL UrlForClassicScript() const = 0; virtual void RemoveFromMemoryCache() = 0; void Dispose();
diff --git a/third_party/WebKit/Source/core/dom/Script.h b/third_party/WebKit/Source/core/dom/Script.h index b3e1be5..471751b 100644 --- a/third_party/WebKit/Source/core/dom/Script.h +++ b/third_party/WebKit/Source/core/dom/Script.h
@@ -19,11 +19,9 @@ enum class ScriptType { kClassic, kModule }; // https://html.spec.whatwg.org/#concept-script -class CORE_EXPORT Script : public GarbageCollectedFinalized<Script>, - public TraceWrapperBase { +class CORE_EXPORT Script : public GarbageCollectedFinalized<Script> { public: DEFINE_INLINE_VIRTUAL_TRACE() {} - DEFINE_INLINE_VIRTUAL_TRACE_WRAPPERS() {} virtual ~Script() {}
diff --git a/third_party/WebKit/Source/core/frame/ImageBitmap.cpp b/third_party/WebKit/Source/core/frame/ImageBitmap.cpp index abd2738..74e0cefd 100644 --- a/third_party/WebKit/Source/core/frame/ImageBitmap.cpp +++ b/third_party/WebKit/Source/core/frame/ImageBitmap.cpp
@@ -554,7 +554,7 @@ return StaticBitmapImage::Create(PremulSkImageToUnPremul(skia_image.get())); } -ImageBitmap::ImageBitmap(ImageElementBase* image, +ImageBitmap::ImageBitmap(HTMLImageElement* image, Optional<IntRect> crop_rect, Document* document, const ImageBitmapOptions& options) { @@ -996,7 +996,7 @@ ImageBitmap::~ImageBitmap() {} -ImageBitmap* ImageBitmap::Create(ImageElementBase* image, +ImageBitmap* ImageBitmap::Create(HTMLImageElement* image, Optional<IntRect> crop_rect, Document* document, const ImageBitmapOptions& options) {
diff --git a/third_party/WebKit/Source/core/frame/ImageBitmap.h b/third_party/WebKit/Source/core/frame/ImageBitmap.h index 3684c0c3..db57545 100644 --- a/third_party/WebKit/Source/core/frame/ImageBitmap.h +++ b/third_party/WebKit/Source/core/frame/ImageBitmap.h
@@ -8,8 +8,8 @@ #include <memory> #include "bindings/core/v8/ScriptWrappable.h" #include "core/CoreExport.h" +#include "core/html/HTMLImageElement.h" #include "core/html/canvas/CanvasImageSource.h" -#include "core/html/canvas/ImageElementBase.h" #include "core/imagebitmap/ImageBitmapOptions.h" #include "core/imagebitmap/ImageBitmapSource.h" #include "platform/geometry/IntRect.h" @@ -21,7 +21,6 @@ #include "third_party/skia/include/core/SkRefCnt.h" namespace blink { -class Document; class HTMLCanvasElement; class HTMLVideoElement; class ImageData; @@ -49,7 +48,7 @@ DEFINE_WRAPPERTYPEINFO(); public: - static ImageBitmap* Create(ImageElementBase*, + static ImageBitmap* Create(HTMLImageElement*, Optional<IntRect>, Document*, const ImageBitmapOptions& = ImageBitmapOptions()); @@ -139,7 +138,7 @@ DECLARE_VIRTUAL_TRACE(); private: - ImageBitmap(ImageElementBase*, + ImageBitmap(HTMLImageElement*, Optional<IntRect>, Document*, const ImageBitmapOptions&);
diff --git a/third_party/WebKit/Source/core/html/BUILD.gn b/third_party/WebKit/Source/core/html/BUILD.gn index 865c8db2..66703ff 100644 --- a/third_party/WebKit/Source/core/html/BUILD.gn +++ b/third_party/WebKit/Source/core/html/BUILD.gn
@@ -263,12 +263,12 @@ "canvas/CanvasDrawListener.h", "canvas/CanvasFontCache.cpp", "canvas/CanvasFontCache.h", + "canvas/CanvasImageElementSource.cpp", + "canvas/CanvasImageElementSource.h", "canvas/CanvasImageSource.h", "canvas/CanvasRenderingContext.cpp", "canvas/CanvasRenderingContext.h", "canvas/CanvasRenderingContextFactory.h", - "canvas/ImageElementBase.cpp", - "canvas/ImageElementBase.h", "forms/BaseButtonInputType.cpp", "forms/BaseButtonInputType.h", "forms/BaseCheckableInputType.cpp", @@ -485,6 +485,10 @@ "shadow/MediaControlElements.h", "shadow/MediaControlTimelineMetrics.cpp", "shadow/MediaControlTimelineMetrics.h", + "shadow/MediaRemotingElements.cpp", + "shadow/MediaRemotingElements.h", + "shadow/MediaRemotingInterstitial.cpp", + "shadow/MediaRemotingInterstitial.h", "shadow/ProgressShadowElement.cpp", "shadow/ProgressShadowElement.h", "shadow/ShadowElementNames.cpp",
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp index 105b9f0..c16060d 100644 --- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.cpp
@@ -74,6 +74,7 @@ #include "platform/graphics/StaticBitmapImage.h" #include "platform/graphics/UnacceleratedImageBufferSurface.h" #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h" +#include "platform/graphics/gpu/SharedGpuContext.h" #include "platform/graphics/paint/PaintCanvas.h" #include "platform/image-encoders/ImageEncoderUtils.h" #include "platform/transforms/AffineTransform.h" @@ -276,13 +277,12 @@ probe::didCreateCanvasContext(&GetDocument()); - if (context_->Is3d()) { + if (Is3d()) { UpdateExternallyAllocatedMemory(); } LayoutObject* layout_object = this->GetLayoutObject(); - if (layout_object && context_->Is2d() && - !context_->CreationAttributes().alpha()) { + if (layout_object && Is2d() && !context_->CreationAttributes().alpha()) { // In the alpha false case, canvas is initially opaque even though there is // no ImageBuffer, so we need to trigger an invalidation. DidDraw(); @@ -315,15 +315,15 @@ ClearCopiedImage(); if (GetLayoutObject()) GetLayoutObject()->SetMayNeedPaintInvalidation(); - if (context_ && context_->Is2d() && context_->ShouldAntialias() && - GetPage() && GetPage()->DeviceScaleFactorDeprecated() > 1.0f) { + if (Is2d() && context_->ShouldAntialias() && GetPage() && + GetPage()->DeviceScaleFactorDeprecated() > 1.0f) { FloatRect inflated_rect = rect; inflated_rect.Inflate(1); dirty_rect_.Unite(inflated_rect); } else { dirty_rect_.Unite(rect); } - if (context_ && context_->Is2d() && HasImageBuffer()) + if (Is2d() && HasImageBuffer()) Buffer()->DidDraw(rect); } @@ -362,7 +362,7 @@ void HTMLCanvasElement::DoDeferredPaintInvalidation() { DCHECK(!dirty_rect_.IsEmpty()); - if (context_->Is2d()) { + if (Is2d()) { FloatRect src_rect(0, 0, size().Width(), size().Height()); dirty_rect_.Intersect(src_rect); LayoutBox* lb = GetLayoutBox(); @@ -428,7 +428,7 @@ if (RuntimeEnabledFeatures:: enableCanvas2dDynamicRenderingModeSwitchingEnabled() && !RuntimeEnabledFeatures::canvas2dFixedRenderingModeEnabled()) { - if (context_->Is2d() && HasImageBuffer() && Buffer()->IsAccelerated() && + if (Is2d() && HasImageBuffer() && Buffer()->IsAccelerated() && num_frames_since_last_rendering_mode_switch_ >= ExpensiveCanvasHeuristicParameters::kMinFramesBeforeSwitch && !pending_rendering_mode_switch_) { @@ -475,7 +475,7 @@ if (!ok || h < 0) h = kDefaultHeight; - if (context_ && context_->Is2d()) + if (Is2d()) context_->Reset(); IntSize old_size = size(); @@ -483,8 +483,8 @@ // If the size of an existing buffer matches, we can just clear it instead of // reallocating. This optimization is only done for 2D canvases for now. - if (had_image_buffer && old_size == new_size && context_ && - context_->Is2d() && !Buffer()->IsRecording()) { + if (had_image_buffer && old_size == new_size && Is2d() && + !Buffer()->IsRecording()) { if (!image_buffer_is_clear_) { image_buffer_is_clear_ = true; context_->clearRect(0, 0, width(), height()); @@ -494,7 +494,7 @@ SetSurfaceSize(new_size); - if (context_ && context_->Is3d() && old_size != size()) + if (Is3d() && old_size != size()) context_->Reshape(width(), height()); if (LayoutObject* layout_object = this->GetLayoutObject()) { @@ -566,7 +566,7 @@ ? kNone_SkFilterQuality : kLow_SkFilterQuality; - if (Is3D()) { + if (Is3d()) { context_->SetFilterQuality(filter_quality); } else if (HasImageBuffer()) { image_buffer_->SetFilterQuality(filter_quality); @@ -605,17 +605,20 @@ context.FillRect(FloatRect(r), Color(0, 0, 0)); } - if (Is3D() && PaintsIntoCanvasBuffer()) + if (Is3d() && PaintsIntoCanvasBuffer()) context_->MarkLayerComposited(); } -bool HTMLCanvasElement::Is3D() const { +bool HTMLCanvasElement::Is3d() const { return context_ && context_->Is3d(); } -bool HTMLCanvasElement::IsAnimated2D() const { - return context_ && context_->Is2d() && HasImageBuffer() && - image_buffer_->WasDrawnToAfterSnapshot(); +bool HTMLCanvasElement::Is2d() const { + return context_ && context_->Is2d(); +} + +bool HTMLCanvasElement::IsAnimated2d() const { + return Is2d() && HasImageBuffer() && image_buffer_->WasDrawnToAfterSnapshot(); } void HTMLCanvasElement::SetSurfaceSize(const IntSize& size) { @@ -623,7 +626,7 @@ did_fail_to_create_image_buffer_ = false; DiscardImageBuffer(); ClearCopiedImage(); - if (context_ && context_->Is2d() && context_->isContextLost()) { + if (Is2d() && context_->isContextLost()) { context_->DidSetSurfaceSize(); } } @@ -634,8 +637,7 @@ } void HTMLCanvasElement::PrepareSurfaceForPaintingIfNeeded() const { - DCHECK(context_ && - context_->Is2d()); // This function is called by the 2d context + DCHECK(Is2d()); // This function is called by the 2d context if (Buffer()) image_buffer_->PrepareSurfaceForPaintingIfNeeded(); } @@ -643,7 +645,7 @@ ImageData* HTMLCanvasElement::ToImageData(SourceDrawingBuffer source_buffer, SnapshotReason reason) const { ImageData* image_data; - if (Is3D()) { + if (Is3d()) { // Get non-premultiplied data because of inaccurate premultiplied alpha // conversion of buffer()->toDataURL(). image_data = context_->PaintRenderingResultsToImageData(source_buffer); @@ -670,7 +672,7 @@ if ((!context_ || !image_data) && !PlaceholderFrame()) return image_data; - DCHECK((context_ && context_->Is2d()) || PlaceholderFrame()); + DCHECK(Is2d() || PlaceholderFrame()); sk_sp<SkImage> snapshot; if (HasImageBuffer()) { snapshot = Buffer()->NewSkImageSnapshot(kPreferNoAcceleration, reason); @@ -820,7 +822,7 @@ } bool HTMLCanvasElement::ShouldAccelerate(AccelerationCriteria criteria) const { - if (context_ && !context_->Is2d()) + if (context_ && !Is2d()) return false; if (RuntimeEnabledFeatures::forceDisplayList2dCanvasEnabled()) @@ -916,7 +918,7 @@ std::unique_ptr<ImageBufferSurface> HTMLCanvasElement::CreateWebGLImageBufferSurface(OpacityMode opacity_mode) { - DCHECK(Is3D()); + DCHECK(Is3d()); // If 3d, but the use of the canvas will be for non-accelerated content // then make a non-accelerated ImageBuffer. This means copying the internal // Image will require a pixel readback, but that is unavoidable in this case. @@ -998,7 +1000,7 @@ void HTMLCanvasElement::CreateImageBuffer() { CreateImageBufferInternal(nullptr); - if (did_fail_to_create_image_buffer_ && context_->Is2d() && !size().IsEmpty()) + if (did_fail_to_create_image_buffer_ && Is2d() && !size().IsEmpty()) context_->LoseContext(CanvasRenderingContext::kSyntheticLostContext); } @@ -1020,7 +1022,7 @@ if (external_surface) { if (external_surface->IsValid()) surface = std::move(external_surface); - } else if (Is3D()) { + } else if (Is3d()) { surface = CreateWebGLImageBufferSurface(opacity_mode); } else { if (ShouldAccelerate(kNormalAccelerationCriteria)) { @@ -1042,7 +1044,7 @@ UpdateExternallyAllocatedMemory(); - if (Is3D()) { + if (Is3d()) { // Early out for WebGL canvases return; } @@ -1060,7 +1062,7 @@ } void HTMLCanvasElement::NotifySurfaceInvalid() { - if (context_ && context_->Is2d()) + if (Is2d()) context_->LoseContext(CanvasRenderingContext::kRealLostContext); } @@ -1095,7 +1097,7 @@ // Four bytes per pixel per buffer. CheckedNumeric<intptr_t> checked_externally_allocated_memory = 4 * buffer_count; - if (Is3D()) { + if (Is3d()) { checked_externally_allocated_memory += context_->ExternallyAllocatedBytesPerPixel(); } @@ -1215,7 +1217,7 @@ context_->SetIsHidden(hidden); if (hidden) { ClearCopiedImage(); - if (Is3D()) { + if (Is3d()) { DiscardImageBuffer(); } } @@ -1240,6 +1242,7 @@ void HTMLCanvasElement::WillDrawImageTo2DContext(CanvasImageSource* source) { if (ExpensiveCanvasHeuristicParameters::kEnableAccelerationToAvoidReadbacks && + SharedGpuContext::AllowSoftwareToAcceleratedCanvasUpgrade() && source->IsAccelerated() && !Buffer()->IsAccelerated() && ShouldAccelerate(kIgnoreResourceLimitCriteria)) { OpacityMode opacity_mode = @@ -1289,7 +1292,7 @@ sk_sp<SkImage> sk_image; // TODO(ccameron): Canvas should produce sRGB images. // https://crbug.com/672299 - if (context_->Is3d()) { + if (Is3d()) { // Because WebGL sources always require making a copy of the back buffer, we // use paintRenderingResultsToCanvas instead of getImage in order to keep a // cached copy of the backing in the canvas's ImageBuffer. @@ -1451,7 +1454,7 @@ HitTestCanvasResult* HTMLCanvasElement::GetControlAndIdIfHitRegionExists( const LayoutPoint& location) { - if (context_ && context_->Is2d()) + if (Is2d()) return context_->GetControlAndIdIfHitRegionExists(location); return HitTestCanvasResult::Create(String(), nullptr); }
diff --git a/third_party/WebKit/Source/core/html/HTMLCanvasElement.h b/third_party/WebKit/Source/core/html/HTMLCanvasElement.h index 5ca64bd..0ca28af7 100644 --- a/third_party/WebKit/Source/core/html/HTMLCanvasElement.h +++ b/third_party/WebKit/Source/core/html/HTMLCanvasElement.h
@@ -157,8 +157,9 @@ AffineTransform BaseTransform() const; - bool Is3D() const; - bool IsAnimated2D() const; + bool Is3d() const; + bool Is2d() const; + bool IsAnimated2d() const; bool HasImageBuffer() const { return image_buffer_.get(); } void DiscardImageBuffer();
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp index 9a72937..54cb6398 100644 --- a/third_party/WebKit/Source/core/html/HTMLImageElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLImageElement.cpp
@@ -688,6 +688,28 @@ referrer_policy_); } +ScriptPromise HTMLImageElement::CreateImageBitmap( + ScriptState* script_state, + EventTarget& event_target, + Optional<IntRect> crop_rect, + const ImageBitmapOptions& options, + ExceptionState& exception_state) { + DCHECK(event_target.ToLocalDOMWindow()); + if ((crop_rect && + !ImageBitmap::IsSourceSizeValid(crop_rect->Width(), crop_rect->Height(), + exception_state)) || + !ImageBitmap::IsSourceSizeValid(BitmapSourceSize().Width(), + BitmapSourceSize().Height(), + exception_state)) + return ScriptPromise(); + if (!ImageBitmap::IsResizeOptionValid(options, exception_state)) + return ScriptPromise(); + return ImageBitmapSource::FulfillImageBitmap( + script_state, ImageBitmap::Create( + this, crop_rect, + event_target.ToLocalDOMWindow()->document(), options)); +} + void HTMLImageElement::SelectSourceURL( ImageLoader::UpdateFromElementBehavior behavior) { if (!GetDocument().IsActive()) @@ -811,6 +833,16 @@ } } +IntSize HTMLImageElement::BitmapSourceSize() const { + ImageResourceContent* image = CachedImage(); + if (!image) + return IntSize(); + LayoutSize l_size = image->ImageSize( + LayoutObject::ShouldRespectImageOrientation(GetLayoutObject()), 1.0f); + DCHECK(l_size.Fraction().IsZero()); + return IntSize(l_size.Width().ToInt(), l_size.Height().ToInt()); +} + void HTMLImageElement::AssociateWith(HTMLFormElement* form) { if (form && form->isConnected()) { form_ = form;
diff --git a/third_party/WebKit/Source/core/html/HTMLImageElement.h b/third_party/WebKit/Source/core/html/HTMLImageElement.h index 195cee7f..a85704f6 100644 --- a/third_party/WebKit/Source/core/html/HTMLImageElement.h +++ b/third_party/WebKit/Source/core/html/HTMLImageElement.h
@@ -29,7 +29,8 @@ #include "core/html/FormAssociated.h" #include "core/html/HTMLElement.h" #include "core/html/HTMLImageLoader.h" -#include "core/html/canvas/ImageElementBase.h" +#include "core/html/canvas/CanvasImageElementSource.h" +#include "core/imagebitmap/ImageBitmapSource.h" #include "platform/graphics/GraphicsTypes.h" #include "platform/loader/fetch/FetchParameters.h" #include "platform/loader/fetch/ResourceResponse.h" @@ -39,10 +40,12 @@ class HTMLFormElement; class ImageCandidate; class ShadowRoot; +class ImageBitmapOptions; class CORE_EXPORT HTMLImageElement final : public HTMLElement, - public ImageElementBase, + public CanvasImageElementSource, + public ImageBitmapSource, public ActiveScriptWrappable<HTMLImageElement>, public FormAssociated { DEFINE_WRAPPERTYPEINFO(); @@ -130,6 +133,14 @@ void ForceReload() const; + // ImageBitmapSource implementation + IntSize BitmapSourceSize() const override; + ScriptPromise CreateImageBitmap(ScriptState*, + EventTarget&, + Optional<IntRect> crop_rect, + const ImageBitmapOptions&, + ExceptionState&) override; + FormAssociated* ToFormAssociatedOrNull() override { return this; }; void AssociateWith(HTMLFormElement*) override;
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp index d707de7..d3e0fc4 100644 --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.cpp
@@ -3637,20 +3637,32 @@ return text_tracks_visible_; } -static void AssertShadowRootChildren(ShadowRoot& shadow_root) { +// static +void HTMLMediaElement::AssertShadowRootChildren(ShadowRoot& shadow_root) { #if DCHECK_IS_ON() - // There can be up to two children, either or both of the text - // track container and media controls. If both are present, the - // text track container must be the first child. + // There can be up to three children: media remoting interstitial, text track + // container, and media controls. The media controls has to be the last child + // if presend, and has to be the next sibling of the text track container if + // both present. When present, media remoting interstitial has to be the first + // child. unsigned number_of_children = shadow_root.CountChildren(); - DCHECK_LE(number_of_children, 2u); + DCHECK_LE(number_of_children, 3u); Node* first_child = shadow_root.FirstChild(); Node* last_child = shadow_root.LastChild(); if (number_of_children == 1) { DCHECK(first_child->IsTextTrackContainer() || - first_child->IsMediaControls()); + first_child->IsMediaControls() || + first_child->IsMediaRemotingInterstitial()); } else if (number_of_children == 2) { - DCHECK(first_child->IsTextTrackContainer()); + DCHECK(first_child->IsTextTrackContainer() || + first_child->IsMediaRemotingInterstitial()); + DCHECK(last_child->IsTextTrackContainer() || last_child->IsMediaControls()); + if (first_child->IsTextTrackContainer()) + DCHECK(last_child->IsMediaControls()); + } else if (number_of_children == 3) { + Node* second_child = first_child->nextSibling(); + DCHECK(first_child->IsMediaRemotingInterstitial()); + DCHECK(second_child->IsTextTrackContainer()); DCHECK(last_child->IsMediaControls()); } #endif @@ -3663,12 +3675,20 @@ Node* first_child = shadow_root.FirstChild(); if (first_child && first_child->IsTextTrackContainer()) return ToTextTrackContainer(*first_child); + Node* to_be_inserted = first_child; + + if (first_child && first_child->IsMediaRemotingInterstitial()) { + Node* second_child = first_child->nextSibling(); + if (second_child && second_child->IsTextTrackContainer()) + return ToTextTrackContainer(*second_child); + to_be_inserted = second_child; + } TextTrackContainer* text_track_container = TextTrackContainer::Create(*this); // The text track container should be inserted before the media controls, // so that they are rendered behind them. - shadow_root.InsertBefore(text_track_container, first_child); + shadow_root.InsertBefore(text_track_container, to_be_inserted); AssertShadowRootChildren(shadow_root);
diff --git a/third_party/WebKit/Source/core/html/HTMLMediaElement.h b/third_party/WebKit/Source/core/html/HTMLMediaElement.h index 85a51d2..c61d5df 100644 --- a/third_party/WebKit/Source/core/html/HTMLMediaElement.h +++ b/third_party/WebKit/Source/core/html/HTMLMediaElement.h
@@ -350,6 +350,9 @@ DisplayMode GetDisplayMode() const { return display_mode_; } virtual void SetDisplayMode(DisplayMode mode) { display_mode_ = mode; } + // Assert the correct order of the children in shadow dom when DCHECK is on. + static void AssertShadowRootChildren(ShadowRoot&); + private: // Friend class for testing. friend class MediaElementFillingViewportTest;
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp index 2e741f235..c8fa9d8 100644 --- a/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp +++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.cpp
@@ -39,6 +39,7 @@ #include "core/frame/Settings.h" #include "core/html/media/MediaCustomControlsFullscreenDetector.h" #include "core/html/parser/HTMLParserIdioms.h" +#include "core/html/shadow/MediaRemotingInterstitial.h" #include "core/imagebitmap/ImageBitmapOptions.h" #include "core/layout/LayoutImage.h" #include "core/layout/LayoutVideo.h" @@ -66,7 +67,9 @@ } // anonymous namespace inline HTMLVideoElement::HTMLVideoElement(Document& document) - : HTMLMediaElement(videoTag, document) { + : HTMLMediaElement(videoTag, document), + media_remoting_status_(MediaRemotingStatus::kNotStarted), + remoting_interstitial_(nullptr) { if (document.GetSettings()) { default_poster_url_ = AtomicString(document.GetSettings()->GetDefaultVideoPosterURL()); @@ -88,6 +91,7 @@ DEFINE_TRACE(HTMLVideoElement) { visitor->Trace(image_loader_); visitor->Trace(custom_controls_fullscreen_detector_); + visitor->Trace(remoting_interstitial_); HTMLMediaElement::Trace(visitor); } @@ -179,6 +183,10 @@ // Notify the player when the poster image URL changes. if (GetWebMediaPlayer()) GetWebMediaPlayer()->SetPoster(PosterImageURL()); + // Media remoting doesn't show the original poster image, instead, it shows + // a grayscaled and blurred copy. + if (remoting_interstitial_) + remoting_interstitial_->OnPosterImageChanged(); } else { HTMLMediaElement::ParseAttribute(params); } @@ -483,4 +491,30 @@ event_target.ToLocalDOMWindow()->document(), options)); } +void HTMLVideoElement::MediaRemotingStarted() { + DCHECK_EQ(media_remoting_status_, MediaRemotingStatus::kNotStarted); + media_remoting_status_ = MediaRemotingStatus::kStarted; + if (!remoting_interstitial_) { + remoting_interstitial_ = new MediaRemotingInterstitial(*this); + ShadowRoot& shadow_root = EnsureUserAgentShadowRoot(); + shadow_root.InsertBefore(remoting_interstitial_, shadow_root.FirstChild()); + HTMLMediaElement::AssertShadowRootChildren(shadow_root); + } + remoting_interstitial_->Show(); +} + +void HTMLVideoElement::MediaRemotingStopped() { + if (media_remoting_status_ != MediaRemotingStatus::kDisabled) + media_remoting_status_ = MediaRemotingStatus::kNotStarted; + DCHECK(remoting_interstitial_); + remoting_interstitial_->Hide(); +} + +void HTMLVideoElement::DisableMediaRemoting() { + if (GetWebMediaPlayer()) + GetWebMediaPlayer()->RequestRemotePlaybackDisabled(true); + media_remoting_status_ = MediaRemotingStatus::kDisabled; + MediaRemotingStopped(); +} + } // namespace blink
diff --git a/third_party/WebKit/Source/core/html/HTMLVideoElement.h b/third_party/WebKit/Source/core/html/HTMLVideoElement.h index 6f14c678..cef9ba0 100644 --- a/third_party/WebKit/Source/core/html/HTMLVideoElement.h +++ b/third_party/WebKit/Source/core/html/HTMLVideoElement.h
@@ -43,6 +43,7 @@ class ExceptionState; class ImageBitmapOptions; class MediaCustomControlsFullscreenDetector; +class MediaRemotingInterstitial; class CORE_EXPORT HTMLVideoElement final : public HTMLMediaElement, public CanvasImageSource, @@ -53,6 +54,8 @@ static HTMLVideoElement* Create(Document&); DECLARE_VIRTUAL_TRACE(); + enum class MediaRemotingStatus { kNotStarted, kStarted, kDisabled }; + // Node override. Node::InsertionNotificationRequest InsertedInto(ContainerNode*) override; void RemovedFrom(ContainerNode*) override; @@ -132,6 +135,11 @@ bool IsPersistent() const; + MediaRemotingStatus GetMediaRemotingStatus() const { + return media_remoting_status_; + } + void DisableMediaRemoting(); + private: friend class MediaCustomControlsFullscreenDetectorTest; friend class HTMLMediaElementEventListenersTest; @@ -156,11 +164,17 @@ void UpdateDisplayState() override; void DidMoveToNewDocument(Document& old_document) override; void SetDisplayMode(DisplayMode) override; + void MediaRemotingStarted() final; + void MediaRemotingStopped() final; Member<HTMLImageLoader> image_loader_; Member<MediaCustomControlsFullscreenDetector> custom_controls_fullscreen_detector_; + MediaRemotingStatus media_remoting_status_; + + Member<MediaRemotingInterstitial> remoting_interstitial_; + AtomicString default_poster_url_; bool is_persistent_ = false;
diff --git a/third_party/WebKit/Source/core/html/canvas/ImageElementBase.cpp b/third_party/WebKit/Source/core/html/canvas/CanvasImageElementSource.cpp similarity index 62% rename from third_party/WebKit/Source/core/html/canvas/ImageElementBase.cpp rename to third_party/WebKit/Source/core/html/canvas/CanvasImageElementSource.cpp index e41aa983..64277260 100644 --- a/third_party/WebKit/Source/core/html/canvas/ImageElementBase.cpp +++ b/third_party/WebKit/Source/core/html/canvas/CanvasImageElementSource.cpp
@@ -2,29 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "core/html/canvas/ImageElementBase.h" +#include "core/html/canvas/CanvasImageElementSource.h" -#include "core/frame/ImageBitmap.h" -#include "core/frame/LocalDOMWindow.h" #include "core/layout/LayoutObject.h" #include "core/loader/ImageLoader.h" #include "core/svg/graphics/SVGImageForContainer.h" namespace blink { -ImageResourceContent* ImageElementBase::CachedImage() const { +ImageResourceContent* CanvasImageElementSource::CachedImage() const { return GetImageLoader().GetImage(); } -const Element& ImageElementBase::GetElement() const { +const Element& CanvasImageElementSource::GetElement() const { return *GetImageLoader().GetElement(); } -bool ImageElementBase::IsSVGSource() const { +bool CanvasImageElementSource::IsSVGSource() const { return CachedImage() && CachedImage()->GetImage()->IsSVGImage(); } -PassRefPtr<Image> ImageElementBase::GetSourceImageForCanvas( +PassRefPtr<Image> CanvasImageElementSource::GetSourceImageForCanvas( SourceImageStatus* status, AccelerationHint, SnapshotReason, @@ -56,13 +54,13 @@ return source_image->ImageForDefaultFrame(); } -bool ImageElementBase::WouldTaintOrigin( +bool CanvasImageElementSource::WouldTaintOrigin( SecurityOrigin* destination_security_origin) const { return CachedImage() && !CachedImage()->IsAccessAllowed(destination_security_origin); } -FloatSize ImageElementBase::ElementSize( +FloatSize CanvasImageElementSource::ElementSize( const FloatSize& default_object_size) const { ImageResourceContent* image = CachedImage(); if (!image) @@ -78,7 +76,7 @@ 1.0f)); } -FloatSize ImageElementBase::DefaultDestinationSize( +FloatSize CanvasImageElementSource::DefaultDestinationSize( const FloatSize& default_object_size) const { ImageResourceContent* image = CachedImage(); if (!image) @@ -96,15 +94,15 @@ return FloatSize(size); } -bool ImageElementBase::IsAccelerated() const { +bool CanvasImageElementSource::IsAccelerated() const { return false; } -const KURL& ImageElementBase::SourceURL() const { +const KURL& CanvasImageElementSource::SourceURL() const { return CachedImage()->GetResponse().Url(); } -int ImageElementBase::SourceWidth() { +int CanvasImageElementSource::SourceWidth() { SourceImageStatus status; RefPtr<Image> image = GetSourceImageForCanvas(&status, kPreferNoAcceleration, kSnapshotReasonUnknown, @@ -112,7 +110,7 @@ return image->width(); } -int ImageElementBase::SourceHeight() { +int CanvasImageElementSource::SourceHeight() { SourceImageStatus status; RefPtr<Image> image = GetSourceImageForCanvas(&status, kPreferNoAcceleration, kSnapshotReasonUnknown, @@ -120,43 +118,9 @@ return image->height(); } -bool ImageElementBase::IsOpaque() const { +bool CanvasImageElementSource::IsOpaque() const { Image* image = const_cast<Element&>(GetElement()).ImageContents(); return image && image->CurrentFrameKnownToBeOpaque(); } -IntSize ImageElementBase::BitmapSourceSize() const { - ImageResourceContent* image = CachedImage(); - if (!image) - return IntSize(); - LayoutSize lSize = - image->ImageSize(LayoutObject::ShouldRespectImageOrientation( - GetElement().GetLayoutObject()), - 1.0f); - DCHECK(lSize.Fraction().IsZero()); - return IntSize(lSize.Width().ToInt(), lSize.Height().ToInt()); -} - -ScriptPromise ImageElementBase::CreateImageBitmap( - ScriptState* script_state, - EventTarget& event_target, - Optional<IntRect> crop_rect, - const ImageBitmapOptions& options, - ExceptionState& exception_state) { - DCHECK(event_target.ToLocalDOMWindow()); - if ((crop_rect && - !ImageBitmap::IsSourceSizeValid(crop_rect->Width(), crop_rect->Height(), - exception_state)) || - !ImageBitmap::IsSourceSizeValid(BitmapSourceSize().Width(), - BitmapSourceSize().Height(), - exception_state)) - return ScriptPromise(); - if (!ImageBitmap::IsResizeOptionValid(options, exception_state)) - return ScriptPromise(); - return ImageBitmapSource::FulfillImageBitmap( - script_state, ImageBitmap::Create( - this, crop_rect, - event_target.ToLocalDOMWindow()->document(), options)); -} - } // namespace blink
diff --git a/third_party/WebKit/Source/core/html/canvas/ImageElementBase.h b/third_party/WebKit/Source/core/html/canvas/CanvasImageElementSource.h similarity index 69% rename from third_party/WebKit/Source/core/html/canvas/ImageElementBase.h rename to third_party/WebKit/Source/core/html/canvas/CanvasImageElementSource.h index d8060f49..4497a79 100644 --- a/third_party/WebKit/Source/core/html/canvas/ImageElementBase.h +++ b/third_party/WebKit/Source/core/html/canvas/CanvasImageElementSource.h
@@ -2,12 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef ImageElementBase_h -#define ImageElementBase_h +#ifndef CanvasImageElementSource_h +#define CanvasImageElementSource_h #include "core/CoreExport.h" #include "core/html/canvas/CanvasImageSource.h" -#include "core/imagebitmap/ImageBitmapSource.h" namespace blink { @@ -15,19 +14,11 @@ class ImageLoader; class ImageResourceContent; -class CORE_EXPORT ImageElementBase : public CanvasImageSource, - public ImageBitmapSource { +class CORE_EXPORT CanvasImageElementSource : public CanvasImageSource { public: virtual ImageLoader& GetImageLoader() const = 0; virtual FloatSize SourceDefaultObjectSize() = 0; - IntSize BitmapSourceSize() const override; - ScriptPromise CreateImageBitmap(ScriptState*, - EventTarget&, - Optional<IntRect>, - const ImageBitmapOptions&, - ExceptionState&) override; - PassRefPtr<Image> GetSourceImageForCanvas(SourceImageStatus*, AccelerationHint, SnapshotReason, @@ -51,12 +42,11 @@ const KURL& SourceURL() const override; - ImageResourceContent* CachedImage() const; - private: + ImageResourceContent* CachedImage() const; const Element& GetElement() const; }; } // namespace blink -#endif // ImageElementBase_h +#endif // CanvasImageElementSource_h
diff --git a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp index 1b94d33a..5884247d 100644 --- a/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp +++ b/third_party/WebKit/Source/core/html/parser/HTMLParserScriptRunner.cpp
@@ -339,8 +339,8 @@ CHECK_EQ(pending_script->GetScriptType(), ScriptType::kClassic); if (!pending_script->ErrorOccurred()) { - EmitWarningForDocWriteScripts(pending_script->Url().GetString(), - *document_); + EmitWarningForDocWriteScripts( + pending_script->UrlForClassicScript().GetString(), *document_); return; } @@ -348,7 +348,8 @@ // ERR_CACHE_MISS but other errors are rare with // WebCachePolicy::ReturnCacheDataDontLoad. - EmitErrorForDocWriteScripts(pending_script->Url().GetString(), *document_); + EmitErrorForDocWriteScripts(pending_script->UrlForClassicScript().GetString(), + *document_); TextPosition starting_position = ParserBlockingScript()->StartingPosition(); bool is_parser_inserted = script_loader->IsParserInserted(); // Remove this resource entry from memory cache as the new request
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaRemotingElements.cpp b/third_party/WebKit/Source/core/html/shadow/MediaRemotingElements.cpp new file mode 100644 index 0000000..c53bc9d8 --- /dev/null +++ b/third_party/WebKit/Source/core/html/shadow/MediaRemotingElements.cpp
@@ -0,0 +1,103 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/html/shadow/MediaRemotingElements.h" + +#include "core/dom/ClientRect.h" +#include "core/dom/shadow/ShadowRoot.h" +#include "core/events/MouseEvent.h" +#include "core/html/HTMLVideoElement.h" +#include "core/input/EventHandler.h" +#include "platform/text/PlatformLocale.h" +#include "public/platform/WebLocalizedString.h" + +namespace blink { + +using namespace HTMLNames; + +// ---------------------------- + +class MediaRemotingExitButtonElement::MouseEventsListener final + : public EventListener { + public: + explicit MouseEventsListener(MediaRemotingExitButtonElement& element) + : EventListener(kCPPEventListenerType), element_(element) {} + + bool operator==(const EventListener& other) const override { + return this == &other; + } + + void Trace(blink::Visitor* visitor) { + visitor->Trace(element_); + EventListener::Trace(visitor); + } + + private: + void handleEvent(ExecutionContext* context, Event* event) override { + DCHECK_EQ(event->type(), EventTypeNames::click); + + MouseEvent* mouse_event = ToMouseEvent(event); + ClientRect* client_rect = element_->getBoundingClientRect(); + const double x = mouse_event->x(); + const double y = mouse_event->y(); + if (x < client_rect->left() || x > client_rect->right() || + y < client_rect->top() || y > client_rect->bottom()) + return; + + element_->GetVideoElement().DisableMediaRemoting(); + event->SetDefaultHandled(); + event->stopPropagation(); + } + + Member<MediaRemotingExitButtonElement> element_; +}; + +MediaRemotingExitButtonElement::MediaRemotingExitButtonElement( + MediaRemotingInterstitial& interstitial) + : HTMLDivElement(interstitial.GetDocument()), interstitial_(interstitial) { + listener_ = new MouseEventsListener(*this); + SetShadowPseudoId(AtomicString("-internal-media-remoting-disable-button")); + setInnerText(interstitial.GetVideoElement().GetLocale().QueryString( + WebLocalizedString::kMediaRemotingDisableText), + ASSERT_NO_EXCEPTION); +} + +void MediaRemotingExitButtonElement::OnShown() { + GetDocument().addEventListener(EventTypeNames::click, listener_, true); +} + +void MediaRemotingExitButtonElement::OnHidden() { + GetDocument().removeEventListener(EventTypeNames::click, listener_, true); +} + +HTMLVideoElement& MediaRemotingExitButtonElement::GetVideoElement() const { + return interstitial_->GetVideoElement(); +} + +DEFINE_TRACE(MediaRemotingExitButtonElement) { + visitor->Trace(interstitial_); + visitor->Trace(listener_); + HTMLDivElement::Trace(visitor); +} + +// ---------------------------- + +MediaRemotingCastMessageElement::MediaRemotingCastMessageElement( + MediaRemotingInterstitial& interstitial) + : HTMLDivElement(interstitial.GetDocument()) { + SetShadowPseudoId(AtomicString("-internal-media-remoting-cast-text-message")); + setInnerText(interstitial.GetVideoElement().GetLocale().QueryString( + WebLocalizedString::kMediaRemotingCastText), + ASSERT_NO_EXCEPTION); +} + +// ---------------------------- + +MediaRemotingCastIconElement::MediaRemotingCastIconElement( + MediaRemotingInterstitial& interstitial) + : HTMLDivElement(interstitial.GetDocument()) { + SetShadowPseudoId(AtomicString("-internal-media-remoting-cast-icon")); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaRemotingElements.h b/third_party/WebKit/Source/core/html/shadow/MediaRemotingElements.h new file mode 100644 index 0000000..605678e --- /dev/null +++ b/third_party/WebKit/Source/core/html/shadow/MediaRemotingElements.h
@@ -0,0 +1,49 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MediaRemotingElements_h +#define MediaRemotingElements_h + +#include "core/html/shadow/MediaRemotingInterstitial.h" + +namespace blink { + +// A button shown when media remoting starts. It allows user to click to exit +// media remoting and back to traditional tab mirroring. +class MediaRemotingExitButtonElement final : public HTMLDivElement { + public: + explicit MediaRemotingExitButtonElement(MediaRemotingInterstitial&); + + void OnShown(); + void OnHidden(); + HTMLVideoElement& GetVideoElement() const; + + DECLARE_VIRTUAL_TRACE(); + + private: + class MouseEventsListener; + + Member<MediaRemotingInterstitial> interstitial_; + // A document event listener to listen to the mouse clicking events while + // media remoting is ongoing. This listener is used instead of the + // DefaultEventHandler() to avoid other web sites stopping propagating the + // events. It will be removed once media remoting stops. + Member<MouseEventsListener> listener_; +}; + +// A text message shown to indicate media remoting is ongoing. +class MediaRemotingCastMessageElement final : public HTMLDivElement { + public: + explicit MediaRemotingCastMessageElement(MediaRemotingInterstitial&); +}; + +// An icon shown to indicate media remoting is ongoing. +class MediaRemotingCastIconElement final : public HTMLDivElement { + public: + explicit MediaRemotingCastIconElement(MediaRemotingInterstitial&); +}; + +} // namespace blink + +#endif // MediaRemotingElements_h
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaRemotingInterstitial.cpp b/third_party/WebKit/Source/core/html/shadow/MediaRemotingInterstitial.cpp new file mode 100644 index 0000000..e183d8e5 --- /dev/null +++ b/third_party/WebKit/Source/core/html/shadow/MediaRemotingInterstitial.cpp
@@ -0,0 +1,58 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "core/html/shadow/MediaRemotingInterstitial.h" + +#include "core/html/HTMLImageElement.h" +#include "core/html/HTMLVideoElement.h" +#include "core/html/shadow/MediaRemotingElements.h" + +namespace blink { + +MediaRemotingInterstitial::MediaRemotingInterstitial( + HTMLVideoElement& videoElement) + : HTMLDivElement(videoElement.GetDocument()), + video_element_(&videoElement) { + SetShadowPseudoId(AtomicString("-internal-media-remoting-interstitial")); + background_image_ = HTMLImageElement::Create(videoElement.GetDocument()); + background_image_->SetShadowPseudoId( + AtomicString("-internal-media-remoting-background-image")); + background_image_->SetSrc(videoElement.getAttribute(HTMLNames::posterAttr)); + AppendChild(background_image_); + + cast_icon_ = new MediaRemotingCastIconElement(*this); + AppendChild(cast_icon_); + + cast_text_message_ = new MediaRemotingCastMessageElement(*this); + AppendChild(cast_text_message_); + + exit_button_ = new MediaRemotingExitButtonElement(*this); + AppendChild(exit_button_); +} + +void MediaRemotingInterstitial::Show() { + RemoveInlineStyleProperty(CSSPropertyDisplay); + exit_button_->OnShown(); +} + +void MediaRemotingInterstitial::Hide() { + SetInlineStyleProperty(CSSPropertyDisplay, CSSValueNone); + exit_button_->OnHidden(); +} + +void MediaRemotingInterstitial::OnPosterImageChanged() { + background_image_->SetSrc( + GetVideoElement().getAttribute(HTMLNames::posterAttr)); +} + +DEFINE_TRACE(MediaRemotingInterstitial) { + visitor->Trace(video_element_); + visitor->Trace(background_image_); + visitor->Trace(exit_button_); + visitor->Trace(cast_icon_); + visitor->Trace(cast_text_message_); + HTMLDivElement::Trace(visitor); +} + +} // namespace blink
diff --git a/third_party/WebKit/Source/core/html/shadow/MediaRemotingInterstitial.h b/third_party/WebKit/Source/core/html/shadow/MediaRemotingInterstitial.h new file mode 100644 index 0000000..0baaa2b --- /dev/null +++ b/third_party/WebKit/Source/core/html/shadow/MediaRemotingInterstitial.h
@@ -0,0 +1,56 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MediaRemotingInterstitial_h +#define MediaRemotingInterstitial_h + +#include "core/html/HTMLDivElement.h" + +namespace blink { + +class HTMLImageElement; +class HTMLVideoElement; +class MediaRemotingExitButtonElement; +class MediaRemotingCastMessageElement; +class MediaRemotingCastIconElement; + +// Media Remoting UI. DOM structure looks like: +// +// MediaRemotingInterstitial +// (-internal-media-remoting-interstitial) +// +-HTMLImageElement +// | (-internal-media-remoting-background-image) +// \-MediaRemotingCastIconElement +// | (-internal-media-remoting-cast-icon) +// \-MediaRemotingCastMessageElement +// | (-internal-media-remoting-cast-text-message) +// \-MediaRemotingExitButtonElement +// (-internal-media-remoting-disable-button) +class MediaRemotingInterstitial final : public HTMLDivElement { + public: + explicit MediaRemotingInterstitial(HTMLVideoElement&); + + // Show/Hide Media Remoting interstitial. + void Show(); + void Hide(); + void OnPosterImageChanged(); + + HTMLVideoElement& GetVideoElement() const { return *video_element_; } + + DECLARE_VIRTUAL_TRACE(); + + private: + // Node override. + bool IsMediaRemotingInterstitial() const override { return true; } + + Member<HTMLVideoElement> video_element_; + Member<HTMLImageElement> background_image_; + Member<MediaRemotingExitButtonElement> exit_button_; + Member<MediaRemotingCastIconElement> cast_icon_; + Member<MediaRemotingCastMessageElement> cast_text_message_; +}; + +} // namespace + +#endif // MediaRemotingInterstitial_h
diff --git a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp index a3c4e95..f273bc8 100644 --- a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp +++ b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.cpp
@@ -45,7 +45,6 @@ #include "core/html/ImageData.h" #include "core/imagebitmap/ImageBitmapOptions.h" #include "core/offscreencanvas/OffscreenCanvas.h" -#include "core/svg/SVGImageElement.h" #include "core/svg/graphics/SVGImage.h" #include "core/workers/WorkerGlobalScope.h" #include "platform/CrossThreadFunctional.h" @@ -64,16 +63,9 @@ ExceptionState& exception_state, const ImageBitmapOptions& options, bool has_crop_rect) { - ImageElementBase* image_element = nullptr; if (value.isHTMLImageElement()) { - if (!(image_element = value.getAsHTMLImageElement())) - return nullptr; - } else if (value.isSVGImageElement()) { - if (!(image_element = value.getAsSVGImageElement())) - return nullptr; - } - if (image_element) { - if (!image_element->CachedImage()) { + HTMLImageElement* image_element = value.getAsHTMLImageElement(); + if (!image_element || !image_element->CachedImage()) { exception_state.ThrowDOMException( kInvalidStateError, "No image can be retrieved from the provided element.");
diff --git a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.h b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.h index 009bcb1..71124ca 100644 --- a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.h +++ b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.h
@@ -31,8 +31,7 @@ #ifndef ImageBitmapFactories_h #define ImageBitmapFactories_h -#include <memory> -#include "bindings/core/v8/HTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas.h" +#include "bindings/core/v8/HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas.h" #include "bindings/core/v8/ScriptPromise.h" #include "bindings/core/v8/ScriptPromiseResolver.h" #include "bindings/core/v8/ScriptState.h" @@ -44,6 +43,7 @@ #include "platform/Supplementable.h" #include "platform/geometry/IntRect.h" #include "third_party/skia/include/core/SkRefCnt.h" +#include <memory> class SkImage; @@ -57,7 +57,7 @@ class ImageBitmapOptions; class WebTaskRunner; -typedef HTMLImageElementOrSVGImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas +typedef HTMLImageElementOrHTMLVideoElementOrHTMLCanvasElementOrBlobOrImageDataOrImageBitmapOrOffscreenCanvas ImageBitmapSourceUnion; class ImageBitmapFactories final
diff --git a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.idl b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.idl index 8d64642..13ace66 100644 --- a/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.idl +++ b/third_party/WebKit/Source/core/imagebitmap/ImageBitmapFactories.idl
@@ -31,7 +31,6 @@ // https://html.spec.whatwg.org/#imagebitmapfactories typedef (HTMLImageElement or - SVGImageElement or HTMLVideoElement or HTMLCanvasElement or Blob or
diff --git a/third_party/WebKit/Source/core/layout/LayoutBox.cpp b/third_party/WebKit/Source/core/layout/LayoutBox.cpp index 384b01b..9b41ff4 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBox.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBox.cpp
@@ -1207,14 +1207,14 @@ transform.Multiply(Layer()->CurrentTransform()); // 2. Container offset. - transform.TranslateRight(container_offset.X().ToFloat(), - container_offset.Y().ToFloat()); + transform.PostTranslate(container_offset.X().ToFloat(), + container_offset.Y().ToFloat()); // 3. Container scroll offset. if (container_object->IsBox() && container_object != ancestor && container_object->HasOverflowClip()) { IntSize offset = -ToLayoutBox(container_object)->ScrolledContentOffset(); - transform.TranslateRight(offset.Width(), offset.Height()); + transform.PostTranslate(offset.Width(), offset.Height()); } // 4. Perspective applied by container.
diff --git a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp index 30426d7..562f5bb 100644 --- a/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutBoxModelObject.cpp
@@ -1383,8 +1383,8 @@ if (ShouldUseTransformFromContainer(container)) { TransformationMatrix t; GetTransformFromContainer(container, container_offset, t); - t.TranslateRight(adjustment_for_skipped_ancestor.Width().ToFloat(), - adjustment_for_skipped_ancestor.Height().ToFloat()); + t.PostTranslate(adjustment_for_skipped_ancestor.Width().ToFloat(), + adjustment_for_skipped_ancestor.Height().ToFloat()); geometry_map.Push(this, t, flags, LayoutSize()); } else { container_offset += adjustment_for_skipped_ancestor;
diff --git a/third_party/WebKit/Source/core/layout/LayoutMedia.cpp b/third_party/WebKit/Source/core/layout/LayoutMedia.cpp index 50fd030e..e0dcf82 100644 --- a/third_party/WebKit/Source/core/layout/LayoutMedia.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutMedia.cpp
@@ -59,16 +59,22 @@ // overlap checking, see LayoutVTTCue. #if DCHECK_IS_ON() bool seen_text_track_container = false; + bool seen_media_remoting_interstitial = false; #endif for (LayoutObject* child = children_.LastChild(); child; child = child->PreviousSibling()) { #if DCHECK_IS_ON() - if (child->GetNode()->IsMediaControls()) + if (child->GetNode()->IsMediaControls()) { DCHECK(!seen_text_track_container); - else if (child->GetNode()->IsTextTrackContainer()) + DCHECK(!seen_media_remoting_interstitial); + } else if (child->GetNode()->IsTextTrackContainer()) { seen_text_track_container = true; - else + DCHECK(!seen_media_remoting_interstitial); + } else if (child->GetNode()->IsMediaRemotingInterstitial()) { + seen_media_remoting_interstitial = true; + } else { NOTREACHED(); + } #endif // TODO(mlamouri): we miss some layouts because needsLayout returns false in @@ -115,7 +121,8 @@ if (child->GetNode()->IsMediaControls()) return child->IsFlexibleBox(); - if (child->GetNode()->IsTextTrackContainer()) + if (child->GetNode()->IsTextTrackContainer() || + child->GetNode()->IsMediaRemotingInterstitial()) return true; return false;
diff --git a/third_party/WebKit/Source/core/layout/LayoutObject.cpp b/third_party/WebKit/Source/core/layout/LayoutObject.cpp index a40effc..d71a514 100644 --- a/third_party/WebKit/Source/core/layout/LayoutObject.cpp +++ b/third_party/WebKit/Source/core/layout/LayoutObject.cpp
@@ -2306,8 +2306,8 @@ if (layer && layer->Transform()) transform.Multiply(layer->CurrentTransform()); - transform.TranslateRight(offset_in_container.Width().ToFloat(), - offset_in_container.Height().ToFloat()); + transform.PostTranslate(offset_in_container.Width().ToFloat(), + offset_in_container.Height().ToFloat()); if (container_object && container_object->HasLayer() && container_object->Style()->HasPerspective()) {
diff --git a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc index 119147a..4976df78 100644 --- a/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc +++ b/third_party/WebKit/Source/core/layout/ng/inline/ng_inline_layout_algorithm_test.cc
@@ -131,7 +131,7 @@ LayoutText* layout_text = ToLayoutText(GetLayoutObjectByElementId("text")->SlowFirstChild()); - ASSERT(layout_text->HasTextBoxes()); + DCHECK(layout_text->HasTextBoxes()); ASSERT_EQ(4UL, text_fragments.size()); @@ -181,7 +181,7 @@ )HTML"); LayoutText* layout_text = ToLayoutText(GetLayoutObjectByElementId("text")->SlowFirstChild()); - ASSERT(layout_text->HasTextBoxes()); + DCHECK(layout_text->HasTextBoxes()); InlineTextBox* inline_text_box1 = layout_text->FirstTextBox(); // 30 == narrow-float's width. @@ -219,7 +219,7 @@ )HTML"); LayoutText* layout_text = ToLayoutText(GetLayoutObjectByElementId("text")->SlowFirstChild()); - ASSERT(layout_text->HasTextBoxes()); + DCHECK(layout_text->HasTextBoxes()); InlineTextBox* inline_text_box1 = layout_text->FirstTextBox(); EXPECT_EQ(LayoutUnit(), inline_text_box1->X()); @@ -292,7 +292,7 @@ )HTML"); LayoutText* layout_text = ToLayoutText(GetLayoutObjectByElementId("text")->SlowFirstChild()); - ASSERT(layout_text->HasTextBoxes()); + DCHECK(layout_text->HasTextBoxes()); InlineTextBox* inline_text_box1 = layout_text->FirstTextBox(); // 45 = sum of left's inline margins: 40 + left's width: 5
diff --git a/third_party/WebKit/Source/core/layout/ng/ng_space_utils.cc b/third_party/WebKit/Source/core/layout/ng/ng_space_utils.cc index 29142258..dbf73c81 100644 --- a/third_party/WebKit/Source/core/layout/ng/ng_space_utils.cc +++ b/third_party/WebKit/Source/core/layout/ng/ng_space_utils.cc
@@ -88,7 +88,7 @@ case EClear::kBoth: return OptionalMax<LayoutUnit>(left_offset, right_offset); default: - ASSERT_NOT_REACHED(); + NOTREACHED(); } return WTF::kNullopt; }
diff --git a/third_party/WebKit/Source/core/paint/MediaControlsPainter.cpp b/third_party/WebKit/Source/core/paint/MediaControlsPainter.cpp index 48ec709..6504977 100644 --- a/third_party/WebKit/Source/core/paint/MediaControlsPainter.cpp +++ b/third_party/WebKit/Source/core/paint/MediaControlsPainter.cpp
@@ -217,6 +217,18 @@ slider_background_color); } +bool MediaControlsPainter::PaintMediaRemotingCastIcon( + const LayoutObject& object, + const PaintInfo& paintInfo, + const IntRect& rect) { + const HTMLMediaElement* media_element = ToParentMediaElement(object); + if (!media_element) + return false; + static Image* cast_icon = PlatformResource("mediaRemotingCastIcon"); + + return PaintMediaButton(paintInfo.context, rect, cast_icon); +} + static void PaintSliderRangeHighlight(const IntRect& rect, const ComputedStyle& style, GraphicsContext& context,
diff --git a/third_party/WebKit/Source/core/paint/MediaControlsPainter.h b/third_party/WebKit/Source/core/paint/MediaControlsPainter.h index 0e39d63..2784624 100644 --- a/third_party/WebKit/Source/core/paint/MediaControlsPainter.h +++ b/third_party/WebKit/Source/core/paint/MediaControlsPainter.h
@@ -88,6 +88,9 @@ const PaintInfo&, const IntRect&); static void AdjustMediaSliderThumbSize(ComputedStyle&); + static bool PaintMediaRemotingCastIcon(const LayoutObject&, + const PaintInfo&, + const IntRect&); private: static void AdjustMediaSliderThumbPaintSize(const IntRect&,
diff --git a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp index 587c3631..df10d3c 100644 --- a/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp +++ b/third_party/WebKit/Source/core/paint/PaintLayerPainter.cpp
@@ -815,7 +815,7 @@ TransformationMatrix transform( paint_layer_.RenderableTransform(painting_info.GetGlobalPaintFlags())); IntPoint rounded_delta = RoundedIntPoint(delta); - transform.TranslateRight(rounded_delta.X(), rounded_delta.Y()); + transform.PostTranslate(rounded_delta.X(), rounded_delta.Y()); LayoutSize adjusted_sub_pixel_accumulation = painting_info.sub_pixel_accumulation + (delta - rounded_delta);
diff --git a/third_party/WebKit/Source/core/paint/ThemePainter.cpp b/third_party/WebKit/Source/core/paint/ThemePainter.cpp index c39cdeb..f4a6cca 100644 --- a/third_party/WebKit/Source/core/paint/ThemePainter.cpp +++ b/third_party/WebKit/Source/core/paint/ThemePainter.cpp
@@ -161,6 +161,8 @@ return PaintSearchField(o, paint_info, r); case kSearchFieldCancelButtonPart: return PaintSearchFieldCancelButton(o, paint_info, r); + case kMediaRemotingCastIconPart: + return MediaControlsPainter::PaintMediaRemotingCastIcon(o, paint_info, r); default: break; }
diff --git a/third_party/WebKit/Source/core/svg/SVGImageElement.cpp b/third_party/WebKit/Source/core/svg/SVGImageElement.cpp index 19538f8..7b035be 100644 --- a/third_party/WebKit/Source/core/svg/SVGImageElement.cpp +++ b/third_party/WebKit/Source/core/svg/SVGImageElement.cpp
@@ -23,8 +23,6 @@ #include "core/CSSPropertyNames.h" #include "core/dom/StyleChangeReason.h" -#include "core/frame/ImageBitmap.h" -#include "core/frame/LocalDOMWindow.h" #include "core/layout/LayoutImageResource.h" #include "core/layout/svg/LayoutSVGImage.h"
diff --git a/third_party/WebKit/Source/core/svg/SVGImageElement.h b/third_party/WebKit/Source/core/svg/SVGImageElement.h index b7a2260..90fc988 100644 --- a/third_party/WebKit/Source/core/svg/SVGImageElement.h +++ b/third_party/WebKit/Source/core/svg/SVGImageElement.h
@@ -22,7 +22,7 @@ #define SVGImageElement_h #include "core/SVGNames.h" -#include "core/html/canvas/ImageElementBase.h" +#include "core/html/canvas/CanvasImageElementSource.h" #include "core/svg/SVGAnimatedLength.h" #include "core/svg/SVGAnimatedPreserveAspectRatio.h" #include "core/svg/SVGGraphicsElement.h" @@ -33,7 +33,7 @@ namespace blink { class CORE_EXPORT SVGImageElement final : public SVGGraphicsElement, - public ImageElementBase, + public CanvasImageElementSource, public SVGURIReference { DEFINE_WRAPPERTYPEINFO(); USING_GARBAGE_COLLECTED_MIXIN(SVGImageElement);
diff --git a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp index 39564a8..445cfe074 100644 --- a/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp +++ b/third_party/WebKit/Source/core/svg/graphics/SVGImage.cpp
@@ -365,7 +365,7 @@ FloatRect float_bounds(FloatPoint(), size); const SkRect bounds(float_bounds); - flags.setShader(MakePaintShaderRecord( + flags.setShader(SkShader::MakePictureShader( PaintRecordForCurrentFrame(float_bounds, url), SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, &local_matrix, &bounds));
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/smallIcons.png b/third_party/WebKit/Source/devtools/front_end/Images/smallIcons.png index 9e40e19..be3d406 100644 --- a/third_party/WebKit/Source/devtools/front_end/Images/smallIcons.png +++ b/third_party/WebKit/Source/devtools/front_end/Images/smallIcons.png Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png b/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png index 3d23b09..97ac39aa 100644 --- a/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png +++ b/third_party/WebKit/Source/devtools/front_end/Images/smallIcons_2x.png Binary files differ
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes index 747c4f3b..2d58d84 100644 --- a/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes +++ b/third_party/WebKit/Source/devtools/front_end/Images/src/optimize_png.hashes
@@ -4,7 +4,7 @@ "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28", "checkboxCheckmark.svg": "f039bf85cee42ad5c30ca3bfdce7912a", "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45", - "smallIcons.svg": "55c46735baa910dbe8f6b9602eb2f464", + "smallIcons.svg": "a612780441b0cc423900f6343ee572ab", "mediumIcons.svg": "e63ac6385b0a6efb783d9838517e5e44", "breakpoint.svg": "69cd92d807259c022791112809b97799", "treeoutlineTriangles.svg": "017d2f89437df0afc6b9cd5ff43735d9",
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/smallIcons.svg b/third_party/WebKit/Source/devtools/front_end/Images/src/smallIcons.svg index ef36848..0b61ec60 100644 --- a/third_party/WebKit/Source/devtools/front_end/Images/src/smallIcons.svg +++ b/third_party/WebKit/Source/devtools/front_end/Images/src/smallIcons.svg
@@ -36,15 +36,15 @@ guidetolerance="10" inkscape:pageopacity="0" inkscape:pageshadow="2" - inkscape:window-width="1986" - inkscape:window-height="1353" + inkscape:window-width="938" + inkscape:window-height="826" id="namedview4455" showgrid="true" - inkscape:zoom="10.488889" - inkscape:cx="52.875124" - inkscape:cy="43.09204" - inkscape:window-x="201" - inkscape:window-y="36" + inkscape:zoom="3.7083823" + inkscape:cx="22.472364" + inkscape:cy="66.155904" + inkscape:window-x="940" + inkscape:window-y="26" inkscape:window-maximized="0" inkscape:current-layer="svg4185"> <inkscape:grid @@ -755,4 +755,19 @@ id="tspan5193-90" x="-7.1080513" y="8.0561762">5</tspan></text> + <g + transform="matrix(1.1320755,0,0,1.1320754,81.603774,82.603774)" + style="fill:#00bcd4;stroke:#000000;stroke-width:0.30000001" + id="g3669"> + <circle + d="M 5.5,3 C 5.5,4.3807119 4.3807119,5.5 3,5.5 1.6192881,5.5 0.5,4.3807119 0.5,3 0.5,1.6192881 1.6192881,0.5 3,0.5 4.3807119,0.5 5.5,1.6192881 5.5,3 z" + sodipodi:ry="2.5" + sodipodi:rx="2.5" + sodipodi:cy="3" + sodipodi:cx="3" + cx="3" + cy="3" + r="2.5" + id="circle3671" /> + </g> </svg>
diff --git a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes index 747c4f3b..2d58d84 100644 --- a/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes +++ b/third_party/WebKit/Source/devtools/front_end/Images/src/svg2png.hashes
@@ -4,7 +4,7 @@ "breakpointConditional.svg": "4cf90210b2af2ed84db2f60b07bcde28", "checkboxCheckmark.svg": "f039bf85cee42ad5c30ca3bfdce7912a", "errorWave.svg": "e183fa242a22ed4784a92f6becbc2c45", - "smallIcons.svg": "55c46735baa910dbe8f6b9602eb2f464", + "smallIcons.svg": "a612780441b0cc423900f6343ee572ab", "mediumIcons.svg": "e63ac6385b0a6efb783d9838517e5e44", "breakpoint.svg": "69cd92d807259c022791112809b97799", "treeoutlineTriangles.svg": "017d2f89437df0afc6b9cd5ff43735d9",
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js index aafd190d..015fb7d 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js +++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkDataGridNode.js
@@ -44,6 +44,8 @@ this._showingInitiatorChain = false; /** @type {?SDK.NetworkRequest} */ this._requestOrFirstKnownChildRequest = null; + /** @type {!Map<string, !UI.Icon>} */ + this._columnIcons = new Map(); } /** @@ -612,15 +614,25 @@ element.classList.toggle('network-error-row', this._isFailed()); element.classList.toggle('network-navigation-row', this._isNavigationRequest); + for (var rowDecorator of this._parentView.rowDecorators()) + rowDecorator.decorate(this); super.createCells(element); } /** + * @param {string} columnId + * @param {!UI.Icon} icon + */ + setIconForColumn(columnId, icon) { + this._columnIcons.set(columnId, icon); + } + + /** * @param {!Element} element * @param {string} text */ _setTextAndTitle(element, text) { - element.textContent = text; + element.createTextChild(text); element.title = text; } @@ -631,6 +643,9 @@ */ createCell(columnIdentifier) { var cell = this.createTD(columnIdentifier); + var icon = this._columnIcons.get(columnIdentifier); + if (icon) + cell.appendChild(icon); // If the key exists but the value is null it means the extension instance has not resolved yet. // The view controller will force all rows to update when extension is resolved. if (this._columnExtensions.has(columnIdentifier)) {
diff --git a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js index 5b19f71c6..7e8d4a0 100644 --- a/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js +++ b/third_party/WebKit/Source/devtools/front_end/network/NetworkLogView.js
@@ -76,6 +76,9 @@ this._nodesByRequestId = new Map(); /** @type {!Map<*, !Network.NetworkGroupNode>} */ this._nodeGroups = new Map(); + /** @type {!Set<!Network.NetworkRowDecorator>} */ + this._rowDecorators = new Set(); + /** @type {!Object.<string, boolean>} */ this._staleRequestIds = {}; /** @type {number} */ @@ -358,6 +361,13 @@ } /** + * @return {!Set<!Network.NetworkRowDecorator>} + */ + rowDecorators() { + return this._rowDecorators; + } + + /** * @param {!SDK.NetworkRequest} request * @return {?Network.NetworkRequestNode} */ @@ -488,6 +498,12 @@ this.element.id = 'network-container'; this._setupDataGrid(); + self.runtime.allInstances(Network.NetworkRowDecorator).then(instances => { + for (var instance of instances) + this._rowDecorators.add(instance); + this._invalidateAllItems(true); + }); + this._columns.show(this.element); this._summaryBarElement = this.element.createChild('div', 'network-summary-bar'); @@ -1818,3 +1834,15 @@ */ groupName(key) {} }; + +/** + * @interface + */ +Network.NetworkRowDecorator = function() {}; + +Network.NetworkRowDecorator.prototype = { + /** + * @param {!Network.NetworkNode} node + */ + decorate(node) {} +};
diff --git a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js index 356b4a1..cdee8495 100644 --- a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js +++ b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/NetworkProductGroupLookup.js
@@ -113,4 +113,29 @@ } }; +/** + * @implements {Network.NetworkRowDecorator} + */ +NetworkGroupLookup.NetworkProductTypeGroupLookup = class { + /** + * @override + * @param {!Network.NetworkNode} node + */ + decorate(node) { + var request = node.request(); + var element = node.existingElement(); + if (!request || !element) + return; + var typeName = ProductRegistry.typeForUrl(request.parsedURL); + if (!typeName) + return; + var icon = UI.Icon.create('smallicon-network-product'); + if (typeName === 'Tracking') + icon.style.filter = 'hue-rotate(220deg) brightness(1.5)'; + if (typeName === 'CDN') + icon.style.filter = 'hue-rotate(-90deg) brightness(1.5)'; + node.setIconForColumn('product-extension', icon); + } +}; + NetworkGroupLookup.NetworkProductFrameGroupLookup._productFrameGroupNameSymbol = Symbol('ProductFrameGroupName');
diff --git a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json index f933c617..9753bb9 100644 --- a/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json +++ b/third_party/WebKit/Source/devtools/front_end/network_group_lookup/module.json
@@ -16,6 +16,10 @@ "type": "@Network.NetworkColumnExtensionInterface", "className": "NetworkGroupLookup.NetworkProductGroupLookup", "title": "Product" + }, + { + "type": "@Network.NetworkRowDecorator", + "className": "NetworkGroupLookup.NetworkProductTypeGroupLookup" } ], "dependencies": [
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js index 2a1f885b..29c6dcd1 100644 --- a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js +++ b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductNameForURL.js
@@ -6,6 +6,17 @@ * @return {?string} */ ProductRegistry.nameForUrl = function(parsedUrl) { + var entry = ProductRegistry.entryForUrl(parsedUrl); + if (entry) + return entry.name; + return null; +}; + +/** + * @param {!Common.ParsedURL} parsedUrl + * @return {?ProductRegistry.ProductEntry} + */ +ProductRegistry.entryForUrl = function(parsedUrl) { if (parsedUrl.isDataURL()) return null; // TODO(allada) This should be expanded to allow paths as as well as domain to find a product. @@ -42,6 +53,17 @@ }; /** + * @param {!Common.ParsedURL} parsedUrl + * @return {?string} + */ +ProductRegistry.typeForUrl = function(parsedUrl) { + var entry = ProductRegistry.entryForUrl(parsedUrl); + if (entry) + return entry.type; + return null; +}; + +/** * @param {string} domain * @return {string} */ @@ -50,20 +72,29 @@ }; /** + * @param {!Array<string>} productTypes * @param {!Array<string>} productNames - * @param {!Array<!{hash: string, prefixes: !Object<string, number>}>} data + * @param {!Array<!{hash: string, prefixes: !Object<string, !{product: number, type: (number|undefined)}>}>} data */ -ProductRegistry.register = function(productNames, data) { +ProductRegistry.register = function(productTypes, productNames, data) { + var typesMap = /** @type {!Map<number, string>} */ (new Map()); + for (var i = 0; i < productTypes.length; i++) + typesMap.set(i, productTypes[i]); + for (var i = 0; i < data.length; i++) { var entry = data[i]; var prefixes = {}; for (var prefix in entry.prefixes) { - var productNameIndex = entry.prefixes[prefix]; - prefixes[prefix] = productNames[productNameIndex]; + var prefixEntry = entry.prefixes[prefix]; + var type = prefixEntry.type !== undefined ? (typesMap.get(prefixEntry.type) || null) : null; + prefixes[prefix] = {name: productNames[prefixEntry.product], type: type}; } ProductRegistry._productsByDomainHash.set(entry.hash, prefixes); } }; -/** @type {!Map<string, !Object<string, string>>}} */ +/** @typedef {!{name: string, type: ?string}} */ +ProductRegistry.ProductEntry; + +/** @type {!Map<string, !Object<string, !ProductRegistry.ProductEntry>>}} */ ProductRegistry._productsByDomainHash = new Map();
diff --git a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js index a81166545..fe780fa 100644 --- a/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js +++ b/third_party/WebKit/Source/devtools/front_end/product_registry/ProductRegistryData.js
@@ -4,6 +4,8 @@ // clang-format off /* eslint-disable */ ProductRegistry.register([ +], +[ "1&1 Internet AG", "A9", "AdGenie", @@ -1499,5333 +1501,5333 @@ "4Info, Inc." ], [ - {"hash":"20d393bb0e5c4de5","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"583365b5f9d44cae","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"dcde9b6a7ed731d0","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"0b618d2d6d12ff65","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"5ca7dfe6f0741d1b","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"f10aac9be30b9153","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"a29e3fb3581debcc","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"586a404a027dd51d","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"b772cd4d229b926f","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"5f153f573d601ed5","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"d08d00723aa41a8b","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"154c00442038842c","prefixes":{"":1}}, // [A9] - {"hash":"bb5191c2744e5157","prefixes":{"":1}}, // [A9] - {"hash":"24f8ee8d41010340","prefixes":{"":1}}, // [A9] - {"hash":"dde97a25d741083b","prefixes":{"":1}}, // [A9] - {"hash":"c40e51f0d4308aef","prefixes":{"":1}}, // [A9] - {"hash":"dafae404fe4bf9e0","prefixes":{"":1}}, // [A9] - {"hash":"2965d60c41f24692","prefixes":{"*":2}}, // [AdGenie] - {"hash":"947df9bf1ccc5af0","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"5874ab2040fd92e5","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"12c9a3fc47eec655","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"ff950154b65538b5","prefixes":{"":5}}, // [Conversant Ad Server] - {"hash":"cca88c18c3a955c3","prefixes":{"*":6}}, // [Napster Luxemburg SARL] - {"hash":"cf583a4f1b85a63d","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"b1f24fb9f4e82bc4","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"7ed3ef3bd9b7964c","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"802185637db97f57","prefixes":{"":9}}, // [Spartoo] - {"hash":"69ac52e6452dcdcc","prefixes":{"":9}}, // [Spartoo] - {"hash":"99d6e276a9f3dce4","prefixes":{"*":10}}, // [eBay] - {"hash":"20cea422f633f132","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"abac0e92b7c09189","prefixes":{"":12}}, // [Madeleine Mode GmbH] - {"hash":"ab6d5bcfcb13c98c","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"17c760570121cb8e","prefixes":{"":14}}, // [Telefonica UK / O2 UK] - {"hash":"cca991b6ab7e6463","prefixes":{"":15}}, // [Tamome] - {"hash":"b24aab8ad8822e3b","prefixes":{"":15}}, // [Tamome] - {"hash":"e068190951dbb5ef","prefixes":{"":15}}, // [Tamome] - {"hash":"c5a5fd495c44b8e2","prefixes":{"":15}}, // [Tamome] - {"hash":"95305154969e81b9","prefixes":{"":15}}, // [Tamome] - {"hash":"f40a7549c998fd96","prefixes":{"":16}}, // [TUI UK Limited] - {"hash":"96eeaa2c9afb8955","prefixes":{"":16}}, // [TUI UK Limited] - {"hash":"bfeca1a08eac1f5e","prefixes":{"*":17}}, // [iJento] - {"hash":"cfbb894fdba5489a","prefixes":{"":18}}, // [McCann Erikson] - {"hash":"4ce21296b7adb20d","prefixes":{"":19}}, // [Tribes Research Limited] - {"hash":"72b12a834f93bbd1","prefixes":{"":20}}, // [On Device Research Ltd.] - {"hash":"5a885783941e6540","prefixes":{"*":21}}, // [SuperVista AG] - {"hash":"382734d54ddf7100","prefixes":{"":22}}, // [National Lottery] - {"hash":"397eccbf74cd0328","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"bf0f9d6566d29693","prefixes":{"*":0}}, // [1&1 Internet AG] - {"hash":"8fd259996da5f3d8","prefixes":{"":23}}, // [AppNexus Open AdStream] - {"hash":"56837fe2597a4f9e","prefixes":{"":23}}, // [AppNexus Open AdStream] - {"hash":"fb4ddfafadb387cf","prefixes":{"":23}}, // [AppNexus Open AdStream] - {"hash":"07a11d9d007c60f8","prefixes":{"":23}}, // [AppNexus Open AdStream] - {"hash":"9f36fc0bb2911046","prefixes":{"":23}}, // [AppNexus Open AdStream] - {"hash":"57c7af60819b3f3e","prefixes":{"":23}}, // [AppNexus Open AdStream] - {"hash":"c3363b1ccf0e2ab6","prefixes":{"":24}}, // [Interworks Media, Inc.] - {"hash":"9d16bd99e929eccd","prefixes":{"":24}}, // [Interworks Media, Inc.] - {"hash":"211d3b9ea9bf8f2e","prefixes":{"":25}}, // [Action Exchange, Inc.] - {"hash":"414adfe007d3587f","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"4486e584990f9603","prefixes":{"":26}}, // [FSN ASIA PRIVATE LIMITED] - {"hash":"8cfb66b1b7bbe43b","prefixes":{"":27}}, // [iPromote] - {"hash":"fba2a0cd626a5289","prefixes":{"":27}}, // [iPromote] - {"hash":"113620ff3bb3ea60","prefixes":{"":27}}, // [iPromote] - {"hash":"b5f6b4570a07977c","prefixes":{"*":28}}, // [33Across Inc.] - {"hash":"877f3b2ffe34bf4a","prefixes":{"*":29}}, // [8thBridge] - {"hash":"bc5882cc43d24a0b","prefixes":{"":1}}, // [A9] - {"hash":"5506a3935677620c","prefixes":{"":1}}, // [A9] - {"hash":"4a8f0380d8c0ee22","prefixes":{"":1}}, // [A9] - {"hash":"54e1d163948509ad","prefixes":{"":1}}, // [A9] - {"hash":"534e3733e4e5309d","prefixes":{"":1}}, // [A9] - {"hash":"c7db8e0e25c7df2d","prefixes":{"":1}}, // [A9] - {"hash":"222afdeb29be30ca","prefixes":{"":1}}, // [A9] - {"hash":"a082f434d9ec1f91","prefixes":{"":1}}, // [A9] - {"hash":"e00092cf7fb8c74a","prefixes":{"":1}}, // [A9] - {"hash":"6b3557e54073f90d","prefixes":{"":1}}, // [A9] - {"hash":"b825724682d4bd41","prefixes":{"":1}}, // [A9] - {"hash":"62a0c6b3cd3ed567","prefixes":{"":1}}, // [A9] - {"hash":"7e5f80275a654f93","prefixes":{"":1}}, // [A9] - {"hash":"426c5ded1145b847","prefixes":{"*":1}}, // [A9] - {"hash":"14f8bb92c4036403","prefixes":{"*":1}}, // [A9] - {"hash":"8aaada51ee8ebb86","prefixes":{"*":1}}, // [A9] - {"hash":"38eac8937b706b6f","prefixes":{"*":1}}, // [A9] - {"hash":"7e1d4edc3530e448","prefixes":{"":1}}, // [A9] - {"hash":"c0bf2b011199054e","prefixes":{"":1}}, // [A9] - {"hash":"b4dd68c5d9b68922","prefixes":{"":1}}, // [A9] - {"hash":"8d943586f8f86d04","prefixes":{"":1}}, // [A9] - {"hash":"f60b51325cdf4f80","prefixes":{"":1}}, // [A9] - {"hash":"080dea15fa602a8c","prefixes":{"":1}}, // [A9] - {"hash":"a01c9f1f745acb3b","prefixes":{"":1}}, // [A9] - {"hash":"eae49b84950db230","prefixes":{"":1}}, // [A9] - {"hash":"dabd2bc95093ab29","prefixes":{"":1}}, // [A9] - {"hash":"8912c7bdde481292","prefixes":{"":1}}, // [A9] - {"hash":"c735e4adfc754475","prefixes":{"":1}}, // [A9] - {"hash":"297bba8cd045b252","prefixes":{"":1}}, // [A9] - {"hash":"b4ee04e1cb1b0cb0","prefixes":{"":1}}, // [A9] - {"hash":"1f9925b611b3db5e","prefixes":{"":1}}, // [A9] - {"hash":"e63f21a01153ba8c","prefixes":{"":1}}, // [A9] - {"hash":"5df92c4776ddb318","prefixes":{"":1}}, // [A9] - {"hash":"995e4fe402d230d4","prefixes":{"":1}}, // [A9] - {"hash":"f27bb2795a31311c","prefixes":{"":1}}, // [A9] - {"hash":"7ef41a846501bb38","prefixes":{"":1}}, // [A9] - {"hash":"f69c9d0a6726d8a2","prefixes":{"":1}}, // [A9] - {"hash":"fbe82cac507c4685","prefixes":{"":1}}, // [A9] - {"hash":"b30108dc4fef7d3d","prefixes":{"":1}}, // [A9] - {"hash":"3db91f22497fff29","prefixes":{"":1}}, // [A9] - {"hash":"8c93f288d1c9adaa","prefixes":{"":1}}, // [A9] - {"hash":"dd3abfa7756f945e","prefixes":{"":1}}, // [A9] - {"hash":"562f4260ff4f65a6","prefixes":{"":1}}, // [A9] - {"hash":"94d8ddce749b5392","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"031269afdaee38ca","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"0c0ba1aaf374eac4","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"cb1dbab4bb9d139b","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"144dac73c5669441","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"b6b232e5fbf868f0","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"3c15562b6abc1319","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"722c811d70136506","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"78a792effa9e4967","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"a59637a7ca9dc579","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"6b3ce5ea793ecbbf","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"2b5bd0c505a178d0","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"c41cefa39852e538","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"e572b55cf0c6834b","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"80d0b8e46094b1c9","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"343036881041aa23","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"2559ed3163479794","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"95acf3d42f565375","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"a3fe80607786880e","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"b7629e7fab881e7a","prefixes":{"":31}}, // [Retailigence] - {"hash":"03ec1a841574bd4d","prefixes":{"":32}}, // [Logly DSP] - {"hash":"7ff5ce1768464e16","prefixes":{"":32}}, // [Logly DSP] - {"hash":"10fb9dcb49e1abe6","prefixes":{"":33}}, // [App-CM Inc.] - {"hash":"6d3713ad37a0966c","prefixes":{"":33}}, // [App-CM Inc.] - {"hash":"41670ff004a04f19","prefixes":{"":33}}, // [App-CM Inc.] - {"hash":"cd681b4891935464","prefixes":{"":34}}, // [Yahoo! Japan Corporation] - {"hash":"b97383320b594f18","prefixes":{"":34}}, // [Yahoo! Japan Corporation] - {"hash":"e2fff6e598b3035e","prefixes":{"":1}}, // [A9] - {"hash":"cb3ceda4f0c38995","prefixes":{"":1}}, // [A9] - {"hash":"a218e065250d793c","prefixes":{"":1}}, // [A9] - {"hash":"deb1ce911dfb392b","prefixes":{"":1}}, // [A9] - {"hash":"de5ead15f1d0f171","prefixes":{"":1}}, // [A9] - {"hash":"92615d394883ce3c","prefixes":{"":1}}, // [A9] - {"hash":"6cd842b12d20b208","prefixes":{"":1}}, // [A9] - {"hash":"dc6acfacad6c1026","prefixes":{"":1}}, // [A9] - {"hash":"81aa25ff76990996","prefixes":{"":1}}, // [A9] - {"hash":"1254067a80cf3d7b","prefixes":{"":1}}, // [A9] - {"hash":"31e2549e82b92770","prefixes":{"":1}}, // [A9] - {"hash":"be41bb940608434e","prefixes":{"":1}}, // [A9] - {"hash":"05a4ab4a11bd1fbf","prefixes":{"":1}}, // [A9] - {"hash":"05677aca712e9b65","prefixes":{"":1}}, // [A9] - {"hash":"00620801050945d1","prefixes":{"":35}}, // [Oggifinogi] - {"hash":"c0ceae7cfc4144c6","prefixes":{"":36}}, // [PaperG] - {"hash":"996c3990310cfc19","prefixes":{"":37}}, // [Public-Idées] - {"hash":"14210f0f0a8d71cb","prefixes":{"":38}}, // [The Trade Desk Inc.] - {"hash":"0f07ec261c73a8fb","prefixes":{"*":39,"":40}}, // [Amazon] [F# Inc.] - {"hash":"736b87f504b5e113","prefixes":{"":41}}, // [Airpush, Inc.] - {"hash":"0bcef81372eadfe9","prefixes":{"":42}}, // [Clinch.co] - {"hash":"9ea6769e672968e9","prefixes":{"":40}}, // [F# Inc.] - {"hash":"0fde612c20c42c6f","prefixes":{"":43}}, // [Transout Inc.] - {"hash":"5ed995763ce85029","prefixes":{"":44}}, // [ADZIP] - {"hash":"497082972c1c0dfd","prefixes":{"":45}}, // [Adways SAS] - {"hash":"6d7e4a203b969d9e","prefixes":{"":46}}, // [Opera Mediaworks Inc.] - {"hash":"d3a9057528c14a56","prefixes":{"":47}}, // [DistroScale Inc.] - {"hash":"479d2f8d9b7a2efe","prefixes":{"":48}}, // [Enzymic Consultancy] - {"hash":"6f7f9fb0065f745f","prefixes":{"":48}}, // [Enzymic Consultancy] - {"hash":"893d967f4540bb44","prefixes":{"":49}}, // [Fluct Inc.] - {"hash":"3830f0b6ca460fe9","prefixes":{"*":50}}, // [Acuity Ads] - {"hash":"e917237ee7b0ad79","prefixes":{"":50}}, // [Acuity Ads] - {"hash":"e0792ffd5eca94d8","prefixes":{"":50}}, // [Acuity Ads] - {"hash":"29beee316a393584","prefixes":{"":50}}, // [Acuity Ads] - {"hash":"ce9e7bf17bba4aae","prefixes":{"":50}}, // [Acuity Ads] - {"hash":"fcf604f90166bb66","prefixes":{"":50}}, // [Acuity Ads] - {"hash":"f11d9059950b4a07","prefixes":{"":50}}, // [Acuity Ads] - {"hash":"7d142a85f84f3402","prefixes":{"":50}}, // [Acuity Ads] - {"hash":"83c0bb224cb5622f","prefixes":{"*":51}}, // [iLead] - {"hash":"27841e5ac1581a67","prefixes":{"":52}}, // [Canned Banners LLC] - {"hash":"83fbdce4f2519684","prefixes":{"":52}}, // [Canned Banners LLC] - {"hash":"db7bac94f2b2d2b7","prefixes":{"":53}}, // [AdBroker GmbH] - {"hash":"e0de5430253205fc","prefixes":{"":53}}, // [AdBroker GmbH] - {"hash":"bde57b6011499edf","prefixes":{"":53}}, // [AdBroker GmbH] - {"hash":"077ad042e29c79fb","prefixes":{"":53}}, // [AdBroker GmbH] - {"hash":"d3c6fc5ff77f0d5c","prefixes":{"":53}}, // [AdBroker GmbH] - {"hash":"b88fb01ba215894e","prefixes":{"*":54}}, // [InMind Opinion Media] - {"hash":"3d509f557aeca6de","prefixes":{"*":55}}, // [Adcentric] - {"hash":"3a9959c8cadd20af","prefixes":{"*":56}}, // [AdClear GmbH] - {"hash":"e8adb806a39f661d","prefixes":{"":57}}, // [DeinDeal AG] - {"hash":"da7343c16a51f1d4","prefixes":{"":58}}, // [Sixt Leasing SE] - {"hash":"5b4d4595453d5b23","prefixes":{"":59}}, // [Air Berlin] - {"hash":"b5085d3a3ede7503","prefixes":{"":60}}, // [Aegon ESPAÑA, S.A. de Seguros y Reaseguros, Uniper] - {"hash":"076e39b7aa354c83","prefixes":{"":56}}, // [AdClear GmbH] - {"hash":"61c6a6d15b340f45","prefixes":{"":61}}, // [Adform DSP] - {"hash":"36f5cb213a4d1469","prefixes":{"":61}}, // [Adform DSP] - {"hash":"95b61c012402be18","prefixes":{"":61}}, // [Adform DSP] - {"hash":"34e291794974e891","prefixes":{"":61}}, // [Adform DSP] - {"hash":"1d0d393655ae7ce4","prefixes":{"*":62}}, // [Adconion Media Group] - {"hash":"dbeb328acc5a5a14","prefixes":{"*":62}}, // [Adconion Media Group] - {"hash":"9e6e5f626bc3d43c","prefixes":{"":62}}, // [Adconion Media Group] - {"hash":"66659e79ff807f39","prefixes":{"":62}}, // [Adconion Media Group] - {"hash":"ce965bb06760436e","prefixes":{"":62}}, // [Adconion Media Group] - {"hash":"ca8e04a5cdd8fd1f","prefixes":{"":62}}, // [Adconion Media Group] - {"hash":"1084f1e2b04f5f93","prefixes":{"":62}}, // [Adconion Media Group] - {"hash":"04b3778457a3ba99","prefixes":{"*":62}}, // [Adconion Media Group] - {"hash":"1b843f2f1a39c98b","prefixes":{"":63}}, // [Adform] - {"hash":"8c7beead1ea37382","prefixes":{"":63}}, // [Adform] - {"hash":"2b59e3efe59d97d5","prefixes":{"":63}}, // [Adform] - {"hash":"518308907f5f69a2","prefixes":{"":63}}, // [Adform] - {"hash":"07d4474fae9c6b06","prefixes":{"":63}}, // [Adform] - {"hash":"47c590566f0c869a","prefixes":{"":63}}, // [Adform] - {"hash":"409240a38e5e72b9","prefixes":{"":63}}, // [Adform] - {"hash":"98421a6d07f5517b","prefixes":{"":63}}, // [Adform] - {"hash":"4f00fb001635132a","prefixes":{"":63}}, // [Adform] - {"hash":"c6ba94708f6d7d53","prefixes":{"":63}}, // [Adform] - {"hash":"caae109e8a3bec4f","prefixes":{"":63}}, // [Adform] - {"hash":"9137b5cdfcb54ca8","prefixes":{"":63}}, // [Adform] - {"hash":"ae96d818a0674db2","prefixes":{"":63}}, // [Adform] - {"hash":"f67fecc339ee273d","prefixes":{"":63}}, // [Adform] - {"hash":"763cc84e4a6c6fce","prefixes":{"":63}}, // [Adform] - {"hash":"170707627205d14d","prefixes":{"*":64}}, // [Adition] - {"hash":"4b3783127a962269","prefixes":{"":64}}, // [Adition] - {"hash":"457cb5e9c72500bd","prefixes":{"":64}}, // [Adition] - {"hash":"0f529dc32c1b9057","prefixes":{"":64}}, // [Adition] - {"hash":"e63845bad50263e0","prefixes":{"":65}}, // [Active Agent] - {"hash":"08b8d8e397848984","prefixes":{"":65}}, // [Active Agent] - {"hash":"146ac0ff21924e7d","prefixes":{"*":64}}, // [Adition] - {"hash":"a51da6855de3a256","prefixes":{"":66}}, // [mov.ad GmbH] - {"hash":"150498b9d10f053f","prefixes":{"*":64}}, // [Adition] - {"hash":"fa1803e00adafe6b","prefixes":{"":66}}, // [mov.ad GmbH] - {"hash":"ba820e9bd3ccd291","prefixes":{"":66}}, // [mov.ad GmbH] - {"hash":"73f70045beea25cc","prefixes":{"":66}}, // [mov.ad GmbH] - {"hash":"14725cb7b6e3ed94","prefixes":{"":66}}, // [mov.ad GmbH] - {"hash":"fd2740cbee10696e","prefixes":{"":66}}, // [mov.ad GmbH] - {"hash":"e150d6342c6a3952","prefixes":{"*":67}}, // [AdJug] - {"hash":"50b8b93a8b536983","prefixes":{"*":68}}, // [AdJuggler] - {"hash":"85335d83efc9839b","prefixes":{"*":68}}, // [AdJuggler] - {"hash":"08f0bb45326cffe3","prefixes":{"*":69}}, // [AdKeeper] - {"hash":"5e2b2393dad3f5eb","prefixes":{"*":69}}, // [AdKeeper] - {"hash":"da818bcf61df9002","prefixes":{"*":69}}, // [AdKeeper] - {"hash":"846fc96edd68e2f8","prefixes":{"*":70}}, // [AdKnife] - {"hash":"914d48df3d7aeaae","prefixes":{"*":71}}, // [Adku] - {"hash":"8a5b58174cc938bf","prefixes":{"*":72}}, // [AdLantic Online Advertising] - {"hash":"218eb49612488266","prefixes":{"":73}}, // [Adloox] - {"hash":"85c9a55ea5b2a32a","prefixes":{"data":73,"am":73}}, // [Adloox] [Adloox] - {"hash":"9827ba4725b53982","prefixes":{"":73}}, // [Adloox] - {"hash":"bfbbf6b81b109b36","prefixes":{"*":74}}, // [adMarketplace] - {"hash":"006d6718806b7562","prefixes":{"*":75}}, // [AdMotion] - {"hash":"43871f2ce46890ca","prefixes":{"":76}}, // [AdMotion USA Inc.] - {"hash":"2648f20c5fa7ee09","prefixes":{"":76}}, // [AdMotion USA Inc.] - {"hash":"63e0b0f085a8b214","prefixes":{"*":77}}, // [Digilant] - {"hash":"7288a07f340911e5","prefixes":{"":78}}, // [Adnologies GmbH] - {"hash":"a9fe843107d85e5c","prefixes":{"":78}}, // [Adnologies GmbH] - {"hash":"230c59512261d73d","prefixes":{"cdn-":78}}, // [Adnologies GmbH] - {"hash":"8ca4fb13619b77f1","prefixes":{"*":79}}, // [Adometry by Google] - {"hash":"18652cba5445b34f","prefixes":{"":80}}, // [Adperium BV] - {"hash":"6d09f83aefc4501e","prefixes":{"*":81}}, // [AdPredictive] - {"hash":"70732e61af9adb0a","prefixes":{"":82}}, // [Usemax] - {"hash":"b4ede094fe706ace","prefixes":{"*":83}}, // [Adrime] - {"hash":"b19cfc95f67d0fa7","prefixes":{"*":83}}, // [Adrime] - {"hash":"a6e49b6248014fa6","prefixes":{"*":84}}, // [AdRiver] - {"hash":"4a53a59490c4a38e","prefixes":{"*":85}}, // [Adroit Interactive] - {"hash":"683e22cafa5f3bd9","prefixes":{"":85}}, // [Adroit Interactive] - {"hash":"02e8a15ca61fbc33","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"a3c3a62c565da599","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"73e23ad84157f244","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"b8bd71aaaa715a18","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"34049e798c80bbab","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"838eef1c1fe8af0c","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"3a7e3dfea00e8162","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"0173cd7bab46f6fd","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"c14663d78cbafed8","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"308a1884ba632254","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"0e219f63d92e9f36","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"f01c179c5f1e71ec","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"37523eaeb9b1fe5b","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"65eb7e97acb7c098","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"3caa5b10b768447a","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"c8ca517332beacb1","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"8af902c81b793154","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"38ed924c01545a3b","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"5b57e356a62d6828","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"4281a0a413796f1b","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"66e356358c42a9dd","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"ebe26069a0940456","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"9d2283ab81bb7a35","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"d5e05919c2d72fad","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"519afd00add32a74","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"88d48f3ceb8d02bb","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"d93b7d2660301b12","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"6a71009da4358f28","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"71f627786ab80897","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"f1b3147102656b41","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"1a3040f79f3eda5d","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"1c81402568ca4c6f","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"5a0f681f5d3714ed","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"fb3d4d280f621627","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"6811c6e8c717b25b","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"2b559615c990392b","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"d8447dd40d9ce63f","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"98a32e82bd6110c6","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"e3ff44ab0bbc3669","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"d8a244effa85d1e6","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"fa65d62c20cc94bb","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"1d55dfbdf9f318d1","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"e06269227a02ae45","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"2d2bf9a9cb6e9f86","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"0adbf6085946bbf6","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"61d2d4f25972fccd","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"d9506dcf3ab8ec68","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"61448b088aea7146","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"12b1ed92371498e2","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"b8f763d46b3d230a","prefixes":{"":88}}, // [Integral Ad Science Firewall] - {"hash":"d4033ecbe069d318","prefixes":{"":87}}, // [Campaign Monitor] - {"hash":"b45ea1eb9c9290a1","prefixes":{"*":89}}, // [AdShuffle] - {"hash":"333bed71fe872c88","prefixes":{"":90}}, // [ADSOVO] - {"hash":"ad77e48d841a15a8","prefixes":{"":90}}, // [ADSOVO] - {"hash":"f822525b349866b4","prefixes":{"*":91}}, // [ADTECH GmbH] - {"hash":"6cda8dd1b92f6c35","prefixes":{"*":91}}, // [ADTECH GmbH] - {"hash":"4d592efbff40197f","prefixes":{"*":92}}, // [Adtelligence] - {"hash":"0f57dc611a2e469e","prefixes":{"":93}}, // [Adverline] - {"hash":"ef6cb8337223cbd6","prefixes":{"*":93}}, // [Adverline] - {"hash":"1902c407bbf1add8","prefixes":{"*":94}}, // [AOL Advertising.com] - {"hash":"c0956c63084023a8","prefixes":{"":95}}, // [Adap.tv Inc. (AdX)] - {"hash":"f029d231882ed252","prefixes":{"":95}}, // [Adap.tv Inc. (AdX)] - {"hash":"ee175aa478efb9b3","prefixes":{"*":94}}, // [AOL Advertising.com] - {"hash":"c0fff6b8232710c4","prefixes":{"*":94}}, // [AOL Advertising.com] - {"hash":"a846e3340ac27ada","prefixes":{"":96}}, // [Convertro Inc] - {"hash":"548e9e34dbc275b6","prefixes":{"":97}}, // [Tacoda] - {"hash":"e9695f56eaa054e2","prefixes":{"":94}}, // [AOL Advertising.com] - {"hash":"f7f6bc8c4345347a","prefixes":{"*":98}}, // [Digital Control GmbH (Advolution)] - {"hash":"484a656ace404140","prefixes":{"":98}}, // [Digital Control GmbH (Advolution)] - {"hash":"a224f896601ec717","prefixes":{"":98}}, // [Digital Control GmbH (Advolution)] - {"hash":"3a79a6d9af12ef9a","prefixes":{"*":99}}, // [AdXcel] - {"hash":"b3dec77d20a55dcb","prefixes":{"*":99}}, // [AdXcel] - {"hash":"1f25b55eca65221c","prefixes":{"":99}}, // [AdXcel] - {"hash":"f531a655b1982ee7","prefixes":{"":100}}, // [Alenty S.A.S] - {"hash":"b884f71dddd78a28","prefixes":{"":101}}, // [DoubleClick Bid Manager] - {"hash":"839eb75d0e9ef128","prefixes":{"":102}}, // [BigaBid Media Ltd.] - {"hash":"ad920773d5c3e050","prefixes":{"":103}}, // [Cogo Labs, Inc.] - {"hash":"6cbe6bc5d5fed35c","prefixes":{"":104}}, // [AudienceProject] - {"hash":"98bc51a23a37253c","prefixes":{"*":39}}, // [Amazon] - {"hash":"78cfcb50606e44f8","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"37f3aa55bd48760c","prefixes":{"":106}}, // [PocketMath] - {"hash":"fa92c520ca116999","prefixes":{"":107}}, // [Kpsule] - {"hash":"26d760f54a265d93","prefixes":{"":107}}, // [Kpsule] - {"hash":"c046bec428f602d5","prefixes":{"":107}}, // [Kpsule] - {"hash":"7fbe2c9290629394","prefixes":{"":107}}, // [Kpsule] - {"hash":"16d261a6c03c3de9","prefixes":{"":108}}, // [Immedium, Inc.] - {"hash":"1787c49522ce0278","prefixes":{"":109}}, // [Fractional Media, LLC] - {"hash":"45ccf99ac2ed3af1","prefixes":{"":109}}, // [Fractional Media, LLC] - {"hash":"8c431268cc5c116b","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"645dd2a279a77bf5","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"53aaf4b4f2f2cc8b","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"a6175c31fc587fa2","prefixes":{"":111}}, // [Epic Combo Malta Ltd.] - {"hash":"c0e7c2ad3d5d28a0","prefixes":{"":111}}, // [Epic Combo Malta Ltd.] - {"hash":"b6a3ce0dea65f762","prefixes":{"":112}}, // [Quixey] - {"hash":"29e07de898fa18f4","prefixes":{"":112}}, // [Quixey] - {"hash":"1959efaae117c9fa","prefixes":{"":113}}, // [YOOX NET-A-PORTER GROUP SPA] - {"hash":"0a59c33253f15970","prefixes":{"":114}}, // [TapTap Networks S.L.] - {"hash":"383161133467d12b","prefixes":{"":45}}, // [Adways SAS] - {"hash":"e2e18e4ba75c8e58","prefixes":{"":45}}, // [Adways SAS] - {"hash":"1f9da694ae6dd7cf","prefixes":{"":45}}, // [Adways SAS] - {"hash":"5038b4de783848d4","prefixes":{"":45}}, // [Adways SAS] - {"hash":"3542b95dcaa08c84","prefixes":{"":115}}, // [ComScore (AdXpose)] - {"hash":"7bbbdfeb3d7b8ad3","prefixes":{"*":116}}, // [AdYard] - {"hash":"2ff57c503c5f110d","prefixes":{"":117}}, // [Affectv] - {"hash":"7558d4293841e1f0","prefixes":{"":117}}, // [Affectv] - {"hash":"a8adad05a6ff5945","prefixes":{"":117}}, // [Affectv] - {"hash":"cd1a91dee22478d6","prefixes":{"":117}}, // [Affectv] - {"hash":"ebca243af085210f","prefixes":{"":117}}, // [Affectv] - {"hash":"bc7fe0dc5bf3e869","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"c894a0e3f6a6b627","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"bfdba513f8cb5b8b","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"1227c609f898a707","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"4f884065e1e88de2","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"93d5950ea0bfe437","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"560c5b8e23e29733","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"28d74bfc94c9635f","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"8f7f83e4f5f9e2c0","prefixes":{"":3}}, // [Affilinet GmbH] - {"hash":"fd2b5d860fd44749","prefixes":{"*":3}}, // [Affilinet GmbH] - {"hash":"1d0953961a14d2fc","prefixes":{"*":118}}, // [SET.tv] - {"hash":"5edf63353d8e0fcd","prefixes":{"":118}}, // [SET.tv] - {"hash":"e4e5eb11299f703e","prefixes":{"*":119}}, // [Adroit Digital Solutions (ADS)] - {"hash":"e5cf3445847fef93","prefixes":{"*":119}}, // [Adroit Digital Solutions (ADS)] - {"hash":"8441a2216167c3d4","prefixes":{"at":100}}, // [Alenty S.A.S] - {"hash":"b9dab57457cf6477","prefixes":{"":100}}, // [Alenty S.A.S] - {"hash":"94b55596aadb2893","prefixes":{"":100}}, // [Alenty S.A.S] - {"hash":"16b31027fd40c9ad","prefixes":{"":100}}, // [Alenty S.A.S] - {"hash":"bb4adabba7b0b6b8","prefixes":{"*":120}}, // [AppNexus Inc] - {"hash":"3b2105469f3563e3","prefixes":{"*":121}}, // [Adfusion] - {"hash":"2f29d38052b5a8a6","prefixes":{"":122}}, // [ARC Media Group] - {"hash":"c133787bd9e605a7","prefixes":{"":123}}, // [Semasio GmbH] - {"hash":"b9a83d8bd4f31543","prefixes":{"":124}}, // [Mojiva] - {"hash":"2a1b04ad13f24350","prefixes":{"":124}}, // [Mojiva] - {"hash":"a3c6b5ffd4fe1547","prefixes":{"":125}}, // [Mocean mobile, Inc.] - {"hash":"0d52aafa33281228","prefixes":{"":126}}, // [Phluant] - {"hash":"655e31ecb8719994","prefixes":{"":126}}, // [Phluant] - {"hash":"dca17bd2f3edb917","prefixes":{"":126}}, // [Phluant] - {"hash":"53de58004f88a1c2","prefixes":{"*":127}}, // [AdSpirit] - {"hash":"6a1ebf671194b458","prefixes":{"*":127}}, // [AdSpirit] - {"hash":"6ae5a4877aab564f","prefixes":{"":127}}, // [AdSpirit] - {"hash":"9dcf8d17ea42466e","prefixes":{"*":128}}, // [ConvertMedia Ltd.] - {"hash":"32e0acbf3d2ecfcf","prefixes":{"*":128}}, // [ConvertMedia Ltd.] - {"hash":"708d275466f5230b","prefixes":{"*":128}}, // [ConvertMedia Ltd.] - {"hash":"7532d83428e05417","prefixes":{"*":128}}, // [ConvertMedia Ltd.] - {"hash":"3258e49c5f504307","prefixes":{"*":128}}, // [ConvertMedia Ltd.] - {"hash":"2c381faaa3144a37","prefixes":{"*":128}}, // [ConvertMedia Ltd.] - {"hash":"776a64cc3a9c6f81","prefixes":{"*":128}}, // [ConvertMedia Ltd.] - {"hash":"d38247a8e8d14a7c","prefixes":{"*":128}}, // [ConvertMedia Ltd.] - {"hash":"50e5ca70dbb69c03","prefixes":{"":128}}, // [ConvertMedia Ltd.] - {"hash":"c1dfde405c173e3b","prefixes":{"":128}}, // [ConvertMedia Ltd.] - {"hash":"5bce52b0f221aac2","prefixes":{"":128}}, // [ConvertMedia Ltd.] - {"hash":"5a0e5730145156e2","prefixes":{"":128}}, // [ConvertMedia Ltd.] - {"hash":"c081c626ad2c0f1f","prefixes":{"":128}}, // [ConvertMedia Ltd.] - {"hash":"a31f53c0c58e67c0","prefixes":{"":129}}, // [ATK Media GmbH] - {"hash":"514b4f19cbdabb49","prefixes":{"":129}}, // [ATK Media GmbH] - {"hash":"264493bb5046087f","prefixes":{"":129}}, // [ATK Media GmbH] - {"hash":"f4e40d6832493420","prefixes":{"":129}}, // [ATK Media GmbH] - {"hash":"126b6492c0b1b53a","prefixes":{"*":130}}, // [Atlas] - {"hash":"0493e6c92c59fd4d","prefixes":{"":130}}, // [Atlas] - {"hash":"d6d40b4417fefcf6","prefixes":{"":130}}, // [Atlas] - {"hash":"0b16a7cf8dcb70ee","prefixes":{"*":131}}, // [AudienceScience] - {"hash":"edbae80f2449aaee","prefixes":{"":132}}, // [BeWebMedia] - {"hash":"c1196b1418bf9de9","prefixes":{"*":133}}, // [Bidalpha Inc.] - {"hash":"87016c0f34162bc6","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"2db3b8adb9c37590","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"a10756ccfb034a50","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"1ea7188c5cc22ccf","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"c70dfe98c3735972","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"05aa49d024d6d67c","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"27c08401857f47aa","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"9a569266b426d074","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"5a764c1f870e4e38","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"64b993405c35034f","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"afe660eca2d01217","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"cd398b1d3fe6caf4","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"bb2cf5a27552ba10","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"b79ee8778f5b56d6","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"cae97638b357a30e","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"2c552297aa3abdf1","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"75bbf901ed30d891","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"365bc710167be24c","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"49849f3497cc7ff5","prefixes":{"":134}}, // [AdGear Technologies Inc.] - {"hash":"14bb8555907973f5","prefixes":{"":135}}, // [BlueKai] - {"hash":"db6cb381e20a210a","prefixes":{"":135}}, // [BlueKai] - {"hash":"8e4e07437ae4b61c","prefixes":{"*":136}}, // [Bluestreak] - {"hash":"4bb6a17c8b5ec2e7","prefixes":{"":137}}, // [blurbIQ] - {"hash":"4aefb106f857a0de","prefixes":{"":137}}, // [blurbIQ] - {"hash":"a7b4452428f93120","prefixes":{"":137}}, // [blurbIQ] - {"hash":"38f01d050feb50e8","prefixes":{"":137}}, // [blurbIQ] - {"hash":"d4ab70ba7caf1ed7","prefixes":{"":138}}, // [Brand.net] - {"hash":"94655d9cadd8e829","prefixes":{"":138}}, // [Brand.net] - {"hash":"f3347a75843fd9c6","prefixes":{"":138}}, // [Brand.net] - {"hash":"60e2856d32c1da23","prefixes":{"*":139}}, // [Brandscreen Inc.] - {"hash":"16460b1ac6e3b229","prefixes":{"*":140}}, // [BrightRoll Inc.] - {"hash":"814fef93d6498b58","prefixes":{"*":140}}, // [BrightRoll Inc.] - {"hash":"72fc1d7c40b9af03","prefixes":{"":141}}, // [Brightroll Inc.] - {"hash":"80f8944423cc936c","prefixes":{"*":142}}, // [Vindico] - {"hash":"bf2db20b09960c7b","prefixes":{"":142}}, // [Vindico] - {"hash":"e180a56826bf4ba6","prefixes":{"*":143}}, // [Edgesuite] - {"hash":"1e90c625da6683a5","prefixes":{"":144}}, // [Spark Flow S.A.] - {"hash":"d385e514fe92e6d9","prefixes":{"":145}}, // [Aarki, Inc.] - {"hash":"4646e46d55455ffe","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"a8c5f49831b36ab9","prefixes":{"*":147}}, // [Burst Media LLC d/b/a AdConductor] - {"hash":"f22276edaf005231","prefixes":{"*":148}}, // [Advertising.com Dynamic Retargeter] - {"hash":"220db526eb84992d","prefixes":{"*":149}}, // [NetAffiliation] - {"hash":"cda3d9612026866c","prefixes":{"*":149}}, // [NetAffiliation] - {"hash":"9f8018bcbf15a592","prefixes":{"*":150}}, // [C3 Metrics Inc.] - {"hash":"78ec342986654215","prefixes":{"":150}}, // [C3 Metrics Inc.] - {"hash":"e4ad94837095a9bc","prefixes":{"*":150}}, // [C3 Metrics Inc.] - {"hash":"32401b2118eff57a","prefixes":{"":151}}, // [CAPITALDATA (SARL)] - {"hash":"a5f9db92cb5aeb3e","prefixes":{"":151}}, // [CAPITALDATA (SARL)] - {"hash":"ae12db7e0621ec0b","prefixes":{"*":151}}, // [CAPITALDATA (SARL)] - {"hash":"d7c4970fde31fd62","prefixes":{"*":152}}, // [e-Planning] - {"hash":"20cadab7abdae752","prefixes":{"*":153}}, // [Chango Inc.] - {"hash":"38363f7ac872a916","prefixes":{"":154}}, // [Chango] - {"hash":"114215c6a5b66e37","prefixes":{"":154}}, // [Chango] - {"hash":"1ab7b29d135c11fc","prefixes":{"":155}}, // [Channel Intelligence] - {"hash":"6e8e21cf50387d5b","prefixes":{"":155}}, // [Channel Intelligence] - {"hash":"cbd8c46855691ac9","prefixes":{"*":155}}, // [Channel Intelligence] - {"hash":"8bde0d3ba731b24b","prefixes":{"":155}}, // [Channel Intelligence] - {"hash":"d415664f4794184e","prefixes":{"":155}}, // [Channel Intelligence] - {"hash":"bc8edad5208e36aa","prefixes":{"*":155}}, // [Channel Intelligence] - {"hash":"00900e4af3795b7d","prefixes":{"":156}}, // [Chartbeat Inc] - {"hash":"dddd416675de98d6","prefixes":{"":156}}, // [Chartbeat Inc] - {"hash":"085835cbbea3af5b","prefixes":{"":156}}, // [Chartbeat Inc] - {"hash":"0a5fd555ed989cf1","prefixes":{"":156}}, // [Chartbeat Inc] - {"hash":"38e6dfa04599d610","prefixes":{"":156}}, // [Chartbeat Inc] - {"hash":"282ab213b57d8196","prefixes":{"":156}}, // [Chartbeat Inc] - {"hash":"465922959fd197de","prefixes":{"*":157}}, // [Choicestream Inc.] - {"hash":"07b64c6d94aaeede","prefixes":{"*":158}}, // [AddThis, Inc] - {"hash":"8285ed75ebbab1bf","prefixes":{"*":158}}, // [AddThis, Inc] - {"hash":"397da235f9c6c4dc","prefixes":{"*":159}}, // [Platform 161] - {"hash":"3e4380669a97a9da","prefixes":{"":159}}, // [Platform 161] - {"hash":"0cf7c3db283e4fde","prefixes":{"":160}}, // [ClickPoint] - {"hash":"e3f610966ee4998e","prefixes":{"":161}}, // [Cobalt Group] - {"hash":"5458332ef0eff5a0","prefixes":{"":161}}, // [Cobalt Group] - {"hash":"755391831ca3c171","prefixes":{"*":161}}, // [Cobalt Group] - {"hash":"82d7785d13750576","prefixes":{"*":161}}, // [Cobalt Group] - {"hash":"f1b10b28e8a1e8a9","prefixes":{"*":161}}, // [Cobalt Group] - {"hash":"ca9dc80141cf19b5","prefixes":{"*":162}}, // [Cognitive Match Limited] - {"hash":"d02173b857f779a2","prefixes":{"*":162}}, // [Cognitive Match Limited] - {"hash":"987bec8a1c07a02f","prefixes":{"*":162}}, // [Cognitive Match Limited] - {"hash":"da18ae94f7dd99ab","prefixes":{"":163}}, // [Tagtoo Tech Limited] - {"hash":"5e1256c5e919daf9","prefixes":{"":164}}, // [wayStorm Co., Ltd.] - {"hash":"9e92efa06a26b83d","prefixes":{"*":162}}, // [Cognitive Match Limited] - {"hash":"cf3c8c24a0eb24a6","prefixes":{"*":162}}, // [Cognitive Match Limited] - {"hash":"eac24a30a92872f1","prefixes":{"":162}}, // [Cognitive Match Limited] - {"hash":"a508105d922876fe","prefixes":{"":162}}, // [Cognitive Match Limited] - {"hash":"b7f1551e6c1615df","prefixes":{"":162}}, // [Cognitive Match Limited] - {"hash":"e56a388aae6f063e","prefixes":{"*":165}}, // [Collective Media LLC] - {"hash":"adc75d0087df3e89","prefixes":{"":166}}, // [ComScore Campaign Essentials (CE)] - {"hash":"6f0ca09cdc147fb0","prefixes":{"":166}}, // [ComScore Campaign Essentials (CE)] - {"hash":"11f6ca20d229d8ad","prefixes":{"":166}}, // [ComScore Campaign Essentials (CE)] - {"hash":"e4d55634b8d3126d","prefixes":{"":166}}, // [ComScore Campaign Essentials (CE)] - {"hash":"6100cc0a622faa8e","prefixes":{"":167}}, // [ComScore Validated Campaign Essentials (vCE)] - {"hash":"a30e99f000f781c7","prefixes":{"":167}}, // [ComScore Validated Campaign Essentials (vCE)] - {"hash":"1ee7cb55ae2cb071","prefixes":{"":166}}, // [ComScore Campaign Essentials (CE)] - {"hash":"7dfd981d4c018953","prefixes":{"":167}}, // [ComScore Validated Campaign Essentials (vCE)] - {"hash":"5011cb94579baba5","prefixes":{"*":168}}, // [VoiceFive (ComScore)] - {"hash":"a0cff89b286fc309","prefixes":{"":168}}, // [VoiceFive (ComScore)] - {"hash":"4782e75d195d3144","prefixes":{"":168}}, // [VoiceFive (ComScore)] - {"hash":"e63472329b04aab9","prefixes":{"":168}}, // [VoiceFive (ComScore)] - {"hash":"5814e7da11d65131","prefixes":{"":168}}, // [VoiceFive (ComScore)] - {"hash":"26c9b653ef6bfb53","prefixes":{"*":169}}, // [Connexity LLC] - {"hash":"eb38b3659a232a56","prefixes":{"":169}}, // [Connexity LLC] - {"hash":"08abcdb9184877f9","prefixes":{"":169}}, // [Connexity LLC] - {"hash":"65a439ff09c9f7dc","prefixes":{"":169}}, // [Connexity LLC] - {"hash":"b3afc505f1f5487b","prefixes":{"":169}}, // [Connexity LLC] - {"hash":"b35a41328f2a6830","prefixes":{"*":170}}, // [Constant Contact] - {"hash":"e7a596af94ad559e","prefixes":{"":171}}, // [ContextWeb Inc.] - {"hash":"25ccc25c112607e9","prefixes":{"":171}}, // [ContextWeb Inc.] - {"hash":"44e652990824ddee","prefixes":{"":171}}, // [ContextWeb Inc.] - {"hash":"8eac48fda4ccaec8","prefixes":{"":171}}, // [ContextWeb Inc.] - {"hash":"0b4eee92ed72b991","prefixes":{"ant-":172}}, // [Conversive BV] - {"hash":"dae78afe5d5cc699","prefixes":{"":172}}, // [Conversive BV] - {"hash":"7729d423a7fc3adf","prefixes":{"*":96}}, // [Convertro Inc] - {"hash":"cdff1e442509fafe","prefixes":{"":173}}, // [IBM Digital Analytics] - {"hash":"a07829ef9a9d6762","prefixes":{"*":174}}, // [Crimtan] - {"hash":"43c19f0523939982","prefixes":{"":174}}, // [Crimtan] - {"hash":"2c4f3b76ebfafe80","prefixes":{"":174}}, // [Crimtan] - {"hash":"078603692b0949b1","prefixes":{"":174}}, // [Crimtan] - {"hash":"aa8844a1d3e7c3aa","prefixes":{"":174}}, // [Crimtan] - {"hash":"15ef2aafaa03e60b","prefixes":{"":174}}, // [Crimtan] - {"hash":"5f1ef71046a359f7","prefixes":{"":174}}, // [Crimtan] - {"hash":"2fb5d0d81abd43be","prefixes":{"":174}}, // [Crimtan] - {"hash":"75ce1d1edaf8f426","prefixes":{"":174}}, // [Crimtan] - {"hash":"a7b80b09927034f7","prefixes":{"*":175}}, // [Criteo] - {"hash":"3a96f385ff402ab6","prefixes":{"*":175}}, // [Criteo] - {"hash":"189c3bab631d09cf","prefixes":{"":176}}, // [D.A. Consortium Inc. (EffectiveOne)] - {"hash":"3d9109a2e81eee2d","prefixes":{"":176}}, // [D.A. Consortium Inc. (EffectiveOne)] - {"hash":"e7170acff5d6fda1","prefixes":{"":176}}, // [D.A. Consortium Inc. (EffectiveOne)] - {"hash":"e0496c88da22371f","prefixes":{"":176}}, // [D.A. Consortium Inc. (EffectiveOne)] - {"hash":"59ab4e7744543242","prefixes":{"":177}}, // [Platform One] - {"hash":"f6b2b21a0a0417b3","prefixes":{"":177}}, // [Platform One] - {"hash":"4384a84e6276861a","prefixes":{"*":178}}, // [Dapper Inc.] - {"hash":"1ccbc5b91f4c5fc9","prefixes":{"*":179}}, // [DataXu Inc.] - {"hash":"4840192a8cb4b1ca","prefixes":{"*":179}}, // [DataXu Inc.] - {"hash":"b50faf8c7e5fc38c","prefixes":{"":180}}, // [Aperture] - {"hash":"bdcf8ed53f1b3802","prefixes":{"*":181}}, // [Rakuten Attribution] - {"hash":"d31e7c4efd1a5191","prefixes":{"*":181}}, // [Rakuten Attribution] - {"hash":"68318c6d8f72133b","prefixes":{"*":181}}, // [Rakuten Attribution] - {"hash":"c0c4eb0ce58ef4b1","prefixes":{"*":181}}, // [Rakuten Attribution] - {"hash":"ae713ee10231204e","prefixes":{"*":182}}, // [AdAction] - {"hash":"e4282cef85ee5728","prefixes":{"":183}}, // [Demandbase Inc.] - {"hash":"79f6c3d16c278e94","prefixes":{"":183}}, // [Demandbase Inc.] - {"hash":"13e7a7b6c40ef05b","prefixes":{"":183}}, // [Demandbase Inc.] - {"hash":"57e736fc045f8fbd","prefixes":{"":183}}, // [Demandbase Inc.] - {"hash":"82137a4a2aa17f33","prefixes":{"*":184}}, // [Omniture] - {"hash":"ddc305710749f5b7","prefixes":{"*":184}}, // [Omniture] - {"hash":"3cd74441fc237226","prefixes":{"*":184}}, // [Omniture] - {"hash":"5b40000c858b730d","prefixes":{"":185}}, // [Rovion, Inc.] - {"hash":"47d66d29363322cd","prefixes":{"":186}}, // [AdHui.com LLC] - {"hash":"10a0efb999eedf90","prefixes":{"":186}}, // [AdHui.com LLC] - {"hash":"b1bf29ae383ea0cd","prefixes":{"":187}}, // [Bidlab sp. z o.o] - {"hash":"baa26df04dbce196","prefixes":{"":187}}, // [Bidlab sp. z o.o] - {"hash":"c3f807fb12d64ef3","prefixes":{"":188}}, // [CyberAgent Inc.] - {"hash":"8caed248711d066a","prefixes":{"":188}}, // [CyberAgent Inc.] - {"hash":"03524472b71baf6f","prefixes":{"":189}}, // [Relay42 Technology B.V.] - {"hash":"c26a754afcf8496d","prefixes":{"":189}}, // [Relay42 Technology B.V.] - {"hash":"fa7ea4c2400b9304","prefixes":{"":189}}, // [Relay42 Technology B.V.] - {"hash":"f91d12ea09409395","prefixes":{"":189}}, // [Relay42 Technology B.V.] - {"hash":"a3404139a451726c","prefixes":{"":190}}, // [Nielsen Digital Ad Ratings (JS)] - {"hash":"072f7bf8cea1803c","prefixes":{"":190}}, // [Nielsen Digital Ad Ratings (JS)] - {"hash":"8a6e324bbf4e02dc","prefixes":{"":190}}, // [Nielsen Digital Ad Ratings (JS)] - {"hash":"e59faa8d364c3ad2","prefixes":{"":190}}, // [Nielsen Digital Ad Ratings (JS)] - {"hash":"fe39de2cc1550ee8","prefixes":{"":190}}, // [Nielsen Digital Ad Ratings (JS)] - {"hash":"41808f1a9bcd4cdf","prefixes":{"":190}}, // [Nielsen Digital Ad Ratings (JS)] - {"hash":"1774fa7feee9a3ba","prefixes":{"":191}}, // [Nielsen (Audience Measurement Platform)] - {"hash":"8bcfafa30bb9a496","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"04a15100eebd5238","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"37669b02a11574d6","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"b0e609cae76778ef","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"8602961190244c78","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"cf6160cc55083e3f","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"d8e0147c7067e033","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"fa4e638adbad854a","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"bfc104d8ae540578","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"a77b609ecb948524","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"5b2d6c6512b90826","prefixes":{"":193}}, // [Nielsen (Sales Effect)] - {"hash":"3f0eefad5865a4ed","prefixes":{"":194}}, // [Ohana Media India Private Limited] - {"hash":"ba149fc47bfe9e3c","prefixes":{"":194}}, // [Ohana Media India Private Limited] - {"hash":"cfe9fa124d3faa0b","prefixes":{"*":195}}, // [DisplayCDN] - {"hash":"3f8d8705aeaf2a69","prefixes":{"":196}}, // [Avail Intelligence] - {"hash":"3aa373d150e55bb8","prefixes":{"":196}}, // [Avail Intelligence] - {"hash":"01168e37c54407ec","prefixes":{"":196}}, // [Avail Intelligence] - {"hash":"9d0088e875d03291","prefixes":{"":197}}, // [Adobe Scene 7] - {"hash":"7892719f65634daf","prefixes":{"":198}}, // [Asda] - {"hash":"76913c314e7299f2","prefixes":{"":199}}, // [Adsfactor Limited] - {"hash":"ec3d301a1049a29c","prefixes":{"":200}}, // [Trademob GmbH] - {"hash":"0407237113d1e01a","prefixes":{"":201}}, // [Zamplus Technology Co., Ltd.] - {"hash":"3c2b08727db96f57","prefixes":{"":201}}, // [Zamplus Technology Co., Ltd.] - {"hash":"28d8cdf30ea5e75d","prefixes":{"":201}}, // [Zamplus Technology Co., Ltd.] - {"hash":"4ba6006c59a0afb3","prefixes":{"":201}}, // [Zamplus Technology Co., Ltd.] - {"hash":"34b0d82da56a6e14","prefixes":{"":201}}, // [Zamplus Technology Co., Ltd.] - {"hash":"c9293b8324f3a30f","prefixes":{"":201}}, // [Zamplus Technology Co., Ltd.] - {"hash":"78ad5395bf22ddd5","prefixes":{"":201}}, // [Zamplus Technology Co., Ltd.] - {"hash":"b0a5ecf647236d2a","prefixes":{"*":202}}, // [Telemetry Limited] - {"hash":"7d7cd86e56968411","prefixes":{"*":202}}, // [Telemetry Limited] - {"hash":"ccaf0946d1809711","prefixes":{"*":202}}, // [Telemetry Limited] - {"hash":"d08bcd58e153f333","prefixes":{"*":203}}, // [AdPilot] - {"hash":"c1ee73ca85937956","prefixes":{"":203}}, // [AdPilot] - {"hash":"b3ee9fcc7f773476","prefixes":{"":203}}, // [AdPilot] - {"hash":"0dededd99d2513e4","prefixes":{"":203}}, // [AdPilot] - {"hash":"efd59a0198028c96","prefixes":{"":203}}, // [AdPilot] - {"hash":"b43b6c20f3008842","prefixes":{"":203}}, // [AdPilot] - {"hash":"5d96f09a039b26e6","prefixes":{"":203}}, // [AdPilot] - {"hash":"039408a17f3d50d7","prefixes":{"":203}}, // [AdPilot] - {"hash":"16d696fe52cae068","prefixes":{"":203}}, // [AdPilot] - {"hash":"ebb9da6e533fc7e4","prefixes":{"":203}}, // [AdPilot] - {"hash":"c8afb150eb1b6ac1","prefixes":{"":203}}, // [AdPilot] - {"hash":"1e21c92ae4d08330","prefixes":{"":203}}, // [AdPilot] - {"hash":"07ef420d707be72c","prefixes":{"":203}}, // [AdPilot] - {"hash":"3d9fb95e3108b648","prefixes":{"":203}}, // [AdPilot] - {"hash":"938e971e186be4f8","prefixes":{"":203}}, // [AdPilot] - {"hash":"94682de65e11f128","prefixes":{"":204}}, // [ebookers] - {"hash":"32548cc45dcf9e95","prefixes":{"":204}}, // [ebookers] - {"hash":"baa9bf6209e38821","prefixes":{"":204}}, // [ebookers] - {"hash":"25a263451425f7e3","prefixes":{"*":205}}, // [Mashero GmbH] - {"hash":"4871ba81fe73b693","prefixes":{"":206}}, // [4wMarketPlace Srl] - {"hash":"f95f038fea1148ab","prefixes":{"*":207}}, // [Meetic Partners] - {"hash":"8c555d8aa0a96ecc","prefixes":{"*":208}}, // [Adara Media] - {"hash":"a31f9bce31634750","prefixes":{"":209}}, // [Adledge] - {"hash":"bb0d6b4eac177896","prefixes":{"":209}}, // [Adledge] - {"hash":"02712dc83414bc52","prefixes":{"":209}}, // [Adledge] - {"hash":"03cfa92cdadd6c20","prefixes":{"":209}}, // [Adledge] - {"hash":"4d8640e955380082","prefixes":{"":209}}, // [Adledge] - {"hash":"42ab78ba8d28d0d9","prefixes":{"":209}}, // [Adledge] - {"hash":"113f35a595990303","prefixes":{"":209}}, // [Adledge] - {"hash":"9566d797c39bca8e","prefixes":{"":209}}, // [Adledge] - {"hash":"caab6878aaf1b900","prefixes":{"":209}}, // [Adledge] - {"hash":"13581af42d29c3fd","prefixes":{"":209}}, // [Adledge] - {"hash":"8ad4e9b59b6cfebf","prefixes":{"":209}}, // [Adledge] - {"hash":"025223bda779fb50","prefixes":{"":210}}, // [Adledge: Ad Swapping] - {"hash":"6c7970d8fe11d946","prefixes":{"":211}}, // [LifeStreet Corportation] - {"hash":"226c377d19c24dca","prefixes":{"":211}}, // [LifeStreet Corportation] - {"hash":"bc64475cc35aba69","prefixes":{"":211}}, // [LifeStreet Corportation] - {"hash":"8a184297457cd4e1","prefixes":{"":211}}, // [LifeStreet Corportation] - {"hash":"900f97a5320abcc7","prefixes":{"":212}}, // [AdCirrus] - {"hash":"39b102bb2f01d1b4","prefixes":{"":213}}, // [Dimestore] - {"hash":"a144c6442bbc8a0a","prefixes":{"":213}}, // [Dimestore] - {"hash":"8020cac4e8fee023","prefixes":{"":213}}, // [Dimestore] - {"hash":"2dcefe5fe106e478","prefixes":{"*":214}}, // [GfK SE] - {"hash":"d8f9a95c0ea00853","prefixes":{"*":213}}, // [Dimestore] - {"hash":"829fbeefc45bca87","prefixes":{"*":215}}, // [Conversant CRM] - {"hash":"357a04f93d25ae56","prefixes":{"cdn":216,"log":216,"tps":216,"rtb":217}}, // [DoubleVerify Inc.] [DoubleVerify Inc.] [DoubleVerify Inc.] [DoubleVerify Inc. (BrandShield): Ad Swapping] - {"hash":"e88f040d82035294","prefixes":{"":216}}, // [DoubleVerify Inc.] - {"hash":"a378f9f7b3d5241c","prefixes":{"*":218}}, // [Dynamic Video LLC] - {"hash":"665ab90fa95fb304","prefixes":{"":218}}, // [Dynamic Video LLC] - {"hash":"1358623e271c4d67","prefixes":{"":218}}, // [Dynamic Video LLC] - {"hash":"a11e7a579fa98970","prefixes":{"*":219}}, // [Think RealTime, LLC] - {"hash":"9225aaedf058b2a2","prefixes":{"":220}}, // [Effective Measure] - {"hash":"36703887ac49b84b","prefixes":{"":220}}, // [Effective Measure] - {"hash":"eabfdf13f38b7671","prefixes":{"*":221}}, // [Adobe Media Optimizer] - {"hash":"e6c60b5e8bab7589","prefixes":{"*":221}}, // [Adobe Media Optimizer] - {"hash":"d4ca4e6c8a0bedf0","prefixes":{"*":221}}, // [Adobe Media Optimizer] - {"hash":"2e2899c7688fb6b3","prefixes":{"":222}}, // [Effiliation] - {"hash":"3a8f463e807ab596","prefixes":{"":222}}, // [Effiliation] - {"hash":"4c49524999954857","prefixes":{"":222}}, // [Effiliation] - {"hash":"99a4dc2c14dac648","prefixes":{"*":223}}, // [EmediateAd] - {"hash":"eb4d633002c35102","prefixes":{"*":223}}, // [EmediateAd] - {"hash":"a4155988849d9899","prefixes":{"":223}}, // [EmediateAd] - {"hash":"a29dc15ab67e4109","prefixes":{"*":223}}, // [EmediateAd] - {"hash":"244ed2f383a41c11","prefixes":{"*":223}}, // [EmediateAd] - {"hash":"f1d41523d742a4c4","prefixes":{"*":224}}, // [engage:BDR Inc.] - {"hash":"0c985c5bee46dcca","prefixes":{"":224}}, // [engage:BDR Inc.] - {"hash":"6f92a4165360659f","prefixes":{"":224}}, // [engage:BDR Inc.] - {"hash":"e65b14b1de797d5e","prefixes":{"":224}}, // [engage:BDR Inc.] - {"hash":"8908058eec675b88","prefixes":{"*":4}}, // [Eulerian Technologies SARL] - {"hash":"dae357b5105fb541","prefixes":{"*":4}}, // [Eulerian Technologies SARL] - {"hash":"7e5c5286bc6286af","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"6459463a599e26cf","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"d49c14999963fa7c","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"f7508ecdfd1b76f8","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"747ac3c2114de916","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"5445e9650966f2c9","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"b701368b6964f28f","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"b8d4853208b3d665","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"a3c05ce1535a1bb0","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"4113e02fa7f80330","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"1ec6c8299f47c92d","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"6213db1c94ff78b4","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"afdce1f639f05293","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"c00800107f132ba6","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"f989cf5678a6cd73","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"7248d6bb2e2d2812","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"5decec320495d46c","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"ffffd176ae6095cd","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"a0ead46cc7797fc4","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"d1b36183709cf97c","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"f08e0f4d46762277","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"1082450fce471aec","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"0c44a72359ff962b","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"c6ced7239fa526b8","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"bcd4afcb16a1bf83","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"c4629483e0f61849","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"3a8a51dc5059eb82","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"10d4846e0b0f6213","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"8e03e96054cde307","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"d1c591453eeceb89","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"a9ee5e0b07924cbe","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"4909d0f68d7f7dd5","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"1954dcac7d36e9a9","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"855fab1b44b2cc38","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"9a977de69b69e767","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"32c7999b9a328d48","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"675aab76d51e4be4","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"454f35d478088b2f","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"af6f37ce228476e6","prefixes":{"*":4}}, // [Eulerian Technologies SARL] - {"hash":"dfa2565386557dbb","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"7bdee1b08b69b7fa","prefixes":{"*":4}}, // [Eulerian Technologies SARL] - {"hash":"259e3811976143d2","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"29633cedc9fc7e43","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"758fea4a1e3ad80f","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"cf9bd7435c526efc","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"913928e477b69d4d","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"391c770feaa49fc6","prefixes":{"":4}}, // [Eulerian Technologies SARL] - {"hash":"9aea11673c37df0c","prefixes":{"*":225}}, // [Ghostery Enterprise] - {"hash":"5efff70d6937c916","prefixes":{"*":226}}, // [Experteer GmbH] - {"hash":"070f9075bd0072ca","prefixes":{"":227}}, // [Action Allocator] - {"hash":"2a8c1779456949e3","prefixes":{"":228}}, // [AdTraxx] - {"hash":"0d3994abeed42b53","prefixes":{"*":229}}, // [eyeDemand] - {"hash":"f804a9a8eb6b2828","prefixes":{"*":230}}, // [EyeReturn Marketing] - {"hash":"e7b16a7fe9d18804","prefixes":{"*":230}}, // [EyeReturn Marketing] - {"hash":"9a826150ea3a7b3d","prefixes":{"*":231}}, // [EyeView Inc.] - {"hash":"00f40aaf7abec691","prefixes":{"":232}}, // [EyeView, Inc] - {"hash":"a47b1640e108c4e8","prefixes":{"":232}}, // [EyeView, Inc] - {"hash":"73c2e89f340530ca","prefixes":{"*":233}}, // [Eyewonder Inc.] - {"hash":"ab1869399e555ac8","prefixes":{"":234}}, // [MLB Advanced Media, L.P.] - {"hash":"6792c3d3132bf7d7","prefixes":{"":234}}, // [MLB Advanced Media, L.P.] - {"hash":"3d02d3a5042f5ad8","prefixes":{"":234}}, // [MLB Advanced Media, L.P.] - {"hash":"c75b8641d2e585d7","prefixes":{"*":235}}, // [Facilitate For Agencies (FFA)] - {"hash":"535ecf05ae26ef66","prefixes":{"*":235}}, // [Facilitate For Agencies (FFA)] - {"hash":"2393220aafcd7f07","prefixes":{"*":235}}, // [Facilitate For Agencies (FFA)] - {"hash":"ead2626488917d96","prefixes":{"*":235}}, // [Facilitate For Agencies (FFA)] - {"hash":"82402af773d46078","prefixes":{"*":235}}, // [Facilitate For Agencies (FFA)] - {"hash":"0afacf5097d4ca72","prefixes":{"*":236}}, // [Flashtalking] - {"hash":"5215ee9300af8125","prefixes":{"":237}}, // [Hostelworld.com Limited] - {"hash":"e8d6e54e3ce31711","prefixes":{"":238}}, // [Knorex Pte. Ltd.] - {"hash":"a8ac5a522ae936d9","prefixes":{"":238}}, // [Knorex Pte. Ltd.] - {"hash":"c88080a9090fa828","prefixes":{"":238}}, // [Knorex Pte. Ltd.] - {"hash":"90727ba23e427206","prefixes":{"":238}}, // [Knorex Pte. Ltd.] - {"hash":"d496e8a41b845c1e","prefixes":{"":238}}, // [Knorex Pte. Ltd.] - {"hash":"e3463a0b0d9d0b84","prefixes":{"*":239}}, // [Flite Inc.] - {"hash":"93eff417d773ad79","prefixes":{"*":239}}, // [Flite Inc.] - {"hash":"48c727cb39130203","prefixes":{"*":239}}, // [Flite Inc.] - {"hash":"1f8b889072fc177c","prefixes":{"*":240}}, // [Forbes Media LLC] - {"hash":"830e0f5dd1181234","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"daf4edc7545d5166","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"f786c1dcc4eb883e","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"82534a9af0bc48e0","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"091c6487b80c3425","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"5250da9f53cd928c","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"9a8514c0fd5d6754","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"ad91bc98d2a8be55","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"91e5780ca48d75cc","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"481acfc271cd8a12","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"ef2a23dc677d8426","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"c6c8ce28b06bbda5","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"2bb2e1ea24146c0b","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"a0f3f5041ae3893c","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"4d6c5ce49301c775","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"747c952599211285","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"59f1e6a6110f9a68","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"f76895f67d1585b2","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"a2e125e235d1ffd4","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"17cbbfe7617725b3","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"1c118cc87c632e94","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"a48217ad66a56eb4","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"949fd73ca6fcc6f5","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"7d647d3164d8e6e2","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"e6d9d61b6785d883","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"aad9a81ea12d3c42","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"f0d9b5588abc50f9","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"dcb3db79fcab6476","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"1985d21c9c0b8366","prefixes":{"":242}}, // [FreakOut Inc.] - {"hash":"a268d22dc85b4108","prefixes":{"*":243}}, // [FreeWheel] - {"hash":"a796f300b3d2c84e","prefixes":{"*":244}}, // [Fringe81 Inc.] - {"hash":"bb563c63d09f3d76","prefixes":{"":245}}, // [Fringe81 - IBV] - {"hash":"c6ac10cc9ddfb3e7","prefixes":{"":245}}, // [Fringe81 - IBV] - {"hash":"48983a0d5a27b120","prefixes":{"*":246}}, // [FuseBox Inc.] - {"hash":"6f59caebdaac019e","prefixes":{"":246}}, // [FuseBox Inc.] - {"hash":"8090c608651eca30","prefixes":{"*":247}}, // [gemiusDirectEffect+] - {"hash":"e7b75209bd73a1d9","prefixes":{"*":248}}, // [AdOcean Ltd] - {"hash":"24fb4ed27ae8cf88","prefixes":{"*":249}}, // [Intomart GfK (GfK Daphne)] - {"hash":"91571a34ff0fcf44","prefixes":{"*":250}}, // [Gigya] - {"hash":"bd66201f4f935a9d","prefixes":{"*":251}}, // [Global Market Insite Inc.] - {"hash":"c0854a371610fbf2","prefixes":{"*":251}}, // [Global Market Insite Inc.] - {"hash":"4a32fa997117f00d","prefixes":{"*":252,"":253}}, // [DoubleClick Campaign Manager] [DoubleClick Bidder Pilot for Networks] - {"hash":"68d0356c33bd8ec4","prefixes":{"*":252,"":253}}, // [DoubleClick Campaign Manager] [DoubleClick Bidder Pilot for Networks] - {"hash":"813ca9ac3483af55","prefixes":{"*":252,"":253}}, // [DoubleClick Campaign Manager] [DoubleClick Bidder Pilot for Networks] - {"hash":"ee88383142da014d","prefixes":{"*":252,"":253}}, // [DoubleClick Campaign Manager] [DoubleClick Bidder Pilot for Networks] - {"hash":"1332f3da43091ed3","prefixes":{"":252,"*":254}}, // [DoubleClick Campaign Manager] [DoubleClick for Publishers Premium] - {"hash":"f04082d14282d452","prefixes":{"":252}}, // [DoubleClick Campaign Manager] - {"hash":"c6ad6c580aef6ce5","prefixes":{"":252}}, // [DoubleClick Campaign Manager] - {"hash":"642706b0b0335500","prefixes":{"":252}}, // [DoubleClick Campaign Manager] - {"hash":"baea954b95731c68","prefixes":{"*":255}}, // [Google CDN] - {"hash":"8e23699963552b18","prefixes":{"*":252}}, // [DoubleClick Campaign Manager] - {"hash":"bafedfe69ed92305","prefixes":{"*":253}}, // [DoubleClick Bidder Pilot for Networks] - {"hash":"f2b999c597cd97af","prefixes":{"*":253}}, // [DoubleClick Bidder Pilot for Networks] - {"hash":"66399889a04f9513","prefixes":{"cdn":256,"ads":256,"db":256,"img":256,"ssl":256}}, // [GroovinAds] [GroovinAds] [GroovinAds] [GroovinAds] [GroovinAds] - {"hash":"ea8510cdf6991d43","prefixes":{"":256}}, // [GroovinAds] - {"hash":"fc2e03aac9426552","prefixes":{"":257}}, // [Reddion] - {"hash":"6db39bcc7e0f7fed","prefixes":{"":258}}, // [HQ GmbH] - {"hash":"4de0590ca954b13b","prefixes":{"*":259}}, // [Performance Display Advertising] - {"hash":"44f4a1b16ff856e5","prefixes":{"":5}}, // [Conversant Ad Server] - {"hash":"127f97cfaedd763a","prefixes":{"":5}}, // [Conversant Ad Server] - {"hash":"508035bfa2d95844","prefixes":{"*":5}}, // [Conversant Ad Server] - {"hash":"6ea225859ddfba18","prefixes":{"*":5}}, // [Conversant Ad Server] - {"hash":"600a5a5f53775d42","prefixes":{"*":216}}, // [DoubleVerify Inc.] - {"hash":"cabba15c5ee8f039","prefixes":{"*":260}}, // [Avazu Inc.] - {"hash":"ec98881ed60ffe62","prefixes":{"":91}}, // [ADTECH GmbH] - {"hash":"b1e5dca23b928251","prefixes":{"":91}}, // [ADTECH GmbH] - {"hash":"87e919d72e293303","prefixes":{"*":261}}, // [Aerify Media] - {"hash":"652d7fe0079512a8","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"8e1afef4fbf079f4","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"3f697fb2c06659ac","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"cb5ae4cc94e8cb57","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"3cd44a0f52d69fed","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"fd87dfe8678e2630","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"274e4476c8fbfe0c","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"e3f56a006c4f8fc8","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"11d582be18893073","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"54768ce9ed6664fc","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"6de580016869f523","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"7fc8a77e5f366b6d","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"06838afa0ce9cde5","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"d788f92161d75b72","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"bd27e9c073c7b133","prefixes":{"":262}}, // [IClick Interactive Ltd.] - {"hash":"71096991891dc36f","prefixes":{"*":263}}, // [iCrossing] - {"hash":"0a48c6448ca1af83","prefixes":{"":263}}, // [iCrossing] - {"hash":"23cce2ff5ce35111","prefixes":{"*":264}}, // [Impact Engine Inc.] - {"hash":"b63329d63303bcac","prefixes":{"*":265}}, // [Inadco Inc.] - {"hash":"9dab6f7b066953b6","prefixes":{"":265}}, // [Inadco Inc.] - {"hash":"83efdfb00e5e0bb5","prefixes":{"":266}}, // [Innity Singapore Pte Ltd.] - {"hash":"5cdf5c58d4a95757","prefixes":{"":266}}, // [Innity Singapore Pte Ltd.] - {"hash":"e84e4c75279b444c","prefixes":{"":266}}, // [Innity Singapore Pte Ltd.] - {"hash":"0ad278b0f1bf83c7","prefixes":{"":267}}, // [Insight Express (Cross Media - Ignite)] - {"hash":"75089fef153b99d9","prefixes":{"":267}}, // [Insight Express (Cross Media - Ignite)] - {"hash":"17e48170b29e8c55","prefixes":{"":268}}, // [intelliAd Media GmbH] - {"hash":"8ff3a159f3a3dd37","prefixes":{"":268}}, // [intelliAd Media GmbH] - {"hash":"d5ad6ce37d416c20","prefixes":{"":268}}, // [intelliAd Media GmbH] - {"hash":"829116e73bbd766e","prefixes":{"*":269}}, // [DePauli AG] - {"hash":"58b6a73b811a6744","prefixes":{"":268}}, // [intelliAd Media GmbH] - {"hash":"1b02753d1ebc5935","prefixes":{"":270}}, // [Intelliad] - {"hash":"2d4767b28a0a3dc9","prefixes":{"":270}}, // [Intelliad] - {"hash":"04e4138ad91eba43","prefixes":{"*":271}}, // [Genome] - {"hash":"a80259001a08f5fe","prefixes":{"":271}}, // [Genome] - {"hash":"bccc7d85fcbd6406","prefixes":{"*":272}}, // [Intergi LLC dba PlaywireMedia] - {"hash":"c0e12f3f6483d539","prefixes":{"":273}}, // [Intermundo Media LLC] - {"hash":"aa0d2346396993dc","prefixes":{"*":274}}, // [Interpolls] - {"hash":"391f02c89579c41e","prefixes":{"*":275}}, // [Intelligent Reach (Intuitive Search Technologies)] - {"hash":"dd7ece8c8b4dd4db","prefixes":{"*":275}}, // [Intelligent Reach (Intuitive Search Technologies)] - {"hash":"51beebfd5a677c74","prefixes":{"":275}}, // [Intelligent Reach (Intuitive Search Technologies)] - {"hash":"15a557d1be0b55bc","prefixes":{"*":276}}, // [IPONWEB Limited] - {"hash":"ab800ebb45ab5e96","prefixes":{"":276}}, // [IPONWEB Limited] - {"hash":"cf6003cf8be11c49","prefixes":{"*":276}}, // [IPONWEB Limited] - {"hash":"bd97f2dac673ccef","prefixes":{"*":277}}, // [JasperLabs Inc.] - {"hash":"833a1bb3e47002bf","prefixes":{"":278}}, // [Jivox Corporation] - {"hash":"925afb63a81d22ae","prefixes":{"":278}}, // [Jivox Corporation] - {"hash":"e3546ee6177ce1af","prefixes":{"":278}}, // [Jivox Corporation] - {"hash":"a6e81a993b0dc090","prefixes":{"":278}}, // [Jivox Corporation] - {"hash":"57cf72a6bcc6ed09","prefixes":{"":278}}, // [Jivox Corporation] - {"hash":"aef508a87f7b9342","prefixes":{"":278}}, // [Jivox Corporation] - {"hash":"a1a0984cf6e12596","prefixes":{"":278}}, // [Jivox Corporation] - {"hash":"c1b82a9e7564881f","prefixes":{"*":279}}, // [Joinville AB] - {"hash":"e20dd6e83cbd14d9","prefixes":{"":280}}, // [KliKKicom Oy] - {"hash":"a1f03f455fc97850","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"fb87830648f1fe7c","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"93a6bcd7660ef88b","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"14fba951393c4cc1","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"96bf72901db53556","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"00ceea10fed6f827","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"5deb6288aeb4b20d","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"30a9e5cf761c2db9","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"b49fda322943ed64","prefixes":{"":281}}, // [Komli Media Inc.] - {"hash":"30067241c16c9f88","prefixes":{"*":282}}, // [Koninklijke Luchtvaart Maatschappij N.V.] - {"hash":"06076b672be3af43","prefixes":{"*":283}}, // [J.D. Power O2O] - {"hash":"1e23ff61443f919a","prefixes":{"*":284}}, // [Kwanzoo Inc.] - {"hash":"32327b1f6a21f5e3","prefixes":{"":285}}, // [Legolas Media Inc.] - {"hash":"0a98bd3ecf6d4f39","prefixes":{"":285}}, // [Legolas Media Inc.] - {"hash":"4141a8162aadd52a","prefixes":{"":285}}, // [Legolas Media Inc.] - {"hash":"9562185c6a06e194","prefixes":{"*":286}}, // [Undertone Ad System (UAS)] - {"hash":"d8be1c121ccbdb84","prefixes":{"":287}}, // [LEVEL Studios] - {"hash":"1b274516ac601c1d","prefixes":{"*":288}}, // [LinkedIn Corporation] - {"hash":"a9aceb9b28670518","prefixes":{"*":288}}, // [LinkedIn Corporation] - {"hash":"f202e32e7503e766","prefixes":{"":288}}, // [LinkedIn Corporation] - {"hash":"cf2094771b42b367","prefixes":{"*":288}}, // [LinkedIn Corporation] - {"hash":"ad94f98bfc0092ce","prefixes":{"*":289}}, // [Content Directions, Inc. dba Linkstorm] - {"hash":"2c97a2e1f7f59cde","prefixes":{"*":290}}, // [Liverail Inc.] - {"hash":"562dd0a17776cfc3","prefixes":{"*":291}}, // [Lotame Solutions Inc.] - {"hash":"85c409cdb78a6fec","prefixes":{"*":292}}, // [LucidMedia Networks Inc.] - {"hash":"25cdc372f14aa636","prefixes":{"":293}}, // [Magnetic Media Online Inc.] - {"hash":"490a692feb4f5f83","prefixes":{"":293}}, // [Magnetic Media Online Inc.] - {"hash":"8cd182ca413a6770","prefixes":{"":293}}, // [Magnetic Media Online Inc.] - {"hash":"891ff79862603ca1","prefixes":{"*":294}}, // [Mate1.com] - {"hash":"ae4598324ee10cb2","prefixes":{"":295}}, // [MaxPoint Interactive Inc.] - {"hash":"66eafecb064a6d3f","prefixes":{"*":296}}, // [Media Armor Inc.] - {"hash":"d9921090d75b28f3","prefixes":{"*":297}}, // [Xaxis, Inc] - {"hash":"4c897372b3205c67","prefixes":{"*":298}}, // [Dstillery] - {"hash":"78172051b147ff71","prefixes":{"*":298}}, // [Dstillery] - {"hash":"3e32e501635bb994","prefixes":{"*":299}}, // [Rakuten MediaForge] - {"hash":"ab3637f7ff19f3be","prefixes":{"*":86}}, // [MediaMath Inc.] - {"hash":"eacb348c2bfde7c6","prefixes":{"":300}}, // [Sizmek] - {"hash":"6ec884dc33afd8e3","prefixes":{"":300}}, // [Sizmek] - {"hash":"6a0b44c268e0ea34","prefixes":{"":300}}, // [Sizmek] - {"hash":"5ec2ba6f16455240","prefixes":{"":300}}, // [Sizmek] - {"hash":"f6a166105958340d","prefixes":{"":300}}, // [Sizmek] - {"hash":"1bafa194e1b43948","prefixes":{"":300}}, // [Sizmek] - {"hash":"c4c10492cf3f1e5c","prefixes":{"":300}}, // [Sizmek] - {"hash":"fb29996f4dbe86a3","prefixes":{"":300}}, // [Sizmek] - {"hash":"33cba419c0d99c36","prefixes":{"":300}}, // [Sizmek] - {"hash":"1471e355753e333a","prefixes":{"":300}}, // [Sizmek] - {"hash":"8445471e74aaf1ad","prefixes":{"":300}}, // [Sizmek] - {"hash":"fe448fc8fa6792c7","prefixes":{"":300}}, // [Sizmek] - {"hash":"7142d0f7c95e6c11","prefixes":{"":300}}, // [Sizmek] - {"hash":"f71e7939a3a06b85","prefixes":{"":300}}, // [Sizmek] - {"hash":"25c9493f4ceef0b3","prefixes":{"*":300}}, // [Sizmek] - {"hash":"1c0486f5c9ddccc1","prefixes":{"":301}}, // [MediaMind] - {"hash":"511f53552d7c07f9","prefixes":{"*":302}}, // [Paypal] - {"hash":"c23b8169e8d6f935","prefixes":{"*":302}}, // [Paypal] - {"hash":"4cb72fa17c1e6b21","prefixes":{"*":303}}, // [ZEDO Inc.] - {"hash":"1f5866beb3a2da29","prefixes":{"":304}}, // [Contobox] - {"hash":"ab4011df7412e85a","prefixes":{"":304}}, // [Contobox] - {"hash":"4c55c99f7e731e4a","prefixes":{"":304}}, // [Contobox] - {"hash":"b32dad95de4864bb","prefixes":{"":304}}, // [Contobox] - {"hash":"e0636cdabf7592eb","prefixes":{"":305}}, // [Melt Tecnologia e Informatica S.A] - {"hash":"9e011383339e65c3","prefixes":{"":305}}, // [Melt Tecnologia e Informatica S.A] - {"hash":"c7f6b97c8a7defa6","prefixes":{"":305}}, // [Melt Tecnologia e Informatica S.A] - {"hash":"ba29743e534631a5","prefixes":{"":305}}, // [Melt Tecnologia e Informatica S.A] - {"hash":"df986bbd43ef130c","prefixes":{"":305}}, // [Melt Tecnologia e Informatica S.A] - {"hash":"32e39349c0434e31","prefixes":{"":305}}, // [Melt Tecnologia e Informatica S.A] - {"hash":"4b920fabf61fd6f3","prefixes":{"":305}}, // [Melt Tecnologia e Informatica S.A] - {"hash":"068f3a0cd7e03112","prefixes":{"":305}}, // [Melt Tecnologia e Informatica S.A] - {"hash":"95cce4f80c49239f","prefixes":{"*":306}}, // [MeMo2 / Hottraffic] - {"hash":"7672c0e258e0a20b","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"065ac7f58737b759","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"f0c15855e3418345","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"45068fe6a5fe83c0","prefixes":{"*":308}}, // [Mercado Livre.com Atividades de Internet Ltda] - {"hash":"a3fbfa83663a40a6","prefixes":{"*":308}}, // [Mercado Livre.com Atividades de Internet Ltda] - {"hash":"5241d2ae86ff307f","prefixes":{"":309}}, // [Merchenta Limited] - {"hash":"7b92690c1232eb23","prefixes":{"":309}}, // [Merchenta Limited] - {"hash":"e5b1a15c3a7a1ee6","prefixes":{"":309}}, // [Merchenta Limited] - {"hash":"f0b40488d9ecd9a2","prefixes":{"":309}}, // [Merchenta Limited] - {"hash":"63600c9242f10b0d","prefixes":{"":309}}, // [Merchenta Limited] - {"hash":"e89e81c1ed314cc1","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"297f0571158ef576","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"2228e5ce36c007c1","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"798bcbf9415873df","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"ab03f3b496037843","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"eca01f0508fbf30b","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"0d839898fade319a","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"4fc010b1e52ce252","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"31e7fb81bf0ec52f","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"a6edfa074ceff9c5","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"289dc75a912dcc97","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"5a8de8926c410758","prefixes":{"":310}}, // [Metapeople GmbH] - {"hash":"79c798a9d8e32afc","prefixes":{"":311}}, // [MetrixLab B.V.] - {"hash":"61617058e6206283","prefixes":{"":311}}, // [MetrixLab B.V.] - {"hash":"cf945767a2fa79f3","prefixes":{"*":312}}, // [Mixpo Inc.] - {"hash":"541f85cbc710f2b3","prefixes":{"":313}}, // [Monsoon Ads Pvt. Ltd.] - {"hash":"c800e798e160fe89","prefixes":{"*":314}}, // [Monster] - {"hash":"775035c4c49057eb","prefixes":{"":314}}, // [Monster] - {"hash":"2a26221290e24a08","prefixes":{"":314}}, // [Monster] - {"hash":"fda966b5c6ec2b2f","prefixes":{"":314}}, // [Monster] - {"hash":"887d148a8ae98b2e","prefixes":{"*":315}}, // [neckermann.de GmbH] - {"hash":"35c76c9165e3349f","prefixes":{"*":316}}, // [Ad.agio] - {"hash":"d5c7d1240bf8989e","prefixes":{"":316}}, // [Ad.agio] - {"hash":"1e5ea56690a0716d","prefixes":{"*":316}}, // [Ad.agio] - {"hash":"a54661777ef51189","prefixes":{"*":317}}, // [Netmining LLC] - {"hash":"cc673c90c8abe830","prefixes":{"*":318}}, // [AdoTube] - {"hash":"5f4b0cbb95552b3f","prefixes":{"*":319}}, // [Next Audience GmbH] - {"hash":"a78e1096b3c03fbc","prefixes":{"":319}}, // [Next Audience GmbH] - {"hash":"582d973b69391ebe","prefixes":{"":319}}, // [Next Audience GmbH] - {"hash":"18585076a5b0c6dd","prefixes":{"":319}}, // [Next Audience GmbH] - {"hash":"1df4ee86fc680406","prefixes":{"":319}}, // [Next Audience GmbH] - {"hash":"ecd80a6a46f6a1ce","prefixes":{"":319}}, // [Next Audience GmbH] - {"hash":"8ee876aaaaf537e0","prefixes":{"":319}}, // [Next Audience GmbH] - {"hash":"92bc4f011133f05a","prefixes":{"*":320}}, // [Nextperf] - {"hash":"b10d35d6cde76082","prefixes":{"*":320}}, // [Nextperf] - {"hash":"8e6e5d6dc4720f98","prefixes":{"":320}}, // [Nextperf] - {"hash":"edc7f4e87a8ea4a3","prefixes":{"":320}}, // [Nextperf] - {"hash":"f0c76e07985c56c5","prefixes":{"":320}}, // [Nextperf] - {"hash":"cff1b3137482e5e3","prefixes":{"":320}}, // [Nextperf] - {"hash":"33f28c5ebbd4525e","prefixes":{"":320}}, // [Nextperf] - {"hash":"849c8324d4c32ea5","prefixes":{"*":321}}, // [Calibex] - {"hash":"0bbcc61ed60a63c7","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"7da7b69cf130f867","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"524bf7ee34882325","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"0fc0858e8d42a096","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"94d1d06666f458b2","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"9515f9226eae0ee6","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"c610850a50287c6c","prefixes":{"":322}}, // [ViziAds for Advertisers] - {"hash":"1324d12fd047205a","prefixes":{"":322}}, // [ViziAds for Advertisers] - {"hash":"922df75b11e4bdb8","prefixes":{"":322}}, // [ViziAds for Advertisers] - {"hash":"94be9a0ad636acc1","prefixes":{"":322}}, // [ViziAds for Advertisers] - {"hash":"921f858655989bdb","prefixes":{"":322}}, // [ViziAds for Advertisers] - {"hash":"0f44f6f2024007e3","prefixes":{"":323}}, // [FinanceGenerator] - {"hash":"c5d545736ea3f40c","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"c5190fb3c11efd5c","prefixes":{"":324}}, // [Telstra Corporation] - {"hash":"f8e19bba6ecb9d4d","prefixes":{"":324}}, // [Telstra Corporation] - {"hash":"78cb9e34e18a70c6","prefixes":{"":324}}, // [Telstra Corporation] - {"hash":"555b77a6dbc1077b","prefixes":{"":324}}, // [Telstra Corporation] - {"hash":"04cd318d8c9e0aa1","prefixes":{"":325}}, // [The Online Research Unit] - {"hash":"44b28a9d35489468","prefixes":{"":326}}, // [Webling Pty Ltd] - {"hash":"ce2a97a2bc3975b0","prefixes":{"*":327}}, // [Lasoo Pty Ltd] - {"hash":"70c03e717046730c","prefixes":{"":327}}, // [Lasoo Pty Ltd] - {"hash":"a1956c55f379362a","prefixes":{"":327}}, // [Lasoo Pty Ltd] - {"hash":"78761e0920b86d00","prefixes":{"":327}}, // [Lasoo Pty Ltd] - {"hash":"fb49a17ebf575b4b","prefixes":{"":327}}, // [Lasoo Pty Ltd] - {"hash":"3ba767e322876167","prefixes":{"":327}}, // [Lasoo Pty Ltd] - {"hash":"db60b4edeb07e6e0","prefixes":{"*":328}}, // [Salefinder Ltd.] - {"hash":"1a2b2619e15f81ed","prefixes":{"":329}}, // [The Monkeys Pty Ltd] - {"hash":"d33c5423234580d2","prefixes":{"":330}}, // [Louder Digital Pty Ltd] - {"hash":"35c305dfff8e956a","prefixes":{"":331}}, // [Publisher’s Internationale Pty Ltd] - {"hash":"ab95fd39f34e1919","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"e394eac0c09e952a","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"d754759cedf098a4","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"469abb509832dcba","prefixes":{"":7}}, // [Wize Commerce, Inc.] - {"hash":"089ade7154cdafd2","prefixes":{"":7}}, // [Wize Commerce, Inc.] - {"hash":"7c4e77c7146bc27c","prefixes":{"":7}}, // [Wize Commerce, Inc.] - {"hash":"27943a61af56578c","prefixes":{"":7}}, // [Wize Commerce, Inc.] - {"hash":"5b9d47d01e06e207","prefixes":{"":7}}, // [Wize Commerce, Inc.] - {"hash":"efae037999b8354a","prefixes":{"":7}}, // [Wize Commerce, Inc.] - {"hash":"64b1c22f19588e65","prefixes":{"*":7}}, // [Wize Commerce, Inc.] - {"hash":"1b500703c376dccf","prefixes":{"":332}}, // [NowSpots] - {"hash":"0b8a5111da2a18e6","prefixes":{"":332}}, // [NowSpots] - {"hash":"6cbd63b4b5a258e2","prefixes":{"":333}}, // [Ocapi] - {"hash":"2ecb3e4de562d83f","prefixes":{"*":334}}, // [Predicta] - {"hash":"b0032d1f28de6b8e","prefixes":{"*":334}}, // [Predicta] - {"hash":"495b10f97369710c","prefixes":{"":335}}, // [Comune SA] - {"hash":"07b876ce04abdfc9","prefixes":{"":335}}, // [Comune SA] - {"hash":"bd1d888d58054387","prefixes":{"":335}}, // [Comune SA] - {"hash":"deb9a6a0e78770f8","prefixes":{"":335}}, // [Comune SA] - {"hash":"3d9897472cebdff7","prefixes":{"":335}}, // [Comune SA] - {"hash":"a036e7b2680b1720","prefixes":{"":335}}, // [Comune SA] - {"hash":"ae8e27c2a1195c9d","prefixes":{"":335}}, // [Comune SA] - {"hash":"685a4ece024544f3","prefixes":{"":335}}, // [Comune SA] - {"hash":"d705e49e2ecfc500","prefixes":{"":335}}, // [Comune SA] - {"hash":"4fd0a4a032a34191","prefixes":{"":335}}, // [Comune SA] - {"hash":"2640f60d445d4d0d","prefixes":{"":335}}, // [Comune SA] - {"hash":"0067af133fbf162f","prefixes":{"":335}}, // [Comune SA] - {"hash":"62e23e8e0a53b097","prefixes":{"":335}}, // [Comune SA] - {"hash":"57643f79e10fdc91","prefixes":{"":335}}, // [Comune SA] - {"hash":"fe70aef371b87e0d","prefixes":{"":335}}, // [Comune SA] - {"hash":"c80b88907c4da662","prefixes":{"":335}}, // [Comune SA] - {"hash":"3c027bef12167411","prefixes":{"":336}}, // [EuroAds Group A/S] - {"hash":"9e7ef06636d0b122","prefixes":{"":337}}, // [Hotwords Informação LTDA] - {"hash":"ac577d2ba001c2f9","prefixes":{"":337}}, // [Hotwords Informação LTDA] - {"hash":"8baa23f77cec98d3","prefixes":{"":337}}, // [Hotwords Informação LTDA] - {"hash":"86e7352358e7a0a1","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"461a9aacd5db8b62","prefixes":{"":42}}, // [Clinch.co] - {"hash":"4523c8dd39cb10d8","prefixes":{"":338}}, // [uMotion] - {"hash":"a7eaf4ae4152200d","prefixes":{"":338}}, // [uMotion] - {"hash":"1874ed5aa610db51","prefixes":{"*":339}}, // [Google Zoo] - {"hash":"0dc9eb6c8b1c77fa","prefixes":{"*":35}}, // [Oggifinogi] - {"hash":"60d67cd6819e7de4","prefixes":{"*":340}}, // [Audience Manager] - {"hash":"2ae524eac16b06f8","prefixes":{"":341}}, // [ONDCP] - {"hash":"8210c1a27bdc1916","prefixes":{"*":143}}, // [Edgesuite] - {"hash":"ca8cb9c623002dc9","prefixes":{"*":143}}, // [Edgesuite] - {"hash":"3ba6cc7216a7d5ae","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"24267a212835b827","prefixes":{"*":143}}, // [Edgesuite] - {"hash":"060ee4c312a8e977","prefixes":{"":342}}, // [LOKA Research inc.] - {"hash":"c7e974006ec125b7","prefixes":{"*":343}}, // [OpenX OnRamp] - {"hash":"a985fc121086ead8","prefixes":{"*":344}}, // [OpenX Ad Server] - {"hash":"402ca9fbb308a1c5","prefixes":{"*":36}}, // [PaperG] - {"hash":"53f1cd9b06aea643","prefixes":{"*":36}}, // [PaperG] - {"hash":"36b3ccfb92c2fb71","prefixes":{"*":345}}, // [Parship] - {"hash":"28832d626c0b0925","prefixes":{"*":346}}, // [pauldirekt GmbH] - {"hash":"1d474301c170499d","prefixes":{"*":347}}, // [Pictela Inc.] - {"hash":"fd1aa96507b2bf44","prefixes":{"":347}}, // [Pictela Inc.] - {"hash":"7f890c0fd0c6a4d9","prefixes":{"":348}}, // [Pilot 1/0 GmbH & Co KG] - {"hash":"549e294957d756d8","prefixes":{"":348}}, // [Pilot 1/0 GmbH & Co KG] - {"hash":"5dc046f77f32adf1","prefixes":{"*":349}}, // [Piximedia] - {"hash":"637b449ba51266a3","prefixes":{"*":349}}, // [Piximedia] - {"hash":"013ba3dd6b18ee4a","prefixes":{"*":350}}, // [Pointroll] - {"hash":"d96024cf9bafa9d0","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"4f0c9093d18b4ecd","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"d63f961e0c8cc056","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"1bcfb023adee2b08","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"6ed6fb838bd994d7","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"9fd07c030d45578d","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"5370f7b026779f44","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"c367910d58376fa0","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"670926f337b85492","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"77d7124c8aa87819","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"3726ef9236aeff59","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"84078980dc7d0310","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"496666ed92d32be6","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"3e8a19db4e7dd91a","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"33cfdc955bd4ab90","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"543046798ae8e38d","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"54537a7d4ce6fc35","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"242ab5d44fae500c","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"e81d4aa82d04d068","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"7c7f55d8a75cb9bc","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"e4a82caa59141478","prefixes":{"":351}}, // [Beijing PinYou Interactive Information Technology] - {"hash":"caf06dc269e4cbd7","prefixes":{"":352}}, // [YoYi Interactive] - {"hash":"70584cd7bcb88744","prefixes":{"":352}}, // [YoYi Interactive] - {"hash":"c9cabfef49bb4ec1","prefixes":{"":352}}, // [YoYi Interactive] - {"hash":"993089a2d306632e","prefixes":{"":352}}, // [YoYi Interactive] - {"hash":"cf04aaaa5ae5f383","prefixes":{"":352}}, // [YoYi Interactive] - {"hash":"a1cd6a668ae89bc8","prefixes":{"*":353}}, // [AdMaster] - {"hash":"f72dd5293eba3ca3","prefixes":{"":353}}, // [AdMaster] - {"hash":"e946e6b718e0e81a","prefixes":{"":353}}, // [AdMaster] - {"hash":"01b5e469f3b6d1d9","prefixes":{"":354}}, // [D.A.Consortium Beijing (Platform One China)] - {"hash":"f091b3175305cced","prefixes":{"":354}}, // [D.A.Consortium Beijing (Platform One China)] - {"hash":"1a095d1901f0940b","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"f66f99ee20105c2c","prefixes":{"*":356}}, // [PurposeLab] - {"hash":"86c250cf0da31481","prefixes":{"":356}}, // [PurposeLab] - {"hash":"8f204c79d6b23736","prefixes":{"*":334}}, // [Predicta] - {"hash":"43d06db6d6527876","prefixes":{"":357}}, // [Proclivity Systems] - {"hash":"5f76990173953807","prefixes":{"":357}}, // [Proclivity Systems] - {"hash":"7d3947ee9533bf30","prefixes":{"":37}}, // [Public-Idées] - {"hash":"fb241396adc3ebde","prefixes":{"":37}}, // [Public-Idées] - {"hash":"b8c5f46fb8945332","prefixes":{"":37}}, // [Public-Idées] - {"hash":"ade9cbc2becb390f","prefixes":{"":37}}, // [Public-Idées] - {"hash":"874cfa4e9fe27233","prefixes":{"":37}}, // [Public-Idées] - {"hash":"2cd6bee614e910f1","prefixes":{"":37}}, // [Public-Idées] - {"hash":"854f37cd1f444e62","prefixes":{"":37}}, // [Public-Idées] - {"hash":"19a83c7a2b673a6d","prefixes":{"":37}}, // [Public-Idées] - {"hash":"ca312e078723d178","prefixes":{"":37}}, // [Public-Idées] - {"hash":"4aa2d41fa2878d8f","prefixes":{"":37}}, // [Public-Idées] - {"hash":"8931cc3c2d6bbbff","prefixes":{"":37}}, // [Public-Idées] - {"hash":"e67e88dcc0afe050","prefixes":{"":37}}, // [Public-Idées] - {"hash":"3f6c08baf48fa0ae","prefixes":{"":37}}, // [Public-Idées] - {"hash":"6303a58061eaabd1","prefixes":{"*":358}}, // [Viewbix] - {"hash":"4fdc8f2ff122ce2e","prefixes":{"":358}}, // [Viewbix] - {"hash":"ba78a984ad7a8c06","prefixes":{"":358}}, // [Viewbix] - {"hash":"00885ce869b93eab","prefixes":{"":358}}, // [Viewbix] - {"hash":"eb59f3a64b415670","prefixes":{"":359}}, // [Quantcast Inc.] - {"hash":"243c70f61da9731c","prefixes":{"":359}}, // [Quantcast Inc.] - {"hash":"ed6b204d6b8f351e","prefixes":{"":359}}, // [Quantcast Inc.] - {"hash":"e6888b5be4ecca23","prefixes":{"":359}}, // [Quantcast Inc.] - {"hash":"8034327c12c77172","prefixes":{"":359}}, // [Quantcast Inc.] - {"hash":"d48f950e0036e4ee","prefixes":{"*":360}}, // [QuinStreet] - {"hash":"6118e4e0e4001349","prefixes":{"*":361}}, // [Quisma GmbH] - {"hash":"1b2f3dadf0889c1c","prefixes":{"":362}}, // [Quismatch / Quisma Tracker] - {"hash":"491acc9692437dcc","prefixes":{"":362}}, // [Quismatch / Quisma Tracker] - {"hash":"95be38e789f1c074","prefixes":{"":362}}, // [Quismatch / Quisma Tracker] - {"hash":"7d13c9a93867cfd5","prefixes":{"":362}}, // [Quismatch / Quisma Tracker] - {"hash":"aee8b719a85b0392","prefixes":{"":362}}, // [Quismatch / Quisma Tracker] - {"hash":"03c0d3572439e910","prefixes":{"":362}}, // [Quismatch / Quisma Tracker] - {"hash":"42c10737e9d2eb44","prefixes":{"":362}}, // [Quismatch / Quisma Tracker] - {"hash":"1ece70926dbb1e9c","prefixes":{"*":362}}, // [Quismatch / Quisma Tracker] - {"hash":"15b1b56db686d6b8","prefixes":{"":362}}, // [Quismatch / Quisma Tracker] - {"hash":"6f2fb017c596785a","prefixes":{"*":363}}, // [Qwobl Inc.] - {"hash":"45bd1c4bac827eb4","prefixes":{"*":364}}, // [RadiumOne Inc.] - {"hash":"a4cc28a873663be4","prefixes":{"*":365}}, // [Core Audience, Inc] - {"hash":"b1b347108c9875ae","prefixes":{"*":366}}, // [The Reach Group] - {"hash":"9680c32132a31294","prefixes":{"":367}}, // [revenue cloud] - {"hash":"4654f1b3d29f0a2a","prefixes":{"":367}}, // [revenue cloud] - {"hash":"c8daf5a2befc762c","prefixes":{"":367}}, // [revenue cloud] - {"hash":"cd7c04dcaab54cf3","prefixes":{"":366}}, // [The Reach Group] - {"hash":"6a7ac3b20a8267da","prefixes":{"*":368}}, // [Register.it] - {"hash":"24064a7ef37c0464","prefixes":{"":369}}, // [ReleStar] - {"hash":"28a4e40561f9acae","prefixes":{"":369}}, // [ReleStar] - {"hash":"3de1987cea55e4a5","prefixes":{"":369}}, // [ReleStar] - {"hash":"827fd98c61df481c","prefixes":{"":369}}, // [ReleStar] - {"hash":"eec1a8a2a7fb129b","prefixes":{"":369}}, // [ReleStar] - {"hash":"6c1b7209cced7033","prefixes":{"*":370}}, // [Research Horizons LLC dba Phoenix Marketing] - {"hash":"c946c0fe5eecd55f","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"2feb43f5f97da585","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"4847977384b78f1d","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"308241d0fe4d6897","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"2cb30990aa0823eb","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"2c25ebde178886b1","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"d4eeb4a4736e8358","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"12498108d00bd104","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"c7437019688a5c87","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"ebc599442a1e60e0","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"ab9c2a8a5ed26375","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"c9473e1a1e9213bc","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"a15bca05fb29201f","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"8c2336c1a87e9c7e","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"67f7079b53ce160f","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"a07c7ea743bca71c","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"ce0849726cb792fb","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"6d2feeb6316bf983","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"056132328ebc8a3b","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"c0dd251eb151bbdd","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"02e6b230a73d95aa","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"62cef89124c9831e","prefixes":{"*":8}}, // [Research Now Limited] - {"hash":"1bb2fad94e9dfd9e","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"0bca341ab2881187","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"fb725c5783121bc4","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"6d442fcb13dbd9da","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"6020c585ae546ba6","prefixes":{"":8}}, // [Research Now Limited] - {"hash":"bf994d767fc23e27","prefixes":{"":371}}, // [Retention Media Inc.] - {"hash":"5c801bba74bf0884","prefixes":{"":372}}, // [Suite 66] - {"hash":"f727f4c8af36c75c","prefixes":{"":373}}, // [Dynamic Logic / Safecount (AdIndex)] - {"hash":"d0ffa461035bdf6a","prefixes":{"":373}}, // [Dynamic Logic / Safecount (AdIndex)] - {"hash":"f1381513cbb99bf4","prefixes":{"":373}}, // [Dynamic Logic / Safecount (AdIndex)] - {"hash":"2b675a4d048f6d58","prefixes":{"":373}}, // [Dynamic Logic / Safecount (AdIndex)] - {"hash":"ff9153951d29d7eb","prefixes":{"":373}}, // [Dynamic Logic / Safecount (AdIndex)] - {"hash":"9a2d88a64367054f","prefixes":{"":373}}, // [Dynamic Logic / Safecount (AdIndex)] - {"hash":"22fc5e00cc71b2ca","prefixes":{"":373}}, // [Dynamic Logic / Safecount (AdIndex)] - {"hash":"d680287680a3b47b","prefixes":{"*":374}}, // [BridgeTrack] - {"hash":"6659ffe0ec0db1a1","prefixes":{"*":375}}, // [adnanny.com GmbH] - {"hash":"cdef303f827a483a","prefixes":{"":375}}, // [adnanny.com GmbH] - {"hash":"d37d706078f5ef0e","prefixes":{"":375}}, // [adnanny.com GmbH] - {"hash":"cefa23f603f78274","prefixes":{"":375}}, // [adnanny.com GmbH] - {"hash":"9beed54a2448f091","prefixes":{"*":376}}, // [AdRoll] - {"hash":"131ff02cd8b2b585","prefixes":{"*":377}}, // [AdExtent] - {"hash":"7b03a999ce62649a","prefixes":{"*":377}}, // [AdExtent] - {"hash":"d888444955526bb9","prefixes":{"*":378}}, // [ShareThis Inc.] - {"hash":"24bedaecde8de52e","prefixes":{"*":379}}, // [Shirtinator] - {"hash":"b2227832d5aab97c","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"29d1a1af90fe4764","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"55d58888ac21e28e","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"15a0a565c098bb6c","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"c740f7042a936f94","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"83b6131729d98b9a","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"df61075a3761234c","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"31d664b6a0c86fc5","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"b3f1fda5ca08f66c","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"a505bd8c5fdbe348","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"ff4b6ade85c0fa47","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"55fbb05b573abfc0","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"7509d096b740a94e","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"3e05ad70e7b857f6","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"f3122b05b916b4d8","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"7df77439ca0af167","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"7b6f3b92d848f180","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"c50b3a11c75c1de2","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"42bd2bd016c36072","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"d964e110e03add5e","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"b2f31dc5aea81475","prefixes":{"":380}}, // [Simplifi Holdings Inc.] - {"hash":"fd3e5505c689b1bc","prefixes":{"":381}}, // [Centro DSP] - {"hash":"e21937d0011ae5da","prefixes":{"":381}}, // [Centro DSP] - {"hash":"0f5e4a0353dbf996","prefixes":{"":381}}, // [Centro DSP] - {"hash":"0ece74eb8682685e","prefixes":{"":381}}, // [Centro DSP] - {"hash":"3d8ea3f0d5d389d5","prefixes":{"":381}}, // [Centro DSP] - {"hash":"b5602307e99e9538","prefixes":{"":381}}, // [Centro DSP] - {"hash":"c13decc96884eb9a","prefixes":{"":381}}, // [Centro DSP] - {"hash":"b815964fd4b63ffe","prefixes":{"":381}}, // [Centro DSP] - {"hash":"a49f2db509757639","prefixes":{"":381}}, // [Centro DSP] - {"hash":"a3ff80b45a2b0087","prefixes":{"":381}}, // [Centro DSP] - {"hash":"b1b2c1399cbf19be","prefixes":{"":381}}, // [Centro DSP] - {"hash":"ba44917903f4a6c0","prefixes":{"":381}}, // [Centro DSP] - {"hash":"ad5130e5aa2d0bcd","prefixes":{"":381}}, // [Centro DSP] - {"hash":"ceb371583c3948ee","prefixes":{"":381}}, // [Centro DSP] - {"hash":"5d5d4fe496a7b543","prefixes":{"":381}}, // [Centro DSP] - {"hash":"30e80084c1e7285e","prefixes":{"":381}}, // [Centro DSP] - {"hash":"d1d0fc6a034973b5","prefixes":{"":381}}, // [Centro DSP] - {"hash":"e2814abcd5399a68","prefixes":{"":381}}, // [Centro DSP] - {"hash":"9d4acd34336a73d4","prefixes":{"":381}}, // [Centro DSP] - {"hash":"dcbf7d7841f88317","prefixes":{"":381}}, // [Centro DSP] - {"hash":"c19fe37a92d8a1f4","prefixes":{"":381}}, // [Centro DSP] - {"hash":"dd502cbc700beb03","prefixes":{"":381}}, // [Centro DSP] - {"hash":"a76947b64ea18232","prefixes":{"":381}}, // [Centro DSP] - {"hash":"75875c5fcc471f0e","prefixes":{"":381}}, // [Centro DSP] - {"hash":"5323dbf9f56befc9","prefixes":{"":381}}, // [Centro DSP] - {"hash":"fd3df839224700c5","prefixes":{"":381}}, // [Centro DSP] - {"hash":"dc544506e0acf31c","prefixes":{"":381}}, // [Centro DSP] - {"hash":"fbadeeb25ac766a9","prefixes":{"":381}}, // [Centro DSP] - {"hash":"f344fa72c3acea98","prefixes":{"":381}}, // [Centro DSP] - {"hash":"e0d1929f5490ba3c","prefixes":{"":381}}, // [Centro DSP] - {"hash":"150d5c444034d16f","prefixes":{"":381}}, // [Centro DSP] - {"hash":"63c9992c37df81e6","prefixes":{"":381}}, // [Centro DSP] - {"hash":"9a2f65999a2602e7","prefixes":{"":381}}, // [Centro DSP] - {"hash":"b13d21bc9d60e3cf","prefixes":{"":381}}, // [Centro DSP] - {"hash":"36778688c2c8550d","prefixes":{"":381}}, // [Centro DSP] - {"hash":"7764641665318d7a","prefixes":{"":381}}, // [Centro DSP] - {"hash":"137839bac6b52f11","prefixes":{"":381}}, // [Centro DSP] - {"hash":"75b17e7427d84ec0","prefixes":{"":381}}, // [Centro DSP] - {"hash":"f576b8f6b5698927","prefixes":{"":381}}, // [Centro DSP] - {"hash":"a0bef488713fd8ba","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"c4f4b5d7a3bc8772","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"652de2e0257cc6f9","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"e1eb84d2322dd53f","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"0759e6e60e23806b","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"b97f9d3a0092f5df","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"6f7d322aac5f6093","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"d6d2092af97f6672","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"700b4497422246d3","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"24758e00974c4842","prefixes":{"*":382}}, // [Smart Adserver] - {"hash":"229fb1e217ec3254","prefixes":{"*":382}}, // [Smart Adserver] - {"hash":"24f84ec66db45f3d","prefixes":{"":383}}, // [smartclip Holding AG] - {"hash":"7bce0d8d711753ea","prefixes":{"":383}}, // [smartclip Holding AG] - {"hash":"f748f5ce8e9c4b7d","prefixes":{"*":384}}, // [Snap Technologies Inc.] - {"hash":"c17d7dedc318749c","prefixes":{"":385}}, // [So-net Media Networks] - {"hash":"d3a65eea99debc9b","prefixes":{"":385}}, // [So-net Media Networks] - {"hash":"f28c7fc4a06bcfa7","prefixes":{"*":386}}, // [SocialMedia.com] - {"hash":"17591c31cbbe7cf8","prefixes":{"":387}}, // [Content to Emotion (CTE)] - {"hash":"7c350f8bcf5a54b9","prefixes":{"":387}}, // [Content to Emotion (CTE)] - {"hash":"e003938122f9ac4f","prefixes":{"":387}}, // [Content to Emotion (CTE)] - {"hash":"6deac101ca463319","prefixes":{"static":387}}, // [Content to Emotion (CTE)] - {"hash":"304e631663a87e67","prefixes":{"*":388}}, // [Sociomantic.com] - {"hash":"e86474f9c6ad794d","prefixes":{"":389}}, // [AdSpeed] - {"hash":"de0816aa39c8b153","prefixes":{"*":390}}, // [Sophus3] - {"hash":"df8355613a445a85","prefixes":{"":9}}, // [Spartoo] - {"hash":"a72f2b989760c8dc","prefixes":{"":9}}, // [Spartoo] - {"hash":"fbdc1704c28873c8","prefixes":{"":9}}, // [Spartoo] - {"hash":"d4674ccaf8fb7e31","prefixes":{"":9}}, // [Spartoo] - {"hash":"f184677fb7e2abca","prefixes":{"":9}}, // [Spartoo] - {"hash":"e7d695bf507e45d5","prefixes":{"":9}}, // [Spartoo] - {"hash":"ea3f746342f05dbf","prefixes":{"":9}}, // [Spartoo] - {"hash":"96ce83a8f0d84d99","prefixes":{"":9}}, // [Spartoo] - {"hash":"62c6410f3e4000d8","prefixes":{"":9}}, // [Spartoo] - {"hash":"b5172a6eee01acbe","prefixes":{"":9}}, // [Spartoo] - {"hash":"36f9ba3a735ea443","prefixes":{"":9}}, // [Spartoo] - {"hash":"f4d96ab5c7d0f720","prefixes":{"":9}}, // [Spartoo] - {"hash":"7c8204a143e83abb","prefixes":{"":9}}, // [Spartoo] - {"hash":"cb5b72c4fdc90b38","prefixes":{"":9}}, // [Spartoo] - {"hash":"01c1382916f7a580","prefixes":{"":9}}, // [Spartoo] - {"hash":"49505fd99e787062","prefixes":{"":9}}, // [Spartoo] - {"hash":"a931030a5dbd7346","prefixes":{"":9}}, // [Spartoo] - {"hash":"3a09b22266ba0b12","prefixes":{"*":391}}, // [Specific Media Inc.] - {"hash":"2136a3b6f6600ad0","prefixes":{"*":391}}, // [Specific Media Inc.] - {"hash":"600e429523014251","prefixes":{"*":391}}, // [Specific Media Inc.] - {"hash":"e8b1c9274a662ad3","prefixes":{"":392}}, // [Metrigo GmbH] - {"hash":"d0ffd80eda189780","prefixes":{"":392}}, // [Metrigo GmbH] - {"hash":"e4ab3c31faef2b98","prefixes":{"":392}}, // [Metrigo GmbH] - {"hash":"97db8dd1bcbc33b8","prefixes":{"*":393}}, // [SpongeCell] - {"hash":"363a0f26e680d077","prefixes":{"":394}}, // [SpngeCell LLC] - {"hash":"fbef9c1b28b0cb05","prefixes":{"*":393}}, // [SpongeCell] - {"hash":"2218b5ce1e656e21","prefixes":{"":395}}, // [SpotXchange] - {"hash":"a520f16cf6177ebd","prefixes":{"":395}}, // [SpotXchange] - {"hash":"cda310d3654d07a5","prefixes":{"":395}}, // [SpotXchange] - {"hash":"2fc5567578510af6","prefixes":{"":396}}, // [Spotxchange] - {"hash":"abab2d20e98c266d","prefixes":{"":395}}, // [SpotXchange] - {"hash":"0d78cf3b068120c3","prefixes":{"":395}}, // [SpotXchange] - {"hash":"6da7c1b25ca845b3","prefixes":{"":395}}, // [SpotXchange] - {"hash":"c824df0fb0fdf482","prefixes":{"":397}}, // [Encore Attribution Platform] - {"hash":"9796e014709ccbec","prefixes":{"":397}}, // [Encore Attribution Platform] - {"hash":"fced1a8e32fb989c","prefixes":{"*":398}}, // [Steelhouse] - {"hash":"314523d1c217734f","prefixes":{"*":399}}, // [Strike New Media Limited] - {"hash":"276a5e6003750c07","prefixes":{"*":400}}, // [Struq Limited] - {"hash":"e5652beb11c7fceb","prefixes":{"*":401}}, // [SundaySky Inc.] - {"hash":"173a426f6562928a","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"81a2745fc67a8ecf","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"0aea8cde58b4f14e","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"b73e77077b022d36","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"fcf0d2e1f5fa003b","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"361c645b41a96807","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"81df9375fed9172e","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"c1493a0cd3b358a5","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"bdedb9447527960a","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"1cb83aa94e1a92ed","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"2172731c879fab72","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"7dd683bf50ce5c95","prefixes":{"":402}}, // [Symphony Advanced Media] - {"hash":"aa47b70219d2ee5e","prefixes":{"*":403}}, // [TagMan Ltd.] - {"hash":"097edb88beda9a20","prefixes":{"":404}}, // [Taobao] - {"hash":"bc65fc5f46cd88c9","prefixes":{"":404}}, // [Taobao] - {"hash":"a4d1bf696dde4d0b","prefixes":{"":404}}, // [Taobao] - {"hash":"c94f9318ace953f1","prefixes":{"":404}}, // [Taobao] - {"hash":"9e2b38828859944c","prefixes":{"":404}}, // [Taobao] - {"hash":"02319fc6286418de","prefixes":{"":404}}, // [Taobao] - {"hash":"dacc570e07ffd91a","prefixes":{"img":404}}, // [Taobao] - {"hash":"e8ccbc8f652e85a2","prefixes":{"*":404}}, // [Taobao] - {"hash":"da1ba7cc516336c0","prefixes":{"":404}}, // [Taobao] - {"hash":"41035e7733acf867","prefixes":{"":405}}, // [Target.com] - {"hash":"0509bdf297db5010","prefixes":{"*":406}}, // [Teadma] - {"hash":"69925c7888575612","prefixes":{"":407}}, // [BannerConnect BV] - {"hash":"4d44c3cec23aea09","prefixes":{"":407}}, // [BannerConnect BV] - {"hash":"447ea46d0797b32f","prefixes":{"*":408}}, // [Teracent Corporation] - {"hash":"26ca7e33d194e46e","prefixes":{"":408}}, // [Teracent Corporation] - {"hash":"b49b79e2e870c3e9","prefixes":{"":408}}, // [Teracent Corporation] - {"hash":"3a555db76a46f881","prefixes":{"*":38}}, // [The Trade Desk Inc.] - {"hash":"c4684dd3ef28687a","prefixes":{"*":409}}, // [The Travelers Indemnity Company] - {"hash":"1c38e7ac2dcf5d58","prefixes":{"":410}}, // [Videology] - {"hash":"4872cdec0944a698","prefixes":{"":410}}, // [Videology] - {"hash":"d4e30248b2920988","prefixes":{"*":411}}, // [TNS Custom Research Inc.] - {"hash":"48a3c2e049507f03","prefixes":{"*":412}}, // [TrackingSoft LLC] - {"hash":"4c5b26902a10b85b","prefixes":{"*":412}}, // [TrackingSoft LLC] - {"hash":"51a48ea23568f817","prefixes":{"*":413}}, // [Tradedoubler] - {"hash":"14251708a577d8c0","prefixes":{"*":414}}, // [Epic Marketplace] - {"hash":"20f50db7213cc9ee","prefixes":{"*":415}}, // [Tribal Fusion] - {"hash":"37b92f46ce8d0fa5","prefixes":{"":415}}, // [Tribal Fusion] - {"hash":"218cac8e116dcf7a","prefixes":{"":416}}, // [Triggit] - {"hash":"135ee5dfe108d144","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"f6afa26369731900","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"600a58efa4b0a4fa","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"c274f6336d4a40c8","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"0476503f0fc5138a","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"f6364185e21bdfb4","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"3b746cbe2984928e","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"2966c06482ab340c","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"1320f91c88734ed7","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"49e73f2dfe503088","prefixes":{"":417}}, // [True Ultimate Standards Everywhere Inc.] - {"hash":"345de3574a5cef61","prefixes":{"":418}}, // [CarMax Business Services LLC] - {"hash":"8aba77355315b602","prefixes":{"":419}}, // [Charter Communications] - {"hash":"f7e3882cd37d1a5b","prefixes":{"":419}}, // [Charter Communications] - {"hash":"a62179d5dc8551f8","prefixes":{"":419}}, // [Charter Communications] - {"hash":"ac3760dc99259bf9","prefixes":{"":420}}, // [General Nutrition Centers Inc.] - {"hash":"e1c51fbf9b39950a","prefixes":{"":421}}, // [Hamilton Beach] - {"hash":"68a80c829eb9c95e","prefixes":{"":422}}, // [JacquieLawson.com] - {"hash":"a02b27c9af68c051","prefixes":{"":423}}, // [TruEffect] - {"hash":"76b54dcac102ae32","prefixes":{"":423}}, // [TruEffect] - {"hash":"f36743b8c729a3e5","prefixes":{"":424}}, // [American Greetings] - {"hash":"0d479c4b1d2026b9","prefixes":{"":425}}, // [BlueMountain] - {"hash":"c442eae221355ece","prefixes":{"":426}}, // [Cardstore] - {"hash":"1277d75c5bace94e","prefixes":{"":427}}, // [Chemistry.com] - {"hash":"2534adb635b0540f","prefixes":{"":428}}, // [Donald J Pliner] - {"hash":"6e81f110e25ceba1","prefixes":{"*":429}}, // [Gevalia] - {"hash":"58cb024039904795","prefixes":{"":430}}, // [GSI Media] - {"hash":"36b7a450ddb92a81","prefixes":{"":431}}, // [Match.com] - {"hash":"7f6a517c8eef7bf6","prefixes":{"":431}}, // [Match.com] - {"hash":"b5aa9057f4ab3a79","prefixes":{"":431}}, // [Match.com] - {"hash":"177eb410b9eee3c2","prefixes":{"":431}}, // [Match.com] - {"hash":"5a75e9cff48a157c","prefixes":{"":431}}, // [Match.com] - {"hash":"e561536f4b9b0f5e","prefixes":{"*":432}}, // [Optimum Response] - {"hash":"3aaed06dec8e119b","prefixes":{"":433}}, // [Oreck] - {"hash":"e7b19cb5471c10b3","prefixes":{"":434}}, // [Tassimo] - {"hash":"5eca8c8aa7c01b0e","prefixes":{"*":435}}, // [uSwitch] - {"hash":"974734b3e01eab8f","prefixes":{"*":436}}, // [TubeMogul Inc.] - {"hash":"9f562a308fffc5c4","prefixes":{"*":437}}, // [Tumri] - {"hash":"09845e50cbc70b8a","prefixes":{"*":182}}, // [AdAction] - {"hash":"82ae5c9a96804ec8","prefixes":{"":102}}, // [BigaBid Media Ltd.] - {"hash":"b91b0c7d1d3bb786","prefixes":{"":102}}, // [BigaBid Media Ltd.] - {"hash":"2804070c2606fca7","prefixes":{"":102}}, // [BigaBid Media Ltd.] - {"hash":"d601d362a989dcf9","prefixes":{"":438}}, // [iMarker] - {"hash":"0a0084b896887917","prefixes":{"":438}}, // [iMarker] - {"hash":"64596506e8398578","prefixes":{"":438}}, // [iMarker] - {"hash":"4af3f67573bdbad8","prefixes":{"":438}}, // [iMarker] - {"hash":"1348f7d07273dfcf","prefixes":{"":438}}, // [iMarker] - {"hash":"4329a4ad527e4779","prefixes":{"":438}}, // [iMarker] - {"hash":"5eca470b27725f28","prefixes":{"*":439}}, // [Turn Inc.] - {"hash":"83807aae08d0747f","prefixes":{"*":439}}, // [Turn Inc.] - {"hash":"a0542aaf969b6041","prefixes":{"*":440}}, // [Twelvefold Media] - {"hash":"7837c4e8623f0409","prefixes":{"":440}}, // [Twelvefold Media] - {"hash":"13c46ea92caeecf8","prefixes":{"":441}}, // [Tynt] - {"hash":"5f7c83b68361da73","prefixes":{"*":442}}, // [Unica an IBM Company] - {"hash":"bd242bcdc493f1d5","prefixes":{"*":443}}, // [Unicast] - {"hash":"58ad481190b46e2e","prefixes":{"":444}}, // [UniQlick] - {"hash":"7f5d2102ab11e53c","prefixes":{"":444}}, // [UniQlick] - {"hash":"c9dd94a27435350b","prefixes":{"":444}}, // [UniQlick] - {"hash":"4ba8d85cfc1bf5de","prefixes":{"*":445}}, // [United Virtualities] - {"hash":"0e4c474511c6dfff","prefixes":{"":446}}, // [VideoHub] - {"hash":"875bbc4dbaee8734","prefixes":{"":446}}, // [VideoHub] - {"hash":"58b08ddd9e3cccf9","prefixes":{"":446}}, // [VideoHub] - {"hash":"356603d457828911","prefixes":{"":446}}, // [VideoHub] - {"hash":"77c85cb3240da7c5","prefixes":{"":446}}, // [VideoHub] - {"hash":"010912cf400592bc","prefixes":{"":446}}, // [VideoHub] - {"hash":"28e6d587198883be","prefixes":{"":446}}, // [VideoHub] - {"hash":"e64d00be64f97a79","prefixes":{"":446}}, // [VideoHub] - {"hash":"fa0271d4d3cf41f2","prefixes":{"":446}}, // [VideoHub] - {"hash":"a227d04cd3bf04f7","prefixes":{"":446}}, // [VideoHub] - {"hash":"b69205c5372d2357","prefixes":{"":446}}, // [VideoHub] - {"hash":"da30259edef87286","prefixes":{"*":447}}, // [Visible Measures Corp.] - {"hash":"46ff1406588e7ceb","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"32ca2bff73328a83","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"3fc1dc351d4952c8","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"80d2379485452f29","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"35183abb3e2c864c","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"3b235045cc005d15","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"34e6561d9300f451","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"08980f63c2ca7971","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"327755ab5ee91d4e","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"c3d3383046b5690f","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"d196d3b7e439011e","prefixes":{"":192}}, // [Nielsen OBE (Vizu)] - {"hash":"09582bb8c4f0bdcd","prefixes":{"*":448}}, // [Vizury Interactive Solutions Pvt. Ltd.] - {"hash":"ccbe4ed34be8ea01","prefixes":{"":449}}, // [Markit On Demand (Adhesion)] - {"hash":"5c0c23d6d5b2aa7d","prefixes":{"":449}}, // [Markit On Demand (Adhesion)] - {"hash":"6fe5393d68aec474","prefixes":{"":449}}, // [Markit On Demand (Adhesion)] - {"hash":"24de99047e3e2592","prefixes":{"":449}}, // [Markit On Demand (Adhesion)] - {"hash":"a1d810c22ef4c8c1","prefixes":{"":449}}, // [Markit On Demand (Adhesion)] - {"hash":"a961c235191ccc5c","prefixes":{"":449}}, // [Markit On Demand (Adhesion)] - {"hash":"30327e228a69909d","prefixes":{"":450}}, // [WebMetro Inc] - {"hash":"1440114026d0b8a7","prefixes":{"*":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"178b8b7280deb93c","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"8398687ebe990cf1","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"5cad65c27ef8d2b4","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"f28574a110053221","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"64eae6bdfc8cc1be","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"36b0c30f5f967ffd","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"10c282544a39807b","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"ce2ed22191804ffa","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"bd56114f26a64020","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"54e283b8884709c3","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"313822ab7be9585a","prefixes":{"":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"d54943c8060334b5","prefixes":{"":452}}, // [Weborama Campaign Manager] - {"hash":"7928274bf854b28b","prefixes":{"*":451}}, // [Weborama Campaign Manager (former name AdPerf)] - {"hash":"80415fbb74db7704","prefixes":{"":453}}, // [Shopping Network] - {"hash":"0840f634995e25fb","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"5fd40859cc837b00","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"c58731043a973263","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"ee286fbf0440d595","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"660d555331a8be68","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"c412040c18d978ce","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"667b34ab71cb2381","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"b888d9e69003de82","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"38dafaa727179ef7","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"78da2db9d3a51f98","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"94a731a76dd26bfc","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"c4ca9b6aebf488dc","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"0a42e116235fec6e","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"ac4d0bcf3017cc3d","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"6653125411676c31","prefixes":{"":454}}, // [Shanghai WiseMedia Technology Co. Ltd.] - {"hash":"062b10abe02277c1","prefixes":{"":455}}, // [XA.net] - {"hash":"b0e3a57e8f415aec","prefixes":{"":455}}, // [XA.net] - {"hash":"6e1bb44d175a20af","prefixes":{"":455}}, // [XA.net] - {"hash":"34984bab025a1372","prefixes":{"":455}}, // [XA.net] - {"hash":"d9eb2f654e80ebc6","prefixes":{"*":456}}, // [Xaxis LLC] - {"hash":"b86d63843110e02a","prefixes":{"*":457}}, // [Net Edge] - {"hash":"8b526a29c7f2b3dc","prefixes":{"*":458}}, // [Xplusone Solutions Inc.] - {"hash":"fe0ea2a1c212cfde","prefixes":{"":459}}, // [Yahoo! Ad Exchange] - {"hash":"dc06a1403eb499df","prefixes":{"":459}}, // [Yahoo! Ad Exchange] - {"hash":"5a36dbbd382aa12c","prefixes":{"":459}}, // [Yahoo! Ad Exchange] - {"hash":"c8fd15f0a5fc19aa","prefixes":{"":459}}, // [Yahoo! Ad Exchange] - {"hash":"ce63a8ab511ec5bb","prefixes":{"":459}}, // [Yahoo! Ad Exchange] - {"hash":"a6d2abe55f73bc22","prefixes":{"":460}}, // [Yahoo Ad Manager Plus] - {"hash":"707a11e6dcfa57db","prefixes":{"":460}}, // [Yahoo Ad Manager Plus] - {"hash":"f75439bd7693fa84","prefixes":{"":460}}, // [Yahoo Ad Manager Plus] - {"hash":"2f7feb27a1b93524","prefixes":{"":460}}, // [Yahoo Ad Manager Plus] - {"hash":"f801c9750710d30a","prefixes":{"":460}}, // [Yahoo Ad Manager Plus] - {"hash":"9b96f7700d727da9","prefixes":{"":461}}, // [APT from Yahoo!] - {"hash":"e4de2f3b2c06e193","prefixes":{"":461}}, // [APT from Yahoo!] - {"hash":"21aa62a45ef75fb2","prefixes":{"*":462}}, // [Yieldr] - {"hash":"85bf1ed3ee6ee615","prefixes":{"":462}}, // [Yieldr] - {"hash":"2cf45ce27d0b73c2","prefixes":{"":462}}, // [Yieldr] - {"hash":"e8595ade1fd26e2f","prefixes":{"":462}}, // [Yieldr] - {"hash":"5660279fe13cfe3b","prefixes":{"":462}}, // [Yieldr] - {"hash":"ad26a995ee586915","prefixes":{"":462}}, // [Yieldr] - {"hash":"23412da5a5a538cc","prefixes":{"":462}}, // [Yieldr] - {"hash":"6b333dd78d119434","prefixes":{"":462}}, // [Yieldr] - {"hash":"f3621ca5777d61d5","prefixes":{"":462}}, // [Yieldr] - {"hash":"cafa71178e1de4db","prefixes":{"":352}}, // [YoYi Interactive] - {"hash":"2ca490afaa90c2b3","prefixes":{"":352}}, // [YoYi Interactive] - {"hash":"8f0dd7539cf77aa5","prefixes":{"":352}}, // [YoYi Interactive] - {"hash":"005700973b435023","prefixes":{"":463}}, // [YuMe Inc.] - {"hash":"bd83427751c351a9","prefixes":{"":463}}, // [YuMe Inc.] - {"hash":"e504ebfcb6b2722e","prefixes":{"":463}}, // [YuMe Inc.] - {"hash":"8080438b5792b260","prefixes":{"":464}}, // [Ebuzzing] - {"hash":"274889e98fbfc776","prefixes":{"":464}}, // [Ebuzzing] - {"hash":"b62ee5e77fde7ec7","prefixes":{"":464}}, // [Ebuzzing] - {"hash":"cc6a7a565374c257","prefixes":{"":464}}, // [Ebuzzing] - {"hash":"e02936a6a6a2cb94","prefixes":{"":465}}, // [Ziff Davis] - {"hash":"223b94a95f6849cf","prefixes":{"":465}}, // [Ziff Davis] - {"hash":"a8742ddae80b031b","prefixes":{"":466}}, // [DataPoint Media Inc.] - {"hash":"fd4a16bf59718107","prefixes":{"":467}}, // [Simplytics Limited] - {"hash":"a3d523521bc4ba12","prefixes":{"":467}}, // [Simplytics Limited] - {"hash":"9da8a1153308d7f2","prefixes":{"":468}}, // [Makazi] - {"hash":"d9cc216c8d482336","prefixes":{"":468}}, // [Makazi] - {"hash":"166c65d7776be9f8","prefixes":{"":468}}, // [Makazi] - {"hash":"55f15e5b7746999f","prefixes":{"":468}}, // [Makazi] - {"hash":"213194bd445e551d","prefixes":{"":468}}, // [Makazi] - {"hash":"31406bff4dc35eea","prefixes":{"":468}}, // [Makazi] - {"hash":"ea9607b28f4238c2","prefixes":{"":468}}, // [Makazi] - {"hash":"707d5310f27d0dd2","prefixes":{"":469}}, // [Rich Relevance] - {"hash":"59fa19b8a48477fe","prefixes":{"":470}}, // [MainADV] - {"hash":"efb9b464f6ebaa1d","prefixes":{"":470}}, // [MainADV] - {"hash":"8848350bd85fffe4","prefixes":{"":470}}, // [MainADV] - {"hash":"519275c83a5a1c92","prefixes":{"":470}}, // [MainADV] - {"hash":"fc92bec8fbcb03ef","prefixes":{"":470}}, // [MainADV] - {"hash":"91fa39bc27e156b4","prefixes":{"":470}}, // [MainADV] - {"hash":"408d55e8995335ad","prefixes":{"":470}}, // [MainADV] - {"hash":"d0efa7323c77b1a5","prefixes":{"":470}}, // [MainADV] - {"hash":"baa5cf88a0176bc6","prefixes":{"":471}}, // [MediaV Advertising] - {"hash":"3ea0854e74c32c87","prefixes":{"":471}}, // [MediaV Advertising] - {"hash":"88a70c2ebfe38ffe","prefixes":{"":471}}, // [MediaV Advertising] - {"hash":"dc0b1dfe6de6a967","prefixes":{"":471}}, // [MediaV Advertising] - {"hash":"cb234228af27fa32","prefixes":{"":471}}, // [MediaV Advertising] - {"hash":"c7abee58863b27a4","prefixes":{"":472}}, // [MicroAd Inc.] - {"hash":"7e34b0d37ded49ed","prefixes":{"":472}}, // [MicroAd Inc.] - {"hash":"d7255d3135b0294d","prefixes":{"":472}}, // [MicroAd Inc.] - {"hash":"e21264db24be060c","prefixes":{"":472}}, // [MicroAd Inc.] - {"hash":"466676eb4c2066ac","prefixes":{"":472}}, // [MicroAd Inc.] - {"hash":"2505a55ead3f2266","prefixes":{"":472}}, // [MicroAd Inc.] - {"hash":"e97b9e2d31ac962d","prefixes":{"":472}}, // [MicroAd Inc.] - {"hash":"906215055f6880fa","prefixes":{"":472}}, // [MicroAd Inc.] - {"hash":"df04046f11aea628","prefixes":{"*":473}}, // [Platform ID Inc.] - {"hash":"9fe8e93d55562003","prefixes":{"":474}}, // [Xrost] - {"hash":"6b29d401833f79d8","prefixes":{"":473}}, // [Platform ID Inc.] - {"hash":"00928c6f847a4785","prefixes":{"":475}}, // [Sociocast Networks LLC D/B/A Velos] - {"hash":"c3964f299adbe862","prefixes":{"":476}}, // [Trend Research] - {"hash":"76b5ddb10f769c6b","prefixes":{"*":248}}, // [AdOcean Ltd] - {"hash":"f52edd3926705507","prefixes":{"":477}}, // [Black Swan Verification] - {"hash":"d63e38f4eba51f77","prefixes":{"":478}}, // [Momentum K.K] - {"hash":"6c0f8db03b28099b","prefixes":{"":477}}, // [Black Swan Verification] - {"hash":"248772911542d550","prefixes":{"*":479}}, // [Betgenius Limited] - {"hash":"38a72a4924027d8f","prefixes":{"*":480}}, // [AT Internet] - {"hash":"255b63d169bd9d10","prefixes":{"*":480}}, // [AT Internet] - {"hash":"3a7fbbad166769ad","prefixes":{"":481}}, // [DataLab] - {"hash":"fe64230a84967d3c","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"a91296d609efe0b4","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"947fa5564eccc0b2","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"89a64fe46687237f","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"ce1cd4220063df2d","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"1c489f83a79301c6","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"9dc25ef9b3ed3e5d","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"9898ab0c57d683ae","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"0229fe19979f88b0","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"6dedd3fca4d243ac","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"cbc23a15e9f02987","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"95119cbc05f9e42a","prefixes":{"":482}}, // [Innovid Inc.] - {"hash":"784647d3eb0236dc","prefixes":{"*":483}}, // [Adacado] - {"hash":"4be5d3939998bc39","prefixes":{"":483}}, // [Adacado] - {"hash":"cec4d2059bbb1365","prefixes":{"*":484}}, // [NetDNA, LLC] - {"hash":"87a9bf2f660b2673","prefixes":{"":478}}, // [Momentum K.K] - {"hash":"33f6a4c9eb1313f6","prefixes":{"":478}}, // [Momentum K.K] - {"hash":"38afcf5464d4a46e","prefixes":{"":478}}, // [Momentum K.K] - {"hash":"4a13b10532de4033","prefixes":{"*":485}}, // [Pipewave Inc.] - {"hash":"a703750c918980b9","prefixes":{"":486}}, // [US Media Consulting] - {"hash":"77b351109047edb2","prefixes":{"":487}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] - {"hash":"f9f554c96902397e","prefixes":{"":487}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] - {"hash":"e8f2c7f5eb75983e","prefixes":{"":487}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] - {"hash":"f8680fe819857e65","prefixes":{"image":487}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] - {"hash":"fe12b35f78c433a6","prefixes":{"":487}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] - {"hash":"531f5c369a189473","prefixes":{"":488}}, // [Hanesbrands Direct LLC] - {"hash":"4bfbc25da5ec8116","prefixes":{"":489}}, // [A1platform] - {"hash":"56cea5c2b408989a","prefixes":{"*":10}}, // [eBay] - {"hash":"2c24b521f38d28dd","prefixes":{"":10}}, // [eBay] - {"hash":"d6449351ee8e33eb","prefixes":{"*":490}}, // [Agency.com] - {"hash":"5e040f7b958d9ee5","prefixes":{"*":10}}, // [eBay] - {"hash":"379ed8ab1690a74c","prefixes":{"":10}}, // [eBay] - {"hash":"6ed4dac9c333ef4b","prefixes":{"*":10}}, // [eBay] - {"hash":"b4a9e54654a6de08","prefixes":{"*":10}}, // [eBay] - {"hash":"687aa30619ec8e7f","prefixes":{"*":10}}, // [eBay] - {"hash":"5ac823af612c4492","prefixes":{"*":10}}, // [eBay] - {"hash":"ce4e70466c43ade3","prefixes":{"*":10}}, // [eBay] - {"hash":"c865cf822ab64a71","prefixes":{"*":10}}, // [eBay] - {"hash":"083c339aac7e418c","prefixes":{"*":10}}, // [eBay] - {"hash":"8d689bf60029c98f","prefixes":{"*":10}}, // [eBay] - {"hash":"19d0e1eba389dfe1","prefixes":{"*":10}}, // [eBay] - {"hash":"b789395149868329","prefixes":{"*":10}}, // [eBay] - {"hash":"25e9f5191f3d2397","prefixes":{"*":10}}, // [eBay] - {"hash":"82a743999425aaad","prefixes":{"*":10}}, // [eBay] - {"hash":"97a729e5b41fd4ed","prefixes":{"*":10}}, // [eBay] - {"hash":"0db7dee9d3c756cc","prefixes":{"*":10}}, // [eBay] - {"hash":"10981271cb66af25","prefixes":{"*":10}}, // [eBay] - {"hash":"8c86bc9ba67482c6","prefixes":{"*":10}}, // [eBay] - {"hash":"d36efad270e3e7c6","prefixes":{"*":10}}, // [eBay] - {"hash":"b709ca3f38e77645","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"3cba207e4aa9d6c6","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"fd8da92d6de2518b","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"d9e4d4444ebd418f","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"4c1c4999d1501943","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"a635762b8125bba5","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"99818fe6fd4c4a9c","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"cae59de4e054a5a8","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"d1ec771fe9489a9f","prefixes":{"":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"d9effad74da2b097","prefixes":{"*":491}}, // [Adap.tv Inc. (AdWords/YouTube)] - {"hash":"46b1fc5fe9da3681","prefixes":{"*":492}}, // [Fjord Technologies S.A.S.] - {"hash":"e0b6d05a8e75e14a","prefixes":{"*":492}}, // [Fjord Technologies S.A.S.] - {"hash":"11a89bdf6bc5cf28","prefixes":{"":493}}, // [Refined Ads] - {"hash":"8e6414939f586d4c","prefixes":{"":493}}, // [Refined Ads] - {"hash":"6462a1b97a382278","prefixes":{"":493}}, // [Refined Ads] - {"hash":"9f99479854cea2e7","prefixes":{"":493}}, // [Refined Ads] - {"hash":"72b40aacd2b4e226","prefixes":{"":493}}, // [Refined Ads] - {"hash":"67401ef133d2ed76","prefixes":{"":494}}, // [nugg.ad AG] - {"hash":"696757eb9e07845f","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"c84660fcf8a4c99c","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"df10c5e0e18fda2b","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"c71dfe726124b24c","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"f603a3b56996fe4c","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"149593d24f74e3e1","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"a1b9a8ff7b829f8f","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"37cb27fb57319c52","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"16e50e7e7b499ab7","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"c10f5df1adb6160f","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"df3ea734f608dc0d","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"3014b8652e4fd340","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"68034e41a59505be","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"4ff550d2f5a384d7","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"266c6cb54cc8f810","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"c174c4c7c469cbb9","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"19b8f26cb5987d86","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"0301de7ebef3ded7","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"5f6ccacecc90b6ab","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"cee43a3e7001e664","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"27d64efa61e9fb8c","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"05eae7c7a976d6b2","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"da375ad9bc86ad05","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"1a62d22c13d4f003","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"65f8018fc9a58ba7","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"c790c87ca2abbb6c","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"5aefca188560242d","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"eaa7adee1317cb7d","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"db5afb1288bacbff","prefixes":{"*":496}}, // [Meetrics GmbH] - {"hash":"7d618e8de39f548c","prefixes":{"*":496}}, // [Meetrics GmbH] - {"hash":"b5c30978f10b1f56","prefixes":{"s":496,"":497}}, // [Meetrics GmbH] [Meetrics] - {"hash":"5c4a10a5bc0897c7","prefixes":{"":498}}, // [Digital Control GmbH & Co. KG] - {"hash":"38d57267aeada6cc","prefixes":{"":498}}, // [Digital Control GmbH & Co. KG] - {"hash":"7dc82de98d234295","prefixes":{"":499}}, // [Dedicated Media] - {"hash":"98cac3ed2db221c5","prefixes":{"":499}}, // [Dedicated Media] - {"hash":"74da617dd78c4c25","prefixes":{"":499}}, // [Dedicated Media] - {"hash":"ad829d4fccaec076","prefixes":{"":499}}, // [Dedicated Media] - {"hash":"93214296470961ea","prefixes":{"":500}}, // [Accuen] - {"hash":"fb8e16c2a3413e04","prefixes":{"*":501}}, // [Pulse 360, Inc.] - {"hash":"56de3497c2b187f9","prefixes":{"*":501}}, // [Pulse 360, Inc.] - {"hash":"1763cb8cd4ecb455","prefixes":{"*":502}}, // [ZANOX AG] - {"hash":"dd420134fd0b31f7","prefixes":{"*":502}}, // [ZANOX AG] - {"hash":"f942b375b4c9d301","prefixes":{"":503}}, // [Webgains Ltd] - {"hash":"03b41741559a5775","prefixes":{"":504}}, // [Virgin Media Limited] - {"hash":"e01702c3ace07a42","prefixes":{"*":505}}, // [MyBuys MyAds] - {"hash":"a09aa03b7ab4a6a0","prefixes":{"*":506}}, // [VCCP Search LTD] - {"hash":"8ae53030752d70fd","prefixes":{"":507}}, // [Conversant Media] - {"hash":"3f911ef887ffe5a7","prefixes":{"":507}}, // [Conversant Media] - {"hash":"dd0195d7ab665db3","prefixes":{"":507}}, // [Conversant Media] - {"hash":"6ac275e8e99e5fb3","prefixes":{"":507}}, // [Conversant Media] - {"hash":"5960f055d64bd6fb","prefixes":{"":507}}, // [Conversant Media] - {"hash":"d07a0c93a2f5da33","prefixes":{"":507}}, // [Conversant Media] - {"hash":"b7e7f7ab4a600ced","prefixes":{"":508}}, // [Up-Value GmbH & Co. KG] - {"hash":"e92a06c68de2a79d","prefixes":{"":509}}, // [Unruly Media] - {"hash":"789659215cb1620d","prefixes":{"":509}}, // [Unruly Media] - {"hash":"b61a48cd46cb7b06","prefixes":{"*":510}}, // [Underdog Media LLC] - {"hash":"eea5fa46bce35e24","prefixes":{"":511}}, // [SVG Media Pvt. Ltd.] - {"hash":"9b346c0d77f1fcf2","prefixes":{"*":512}}, // [TRAFFIQ LLC] - {"hash":"51324f52aa5eb734","prefixes":{"*":513}}, // [TellApart Inc.] - {"hash":"26e3ca8cc9a6afd8","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"2e9a57a3dcc61e3e","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"2e6202e113ae62e3","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"9a1d6d8445e2cce9","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"88015c5982735097","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"9cf10e72a2db48a9","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"cf5ba41c477d8aa5","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"fe50c8a7f665cf78","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"5c9fb160269ccb5c","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"366f4f435bfb9255","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"5dde0b5371aae6bb","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"ebd682e181b553a7","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"df61db1cec60b5a1","prefixes":{"":513}}, // [TellApart Inc.] - {"hash":"bdf8043c482f8afd","prefixes":{"*":514}}, // [Shopzilla Inc.] - {"hash":"83ce758172dc68fb","prefixes":{"*":514}}, // [Shopzilla Inc.] - {"hash":"45cc9e8ea0e405cc","prefixes":{"*":514}}, // [Shopzilla Inc.] - {"hash":"bc8d543772676fe4","prefixes":{"*":514}}, // [Shopzilla Inc.] - {"hash":"6a51a8a28265901e","prefixes":{"*":515}}, // [Reactivpub] - {"hash":"b03903f1abac9b82","prefixes":{"*":515}}, // [Reactivpub] - {"hash":"14281bd870fa9e85","prefixes":{"*":515}}, // [Reactivpub] - {"hash":"52c9dfb471edf4de","prefixes":{"*":515}}, // [Reactivpub] - {"hash":"b9cf807aee694b90","prefixes":{"*":516}}, // [Netseer Inc.] - {"hash":"720c7cdaeb3d3edf","prefixes":{"":516}}, // [Netseer Inc.] - {"hash":"a7e82477ddc48f23","prefixes":{"*":517}}, // [eBay Enterprise] - {"hash":"c35bcff10cd62a44","prefixes":{"":518}}, // [Goodway Group] - {"hash":"a45b6c7292b7b560","prefixes":{"":518}}, // [Goodway Group] - {"hash":"d76efc82324fb8d5","prefixes":{"":518}}, // [Goodway Group] - {"hash":"5eb742c77c59dd05","prefixes":{"":518}}, // [Goodway Group] - {"hash":"33f9a4e4a440eeda","prefixes":{"*":519}}, // [Double Positive Marketing Group Inc.] - {"hash":"285da2e20d7ae651","prefixes":{"*":520}}, // [Chitika Inc.] - {"hash":"9ac26bc76037dd72","prefixes":{"*":521}}, // [Scigineer Inc.] - {"hash":"11971f02aee2996f","prefixes":{"*":522}}, // [Sales Spider Inc.] - {"hash":"0dfb620c1f99f67a","prefixes":{"*":522}}, // [Sales Spider Inc.] - {"hash":"dee182aa38b90802","prefixes":{"":523}}, // [Rocket Fuel Inc.] - {"hash":"ee4bb512aa7bff3f","prefixes":{"":523}}, // [Rocket Fuel Inc.] - {"hash":"051b6a2f5170871a","prefixes":{"":523}}, // [Rocket Fuel Inc.] - {"hash":"ef2266eab416e344","prefixes":{"":523}}, // [Rocket Fuel Inc.] - {"hash":"40701593c87a2a6d","prefixes":{"":523}}, // [Rocket Fuel Inc.] - {"hash":"4dfd82e7fcbfe056","prefixes":{"":523}}, // [Rocket Fuel Inc.] - {"hash":"2848c783f1d69118","prefixes":{"":523}}, // [Rocket Fuel Inc.] - {"hash":"26fd0c0c1bbdf578","prefixes":{"":523}}, // [Rocket Fuel Inc.] - {"hash":"9362d437c6649ac0","prefixes":{"":524}}, // [RTBlab] - {"hash":"83cd554cdc78cc1c","prefixes":{"":524}}, // [RTBlab] - {"hash":"e4e3ec12949d311d","prefixes":{"":524}}, // [RTBlab] - {"hash":"9db675ca1ddbc25e","prefixes":{"":524}}, // [RTBlab] - {"hash":"c407f5eb4837736a","prefixes":{"":524}}, // [RTBlab] - {"hash":"27e52d41a00bf2fd","prefixes":{"":524}}, // [RTBlab] - {"hash":"efa545dd9d577a82","prefixes":{"":525}}, // [abilicom GmbH] - {"hash":"f690d50313c4d883","prefixes":{"":525}}, // [abilicom GmbH] - {"hash":"e59063ed858c1ac6","prefixes":{"":525}}, // [abilicom GmbH] - {"hash":"08ecd0f7d816b029","prefixes":{"":30}}, // [Rakuten Display] - {"hash":"6321d1d2e3ff13cf","prefixes":{"":526}}, // [Pulpo Media Inc] - {"hash":"68778ff49de5f6b7","prefixes":{"":526}}, // [Pulpo Media Inc] - {"hash":"4fe62a295bf7110a","prefixes":{"*":527}}, // [OneSpot] - {"hash":"0a03e9309eac6fef","prefixes":{"*":528}}, // [MyThings UK Ltd] - {"hash":"aefee6cf21fb21d0","prefixes":{"*":528}}, // [MyThings UK Ltd] - {"hash":"013cf49561a61d0a","prefixes":{"":529}}, // [Exactag] - {"hash":"a5db93b0474bf88a","prefixes":{"":529}}, // [Exactag] - {"hash":"8eaf146d58acaea3","prefixes":{"":353}}, // [AdMaster] - {"hash":"62f9a92e2fa55157","prefixes":{"":353}}, // [AdMaster] - {"hash":"2246fe417fb594d4","prefixes":{"":353}}, // [AdMaster] - {"hash":"46be6b3d05c938b5","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"b588a450d30940dc","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"9013d3536e80f01a","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"705829297e5108ae","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"bfd58f70292d02ab","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"ff5875b2546d8499","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"ccc16a9e8656612f","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"a764b52c155c440b","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"a8a184b600fd3af1","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"6c106d279e66ed61","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"7c8663ad744aeec9","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"c3860e5673290a36","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"485e37ef683e6da8","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"03c59eef464ffa9d","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"e8d28d6ae9ed1064","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"1fc896b89972c4a3","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"89d45cf6716d92c4","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"c374b8ef2cc0a6d3","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"e0a60df6ec972762","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"dfd819866072d81c","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"dbaab38c6254fc44","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"25e404b1d7cfa7b3","prefixes":{"":530}}, // [Kyocera Communication Systems Co. Ltd.] - {"hash":"f9b4a878a9f66629","prefixes":{"":531}}, // [Beijing LangTaoJin Interactive] - {"hash":"5be19b5ec9da0217","prefixes":{"":531}}, // [Beijing LangTaoJin Interactive] - {"hash":"c3dd7532c047b725","prefixes":{"":532}}, // [Conversant Mobile Media] - {"hash":"fd89a621a7abcb21","prefixes":{"":532}}, // [Conversant Mobile Media] - {"hash":"8b03c1ce3a37af3e","prefixes":{"":533}}, // [MM1X.nl] - {"hash":"51f16f2963b5ab62","prefixes":{"":533}}, // [MM1X.nl] - {"hash":"a34aa5b1e90b35d0","prefixes":{"":534}}, // [Trivu Media Inc.] - {"hash":"f5fe0a711d905029","prefixes":{"":534}}, // [Trivu Media Inc.] - {"hash":"284614e662fae11d","prefixes":{"*":535}}, // [BlueCava Inc.] - {"hash":"cd9e8f6b8e3bb0bb","prefixes":{"":536}}, // [Beijing Gridsum Technology Co. Ltd.] - {"hash":"97de03485c681efe","prefixes":{"":536}}, // [Beijing Gridsum Technology Co. Ltd.] - {"hash":"6c624568b77c2f15","prefixes":{"":536}}, // [Beijing Gridsum Technology Co. Ltd.] - {"hash":"2b74e65baabdf4a8","prefixes":{"":537}}, // [Adsvana DSP] - {"hash":"fcd2a83347336f39","prefixes":{"":537}}, // [Adsvana DSP] - {"hash":"7cadddba30cbf68b","prefixes":{"":367}}, // [revenue cloud] - {"hash":"39fe4b58f8d0c85a","prefixes":{"":538}}, // [Target Performance] - {"hash":"219259d49fac1ccb","prefixes":{"":539}}, // [Content Spread] - {"hash":"ca8d76b63fdfd7e7","prefixes":{"":367}}, // [revenue cloud] - {"hash":"ed86b10928624d27","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"205f80e614b45d0c","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"88c9d142721a0010","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"c918497df4612dee","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"ecc68ce18bc92eac","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"acd6e7d757515791","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"611fb7a1125a9f9d","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"b47f7e78adfcce8c","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"e7d6867de133fef3","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"022353c9ab0821d7","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"2571a0502f188936","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"f1efca13f787818a","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"1c982ec87cdd98c7","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"ffa62eae8604dfb9","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"8c7ba8485b195fda","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"188fb424b02d01b3","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"92807e8eb5ceed53","prefixes":{"":540}}, // [Datamind Effective Media] - {"hash":"b01a0f9c920b194e","prefixes":{"":541}}, // [ScaleOut Inc.] - {"hash":"da51b7d5383143f5","prefixes":{"":541}}, // [ScaleOut Inc.] - {"hash":"acca27df7ac50ef3","prefixes":{"":541}}, // [ScaleOut Inc.] - {"hash":"8a45863cac015863","prefixes":{"":541}}, // [ScaleOut Inc.] - {"hash":"ae50046a864ceafd","prefixes":{"":541}}, // [ScaleOut Inc.] - {"hash":"de57dfdaf9a2c48a","prefixes":{"":542}}, // [Ensighten] - {"hash":"b1235d3bdac69ad1","prefixes":{"":542}}, // [Ensighten] - {"hash":"b2717d6d54a069e3","prefixes":{"":542}}, // [Ensighten] - {"hash":"d7152b8a7fbdcfd9","prefixes":{"":542}}, // [Ensighten] - {"hash":"66d946b524ae15cb","prefixes":{"":542}}, // [Ensighten] - {"hash":"cb87adb62a805e43","prefixes":{"":542}}, // [Ensighten] - {"hash":"64b417a15cd2caba","prefixes":{"":542}}, // [Ensighten] - {"hash":"fe5fbbb5f87923ec","prefixes":{"":542}}, // [Ensighten] - {"hash":"7955dee10fac2b43","prefixes":{"":543}}, // [Sony Electronics Inc.] - {"hash":"18b842a485714b88","prefixes":{"":543}}, // [Sony Electronics Inc.] - {"hash":"5e881c11a6141a0e","prefixes":{"*":544}}, // [Gravity Research and Development LTD] - {"hash":"eb0586aa7b44d1a7","prefixes":{"":545}}, // [Project SunBlock] - {"hash":"e49a615e7e072e05","prefixes":{"":546}}, // [Novem sp. z o.o.] - {"hash":"c36795d664275022","prefixes":{"*":547}}, // [Econda GmbH] - {"hash":"1dafa12902626628","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"ddc49ed0d5adc099","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"43e36030afe70656","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"f4acd265af4a8b2c","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"2d00af9d31137b46","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"6330d2d69fac5592","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"07924db4fbf62ed8","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"40fa76b0dfe6cba9","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"cb5e5323270dac31","prefixes":{"":548}}, // [AdMaxim LLC] - {"hash":"644395b50223d6f0","prefixes":{"":78}}, // [Adnologies GmbH] - {"hash":"e816f89057c8ad52","prefixes":{"":78}}, // [Adnologies GmbH] - {"hash":"8d2b1cab5958796f","prefixes":{"":549}}, // [Alliance Health Networks] - {"hash":"3568ed34edbd0a3b","prefixes":{"":550}}, // [Adsame Advertising Co., Ltd] - {"hash":"5fd75bdc8d48469a","prefixes":{"":550}}, // [Adsame Advertising Co., Ltd] - {"hash":"f24f098ee46d7e52","prefixes":{"":550}}, // [Adsame Advertising Co., Ltd] - {"hash":"695c924af8b7be90","prefixes":{"":551}}, // [Guangzhou Shunfei Infomation Technology Corporatio] - {"hash":"8c21b1544107f44b","prefixes":{"":552}}, // [PK Online Ventures Limited] - {"hash":"b709e5fa7a0a3284","prefixes":{"":553}}, // [Bigpoint Gmbh] - {"hash":"54393b0479bf4296","prefixes":{"":553}}, // [Bigpoint Gmbh] - {"hash":"0228e75fa4f7ba29","prefixes":{"":553}}, // [Bigpoint Gmbh] - {"hash":"161b02c3430e8493","prefixes":{"":554}}, // [Mirapodo GmbH] - {"hash":"cbfa294f671394f5","prefixes":{"":555}}, // [Digitize New Media Ltd.] - {"hash":"126e76895e35030f","prefixes":{"":556}}, // [AdSage] - {"hash":"d1a3397960a0c962","prefixes":{"":556}}, // [AdSage] - {"hash":"27111f31c7f7870c","prefixes":{"":556}}, // [AdSage] - {"hash":"5066a54cd7ed6242","prefixes":{"":556}}, // [AdSage] - {"hash":"5447e7edfecbefc9","prefixes":{"":556}}, // [AdSage] - {"hash":"763e1c5a534cdce1","prefixes":{"":556}}, // [AdSage] - {"hash":"664f6761d6e1bdc5","prefixes":{"":556}}, // [AdSage] - {"hash":"85dc2dbb9cf8ba01","prefixes":{"":556}}, // [AdSage] - {"hash":"efd9939ccbcea7ca","prefixes":{"":556}}, // [AdSage] - {"hash":"5095b815554d166d","prefixes":{"":556}}, // [AdSage] - {"hash":"bbd5220662a5b37e","prefixes":{"":556}}, // [AdSage] - {"hash":"ad929f7cd3353264","prefixes":{"":556}}, // [AdSage] - {"hash":"aebf7b2a7ee1ff12","prefixes":{"":557}}, // [Fingereach] - {"hash":"01ef28aab34283cc","prefixes":{"":557}}, // [Fingereach] - {"hash":"c075f26f4aa57d43","prefixes":{"":557}}, // [Fingereach] - {"hash":"f651f54211351ca7","prefixes":{"":557}}, // [Fingereach] - {"hash":"98e7e8f01b1e7221","prefixes":{"":557}}, // [Fingereach] - {"hash":"7f46832cdf7b45db","prefixes":{"":557}}, // [Fingereach] - {"hash":"4a37345bf96b65b5","prefixes":{"":557}}, // [Fingereach] - {"hash":"ee9f377ce8711040","prefixes":{"":557}}, // [Fingereach] - {"hash":"f6f6db9a07009087","prefixes":{"":557}}, // [Fingereach] - {"hash":"37414a33ff3de4a0","prefixes":{"":557}}, // [Fingereach] - {"hash":"7cc06f83f0710408","prefixes":{"":557}}, // [Fingereach] - {"hash":"beb1894d6a5dd8d5","prefixes":{"":557}}, // [Fingereach] - {"hash":"ab305fad96c94e68","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"abb33b12777d693c","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"cf57baa1fe60d65d","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"1e230cd699b878e6","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"0e9f493079696ce3","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"7d3350cd22011e53","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"f50b2ec037f5428b","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"c171c26c4996ef48","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"558975de73dc2727","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"ee6b8472f5558865","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"8183f3c56cedc5d3","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"9e46a78131a70a25","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"9805160753b78db8","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"9665241d465c8ced","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"0886098ece10411f","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"1b9aa72e21da1cf9","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"75eaf92a0259fced","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"6647ef959a9060b1","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"55bbd43420c048ba","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"769a3492166f2234","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"88bf787fe78aa617","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"62db61a5802ee5f9","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"ecc8b48b5b165bff","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"3b713a488717f579","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"7d501d2362243528","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"1e2c100fab457dce","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"7adb32ad76289644","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"3f4747021104b435","prefixes":{"":307}}, // [DMA Institute dba Hottraffic] - {"hash":"55ade41cc24f1b54","prefixes":{"":558}}, // [Calvin Klein] - {"hash":"210f025e6c6b8122","prefixes":{"":559}}, // [Medialets Servo] - {"hash":"233555f3daf9abb0","prefixes":{"":559}}, // [Medialets Servo] - {"hash":"eea93545b322651b","prefixes":{"":559}}, // [Medialets Servo] - {"hash":"d9fe49eeaf35eb01","prefixes":{"":559}}, // [Medialets Servo] - {"hash":"ea41698b95338591","prefixes":{"s-cdn-":559}}, // [Medialets Servo] - {"hash":"5f4e90726b181c95","prefixes":{"":560}}, // [Webmoblink Inc.] - {"hash":"9d51efd26ab44317","prefixes":{"":31}}, // [Retailigence] - {"hash":"32440ed26b61d14a","prefixes":{"":31}}, // [Retailigence] - {"hash":"8234d0306d8984eb","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"e35bf94930bc4d1a","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"aec3f56c7f99151f","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"ef395b96d922c238","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"5a053ab1bcfcfb41","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"2cf811bb8aa279b5","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"cd383e750e6cfcdb","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"c30fc77cd822188e","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"fb9678eef7ea227d","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"102facb286a88bbe","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"aa39a58ff4ed2114","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"e33908ca9604cfa4","prefixes":{"*":561,"":562}}, // [Moat Inc.] [Bannerflow AB] - {"hash":"31902ac0cc3e362b","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"796535f10f1e1ea0","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"681a390e8faf08c1","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"1ef04ebc565a6f48","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"1461b89b1a4a5e39","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"a7c34fe979c7afe2","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"30142bb0f24faa5a","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"dc8fdd8b9daae8d5","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"80a2d23602bd08d7","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"e8713f2da85d0fff","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"08fe20173c2a758a","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"105d629b41621e8f","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"c2c8a87c65874f50","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"ecc3d954923aa593","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"313e3d29b4712d5d","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"bf332f1705b05b91","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"c2fac5215807677b","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"b1db7fe55b719fd0","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"c405962725b0a49f","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"f30974f87632ff65","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"c3a0f51c7a475e24","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"470beffefc05bd89","prefixes":{"":561}}, // [Moat Inc.] - {"hash":"44c6bfd7fd3ae379","prefixes":{"*":561}}, // [Moat Inc.] - {"hash":"ed26e1a4862cb993","prefixes":{"":563}}, // [Batch Media Gmbh] - {"hash":"8cc35cd28ab69e7b","prefixes":{"":563}}, // [Batch Media Gmbh] - {"hash":"b480ee3cdb39aba3","prefixes":{"":563}}, // [Batch Media Gmbh] - {"hash":"42b8cf3c1fba5649","prefixes":{"":563}}, // [Batch Media Gmbh] - {"hash":"9c59afa20426083f","prefixes":{"":564}}, // [Twenga] - {"hash":"96fa4f0d4282b74b","prefixes":{"":564}}, // [Twenga] - {"hash":"147ff4cde8f7b63b","prefixes":{"":564}}, // [Twenga] - {"hash":"e6d43f305d0372aa","prefixes":{"":565}}, // [TraceAd] - {"hash":"e2a94bb0b8f09309","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"7b17e990632021fb","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"c82cc94c761aff4f","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"9bb0143d6745a1fc","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"9906e8d23a282ab0","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"c29531bb7c2a5a95","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"53972f8a807a3522","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"d8acabec202014c0","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"4062ceb50584895d","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"776d48fd287f99c8","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"caf4c25a1be2f9cc","prefixes":{"":566}}, // [Speed Shift Media] - {"hash":"ad66fb580dfc0f61","prefixes":{"*":567}}, // [CCB Paris] - {"hash":"2cd1c0997e13f343","prefixes":{"":568}}, // [Nielsen Digital Ad Ratings] - {"hash":"cc67f7e32922d026","prefixes":{"*":569}}, // [OpenDSP] - {"hash":"37ac881f55f6b624","prefixes":{"":551}}, // [Guangzhou Shunfei Infomation Technology Corporatio] - {"hash":"6eaeb2fb0379f719","prefixes":{"":551}}, // [Guangzhou Shunfei Infomation Technology Corporatio] - {"hash":"0f37e190c3f7659c","prefixes":{"":551}}, // [Guangzhou Shunfei Infomation Technology Corporatio] - {"hash":"cd5e1f475a9a9cb9","prefixes":{"":551}}, // [Guangzhou Shunfei Infomation Technology Corporatio] - {"hash":"70dae8af3c6cb2af","prefixes":{"":551}}, // [Guangzhou Shunfei Infomation Technology Corporatio] - {"hash":"60e90d920044304d","prefixes":{"":551}}, // [Guangzhou Shunfei Infomation Technology Corporatio] - {"hash":"9d34d5e680004543","prefixes":{"":551}}, // [Guangzhou Shunfei Infomation Technology Corporatio] - {"hash":"b4b546c24d350673","prefixes":{"":570}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] - {"hash":"d0b7581f4a3ff9b8","prefixes":{"":570}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] - {"hash":"0c865d217e1f18f3","prefixes":{"":570}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] - {"hash":"6333a18d75125edb","prefixes":{"":570}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] - {"hash":"ed4cdc36d8829848","prefixes":{"":570}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] - {"hash":"3643c909d7cbe0b8","prefixes":{"":570}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] - {"hash":"296c21a7001df348","prefixes":{"":570}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] - {"hash":"97079bad5fb6ead8","prefixes":{"":570}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] - {"hash":"7b936711dd1e9a43","prefixes":{"":571}}, // [Mediarithmics] - {"hash":"c1d09d8bf99adaae","prefixes":{"":572}}, // [RUN, INC.] - {"hash":"c1a2559db78be890","prefixes":{"":572}}, // [RUN, INC.] - {"hash":"99a2370c6cb03aad","prefixes":{"":572}}, // [RUN, INC.] - {"hash":"58188c996594e74c","prefixes":{"":573}}, // [Shanghai Menlo Network Technologies Co.,Ltd] - {"hash":"ae01937a71f3c8ec","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"bb20b492d8e895ee","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"a174c7ffc06f1430","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"b04d26682765cf0f","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"3d8976d50a72415a","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"0b937695e283332d","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"5a633d8ed4134176","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"a3d5abb1d31313bb","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"651fcbfafdaa1c3b","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"210045798925d1ec","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"cb9c94ed4430c0d7","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"eb42b6b78e25a31e","prefixes":{"":574}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] - {"hash":"e21aeaa35d9e4534","prefixes":{"*":575}}, // [NET-Metrix-Audit] - {"hash":"d563d7cb84148f5b","prefixes":{"":576}}, // [New Allyes Information Technology (Quantone)] - {"hash":"881b6320fa6c5a13","prefixes":{"":576}}, // [New Allyes Information Technology (Quantone)] - {"hash":"648b287843959e5a","prefixes":{"":576}}, // [New Allyes Information Technology (Quantone)] - {"hash":"aa60743a7bfcd1af","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"afa7ee2a02f86074","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"3b21af5a3fa47754","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"e8f690279ec23223","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"8763304c40959465","prefixes":{"":576}}, // [New Allyes Information Technology (Quantone)] - {"hash":"97be7bbbd0aa71f1","prefixes":{"":576}}, // [New Allyes Information Technology (Quantone)] - {"hash":"1610ab8331e9fa67","prefixes":{"":576}}, // [New Allyes Information Technology (Quantone)] - {"hash":"5cf6c3a758fb5a50","prefixes":{"":576}}, // [New Allyes Information Technology (Quantone)] - {"hash":"753372717104cecb","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"9868ccc3384ca704","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"834fc9317bd55432","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"77a58434eda65f1d","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"d6c8f549569824c7","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"605f200bf3a42aec","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"24475a888daf195d","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"577626a5c9b3a87c","prefixes":{"":577}}, // [Resonate Networks, Inc] - {"hash":"c258f6da0cd8b8cb","prefixes":{"":577}}, // [Resonate Networks, Inc] - {"hash":"d9483fc3fe68e764","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"7caa53e1a6ec2bae","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"dcd74656fa3b323d","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"10fa95c5f15310d2","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"6f83daf020ab465f","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"d3d1a09a28024ddc","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"9b208cc58454fb78","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"4645c6078dc34d50","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"b238cbc325c6ef22","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"49deee4d32200263","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"773d9411487a25e3","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"0f6cf491f7eb2baa","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"8dfcd3664fa1302b","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"800bb9e7968e58aa","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"b05b363e5a0511e8","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"20c70723848457f0","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"66114e4ff94b609f","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"f2d188b48acaeb7f","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"409d1c85462adead","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"4d380a7d15d0a78a","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"d5449f5767bfafa7","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"bd39dc0af22a3152","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"f86c1368c7c5eb92","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"e8f74e435e9df1dd","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"9b5fa6e2f606ce7d","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"9a9e03501130b17a","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"2a12549f5c240468","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"215711257cc47c2c","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"77f3f7c79d95de48","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"6d31870e0469ddc6","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"d43e8e7f0b7db722","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"cf4d5bbc8b02dc1c","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"fa69e0b93ab494f2","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"5df810b59c15eb88","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"1bad3858e6b1db68","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"1f0663d5a32c3b7d","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"0accadb2b8868065","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"29fa86d0b3c09f26","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"6c3481a4787dc989","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"dd2cdf0ae459961c","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"b7ddbc93720778f5","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"65db5bf588a1552c","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"d63dadf02badb9bc","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"12a0ac899815d56b","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"4e73ffb09594e8a6","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"7bd28c9beb85782f","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"e5a3c52df181ad93","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"5db249180904a1af","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"f33943be4b14e2cc","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"3304828c6a87914b","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"0dee81e461b3c8bb","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"23282a1244ed862f","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"8b1d859efda195f4","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"e22129fd14c58ef7","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"c11b0627d1105ed8","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"fee7a2b84c23b6bb","prefixes":{"":11}}, // [advanced STORE GmbH] - {"hash":"3f3d4a96d97bc12a","prefixes":{"":578}}, // [Triple Lift, Inc.] - {"hash":"7e4d08be26af0913","prefixes":{"":578}}, // [Triple Lift, Inc.] - {"hash":"ab1718aa8ada6ceb","prefixes":{"":578}}, // [Triple Lift, Inc.] - {"hash":"00810432ca3a5adf","prefixes":{"":578}}, // [Triple Lift, Inc.] - {"hash":"e597ba5088abb36a","prefixes":{"":578}}, // [Triple Lift, Inc.] - {"hash":"d206640e7ca174cd","prefixes":{"":579}}, // [Indie Ads] - {"hash":"d8ed39f1e603b870","prefixes":{"":580}}, // [Ocean Park Interactive] - {"hash":"a241e76a04cef012","prefixes":{"":580}}, // [Ocean Park Interactive] - {"hash":"dcc0a9d204aaa55d","prefixes":{"":580}}, // [Ocean Park Interactive] - {"hash":"8372761b0392f8a9","prefixes":{"":580}}, // [Ocean Park Interactive] - {"hash":"e7b8261c49b1fbf6","prefixes":{"":580}}, // [Ocean Park Interactive] - {"hash":"d5094d4667f378a7","prefixes":{"":580}}, // [Ocean Park Interactive] - {"hash":"4091c3043c2da68a","prefixes":{"":581}}, // [Impulse Media Pvt. Ltd.] - {"hash":"6eb5dacfebab8b02","prefixes":{"":582}}, // [CrowdTwist] - {"hash":"123db0626c48324c","prefixes":{"":582}}, // [CrowdTwist] - {"hash":"4f356e0d187085b0","prefixes":{"":583}}, // [Adzerk Inc.] - {"hash":"79c1600f16f0c0ad","prefixes":{"":583}}, // [Adzerk Inc.] - {"hash":"170c4bddaa53e87c","prefixes":{"":584}}, // [Adtarget.me] - {"hash":"b514da98fbd70da7","prefixes":{"":584}}, // [Adtarget.me] - {"hash":"e585ef712f906ce4","prefixes":{"":584}}, // [Adtarget.me] - {"hash":"ed5e6e621b9e9e9e","prefixes":{"":584}}, // [Adtarget.me] - {"hash":"904a8c71f0ac4b5c","prefixes":{"":584}}, // [Adtarget.me] - {"hash":"138d782ff8ef8b3d","prefixes":{"":585}}, // [Recruit Marketing Partners Co.,Ltd] - {"hash":"8899c2499d762095","prefixes":{"":586}}, // [Beijing Emar Online Technology Co.,Ltd] - {"hash":"29f87adefe40afd5","prefixes":{"":586}}, // [Beijing Emar Online Technology Co.,Ltd] - {"hash":"1f57d0a967ca4f30","prefixes":{"":586}}, // [Beijing Emar Online Technology Co.,Ltd] - {"hash":"378c4fbbd1d161e2","prefixes":{"":587}}, // [AdMagnet] - {"hash":"bb42bdad4318ee55","prefixes":{"":587}}, // [AdMagnet] - {"hash":"71d1cec71f53e140","prefixes":{"":587}}, // [AdMagnet] - {"hash":"e3bad45f1f3d21a5","prefixes":{"":587}}, // [AdMagnet] - {"hash":"aa63c60aa73e9d29","prefixes":{"":588}}, // [Momondo A/S] - {"hash":"b34444583646725a","prefixes":{"*":588}}, // [Momondo A/S] - {"hash":"cbf8a85fbf4c22e5","prefixes":{"*":589}}, // [DKK Agency] - {"hash":"e775e327a7a42402","prefixes":{"":590}}, // [OneScreen Inc.] - {"hash":"080fcbb984e6ecdd","prefixes":{"":590}}, // [OneScreen Inc.] - {"hash":"8c1ae0a34a3df5d0","prefixes":{"":590}}, // [OneScreen Inc.] - {"hash":"ba491e57276128df","prefixes":{"":591}}, // [AdElement Media Solutions Pvt Ltd] - {"hash":"620a368e66b0d054","prefixes":{"":591}}, // [AdElement Media Solutions Pvt Ltd] - {"hash":"f100a71a63d2ccfa","prefixes":{"":591}}, // [AdElement Media Solutions Pvt Ltd] - {"hash":"636877d8cc1827f4","prefixes":{"":592}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] - {"hash":"e066625a70a1d5c0","prefixes":{"":592}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] - {"hash":"117aaf00ef5036e3","prefixes":{"":592}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] - {"hash":"4f795b8396901621","prefixes":{"":592}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] - {"hash":"b8da1e9feb0bc01d","prefixes":{"":592}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] - {"hash":"433e8fe3ca1684d7","prefixes":{"":592}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] - {"hash":"f2fc51beddbeb4a9","prefixes":{"":593}}, // [Adsit Media Advertising Ltd. Co., (AdMan)] - {"hash":"ba7f680031ef6ba6","prefixes":{"":594}}, // [AdMan] - {"hash":"cba16dd5f4409e30","prefixes":{"":594}}, // [AdMan] - {"hash":"3e32b1ab3e9c66b6","prefixes":{"":593}}, // [Adsit Media Advertising Ltd. Co., (AdMan)] - {"hash":"59225b3e2dce506e","prefixes":{"":595}}, // [MicroAd Inc. (China)] - {"hash":"8a3b315693f80801","prefixes":{"":595}}, // [MicroAd Inc. (China)] - {"hash":"9ecae05881ad91ad","prefixes":{"":595}}, // [MicroAd Inc. (China)] - {"hash":"86a1bc63ec059f25","prefixes":{"":596}}, // [Adfonic] - {"hash":"ea28de00a6512e88","prefixes":{"":596}}, // [Adfonic] - {"hash":"42bb8749687dee83","prefixes":{"":597}}, // [OwnerIQ Inc.] - {"hash":"125539a4b58c9087","prefixes":{"":597}}, // [OwnerIQ Inc.] - {"hash":"895ef87fbdd546df","prefixes":{"":598}}, // [Mobile Space Ltd] - {"hash":"55e398544c3aeb40","prefixes":{"":598}}, // [Mobile Space Ltd] - {"hash":"c5e19d61efa129d7","prefixes":{"ap":598,"e":598,"":599}}, // [Mobile Space Ltd] [Mobile Space Ltd] [Jaduda GmbH] - {"hash":"3668b933b223ee50","prefixes":{"":600}}, // [Omotenashi Banner] - {"hash":"209fb323c4db09ef","prefixes":{"":600}}, // [Omotenashi Banner] - {"hash":"d95ee45ed75216b1","prefixes":{"":600}}, // [Omotenashi Banner] - {"hash":"7fb039cd298570d4","prefixes":{"":600}}, // [Omotenashi Banner] - {"hash":"a7d0a34772320297","prefixes":{"":600}}, // [Omotenashi Banner] - {"hash":"a86ff9f748f6a5ab","prefixes":{"":600}}, // [Omotenashi Banner] - {"hash":"046d69c93f0ab29e","prefixes":{"":601}}, // [Infectious Media Ltd.] - {"hash":"735a86882c4fccc6","prefixes":{"":602}}, // [One97 Communications Limited (Ad Works)] - {"hash":"c454c081e737b7e2","prefixes":{"":602}}, // [One97 Communications Limited (Ad Works)] - {"hash":"1e8ed560dc22bbb5","prefixes":{"":602}}, // [One97 Communications Limited (Ad Works)] - {"hash":"3a57d7982ec36d46","prefixes":{"":602}}, // [One97 Communications Limited (Ad Works)] - {"hash":"282d5dfa72aee67e","prefixes":{"":603}}, // [Walk Light Media Inc.] - {"hash":"350189fcd66d737d","prefixes":{"":604}}, // [AdBrite Inc.] - {"hash":"727c2c9dbde23217","prefixes":{"":604}}, // [AdBrite Inc.] - {"hash":"f0011932876966b7","prefixes":{"":605}}, // [Hi-Media] - {"hash":"49a154fb3339d1c9","prefixes":{"":605}}, // [Hi-Media] - {"hash":"bc9403b31553c2c3","prefixes":{"":605}}, // [Hi-Media] - {"hash":"4a763539fb83a309","prefixes":{"":605}}, // [Hi-Media] - {"hash":"ad517dcc393c2311","prefixes":{"":605}}, // [Hi-Media] - {"hash":"ea321c60d183257c","prefixes":{"":606}}, // [Adlabs] - {"hash":"44405675d1af2241","prefixes":{"":606}}, // [Adlabs] - {"hash":"703f39c13b597e32","prefixes":{"":606}}, // [Adlabs] - {"hash":"5bdae611f2bbe659","prefixes":{"":606}}, // [Adlabs] - {"hash":"87862d3ee53095a7","prefixes":{"":606}}, // [Adlabs] - {"hash":"376d93acf5e8d717","prefixes":{"":606}}, // [Adlabs] - {"hash":"3f0e8ced0049c1d6","prefixes":{"":606}}, // [Adlabs] - {"hash":"a04ea3bd56ab64bb","prefixes":{"":607}}, // [Arth Salutions] - {"hash":"b6ce2c8ae8e9a415","prefixes":{"":608}}, // [Konverta] - {"hash":"ebd466a03db9081b","prefixes":{"":608}}, // [Konverta] - {"hash":"2df213fa126d1ca4","prefixes":{"":608}}, // [Konverta] - {"hash":"901ec1b8062c3af6","prefixes":{"":608}}, // [Konverta] - {"hash":"3cdfbabfa37b6bd8","prefixes":{"":608}}, // [Konverta] - {"hash":"4a23a2c45d1e92c3","prefixes":{"":608}}, // [Konverta] - {"hash":"72784278d856920c","prefixes":{"":608}}, // [Konverta] - {"hash":"61aa8fff3eb4e893","prefixes":{"":103}}, // [Cogo Labs, Inc.] - {"hash":"def94469bdc7241e","prefixes":{"":103}}, // [Cogo Labs, Inc.] - {"hash":"f17d17eabacc3756","prefixes":{"":103}}, // [Cogo Labs, Inc.] - {"hash":"8644dbe4c2172593","prefixes":{"":609}}, // [Shopall] - {"hash":"4b55c6a7d9c2a9a2","prefixes":{"":609}}, // [Shopall] - {"hash":"305615cf8dbc9be2","prefixes":{"":610}}, // [targeting360 GmbH] - {"hash":"bed5b722b1bc1ba5","prefixes":{"":611}}, // [xplosion interactive GmbH] - {"hash":"1528f8323f342e8b","prefixes":{"":611}}, // [xplosion interactive GmbH] - {"hash":"3c0f20ff410483fa","prefixes":{"":611}}, // [xplosion interactive GmbH] - {"hash":"929748002c9b8ecf","prefixes":{"":611}}, // [xplosion interactive GmbH] - {"hash":"da8d84b561cc6dae","prefixes":{"":611}}, // [xplosion interactive GmbH] - {"hash":"4d73c36d7413bcac","prefixes":{"":611}}, // [xplosion interactive GmbH] - {"hash":"fa03c22fb3892bf3","prefixes":{"":611}}, // [xplosion interactive GmbH] - {"hash":"9671a9ac26a91d0a","prefixes":{"":611}}, // [xplosion interactive GmbH] - {"hash":"ca13edee3fffee4e","prefixes":{"":612}}, // [Hubrus LLC] - {"hash":"e6c8579483f7b40c","prefixes":{"":612}}, // [Hubrus LLC] - {"hash":"5426e12fbea3fce8","prefixes":{"":612}}, // [Hubrus LLC] - {"hash":"e1b8d2118c9dc529","prefixes":{"":613}}, // [Here Media Inc.] - {"hash":"6ab38da002eadbc3","prefixes":{"":614}}, // [Silver Egg Technology Co., Ltd.] - {"hash":"8ef09da008fcfae7","prefixes":{"":614}}, // [Silver Egg Technology Co., Ltd.] - {"hash":"f09f918279f1e9ac","prefixes":{"":614}}, // [Silver Egg Technology Co., Ltd.] - {"hash":"ddfd965ee920e41c","prefixes":{"":614}}, // [Silver Egg Technology Co., Ltd.] - {"hash":"47758f4c45a57ea4","prefixes":{"":614}}, // [Silver Egg Technology Co., Ltd.] - {"hash":"9b402085270b3ed3","prefixes":{"":614}}, // [Silver Egg Technology Co., Ltd.] - {"hash":"5a4e20d5da4d99f0","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"1a32d33420507504","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"c841d6764f10305d","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"c0be785071fbf1f8","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"a48b68d5e7caa5da","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"93764efbaea60a26","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"2d1e1dbc88160b0a","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"49c9a4898d7eda62","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"51f27880010fd487","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"1930e597dd2d66f9","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"9138d5d361766aa2","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"942211eb94fc3c91","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"ac957255586d114c","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"1174020d0986b7c7","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"1386eff7c87966ca","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"d3c3497dc1813ac6","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"ec8bb52d56c92778","prefixes":{"":615}}, // [QuarticOn.com] - {"hash":"b999321725f081a5","prefixes":{"":616}}, // [Kavanga LLC] - {"hash":"43f2f88cac5c1e66","prefixes":{"":617}}, // [Vodafone D2 GmbH] - {"hash":"71f43d2f0436d908","prefixes":{"":617}}, // [Vodafone D2 GmbH] - {"hash":"4b13b8a28b6556d3","prefixes":{"":617}}, // [Vodafone D2 GmbH] - {"hash":"b9c154f4d363eebc","prefixes":{"":618}}, // [Qubit Digital Ltd] - {"hash":"f3c32c6c7e1ac882","prefixes":{"":538,"ad":619,"tm":619}}, // [Target Performance] [NEORY GmbH] [NEORY GmbH] - {"hash":"728524adefe77058","prefixes":{"static-":620}}, // [Populis Ireland Limited] - {"hash":"7f78aff31bba38ad","prefixes":{"":620}}, // [Populis Ireland Limited] - {"hash":"cacb07348e9e3ca3","prefixes":{"":620}}, // [Populis Ireland Limited] - {"hash":"ce069e74d10aa948","prefixes":{"":621}}, // [LiquidM Technology GmbH] - {"hash":"b6284e5bbeacee78","prefixes":{"":622}}, // [Celtra Inc.] - {"hash":"f71102b0f2425eb8","prefixes":{"":622}}, // [Celtra Inc.] - {"hash":"da76d0a397ab42fc","prefixes":{"":622}}, // [Celtra Inc.] - {"hash":"d30b0fd7cd3dc387","prefixes":{"":622}}, // [Celtra Inc.] - {"hash":"d3643510dc9a55a3","prefixes":{"":622}}, // [Celtra Inc.] - {"hash":"f74f9b96eb974ad7","prefixes":{"":622}}, // [Celtra Inc.] - {"hash":"538d09650aaff88b","prefixes":{"":622}}, // [Celtra Inc.] - {"hash":"7533124cd83c8651","prefixes":{"":622}}, // [Celtra Inc.] - {"hash":"8413a3afa447ddd6","prefixes":{"":623}}, // [Adfox] - {"hash":"28b1ebc0a8b51b58","prefixes":{"":623}}, // [Adfox] - {"hash":"0c6cea5dd49d0361","prefixes":{"sd":623,"rt":623}}, // [Adfox] [Adfox] - {"hash":"2e3df7d60b00392d","prefixes":{"":623}}, // [Adfox] - {"hash":"4cdee950d7417a19","prefixes":{"":623}}, // [Adfox] - {"hash":"2060d81c3f4dbace","prefixes":{"":624}}, // [Accordant Media LLC] - {"hash":"f2c07b1834365ee5","prefixes":{"":625}}, // [Fiksu DSP] - {"hash":"c61068cc57f0f219","prefixes":{"":625}}, // [Fiksu DSP] - {"hash":"a1f4b4a83add949d","prefixes":{"":625}}, // [Fiksu DSP] - {"hash":"70b9063aeb27d88a","prefixes":{"":625}}, // [Fiksu DSP] - {"hash":"f66693ef14e4cd79","prefixes":{"":625}}, // [Fiksu DSP] - {"hash":"7f9c748aa884dba0","prefixes":{"":626}}, // [MdotM, Inc.] - {"hash":"13c8f5f12f3b9ca6","prefixes":{"":626}}, // [MdotM, Inc.] - {"hash":"c47f6172a03d7b4c","prefixes":{"":627}}, // [TNS GALLUP ADFACT, ZAO] - {"hash":"f9a74c430e157e57","prefixes":{"":627}}, // [TNS GALLUP ADFACT, ZAO] - {"hash":"534ec1b9f199f18a","prefixes":{"":628}}, // [MicroAd Inc. (APAC)] - {"hash":"98bec02f53970649","prefixes":{"":628}}, // [MicroAd Inc. (APAC)] - {"hash":"dab5b2f5150fe52c","prefixes":{"*":629}}, // [ECRITEL SARL] - {"hash":"039caee3c590ef51","prefixes":{"*":629}}, // [ECRITEL SARL] - {"hash":"56d4c8e77cf378ca","prefixes":{"*":629}}, // [ECRITEL SARL] - {"hash":"ea054eef4a7b535d","prefixes":{"*":629}}, // [ECRITEL SARL] - {"hash":"503f3e019cf902f7","prefixes":{"":104}}, // [AudienceProject] - {"hash":"d07ac29d5418a5a2","prefixes":{"":104}}, // [AudienceProject] - {"hash":"252a5f53fef3adef","prefixes":{"":630}}, // [Gloto Corp.] - {"hash":"c45cf16c98289ef3","prefixes":{"":630}}, // [Gloto Corp.] - {"hash":"43869a9fbc1e93ac","prefixes":{"":631}}, // [OOO GPM-Digital] - {"hash":"4334255596024dac","prefixes":{"*":632}}, // [adverserve digital advertising services] - {"hash":"c1cbc8c1a131b486","prefixes":{"":633}}, // [Abstract] - {"hash":"f0a24e1beb2ff006","prefixes":{"":633}}, // [Abstract] - {"hash":"5d7848291e6b2aac","prefixes":{"":633}}, // [Abstract] - {"hash":"678875d741d45390","prefixes":{"":634}}, // [Adscale GmbH] - {"hash":"bdf162f8a955792f","prefixes":{"":634}}, // [Adscale GmbH] - {"hash":"e2aa93ef150b6625","prefixes":{"":634}}, // [Adscale GmbH] - {"hash":"fbecc41bd388e9da","prefixes":{"":634}}, // [Adscale GmbH] - {"hash":"33d677c1746e032b","prefixes":{"":634}}, // [Adscale GmbH] - {"hash":"7b632e1d26173d9a","prefixes":{"":635}}, // [Weebly, Inc.] - {"hash":"d60691434c15d686","prefixes":{"":636}}, // [KPI Solutions Co.,Ltd.] - {"hash":"6bcb40c428dea82d","prefixes":{"*":636}}, // [KPI Solutions Co.,Ltd.] - {"hash":"a7261ab41534a5a9","prefixes":{"":637}}, // [LuckyBrand.com] - {"hash":"8ade692ce59ddf24","prefixes":{"":638}}, // [Bedrijvenweb.nl] - {"hash":"4f925a01c20725e2","prefixes":{"":639}}, // [CacheFly] - {"hash":"bd7deac394b9d8ec","prefixes":{"*":640}}, // [EdgeCast Networks Inc.] - {"hash":"d14784769a5e3e32","prefixes":{"*":640}}, // [EdgeCast Networks Inc.] - {"hash":"eeb0b9405b4e70a9","prefixes":{"*":640}}, // [EdgeCast Networks Inc.] - {"hash":"799a922119603cf7","prefixes":{"":641}}, // [AdFrontier] - {"hash":"b99f16bd08f23e1d","prefixes":{"":642}}, // [NFQ] - {"hash":"55b741a8d993b7af","prefixes":{"":642}}, // [NFQ] - {"hash":"7733707370fb05c7","prefixes":{"":642}}, // [NFQ] - {"hash":"9a42cb5421abffa9","prefixes":{"":643}}, // [DataLogix, Inc.] - {"hash":"47ee3c7f6cf3211f","prefixes":{"":643}}, // [DataLogix, Inc.] - {"hash":"5e4b5dd08e9b52fd","prefixes":{"":643}}, // [DataLogix, Inc.] - {"hash":"6bc3a667cd012222","prefixes":{"":643}}, // [DataLogix, Inc.] - {"hash":"18656382f2827e60","prefixes":{"":643}}, // [DataLogix, Inc.] - {"hash":"c73db0ed241d269e","prefixes":{"":643}}, // [DataLogix, Inc.] - {"hash":"bc0f02a06b74b7a3","prefixes":{"":643}}, // [DataLogix, Inc.] - {"hash":"0405bea2af551e19","prefixes":{"":643}}, // [DataLogix, Inc.] - {"hash":"c35511ecd49cebb7","prefixes":{"":644}}, // [Brightcove] - {"hash":"22bfe38898977ed4","prefixes":{"":644}}, // [Brightcove] - {"hash":"c095755780478f7e","prefixes":{"":645}}, // [Experian] - {"hash":"0f2014a09acf7abd","prefixes":{"*":646}}, // [Facebook Connect] - {"hash":"b7c70898d90f5bb3","prefixes":{"*":646}}, // [Facebook Connect] - {"hash":"18562cb1149fda1e","prefixes":{"*":647}}, // [Disqus] - {"hash":"37b749b8858eb61b","prefixes":{"*":648}}, // [Namecheap.com] - {"hash":"3d59be0f5f7101a0","prefixes":{"":649}}, // [Polldaddy] - {"hash":"510cff549d2cb397","prefixes":{"":650}}, // [Outbrain Inc.] - {"hash":"e23ebc9959ce4873","prefixes":{"":650}}, // [Outbrain Inc.] - {"hash":"623fd0210b11d4fb","prefixes":{"":651}}, // [Lijit Networks, Inc.] - {"hash":"686b7accbfa398fa","prefixes":{"":651}}, // [Lijit Networks, Inc.] - {"hash":"85c44f0602598dd8","prefixes":{"":651}}, // [Lijit Networks, Inc.] - {"hash":"a107b1fdcffae5b9","prefixes":{"*":484}}, // [NetDNA, LLC] - {"hash":"7f37823a3f5c76b3","prefixes":{"*":652}}, // [PubMatic] - {"hash":"6d306315a1fecfda","prefixes":{"":653}}, // [Say Media Inc.] - {"hash":"c2568516b08f0d3d","prefixes":{"":653}}, // [Say Media Inc.] - {"hash":"308d3a8d1ca52ff3","prefixes":{"":653}}, // [Say Media Inc.] - {"hash":"59e488c5f151e7c3","prefixes":{"":653}}, // [Say Media Inc.] - {"hash":"7efe6908db5a1d44","prefixes":{"":654}}, // [Rubicon Project Turing, Inc. (SSP)] - {"hash":"c6ee8faaa7d6734c","prefixes":{"":655}}, // [Rubicon Project Edison, Inc. (DSP)] - {"hash":"d93c07cc68e53d91","prefixes":{"":655}}, // [Rubicon Project Edison, Inc. (DSP)] - {"hash":"86ae2b216def9790","prefixes":{"":655}}, // [Rubicon Project Edison, Inc. (DSP)] - {"hash":"1267f5857549a6ab","prefixes":{"":655}}, // [Rubicon Project Edison, Inc. (DSP)] - {"hash":"367db80c0776f6d8","prefixes":{"":655}}, // [Rubicon Project Edison, Inc. (DSP)] - {"hash":"c4c762a7e666da1c","prefixes":{"":655}}, // [Rubicon Project Edison, Inc. (DSP)] - {"hash":"bb66261f843823d5","prefixes":{"":656}}, // [Wordpress Stats] - {"hash":"a0f3bb8dcd67010e","prefixes":{"":656}}, // [Wordpress Stats] - {"hash":"4a423f1da960eda6","prefixes":{"*":657}}, // [Twitter] - {"hash":"465806fbb3547c25","prefixes":{"*":657}}, // [Twitter] - {"hash":"13c55ef8102cbdbb","prefixes":{"":658}}, // [Yandex LLC] - {"hash":"db546baba3acb079","prefixes":{"":658}}, // [Yandex LLC] - {"hash":"3dd0667dbad0af61","prefixes":{"":658}}, // [Yandex LLC] - {"hash":"78e224c91aabe6de","prefixes":{"":659}}, // [Rutarget / Segmento] - {"hash":"75acb8a5a60ef63d","prefixes":{"":659}}, // [Rutarget / Segmento] - {"hash":"6f7720a054c19a2b","prefixes":{"":659}}, // [Rutarget / Segmento] - {"hash":"dbd76c26579bf2b1","prefixes":{"":659}}, // [Rutarget / Segmento] - {"hash":"2cd7f311595e164e","prefixes":{"":659}}, // [Rutarget / Segmento] - {"hash":"b08380cf2fcb4415","prefixes":{"":659}}, // [Rutarget / Segmento] - {"hash":"59ac14277edec497","prefixes":{"":659}}, // [Rutarget / Segmento] - {"hash":"88893eeb26e546a0","prefixes":{"":659}}, // [Rutarget / Segmento] - {"hash":"da151dbe8b815dfb","prefixes":{"":660}}, // [Addoor LatinMarkets, SL] - {"hash":"b2a44f920e268749","prefixes":{"":661}}, // [Kate Spade] - {"hash":"96a8c3ab1740bf2f","prefixes":{"":97}}, // [Tacoda] - {"hash":"95e85e3cbd858779","prefixes":{"":662}}, // [TARGUSinfo] - {"hash":"dbf2963c9bf26f55","prefixes":{"":663}}, // [Adblade] - {"hash":"8064758f6c80afed","prefixes":{"":663}}, // [Adblade] - {"hash":"b7aaa083e4151ca8","prefixes":{"":663}}, // [Adblade] - {"hash":"f4d5f13eb7d1ba2b","prefixes":{"":663}}, // [Adblade] - {"hash":"eca68f2e6cd8a07e","prefixes":{"":664}}, // [Adiant] - {"hash":"30f4c3f682592add","prefixes":{"":665}}, // [AddToAny] - {"hash":"39b76d24e28075d4","prefixes":{"*":666}}, // [Bizo Inc] - {"hash":"dabe4d73219c06e0","prefixes":{"":667}}, // [Google Analytics] - {"hash":"f8c6758a214299be","prefixes":{"":668}}, // [Gravatar] - {"hash":"0a130612d187bc06","prefixes":{"":668}}, // [Gravatar] - {"hash":"6802f0145385df51","prefixes":{"":669}}, // [MediaGlu] - {"hash":"ccbeaf028d3c721b","prefixes":{"":669}}, // [MediaGlu] - {"hash":"1d7daeed381c33aa","prefixes":{"":669}}, // [MediaGlu] - {"hash":"038b4d8cab2d2a58","prefixes":{"":669}}, // [MediaGlu] - {"hash":"bd4919274e19987e","prefixes":{"":669}}, // [MediaGlu] - {"hash":"dcdf95e5abf7c100","prefixes":{"":670}}, // [Tapstream Network Inc.] - {"hash":"1f751fd80b11ad3c","prefixes":{"":671}}, // [HUNT Mobile Ads] - {"hash":"de1c8591006ce969","prefixes":{"":672}}, // [Apsalar, Inc.] - {"hash":"9fd29903977b5176","prefixes":{"":672}}, // [Apsalar, Inc.] - {"hash":"c3930fc2f4cd70df","prefixes":{"*":673}}, // [Videology DSP] - {"hash":"8924b56175f6f114","prefixes":{"":674}}, // [Videostrip] - {"hash":"f4b2d76af9987952","prefixes":{"":675}}, // [Affinity – Hostway Corporation] - {"hash":"b11ec5ee5b26491a","prefixes":{"*":241,"":676}}, // [Scene Stealer Ltd.] [Rackspace, US Inc.] - {"hash":"5177084498d58bac","prefixes":{"":676}}, // [Rackspace, US Inc.] - {"hash":"3a0fe0aeaa847996","prefixes":{"*":676}}, // [Rackspace, US Inc.] - {"hash":"2b344a3d6c766cb7","prefixes":{"":676}}, // [Rackspace, US Inc.] - {"hash":"a0b6774e583d1787","prefixes":{"":677}}, // [Taptica] - {"hash":"e6ad999a7fc77500","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"4056609d04bfec9c","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"d1f59501d08f217a","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"bd467d941e65225d","prefixes":{"":241}}, // [Scene Stealer Ltd.] - {"hash":"0de83b2d387d2a84","prefixes":{"*":241}}, // [Scene Stealer Ltd.] - {"hash":"227a1699196d5009","prefixes":{"":678}}, // [LiveRamp, Inc.] - {"hash":"2077744d64232ddc","prefixes":{"":678}}, // [LiveRamp, Inc.] - {"hash":"5ff42e5327d8c926","prefixes":{"":679}}, // [Plista GmbH] - {"hash":"1d36941de87ee056","prefixes":{"":679}}, // [Plista GmbH] - {"hash":"0534ec41ce34cced","prefixes":{"":679}}, // [Plista GmbH] - {"hash":"c5ca32bf780ff41c","prefixes":{"":680}}, // [Netquest Ad Tracking] - {"hash":"fcb4d508b7c17d00","prefixes":{"":681}}, // [Mediasmart Mobile S.L.] - {"hash":"32de5cdddc7878d9","prefixes":{"":681}}, // [Mediasmart Mobile S.L.] - {"hash":"832d7e03cfa50775","prefixes":{"":682}}, // [Netshelter Technology Media, Inc.] - {"hash":"5d0747bdde7700a5","prefixes":{"":682}}, // [Netshelter Technology Media, Inc.] - {"hash":"6cda89e3ca547147","prefixes":{"":682}}, // [Netshelter Technology Media, Inc.] - {"hash":"6825c9c2052b6d49","prefixes":{"":682}}, // [Netshelter Technology Media, Inc.] - {"hash":"dac91e433fa392a8","prefixes":{"":682}}, // [Netshelter Technology Media, Inc.] - {"hash":"5112708cd650c1bf","prefixes":{"":682}}, // [Netshelter Technology Media, Inc.] - {"hash":"8c56edbaad9d7666","prefixes":{"":682}}, // [Netshelter Technology Media, Inc.] - {"hash":"e3f867313a3a22ff","prefixes":{"*":683}}, // [Mixmarket Affiliate Network] - {"hash":"337d4f2254b699bd","prefixes":{"":683}}, // [Mixmarket Affiliate Network] - {"hash":"7dd6cb3709637812","prefixes":{"*":684}}, // [Turbobytes] - {"hash":"b42ea914a5e25208","prefixes":{"":685}}, // [Voodoo Video AG] - {"hash":"441e902171e2e03a","prefixes":{"*":255}}, // [Google CDN] - {"hash":"ed45a52fc3b3ded7","prefixes":{"*":255}}, // [Google CDN] - {"hash":"b70fc2e7ac8321a4","prefixes":{"*":255}}, // [Google CDN] - {"hash":"a7ab005368d54a08","prefixes":{"*":255}}, // [Google CDN] - {"hash":"b3808fd8fb0b9d9b","prefixes":{"*":255}}, // [Google CDN] - {"hash":"273fe37ef5880422","prefixes":{"lh":255,"geo":255}}, // [Google CDN] [Google CDN] - {"hash":"cf71755ff09e2183","prefixes":{"":255}}, // [Google CDN] - {"hash":"3a32bf25eec1a95b","prefixes":{"*":686}}, // [Full Performance] - {"hash":"783c5679a80d5c10","prefixes":{"":687}}, // [eXelate Inc.] - {"hash":"40f709f76b1af3f1","prefixes":{"":687}}, // [eXelate Inc.] - {"hash":"a97762efe739bb85","prefixes":{"*":688}}, // [Oreck Canada] - {"hash":"f3460c4c9a5112cc","prefixes":{"":689}}, // [Beijing WuShuang Technology Ltd. (AGrant)] - {"hash":"1c1f6d178312c71a","prefixes":{"*":690}}, // [Limelight] - {"hash":"227010fe11d13050","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"5460e19d75ae0d01","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"f132b41fc3fed2c5","prefixes":{"":692}}, // [Matomy Media] - {"hash":"930103c79d1886c9","prefixes":{"":693}}, // [Mail.Ru Group] - {"hash":"befa931c2b772e6d","prefixes":{"":693}}, // [Mail.Ru Group] - {"hash":"e1a46a5a294589c8","prefixes":{"":693}}, // [Mail.Ru Group] - {"hash":"7aaf1724444529c4","prefixes":{"":693}}, // [Mail.Ru Group] - {"hash":"a8e6cfd540b8a74b","prefixes":{"":693}}, // [Mail.Ru Group] - {"hash":"4dd30b722c120fe4","prefixes":{"":693}}, // [Mail.Ru Group] - {"hash":"5785365f3a4da9e4","prefixes":{"":693}}, // [Mail.Ru Group] - {"hash":"b4cbadcc4ab58981","prefixes":{"":694}}, // [LSi - Lionsoft Studios, spol. s r.o.] - {"hash":"a41a0099a363da99","prefixes":{"":695}}, // [Adelphic Inc.] - {"hash":"9a9d3ceef9c489c7","prefixes":{"":695}}, // [Adelphic Inc.] - {"hash":"8c652e1d454ab3a2","prefixes":{"":695}}, // [Adelphic Inc.] - {"hash":"36e0703b50fb3eed","prefixes":{"":695}}, // [Adelphic Inc.] - {"hash":"a437c97b4d64cafd","prefixes":{"":696}}, // [Lincoln Technical Institute, Inc.] - {"hash":"f69a68a2d09d0720","prefixes":{"":697}}, // [Smartstream.tv] - {"hash":"26dfb2b8bad92f4f","prefixes":{"":697}}, // [Smartstream.tv] - {"hash":"dc67b62ecf5a9b08","prefixes":{"":697}}, // [Smartstream.tv] - {"hash":"23b6f2b40a209645","prefixes":{"":697}}, // [Smartstream.tv] - {"hash":"40536f7cc3f3bcdb","prefixes":{"":697}}, // [Smartstream.tv] - {"hash":"7eab1c802c1cb708","prefixes":{"*":697}}, // [Smartstream.tv] - {"hash":"8381e43653c976d7","prefixes":{"":697}}, // [Smartstream.tv] - {"hash":"010611867004fb28","prefixes":{"":698}}, // [Reklamport] - {"hash":"8c138e749437f931","prefixes":{"":698}}, // [Reklamport] - {"hash":"735ef6cfbe0d0549","prefixes":{"":699}}, // [Fattext LLC (DBA Moolah Media)] - {"hash":"7a17aa6bfdfb3de1","prefixes":{"":699}}, // [Fattext LLC (DBA Moolah Media)] - {"hash":"3c593814e40d8a9e","prefixes":{"":700}}, // [MoGo Marketing & Media Inc.] - {"hash":"0f36027c1644631a","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"ec8b441e84a18442","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"b923ca8213dfa24a","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"fe0b93fc6d0ad89a","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"b7164c05e2fb0d2f","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"6e085b0ec37b404f","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"d282ecf2666110dd","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"131054b9a54841da","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"556351c03849a4c1","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"bebf7033727205d4","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"e77b9add119d1d72","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"fbcb122264bcdbda","prefixes":{"":701}}, // [SA Media, llc] - {"hash":"10f64b8265e0b1be","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"cc468e0676f36482","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"a445930c60af8d98","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"63e594feafaae08b","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"045d86072bc5dc10","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"ea9d0ea08d06528d","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"94bc93c21ca5d014","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"bcfa082cf700866c","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"17f352a8e88349c9","prefixes":{"":702}}, // [Barons Media LLC] - {"hash":"8b18a83b14ef8906","prefixes":{"":703}}, // [apprupt GmbH] - {"hash":"46ba596945892a31","prefixes":{"":703}}, // [apprupt GmbH] - {"hash":"b0780943092b032a","prefixes":{"":703}}, // [apprupt GmbH] - {"hash":"7d76822bfe9ffcd9","prefixes":{"":703}}, // [apprupt GmbH] - {"hash":"87582b95561cf002","prefixes":{"":703}}, // [apprupt GmbH] - {"hash":"eb0a0cd03ec7f57b","prefixes":{"":704}}, // [PropellerADs media Ltd] - {"hash":"73009a8d32988e92","prefixes":{"*":705}}, // [CJ Affiliate by Conversant] - {"hash":"698b1ec9df8d6759","prefixes":{"*":705}}, // [CJ Affiliate by Conversant] - {"hash":"00e17f99243707e9","prefixes":{"":706}}, // [Millennial Media Inc] - {"hash":"62ddc5a87d328e5a","prefixes":{"":706}}, // [Millennial Media Inc] - {"hash":"0e02d11afea79a5e","prefixes":{"":707}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] - {"hash":"56b1e984216b464b","prefixes":{"":707}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] - {"hash":"815a3c2c1ed6f999","prefixes":{"":707}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] - {"hash":"28175ac65441fbce","prefixes":{"":707}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] - {"hash":"fe775b2f045dcced","prefixes":{"":707}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] - {"hash":"d47b0a700d84bfba","prefixes":{"":707}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] - {"hash":"1c34827419405aee","prefixes":{"":707}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] - {"hash":"7e99ada6b2533f54","prefixes":{"":707}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] - {"hash":"2c5ac4af628363a6","prefixes":{"*":708}}, // [Manage.com Group, Inc.] - {"hash":"f05737fe4b72d22f","prefixes":{"":709}}, // [CloudFlare, Inc.] - {"hash":"1b728798f387160d","prefixes":{"":710}}, // [MASSMOTIONMEDIA SARL] - {"hash":"fb80c91c77673741","prefixes":{"":711}}, // [Brainworks Sp. z.o.o.] - {"hash":"6ff7dff7c1404e1b","prefixes":{"":711}}, // [Brainworks Sp. z.o.o.] - {"hash":"bd6c259e3bb98101","prefixes":{"":711}}, // [Brainworks Sp. z.o.o.] - {"hash":"0dcb02764319e823","prefixes":{"":711}}, // [Brainworks Sp. z.o.o.] - {"hash":"258ec0f081f1c767","prefixes":{"":711}}, // [Brainworks Sp. z.o.o.] - {"hash":"518594f7ef9754dc","prefixes":{"*":712}}, // [Media Intelligence Platform (Aggregate Knowledge)] - {"hash":"223b1b79a6de9f04","prefixes":{"*":712}}, // [Media Intelligence Platform (Aggregate Knowledge)] - {"hash":"848f45768ee0ad4e","prefixes":{"":713}}, // [S4M] - {"hash":"77b7e5f4ec17a6e5","prefixes":{"":713}}, // [S4M] - {"hash":"f965c1f8d2cce837","prefixes":{"":713}}, // [S4M] - {"hash":"3e327352cea146b3","prefixes":{"":713}}, // [S4M] - {"hash":"0658ca5e4baec224","prefixes":{"":713}}, // [S4M] - {"hash":"f2e7a2604319636b","prefixes":{"":713}}, // [S4M] - {"hash":"c825977950596abc","prefixes":{"":713}}, // [S4M] - {"hash":"b55fc4c23b864e56","prefixes":{"":713}}, // [S4M] - {"hash":"3eddbbcdb5a13a1b","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"f133f36dd9e9688d","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"06ead346af8ecc6d","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"16a442ad35246027","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"778068a948335ee6","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"2bb088766e0a77ce","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"f54b41faf3e29ddb","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"3d71c7985374fc62","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"de336679348d46a5","prefixes":{"":714}}, // [Sobmag LTD] - {"hash":"6818fc4cb694f0d4","prefixes":{"*":715}}, // [Netbooster] - {"hash":"5682fbd14ad230ca","prefixes":{"":716}}, // [Musikhaus Thomann e.K.] - {"hash":"abbb14ff2ab0311d","prefixes":{"":716}}, // [Musikhaus Thomann e.K.] - {"hash":"578726cfc508157e","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"a141aaa30beb2f05","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"9161fa28951da89a","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"2e52f74b27e9eb2f","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"0ecdef2f55913790","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"409546c7e7a9ce63","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"3dceafc40a01cd8c","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"e748a008fcd2910c","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"499e3537aead5990","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"aad99655e792b7af","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"df381181135b37f3","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"49fe77e073f907d5","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"fe445db4579e7177","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"f7038d35f22c32db","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"184913b8eec78f63","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"4db6f08af3d6cf66","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"0a71646e475ff9c1","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"7847dd8c5608a507","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"cde33b0511f42c7c","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"6a5ae5d2380647dc","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"0821cc1422e91328","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"0e157baa999502bc","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"cb93fe6df3fe4097","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"657c19e08d899173","prefixes":{"":146}}, // [SiteScout AdServer] - {"hash":"e411fd988f243d89","prefixes":{"":717}}, // [Trovit] - {"hash":"1ea512003540106b","prefixes":{"":717}}, // [Trovit] - {"hash":"48255f09ec2021d3","prefixes":{"":717}}, // [Trovit] - {"hash":"7b5b7cfa3d62a886","prefixes":{"":717}}, // [Trovit] - {"hash":"1235b8279a106276","prefixes":{"":718}}, // [O2online] - {"hash":"cf76706eea2f0be6","prefixes":{"":719}}, // [E-Plus Mobilfunk GmbH & Co. KG] - {"hash":"766b747f8dec4417","prefixes":{"":720}}, // [Meteora] - {"hash":"270677f86b7f115b","prefixes":{"":720}}, // [Meteora] - {"hash":"0a500f94a8a562f0","prefixes":{"":721}}, // [Madison Logic, Inc.] - {"hash":"764d3a17a9512438","prefixes":{"":721}}, // [Madison Logic, Inc.] - {"hash":"38c3aac1b79f0478","prefixes":{"":721}}, // [Madison Logic, Inc.] - {"hash":"babcc401d384553f","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"2ddae58a55ab72ef","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"db564708d2871d3a","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"a4199cfcbe70d7f5","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"e6f5851275bc1fb3","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"16e6d7e84ad7fa3a","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"58ae73a3bb4327d7","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"366a0ad35670aef5","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"5b30682b9dfa7060","prefixes":{"":722}}, // [万相DSP(IZP Technologies)] - {"hash":"67128a22cdd1a7cd","prefixes":{"":23}}, // [AppNexus Open AdStream] - {"hash":"db56992e1a1a3f90","prefixes":{"":723}}, // [righTarget] - {"hash":"479ab75d77a40d72","prefixes":{"":723}}, // [righTarget] - {"hash":"17bcd2af46fb1fe6","prefixes":{"":723}}, // [righTarget] - {"hash":"45681f09a76fe33e","prefixes":{"":724}}, // [e.QQ.com] - {"hash":"612f6aab80bc21df","prefixes":{"":724}}, // [e.QQ.com] - {"hash":"ab7e80409519ca9d","prefixes":{"":724}}, // [e.QQ.com] - {"hash":"a3ae7b8a84f28040","prefixes":{"":724}}, // [e.QQ.com] - {"hash":"b25f21b316565014","prefixes":{"":724}}, // [e.QQ.com] - {"hash":"cc9eeba2126e4261","prefixes":{"":724}}, // [e.QQ.com] - {"hash":"9d4819b235a5272b","prefixes":{"":724}}, // [e.QQ.com] - {"hash":"78bb032f283d8f48","prefixes":{"":725}}, // [Shen Zhen ShiJi KaiXuan Technology Company Ltd.] - {"hash":"de11a5b150526c21","prefixes":{"":725}}, // [Shen Zhen ShiJi KaiXuan Technology Company Ltd.] - {"hash":"bbd9e4a668c4e348","prefixes":{"":725}}, // [Shen Zhen ShiJi KaiXuan Technology Company Ltd.] - {"hash":"c38a1d9eab08d787","prefixes":{"*":724}}, // [e.QQ.com] - {"hash":"5b2c8d90176bd14e","prefixes":{"":724}}, // [e.QQ.com] - {"hash":"62980dc2fa42e1e0","prefixes":{"*":724}}, // [e.QQ.com] - {"hash":"9b37bab01bf28d39","prefixes":{"*":726}}, // [Bannercockpit] - {"hash":"01ec60ee4671b195","prefixes":{"":727}}, // [Outrigger Media Inc.] - {"hash":"4be86c0a73547960","prefixes":{"":727}}, // [Outrigger Media Inc.] - {"hash":"e3ab574adac96838","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"91bb53a0f22994f8","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"fd65eb28a70ed09a","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"9f41f821ddcb5092","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"ee70168f83fb2cce","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"c15fc4a18b25e6d3","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"f5f66dfaf3d2d2a3","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"e6e8716836c8a21d","prefixes":{"":105}}, // [Rockabox Media Ltd] - {"hash":"da37db68457e2782","prefixes":{"*":728}}, // [shopLocal] - {"hash":"ab8c1ee046adfbb1","prefixes":{"*":729}}, // [Impact Radius] - {"hash":"0e8c1ff7462c1084","prefixes":{"*":729}}, // [Impact Radius] - {"hash":"8f228d8317e4718d","prefixes":{"":729}}, // [Impact Radius] - {"hash":"29d7e31af4b1be0b","prefixes":{"":730}}, // [Unister AdServer] - {"hash":"efeab3df1fe3849a","prefixes":{"":730}}, // [Unister AdServer] - {"hash":"8f2fea31a1c79b34","prefixes":{"":730}}, // [Unister AdServer] - {"hash":"9cda8eeaf1e150bf","prefixes":{"":730}}, // [Unister AdServer] - {"hash":"714091c1b8581de0","prefixes":{"":731}}, // [Unister Media GmbH] - {"hash":"c7002b2b9e4b5370","prefixes":{"":730}}, // [Unister AdServer] - {"hash":"1802591342537dae","prefixes":{"":730}}, // [Unister AdServer] - {"hash":"787cbd14bb54b647","prefixes":{"":730}}, // [Unister AdServer] - {"hash":"084ef5666af6a26c","prefixes":{"":730}}, // [Unister AdServer] - {"hash":"df72b5eb4be388d1","prefixes":{"":732}}, // [TLV Media Online Ltd] - {"hash":"64ffcabaf6ec2bcf","prefixes":{"":733}}, // [d3media AG] - {"hash":"a6d58332a182c9f4","prefixes":{"":733}}, // [d3media AG] - {"hash":"0bbd108959983b32","prefixes":{"":734}}, // [Innovative Metrics] - {"hash":"80eab7dfc5e4cc5f","prefixes":{"*":735}}, // [AthenaHealth] - {"hash":"131ce36599fa0d6c","prefixes":{"":736}}, // [TAPVALUE SAS] - {"hash":"9181181a015040bc","prefixes":{"":736}}, // [TAPVALUE SAS] - {"hash":"fca830abc4829657","prefixes":{"":736}}, // [TAPVALUE SAS] - {"hash":"e78333a733c1b05a","prefixes":{"":736}}, // [TAPVALUE SAS] - {"hash":"3f82423bd28a526a","prefixes":{"":736}}, // [TAPVALUE SAS] - {"hash":"f2da501e4086afa3","prefixes":{"":736}}, // [TAPVALUE SAS] - {"hash":"719080d33b283abc","prefixes":{"":736}}, // [TAPVALUE SAS] - {"hash":"3502e0d544b9b739","prefixes":{"":737}}, // [Meteor Worldwide LLC.] - {"hash":"a6227ad6f805c298","prefixes":{"":737}}, // [Meteor Worldwide LLC.] - {"hash":"0933cf49bb328213","prefixes":{"*":189}}, // [Relay42 Technology B.V.] - {"hash":"bea1d6b2a32d3927","prefixes":{"*":189}}, // [Relay42 Technology B.V.] - {"hash":"8d1313bfd522fcf6","prefixes":{"":738}}, // [Audience2Media Limited] - {"hash":"ec4f449051680a17","prefixes":{"":738}}, // [Audience2Media Limited] - {"hash":"9badeeb4c9cff3b3","prefixes":{"":739}}, // [HRB Digital LLC.] - {"hash":"e0e362bb66a6c5dd","prefixes":{"*":740}}, // [!NOOB] - {"hash":"e1e1866cde6f23c7","prefixes":{"":163}}, // [Tagtoo Tech Limited] - {"hash":"60a41acd40c494b4","prefixes":{"":163}}, // [Tagtoo Tech Limited] - {"hash":"4dbc45b703241615","prefixes":{"":163}}, // [Tagtoo Tech Limited] - {"hash":"d11722ff46ba063b","prefixes":{"":163}}, // [Tagtoo Tech Limited] - {"hash":"706cd1e56e4ca9f0","prefixes":{"":163}}, // [Tagtoo Tech Limited] - {"hash":"eabe2f56a564ea71","prefixes":{"":125}}, // [Mocean mobile, Inc.] - {"hash":"9ab3f80172f06a67","prefixes":{"":125}}, // [Mocean mobile, Inc.] - {"hash":"19c44c882811c0bd","prefixes":{"":741}}, // [OSV online] - {"hash":"1f888bdc491d19c5","prefixes":{"":742}}, // [Addroid™] - {"hash":"3259bacc0853f3ce","prefixes":{"":742}}, // [Addroid™] - {"hash":"4a7fe8ed74a69bb1","prefixes":{"":742}}, // [Addroid™] - {"hash":"72680bf564258b75","prefixes":{"":742}}, // [Addroid™] - {"hash":"d25a0b5dd4617071","prefixes":{"":742}}, // [Addroid™] - {"hash":"4e53e370f5c0d9c8","prefixes":{"":742}}, // [Addroid™] - {"hash":"2585ec8519ec2a03","prefixes":{"":742}}, // [Addroid™] - {"hash":"9ebfdd26621d46c7","prefixes":{"":742}}, // [Addroid™] - {"hash":"083fd3b939cbd105","prefixes":{"":742}}, // [Addroid™] - {"hash":"3fd28ace41da6736","prefixes":{"":742}}, // [Addroid™] - {"hash":"3f5d8ed1881e0849","prefixes":{"":742}}, // [Addroid™] - {"hash":"4f0f13d0fae2b262","prefixes":{"":743}}, // [AdAccess] - {"hash":"5a40dbdf15bc0d0c","prefixes":{"":743}}, // [AdAccess] - {"hash":"0eff5ff163f074f0","prefixes":{"":743}}, // [AdAccess] - {"hash":"f5ce32de4ea8770e","prefixes":{"":743}}, // [AdAccess] - {"hash":"8a367cea803ead3b","prefixes":{"":743}}, // [AdAccess] - {"hash":"960b38f18a31bf7f","prefixes":{"":743}}, // [AdAccess] - {"hash":"45b03c5f8881301a","prefixes":{"":744}}, // [Yabuka Media, Inc.] - {"hash":"c2875e9f5741c896","prefixes":{"":744}}, // [Yabuka Media, Inc.] - {"hash":"46751092d6f82d66","prefixes":{"":744}}, // [Yabuka Media, Inc.] - {"hash":"379f0fda3a2142b3","prefixes":{"*":745}}, // [Highwinds CDN] - {"hash":"5033b8e8796bc944","prefixes":{"":746}}, // [Bridgewell Incorporated] - {"hash":"fcbb4f6f5d116bcf","prefixes":{"":746}}, // [Bridgewell Incorporated] - {"hash":"6d68bbc023aff7a8","prefixes":{"":746}}, // [Bridgewell Incorporated] - {"hash":"98a17e63899d6f0f","prefixes":{"":746}}, // [Bridgewell Incorporated] - {"hash":"1cae38f50bbcbe1a","prefixes":{"":746}}, // [Bridgewell Incorporated] - {"hash":"df13ce542b6c0e84","prefixes":{"":747}}, // [Bidtheatre AB] - {"hash":"f9e2fc82f27f7b60","prefixes":{"":747}}, // [Bidtheatre AB] - {"hash":"0d324dc8c79d8b3c","prefixes":{"":747}}, // [Bidtheatre AB] - {"hash":"1d9569d0ee02dcee","prefixes":{"":748}}, // [AdMoment] - {"hash":"73bd54b1be5e9df1","prefixes":{"":748}}, // [AdMoment] - {"hash":"dae6130e1d796d90","prefixes":{"":748}}, // [AdMoment] - {"hash":"53335dc721cbee0b","prefixes":{"":748}}, // [AdMoment] - {"hash":"ae279eee4c5b4941","prefixes":{"":748}}, // [AdMoment] - {"hash":"684276719f3b5728","prefixes":{"":748}}, // [AdMoment] - {"hash":"b7376659acddae09","prefixes":{"":748}}, // [AdMoment] - {"hash":"aa7f623da51e1603","prefixes":{"":748}}, // [AdMoment] - {"hash":"6785583c4297f7c5","prefixes":{"":748}}, // [AdMoment] - {"hash":"20138e51a97d179b","prefixes":{"":748}}, // [AdMoment] - {"hash":"cc3a77dca011bc78","prefixes":{"":748}}, // [AdMoment] - {"hash":"6d441f7565cc5de4","prefixes":{"":749}}, // [UDG München GmbH] - {"hash":"f8c1c6199f46e722","prefixes":{"":749}}, // [UDG München GmbH] - {"hash":"151a96d84e31a957","prefixes":{"":749}}, // [UDG München GmbH] - {"hash":"5d34fdb93148d891","prefixes":{"":750}}, // [Pixalate, Inc.] - {"hash":"6855407f17ce6aec","prefixes":{"":750}}, // [Pixalate, Inc.] - {"hash":"b9f9a074ce5d4d07","prefixes":{"":750}}, // [Pixalate, Inc.] - {"hash":"41ddcdd6ba9e42fe","prefixes":{"":750}}, // [Pixalate, Inc.] - {"hash":"c72923bea1fa64ce","prefixes":{"":751}}, // [InMobi Inc.] - {"hash":"9085e035c617887b","prefixes":{"":751}}, // [InMobi Inc.] - {"hash":"0f7037ee4e476493","prefixes":{"":751}}, // [InMobi Inc.] - {"hash":"bdfbe6247c640d80","prefixes":{"":752}}, // [Crisp Media Inc.] - {"hash":"2cb723bdc82533b2","prefixes":{"":752}}, // [Crisp Media Inc.] - {"hash":"be72bf3ef3e3bd14","prefixes":{"":752}}, // [Crisp Media Inc.] - {"hash":"3af5f91fedb0e598","prefixes":{"":752}}, // [Crisp Media Inc.] - {"hash":"9feb6dbe57a5ab9b","prefixes":{"":752}}, // [Crisp Media Inc.] - {"hash":"1953012d699dc24e","prefixes":{"":753}}, // [Choozle, Inc.] - {"hash":"560479fb0478e76e","prefixes":{"":753}}, // [Choozle, Inc.] - {"hash":"fb3c191358de9e97","prefixes":{"*":754}}, // [Tapad] - {"hash":"faa63745af097785","prefixes":{"*":755}}, // [ReachLocal, Inc] - {"hash":"c6a0024a8fdbd244","prefixes":{"":756}}, // [OpenX Ad Exchange] - {"hash":"1b09e9588e619f63","prefixes":{"":756}}, // [OpenX Ad Exchange] - {"hash":"c923ecff86d0026f","prefixes":{"":756}}, // [OpenX Ad Exchange] - {"hash":"0b38e9ab66f134b5","prefixes":{"":756}}, // [OpenX Ad Exchange] - {"hash":"b07b0a6a32b39c67","prefixes":{"rtb-":756}}, // [OpenX Ad Exchange] - {"hash":"a66019bd1d8d6523","prefixes":{"":757}}, // [OpenX] - {"hash":"3461a8e14dfa7234","prefixes":{"*":756}}, // [OpenX Ad Exchange] - {"hash":"50f406d696ea09e2","prefixes":{"*":756}}, // [OpenX Ad Exchange] - {"hash":"28f15a404c22c5ae","prefixes":{"":758}}, // [Placester, Inc.] - {"hash":"2eabf2a9cc47f6db","prefixes":{"":758}}, // [Placester, Inc.] - {"hash":"04e56c0e39ffb379","prefixes":{"":759}}, // [Spiceworks, Inc] - {"hash":"a342cde98acae2a3","prefixes":{"":760}}, // [WapStart] - {"hash":"816943ad229d59bd","prefixes":{"":760}}, // [WapStart] - {"hash":"9aeccecf18437372","prefixes":{"":760}}, // [WapStart] - {"hash":"e24006f3d8614cdf","prefixes":{"":760}}, // [WapStart] - {"hash":"2678adb8d16c1bd0","prefixes":{"":760}}, // [WapStart] - {"hash":"690b9d06ee7b618c","prefixes":{"":760}}, // [WapStart] - {"hash":"ba64173aac6883fa","prefixes":{"":760}}, // [WapStart] - {"hash":"3f334c96b9489b6f","prefixes":{"":760}}, // [WapStart] - {"hash":"4acb6ede5efaa0ce","prefixes":{"":760}}, // [WapStart] - {"hash":"f848368a90033112","prefixes":{"":760}}, // [WapStart] - {"hash":"fc58526ac2c34887","prefixes":{"":760}}, // [WapStart] - {"hash":"bcec64a939aa6248","prefixes":{"":760}}, // [WapStart] - {"hash":"2237ba07a36c2440","prefixes":{"":760}}, // [WapStart] - {"hash":"9b1bd3e45cc8d37b","prefixes":{"":760}}, // [WapStart] - {"hash":"f9447e97a352a2e8","prefixes":{"":760}}, // [WapStart] - {"hash":"4aa3c10d0fa51575","prefixes":{"":760}}, // [WapStart] - {"hash":"edb1b5b0a2ecd2b9","prefixes":{"":760}}, // [WapStart] - {"hash":"3eee53fed713d5b2","prefixes":{"":760}}, // [WapStart] - {"hash":"5562c787acd84d8d","prefixes":{"":760}}, // [WapStart] - {"hash":"27d45cd108fe99b4","prefixes":{"":760}}, // [WapStart] - {"hash":"06da026d99fd30c4","prefixes":{"":760}}, // [WapStart] - {"hash":"43e91c0e2d266107","prefixes":{"":760}}, // [WapStart] - {"hash":"2b8ee71c143b611a","prefixes":{"":760}}, // [WapStart] - {"hash":"31773affdbc2f93f","prefixes":{"":760}}, // [WapStart] - {"hash":"24f5edbbeab30fc6","prefixes":{"":760}}, // [WapStart] - {"hash":"b1275f1680c5e050","prefixes":{"":760}}, // [WapStart] - {"hash":"1f0e98fd24844df9","prefixes":{"":760}}, // [WapStart] - {"hash":"207973ef90437496","prefixes":{"":760}}, // [WapStart] - {"hash":"5e573612b3181547","prefixes":{"":760}}, // [WapStart] - {"hash":"5237a5b231dbf245","prefixes":{"":760}}, // [WapStart] - {"hash":"b4872bd493703ef0","prefixes":{"":760}}, // [WapStart] - {"hash":"7268f40680faccfd","prefixes":{"":760}}, // [WapStart] - {"hash":"f18cc00e2cfb170d","prefixes":{"":760}}, // [WapStart] - {"hash":"2be692602def45f7","prefixes":{"":760}}, // [WapStart] - {"hash":"5d15e875488cccd1","prefixes":{"":760}}, // [WapStart] - {"hash":"7da3afc36d1c6b59","prefixes":{"":760}}, // [WapStart] - {"hash":"e0767b1a03a64ce6","prefixes":{"":760}}, // [WapStart] - {"hash":"202d0d2b1168aeb8","prefixes":{"":760}}, // [WapStart] - {"hash":"5e070df0dbe151e6","prefixes":{"":760}}, // [WapStart] - {"hash":"e4337ec6681cbc61","prefixes":{"":760}}, // [WapStart] - {"hash":"944f2609259bac7c","prefixes":{"":761}}, // [Ambercrow (Epayments)] - {"hash":"8bcd6f943c02d066","prefixes":{"":761}}, // [Ambercrow (Epayments)] - {"hash":"f0529ee08a090045","prefixes":{"":761}}, // [Ambercrow (Epayments)] - {"hash":"f44423b0de62f78e","prefixes":{"":761}}, // [Ambercrow (Epayments)] - {"hash":"00fb6871b70e1fd9","prefixes":{"":761}}, // [Ambercrow (Epayments)] - {"hash":"588e459942f9f953","prefixes":{"":762}}, // [Audiencevalue Pte Ltd] - {"hash":"2b94a7bb996b3a9c","prefixes":{"":762}}, // [Audiencevalue Pte Ltd] - {"hash":"5796b5e246266e0c","prefixes":{"":763}}, // [UNITED. Inc.] - {"hash":"aa1ee475645d94b7","prefixes":{"":763}}, // [UNITED. Inc.] - {"hash":"716f227b7327feba","prefixes":{"":763}}, // [UNITED. Inc.] - {"hash":"44c3a88f2dcdee6f","prefixes":{"":764}}, // [United Bypass Tech] - {"hash":"0f010d866f5763fa","prefixes":{"":765}}, // [SAS Naoplay] - {"hash":"6a978d7b8efc594d","prefixes":{"":765}}, // [SAS Naoplay] - {"hash":"d3e10ee4fc900d26","prefixes":{"":765}}, // [SAS Naoplay] - {"hash":"e83a4038aff02b6e","prefixes":{"":765}}, // [SAS Naoplay] - {"hash":"acce217e5e09149a","prefixes":{"":765}}, // [SAS Naoplay] - {"hash":"3a93fbc111c8603f","prefixes":{"":766}}, // [CuriosityStream] - {"hash":"065426aaa5552ffe","prefixes":{"":767}}, // [Alliance Internet (ntree)] - {"hash":"9a843715746291f3","prefixes":{"":767}}, // [Alliance Internet (ntree)] - {"hash":"2d4ed5b7f85f950a","prefixes":{"n":767}}, // [Alliance Internet (ntree)] - {"hash":"86f628e66b67be4d","prefixes":{"":768}}, // [Centraltag] - {"hash":"419e8a03b3f16a8c","prefixes":{"":769}}, // [eTargeting] - {"hash":"b9ee79ef4e8c15f5","prefixes":{"":769}}, // [eTargeting] - {"hash":"46e3b67ae4549155","prefixes":{"":769}}, // [eTargeting] - {"hash":"3064ccb85e72d2df","prefixes":{"":769}}, // [eTargeting] - {"hash":"1b58e47cefd5e220","prefixes":{"":769}}, // [eTargeting] - {"hash":"93b3f973b09c5513","prefixes":{"":769}}, // [eTargeting] - {"hash":"607d4f0311eec7af","prefixes":{"":769}}, // [eTargeting] - {"hash":"311502ef7317df7d","prefixes":{"":769}}, // [eTargeting] - {"hash":"cdf3897e12242607","prefixes":{"":769}}, // [eTargeting] - {"hash":"a5a0d71d2f6b631c","prefixes":{"":769}}, // [eTargeting] - {"hash":"10f5415b8d24f2b0","prefixes":{"":769}}, // [eTargeting] - {"hash":"c126382fcba55688","prefixes":{"":769}}, // [eTargeting] - {"hash":"a92d9d96a9338816","prefixes":{"":769}}, // [eTargeting] - {"hash":"74549e43319467f8","prefixes":{"":770}}, // [Walmart Inc] - {"hash":"3d8d433ae123d216","prefixes":{"":770}}, // [Walmart Inc] - {"hash":"6baa1cd3500dc43f","prefixes":{"":771}}, // [Walmart.com] - {"hash":"12bc009e74ca3655","prefixes":{"":770}}, // [Walmart Inc] - {"hash":"abb8ec1b9995e7db","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"a489082dd8c6d1b0","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"b8eaffd4f3298628","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"fa888dd08124e8f6","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"d33163e42a34ac6b","prefixes":{"":773}}, // [Extreme Reach Digital (ER Digital)] - {"hash":"26271fd98d79e29f","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"f958e80b82fbeb15","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"0efc1cccd29292d2","prefixes":{"*":772}}, // [Extreme Reach, Inc.] - {"hash":"ec1cfeb7c8ad8e74","prefixes":{"*":772}}, // [Extreme Reach, Inc.] - {"hash":"06ea295dea974069","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"166f6d9041d2cffe","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"d209952bca4546fc","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"8977e19e27e82a31","prefixes":{"":772}}, // [Extreme Reach, Inc.] - {"hash":"f276cc84c3030bf7","prefixes":{"":774}}, // [Netflix] - {"hash":"cf97c9d0a75da1fb","prefixes":{"":774}}, // [Netflix] - {"hash":"acf43321c04d641b","prefixes":{"":774}}, // [Netflix] - {"hash":"bb55681ae13b2fa8","prefixes":{"":774}}, // [Netflix] - {"hash":"2d5400efd94a66d0","prefixes":{"":774}}, // [Netflix] - {"hash":"c4a2daa282a0af48","prefixes":{"":774}}, // [Netflix] - {"hash":"670a8e12f579ee63","prefixes":{"":775}}, // [Netflix, Inc.] - {"hash":"15b7e69ddffa3a8c","prefixes":{"":775}}, // [Netflix, Inc.] - {"hash":"7b7add7c969d93fe","prefixes":{"*":776}}, // [Scarab Personalized Ads] - {"hash":"4e1907aa0eb0ff71","prefixes":{"":776}}, // [Scarab Personalized Ads] - {"hash":"a3315978597aa707","prefixes":{"":777}}, // [CrowdMob Inc.] - {"hash":"452715cede41650c","prefixes":{"":778}}, // [Gruvi Ltd.] - {"hash":"36ce2eb140f75f32","prefixes":{"":777}}, // [CrowdMob Inc.] - {"hash":"ae6b13daf4bc714d","prefixes":{"":106}}, // [PocketMath] - {"hash":"13696777dabedeae","prefixes":{"":779}}, // [Macromill, Inc.] - {"hash":"9971928c88e3fa4e","prefixes":{"*":780}}, // [Nielsen (Cross Platform Brand Effect [IAG/TVBE])] - {"hash":"65df6515d4615cea","prefixes":{"f":781,"vast-":781,"ivid-":781}}, // [GetIntent] [GetIntent] [GetIntent] - {"hash":"b54db54df2c407f8","prefixes":{"":781}}, // [GetIntent] - {"hash":"2258062bb1ab01dd","prefixes":{"":781}}, // [GetIntent] - {"hash":"630338f7109ea305","prefixes":{"*":781}}, // [GetIntent] - {"hash":"b3c93c6142991123","prefixes":{"":781}}, // [GetIntent] - {"hash":"6ef8d315575de4c7","prefixes":{"":781}}, // [GetIntent] - {"hash":"b7769b25a2035147","prefixes":{"vid-":781}}, // [GetIntent] - {"hash":"2e2859be7da1ee82","prefixes":{"":782}}, // [Belboon] - {"hash":"ff591094a6a13480","prefixes":{"*":783}}, // [Ozone Media Solutions Pvt Ltd] - {"hash":"3bd09c8fad6ac0cd","prefixes":{"*":783}}, // [Ozone Media Solutions Pvt Ltd] - {"hash":"41385cd812900dd3","prefixes":{"*":783}}, // [Ozone Media Solutions Pvt Ltd] - {"hash":"78bdf43617c9f3ed","prefixes":{"":784}}, // [Hindustan Times Mobile Solutions Limited] - {"hash":"f669a1035330e753","prefixes":{"*":783}}, // [Ozone Media Solutions Pvt Ltd] - {"hash":"74c5a373ee6172a1","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"3762c6694e48ada4","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"885e91388dd960b6","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"f6a553969be0331b","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"48d6fc29b318593f","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"1e571da94b4242ba","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"e6770fd6222ae990","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"20b17ee9acc1afb9","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"c483854c67d0c950","prefixes":{"":785}}, // [SoftLayer Technologies, Inc.] - {"hash":"5214f14e27a71d7f","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"61c576ffa8275ef3","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"c6af9ddc14757f86","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"86e9128d66cc0207","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"c71d0bd2e6f83f82","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"97be67a3b0baedf9","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"9622fe3e417564fb","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"a63ebe94fca61ed9","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"bbc1dd12d3cdb0b4","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"962da7f5c63ff169","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"94bb51394747c625","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"3ff8867b3d239d21","prefixes":{"":786}}, // [GoldSpot Media] - {"hash":"a970a6e82a98c461","prefixes":{"":787}}, // [Zeeto Media] - {"hash":"4c975affcaea9100","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"dfd86d0838962249","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"91f2b2338b35b7b2","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"3bec5ca280ea1b83","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"3d88758bd6e0003a","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"5c4e7b396a2ff6d6","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"a95488bd107fec32","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"aa8e84bd80448410","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"63a0cca4218135c9","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"91a1733ac5874a1c","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"5c8abc657915fd35","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"917cc91081fe5b07","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"a53545381bdd0cbf","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"4bc186645ccbf442","prefixes":{"":788}}, // [DYNADMIC SAS] - {"hash":"3d6ddf04f9124237","prefixes":{"":789}}, // [Yoc Mobile Advertising GmbH] - {"hash":"ec5fe7560209f819","prefixes":{"":359}}, // [Quantcast Inc.] - {"hash":"5fe3e321ae6e4bf6","prefixes":{"":359}}, // [Quantcast Inc.] - {"hash":"8be056c242dd9d72","prefixes":{"":359}}, // [Quantcast Inc.] - {"hash":"f8ee5abf7ea1abc7","prefixes":{"":790}}, // [Velti] - {"hash":"59f840ce07769698","prefixes":{"":790}}, // [Velti] - {"hash":"e013843f461a8d4b","prefixes":{"":790}}, // [Velti] - {"hash":"bfe3cf089cc18922","prefixes":{"":790}}, // [Velti] - {"hash":"9bd20c9549f20865","prefixes":{"":791}}, // [Lockon] - {"hash":"f24f331de69a707a","prefixes":{"":791}}, // [Lockon] - {"hash":"566f866251bd714e","prefixes":{"":791}}, // [Lockon] - {"hash":"4baa350d3cc68f76","prefixes":{"":792}}, // [Recruit Co., Ltd. Communications] - {"hash":"9bc564a146df07e7","prefixes":{"":792}}, // [Recruit Co., Ltd. Communications] - {"hash":"35c56cfd7e378fad","prefixes":{"":793}}, // [RECRUIT Communications] - {"hash":"6b9aad5cb94eb206","prefixes":{"":793}}, // [RECRUIT Communications] - {"hash":"671a4bc2e5c9113e","prefixes":{"":794}}, // [Hatena Co., Ltd] - {"hash":"6d3fdce985f31bd4","prefixes":{"":795}}, // [Trafmag] - {"hash":"1a311d346529c16b","prefixes":{"":795}}, // [Trafmag] - {"hash":"cd26b0ae13c2440e","prefixes":{"":796}}, // [Pagewoo] - {"hash":"06e9b4b216458951","prefixes":{"":797}}, // [Adnet Media] - {"hash":"cc0e43344ef5e735","prefixes":{"":797}}, // [Adnet Media] - {"hash":"2bff36578242b6b3","prefixes":{"":797}}, // [Adnet Media] - {"hash":"6bcb758317103435","prefixes":{"":797}}, // [Adnet Media] - {"hash":"5819339a3e48afa9","prefixes":{"":797}}, // [Adnet Media] - {"hash":"4249805768b5eaf7","prefixes":{"":798}}, // [Ligatus GmbH] - {"hash":"c5d5cf4beb6462cf","prefixes":{"":798}}, // [Ligatus GmbH] - {"hash":"41b628c1a22b441e","prefixes":{"":798}}, // [Ligatus GmbH] - {"hash":"88d751d3810273e3","prefixes":{"":798}}, // [Ligatus GmbH] - {"hash":"41ec9f7d9d627e03","prefixes":{"":798}}, // [Ligatus GmbH] - {"hash":"02eb3f21dd6df789","prefixes":{"":798}}, // [Ligatus GmbH] - {"hash":"6b48a245a94ab12a","prefixes":{"":798}}, // [Ligatus GmbH] - {"hash":"41d07cfbdb75024a","prefixes":{"":799}}, // [Webtrekk GmbH] - {"hash":"be359c4fcc3f3a06","prefixes":{"":799}}, // [Webtrekk GmbH] - {"hash":"6f767739019dd808","prefixes":{"":799}}, // [Webtrekk GmbH] - {"hash":"f253ef05e042b018","prefixes":{"":800}}, // [Kantar World Panel] - {"hash":"d01896485cd39018","prefixes":{"":800}}, // [Kantar World Panel] - {"hash":"0e0818ed2e61ee95","prefixes":{"":801}}, // [PubSquared LLC] - {"hash":"b05f21964e55a827","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"d333d9afb4898681","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"1639db8eb5956a46","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"5549a4c097f9b83d","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"1dbbc11fea26048b","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"098ed47c32246e74","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"48556c67606eaf5a","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"e9846bc3bb03a920","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"49353a0b9f2b5bb5","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"96bcc9c54b64eb5b","prefixes":{"":802}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] - {"hash":"f6e1c581da74f06c","prefixes":{"":803}}, // [travel audience GmbH] - {"hash":"a00eb1259ded5bb1","prefixes":{"":803}}, // [travel audience GmbH] - {"hash":"8026726dce357c58","prefixes":{"":804}}, // [Krux Digital, Inc.] - {"hash":"95e17daf76b8a492","prefixes":{"":804}}, // [Krux Digital, Inc.] - {"hash":"e2aedb8e35ba9ad0","prefixes":{"":804}}, // [Krux Digital, Inc.] - {"hash":"7e954379b20a3955","prefixes":{"":804}}, // [Krux Digital, Inc.] - {"hash":"bb2cd97362773074","prefixes":{"":804}}, // [Krux Digital, Inc.] - {"hash":"5f545b8ac29b5d2d","prefixes":{"":805}}, // [The Weather Channel] - {"hash":"188340d4038f111f","prefixes":{"":805}}, // [The Weather Channel] - {"hash":"88ce1340354cf56f","prefixes":{"":805}}, // [The Weather Channel] - {"hash":"6aa9e1031d5ce5ea","prefixes":{"":806}}, // [Social Quantum] - {"hash":"924b0bbf98095055","prefixes":{"":806}}, // [Social Quantum] - {"hash":"a8316ec9f577d677","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"4987a53ffae0e799","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"afafb80716e16e29","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"d0487d64f040dbfa","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"2c543734a4cbc5dd","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"067045f689cb5363","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"7ff291f0586a3dca","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"0a19a7d8484e10da","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"4614ce2d8cea29b5","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"c3164e283cfacd29","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"a1df629f546b9e31","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"6cbf35f72f18a347","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"1cb0768d21f55dd6","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"085272e0502f18c3","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"64b888da84877b17","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"c579af2db70b5cc0","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"306de8baaf32da18","prefixes":{"":807}}, // [AdVentori SAS] - {"hash":"1e9f7eb550327800","prefixes":{"":808}}, // [MindTake Research GmbH] - {"hash":"1c922be621564b9b","prefixes":{"":808}}, // [MindTake Research GmbH] - {"hash":"b3f33aa4d02b6fb2","prefixes":{"":809}}, // [Leger Marketing] - {"hash":"eead3245cdc680da","prefixes":{"":809}}, // [Leger Marketing] - {"hash":"1239f4aeb30a0c6e","prefixes":{"":810}}, // [BlisMedia Limited] - {"hash":"544a45aa905f9c6e","prefixes":{"":810}}, // [BlisMedia Limited] - {"hash":"c5e4c8510e3cac80","prefixes":{"":811}}, // [Double6] - {"hash":"d0fa41ada692b1b9","prefixes":{"":812}}, // [TRADEADS INTERACTIVE] - {"hash":"3db0a72bcd75db25","prefixes":{"":813}}, // [Epigrams, Inc.] - {"hash":"f42d4e28510cfcb3","prefixes":{"":813}}, // [Epigrams, Inc.] - {"hash":"22d2d672aad6f400","prefixes":{"":814}}, // [Activecore Inc.] - {"hash":"1ab5c06bec002a04","prefixes":{"":814}}, // [Activecore Inc.] - {"hash":"c34010f1f0a2a2bd","prefixes":{"":814}}, // [Activecore Inc.] - {"hash":"b274e3909d6409ff","prefixes":{"":815}}, // [Activecore,Inc.] - {"hash":"1bdc7aff17b34632","prefixes":{"":815}}, // [Activecore,Inc.] - {"hash":"a5f385ad8794bbf7","prefixes":{"":816}}, // [ADCASH] - {"hash":"7fbf5ab845c319c3","prefixes":{"":816}}, // [ADCASH] - {"hash":"636714c3598df906","prefixes":{"":816}}, // [ADCASH] - {"hash":"9e6785892cd34bdd","prefixes":{"":816}}, // [ADCASH] - {"hash":"047573e0075b371a","prefixes":{"":816}}, // [ADCASH] - {"hash":"30468687bd7540d7","prefixes":{"":816}}, // [ADCASH] - {"hash":"d38c33250529e4cf","prefixes":{"":816}}, // [ADCASH] - {"hash":"311b59743017ebda","prefixes":{"":816}}, // [ADCASH] - {"hash":"fceae2ba76fa56d8","prefixes":{"":816}}, // [ADCASH] - {"hash":"e3e53a304101f0b8","prefixes":{"*":817}}, // [Videoplaza] - {"hash":"66ae7f0627345a0e","prefixes":{"":818}}, // [Targetix LLC] - {"hash":"f2825950bbc13084","prefixes":{"":818}}, // [Targetix LLC] - {"hash":"5844bebbb6ebc2c8","prefixes":{"":818}}, // [Targetix LLC] - {"hash":"af1d9e1f4d4a29ee","prefixes":{"":818}}, // [Targetix LLC] - {"hash":"eabbec12e479563f","prefixes":{"":818}}, // [Targetix LLC] - {"hash":"063e5c59c898b6b2","prefixes":{"":818}}, // [Targetix LLC] - {"hash":"768e497f05eb9210","prefixes":{"":819}}, // [Adriver LLC] - {"hash":"a3e9e1ffdc9ef6f3","prefixes":{"":820}}, // [Beso] - {"hash":"dc3b4a3bb1a98472","prefixes":{"*":821}}, // [Voopter.com] - {"hash":"608c4fc0e1249087","prefixes":{"":822}}, // [Hostbasket] - {"hash":"ec50bd1f8e55703a","prefixes":{"":823}}, // [Rollad] - {"hash":"75f08f4a618e4c29","prefixes":{"":823}}, // [Rollad] - {"hash":"5e28845a397448ed","prefixes":{"":823}}, // [Rollad] - {"hash":"43baf1a2f5c604eb","prefixes":{"":823}}, // [Rollad] - {"hash":"9113c91f7c97eda8","prefixes":{"":823}}, // [Rollad] - {"hash":"6e46faf33af4223b","prefixes":{"":823}}, // [Rollad] - {"hash":"b8629ee4f4882cf4","prefixes":{"":823}}, // [Rollad] - {"hash":"c580bf68b5705ecc","prefixes":{"":823}}, // [Rollad] - {"hash":"30c1badbfab295cc","prefixes":{"":823}}, // [Rollad] - {"hash":"07206ed226f7d186","prefixes":{"":824}}, // [hdtMedia] - {"hash":"af031421c35c12bb","prefixes":{"":824}}, // [hdtMedia] - {"hash":"c1e9215522e84feb","prefixes":{"":824}}, // [hdtMedia] - {"hash":"f99b1e1004ffe36c","prefixes":{"":824}}, // [hdtMedia] - {"hash":"cc5aed10326009cd","prefixes":{"":824}}, // [hdtMedia] - {"hash":"a59bac0d7936d725","prefixes":{"":824}}, // [hdtMedia] - {"hash":"85c92ca45eb6137a","prefixes":{"":824}}, // [hdtMedia] - {"hash":"ecfa86f147eea591","prefixes":{"":824}}, // [hdtMedia] - {"hash":"89c418c61cc46dbf","prefixes":{"":825}}, // [DXP Media] - {"hash":"36712fa880f9f4fd","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"77af178b53829cee","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"19e3098ce56eae94","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"605d50a0c132e0b5","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"9cb69b24762928be","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"7ad4195e3856bc8b","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"a1f419f69f83a3cb","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"a8184454cf33a5e3","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"8c22c5755c763066","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"862c4454fe07929d","prefixes":{"":826}}, // [ebuilders BV] - {"hash":"ac0afa5e86463dcf","prefixes":{"":827}}, // [COADVERTISE] - {"hash":"049d375ddb03cca6","prefixes":{"":827}}, // [COADVERTISE] - {"hash":"2ce968c3c01dac2b","prefixes":{"":828}}, // [Blizzard] - {"hash":"6e4734c6df5289ef","prefixes":{"":828}}, // [Blizzard] - {"hash":"9fe6f25a55c854ab","prefixes":{"":829}}, // [Adgibbon] - {"hash":"008bfe35bcbd016d","prefixes":{"":829}}, // [Adgibbon] - {"hash":"084705198bf3893b","prefixes":{"":829}}, // [Adgibbon] - {"hash":"a1b65ade9cc861cb","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"42f9c0b081bf4152","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"ea90626af94135dd","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"ae4cfcc8964ff838","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"29ed865d96c2134b","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"f0ad420cd06e916d","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"02fa3036dfdd1f55","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"2e1689e37ca87292","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"e0f93c05dad3c3c6","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"3a2e2804dc9fbb61","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"deed6c9248e53552","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"c4597285474e8048","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"e127ffe380012cba","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"2f548a00d118d32e","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"015756b75eb89403","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"35d2e95d1522b0c5","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"a43e409504b254d3","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"5a4147bb67b5b23c","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"9866a49aaacb720e","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"f101934b33880c8d","prefixes":{"":830}}, // [Wider Planet, Inc.] - {"hash":"5fc0aea809357158","prefixes":{"":107}}, // [Kpsule] - {"hash":"ed7b8006e50dc195","prefixes":{"":107}}, // [Kpsule] - {"hash":"fe112ea448efa84b","prefixes":{"":107}}, // [Kpsule] - {"hash":"807a0ad2ef7d844d","prefixes":{"":107}}, // [Kpsule] - {"hash":"24daf2e5c1e30aeb","prefixes":{"":107}}, // [Kpsule] - {"hash":"9091656f8979f5f0","prefixes":{"":107}}, // [Kpsule] - {"hash":"bb5ef61e19944a06","prefixes":{"":831}}, // [ResponsiveAds, Inc] - {"hash":"ef4fefbe73da4a59","prefixes":{"":831}}, // [ResponsiveAds, Inc] - {"hash":"e78ef2faad563e6b","prefixes":{"":831}}, // [ResponsiveAds, Inc] - {"hash":"20e99abfa9e5de7c","prefixes":{"":831}}, // [ResponsiveAds, Inc] - {"hash":"0dedc650dbf5d391","prefixes":{"":831}}, // [ResponsiveAds, Inc] - {"hash":"5bdaec9fd3ff4dac","prefixes":{"":832}}, // [Duepuntozero Research SRL] - {"hash":"318bfe954294ea40","prefixes":{"":833}}, // [AdMovate, Inc.] - {"hash":"e24259ffed4d244f","prefixes":{"":833}}, // [AdMovate, Inc.] - {"hash":"c39b0621d7a8b616","prefixes":{"":834}}, // [go.pl] - {"hash":"29c359aefdd48c40","prefixes":{"":834}}, // [go.pl] - {"hash":"6ecf3c2aeb5aec2b","prefixes":{"":834}}, // [go.pl] - {"hash":"eea1a3478557a7a6","prefixes":{"":834}}, // [go.pl] - {"hash":"0b0e365de9c89436","prefixes":{"":834}}, // [go.pl] - {"hash":"875d9d8b11a6dff7","prefixes":{"":834}}, // [go.pl] - {"hash":"5edb8f1a6337d5d0","prefixes":{"":834}}, // [go.pl] - {"hash":"435808ca4dd1dce5","prefixes":{"":834}}, // [go.pl] - {"hash":"fd43d007391147de","prefixes":{"":710}}, // [MASSMOTIONMEDIA SARL] - {"hash":"633236a94b694dd7","prefixes":{"":710}}, // [MASSMOTIONMEDIA SARL] - {"hash":"a076e91e809efc28","prefixes":{"*":835}}, // [AppLovin Corporation] - {"hash":"14c26da0caac0796","prefixes":{"":836}}, // [twentysix ltd] - {"hash":"1f8d7bcebd64d2c9","prefixes":{"":15}}, // [Tamome] - {"hash":"41f4c0bf12c8f029","prefixes":{"":336}}, // [EuroAds Group A/S] - {"hash":"e674b14a061bb7d7","prefixes":{"":336}}, // [EuroAds Group A/S] - {"hash":"f485e2bb528d3147","prefixes":{"":336}}, // [EuroAds Group A/S] - {"hash":"82c09e315af7d70c","prefixes":{"":336}}, // [EuroAds Group A/S] - {"hash":"fc2b985669148068","prefixes":{"":336}}, // [EuroAds Group A/S] - {"hash":"cf9a9377bc72c2e4","prefixes":{"":336}}, // [EuroAds Group A/S] - {"hash":"d550149a2d5cfdde","prefixes":{"":336}}, // [EuroAds Group A/S] - {"hash":"d51c2c24cd771a6a","prefixes":{"":837}}, // [Epsilon International SA] - {"hash":"d34cf98578200c93","prefixes":{"":838}}, // [Zebestof] - {"hash":"44c2302577bd8e84","prefixes":{"":838}}, // [Zebestof] - {"hash":"52b835c5657206c1","prefixes":{"":838}}, // [Zebestof] - {"hash":"caa0c94cf57e2295","prefixes":{"":838}}, // [Zebestof] - {"hash":"02e54ec6b0aca548","prefixes":{"":839}}, // [SOL UTD Benelux BV] - {"hash":"1cf1a7f5c323d6e0","prefixes":{"":840}}, // [M,P,NEWMEDIA, GmbH] - {"hash":"079a647886a5afa5","prefixes":{"":840}}, // [M,P,NEWMEDIA, GmbH] - {"hash":"ec320d28ebdfac8c","prefixes":{"":840}}, // [M,P,NEWMEDIA, GmbH] - {"hash":"e59449da7e642b45","prefixes":{"":840}}, // [M,P,NEWMEDIA, GmbH] - {"hash":"1d61751db94dfc15","prefixes":{"":840}}, // [M,P,NEWMEDIA, GmbH] - {"hash":"43f7746544162324","prefixes":{"":840}}, // [M,P,NEWMEDIA, GmbH] - {"hash":"4f80c8700c459a79","prefixes":{"":840}}, // [M,P,NEWMEDIA, GmbH] - {"hash":"23f62ba113f0fa4f","prefixes":{"":841}}, // [MediaCrossing Inc.] - {"hash":"62fc0fc3102b918b","prefixes":{"":842}}, // [MBR Targeting Gmbh] - {"hash":"0066f0743ade259b","prefixes":{"":842}}, // [MBR Targeting Gmbh] - {"hash":"4288d021c49b7e1b","prefixes":{"":842}}, // [MBR Targeting Gmbh] - {"hash":"6620a58a1f4cd480","prefixes":{"":843}}, // [VivaKi] - {"hash":"47db57aade94670f","prefixes":{"":843}}, // [VivaKi] - {"hash":"2924b77c1d7eab3b","prefixes":{"":843}}, // [VivaKi] - {"hash":"d2143dbf1699f4fa","prefixes":{"":844}}, // [Affiliate Window] - {"hash":"13c2a94c46886705","prefixes":{"":844}}, // [Affiliate Window] - {"hash":"c0d30b023dc9139f","prefixes":{"*":845}}, // [TubeMogul Inc. (AdWords/YouTube)] - {"hash":"108d788939e4b7a4","prefixes":{"":825}}, // [DXP Media] - {"hash":"6fdc121917e19eb9","prefixes":{"":825}}, // [DXP Media] - {"hash":"5262a88cfa363303","prefixes":{"":825}}, // [DXP Media] - {"hash":"b597af924fe5ff97","prefixes":{"":825}}, // [DXP Media] - {"hash":"dca60f44a70d5d38","prefixes":{"":825}}, // [DXP Media] - {"hash":"c44308d5c2e3bb8a","prefixes":{"":825}}, // [DXP Media] - {"hash":"90f5b971c961816f","prefixes":{"":825}}, // [DXP Media] - {"hash":"5d64c1152c075739","prefixes":{"":846}}, // [Way2traffic Polska S.A.] - {"hash":"01b13121d2ce9699","prefixes":{"":846}}, // [Way2traffic Polska S.A.] - {"hash":"f48791a2746e2a93","prefixes":{"":847}}, // [TNS Sifo AB] - {"hash":"935b8079b74f344c","prefixes":{"":847}}, // [TNS Sifo AB] - {"hash":"24e5b8b342d1b1fe","prefixes":{"":848}}, // [3xchange/Hunkal] - {"hash":"ad8c8bc165611bf2","prefixes":{"":848}}, // [3xchange/Hunkal] - {"hash":"b1b5bdd82d143e90","prefixes":{"":849}}, // [Emerse Sverige AB] - {"hash":"aeddaa071ba3b313","prefixes":{"":849}}, // [Emerse Sverige AB] - {"hash":"474802ea42555e39","prefixes":{"":849}}, // [Emerse Sverige AB] - {"hash":"b28e42ea18df1c00","prefixes":{"":849}}, // [Emerse Sverige AB] - {"hash":"826635800b12eb0c","prefixes":{"":849}}, // [Emerse Sverige AB] - {"hash":"22c10728f555913e","prefixes":{"":849}}, // [Emerse Sverige AB] - {"hash":"0b7f364ef0beed45","prefixes":{"":849}}, // [Emerse Sverige AB] - {"hash":"8d31e79105c5380a","prefixes":{"":850}}, // [Sokno Media] - {"hash":"037264ef7d8383f6","prefixes":{"":850}}, // [Sokno Media] - {"hash":"ff6372e6621d88ba","prefixes":{"":851}}, // [Blackheart, a division of Hot Topic, Inc.] - {"hash":"37742852c2a4ccc8","prefixes":{"":852}}, // [Torrid] - {"hash":"a86786ce90b23e3f","prefixes":{"":853}}, // [Hot Topic, Inc.] - {"hash":"93e1c22a4427a32f","prefixes":{"":854}}, // [WebHue LLC] - {"hash":"fca0a42aad108345","prefixes":{"static":855,"img":855,"log":855}}, // [Content to Emotion] [Content to Emotion] [Content to Emotion] - {"hash":"15948cae619c2e58","prefixes":{"":855}}, // [Content to Emotion] - {"hash":"db2a81c15837ddbd","prefixes":{"":856}}, // [Adman Interactive SL] - {"hash":"accf1565984e2899","prefixes":{"":856}}, // [Adman Interactive SL] - {"hash":"45351e5c4862b2dd","prefixes":{"":856}}, // [Adman Interactive SL] - {"hash":"ba3196c50e6c1be4","prefixes":{"":856}}, // [Adman Interactive SL] - {"hash":"eab724fbba4f83d6","prefixes":{"":856}}, // [Adman Interactive SL] - {"hash":"0edc8e7b8a8fe3ee","prefixes":{"":857}}, // [AudienceFUEL, Inc.] - {"hash":"dfa423315ab48813","prefixes":{"":858}}, // [TapCommerce LLC] - {"hash":"777b0f4301aa1643","prefixes":{"":858}}, // [TapCommerce LLC] - {"hash":"294cba0ae232447c","prefixes":{"":859}}, // [AdTheorent, Inc.] - {"hash":"ba7ad5da6db014c7","prefixes":{"":860}}, // [AdTheorent, Inc] - {"hash":"09bfc8a7f1c7896f","prefixes":{"":859}}, // [AdTheorent, Inc.] - {"hash":"362136e847a35f7e","prefixes":{"":859}}, // [AdTheorent, Inc.] - {"hash":"5492d77bb75e6380","prefixes":{"":861}}, // [Barometric] - {"hash":"94bb816c4a375220","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"f10e1a418576d219","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"245fc951b0a9ce59","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"babdceabca3ae2b8","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"c6474114ede3195a","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"26faef64bd072723","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"0a19b1f547a3a935","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"b4ef56642916cc60","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"c1fc9d245fa85b02","prefixes":{"":862}}, // [CrossInstall, Inc] - {"hash":"0e3ddb4868b0e99f","prefixes":{"":863}}, // [Theorem Inc.] - {"hash":"976d4ea6507dfd2e","prefixes":{"":863}}, // [Theorem Inc.] - {"hash":"70a3fe87d3bbbae9","prefixes":{"":863}}, // [Theorem Inc.] - {"hash":"1cf1ec002a07d6a4","prefixes":{"":864}}, // [KeyVersion] - {"hash":"b2c480f3bc7b03b6","prefixes":{"":864}}, // [KeyVersion] - {"hash":"c94dbc0a3ffc6eda","prefixes":{"":865}}, // [Ancestry] - {"hash":"04fb46af993df556","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"b9adf25b55c4d0f3","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"1c1211c84dbee054","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"575fafb094c71d93","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"f2b020b3d3861db2","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"609d13c5cd3771c8","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"aeae313934102cfe","prefixes":{"":867}}, // [Shanghai Lijing Advertising Co., Ltd] - {"hash":"f8721bcf4d9f6196","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"09e5f7c9f30f4fd6","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"a1ce57d387427206","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"bb4b6377666172c4","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"d6a6a6cd062bc76e","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"0313ce8a9851d837","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"6862450d2314df40","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"0a34c9f6f0cf9556","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"8e311fce80eccafd","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"296fdad7e8336d5a","prefixes":{"":866}}, // [Shanghai Lijing Advertising Co., Ltd.] - {"hash":"27c0e177312a3c0f","prefixes":{"":108}}, // [Immedium, Inc.] - {"hash":"fc3c7114081863bd","prefixes":{"":108}}, // [Immedium, Inc.] - {"hash":"811582f5df157ff9","prefixes":{"":868}}, // [KissNoFrog.com] - {"hash":"284d8a4f9b174bab","prefixes":{"":869}}, // [DigiEQ] - {"hash":"aa0e7b7a1fe279dc","prefixes":{"":869}}, // [DigiEQ] - {"hash":"1f551f934400f81e","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"1c3b94cabbbc5b8c","prefixes":{"":86}}, // [MediaMath Inc.] - {"hash":"505b194aeebd6baf","prefixes":{"":870}}, // [Mobile Professinals BV] - {"hash":"8f2e12e9677e4d41","prefixes":{"":870}}, // [Mobile Professinals BV] - {"hash":"e2a3fe5e482432e8","prefixes":{"":870}}, // [Mobile Professinals BV] - {"hash":"7d03cd1b92c167a8","prefixes":{"":870}}, // [Mobile Professinals BV] - {"hash":"532402cd3ab13402","prefixes":{"":870}}, // [Mobile Professinals BV] - {"hash":"48e577ba3e61c748","prefixes":{"":870}}, // [Mobile Professinals BV] - {"hash":"1480a6580cb81dde","prefixes":{"":870}}, // [Mobile Professinals BV] - {"hash":"b1ec46dd2c4c824e","prefixes":{"*":109}}, // [Fractional Media, LLC] - {"hash":"6c6240697771befd","prefixes":{"*":109}}, // [Fractional Media, LLC] - {"hash":"f2e16038c0d83b85","prefixes":{"":871}}, // [Decisive, Inc.] - {"hash":"654da4ed85d77fb9","prefixes":{"":871}}, // [Decisive, Inc.] - {"hash":"c9e2ea0486216534","prefixes":{"":871}}, // [Decisive, Inc.] - {"hash":"546a2acb9b2363f2","prefixes":{"*":872}}, // [Microsoft Security Essentials] - {"hash":"9e4f5b2f200fbb25","prefixes":{"":873}}, // [Tumi, Inc. US] - {"hash":"4378c60894195c4f","prefixes":{"":874}}, // [Tumi, Inc. UK] - {"hash":"61e82c5e74c5ce7c","prefixes":{"":875}}, // [Tumi, Inc. DE] - {"hash":"712f010eb9792fa4","prefixes":{"":876}}, // [Nanigans, Inc] - {"hash":"589eceaef79619dd","prefixes":{"":876}}, // [Nanigans, Inc] - {"hash":"1d66f17c949dbaee","prefixes":{"":876}}, // [Nanigans, Inc] - {"hash":"6dbd7b9480163af0","prefixes":{"":876}}, // [Nanigans, Inc] - {"hash":"41d56e0870364212","prefixes":{"":139}}, // [Brandscreen Inc.] - {"hash":"2d701495cf72af5b","prefixes":{"*":877}}, // [AdSniper LLC] - {"hash":"0bd14e8d5a6b2f2d","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"9b504cc586da72e5","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"a89d4cb4490b4286","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"dce925353cbc1dbe","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"ff8d26679ce7dafc","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"11db2fec76a76647","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"00a09e8788ee6f2b","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"709d79bfe214aa48","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"7a37fa5ff1a37799","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"42a24e1ab1c2d947","prefixes":{"":877}}, // [AdSniper LLC] - {"hash":"63f4e4d315bc085b","prefixes":{"":878}}, // [Fabric Worldwide Inc] - {"hash":"b9c11e67d7eb2963","prefixes":{"":878}}, // [Fabric Worldwide Inc] - {"hash":"e3882e6bc0c2e382","prefixes":{"":879}}, // [Vorwerk Deutschland Stiftung & Co. KG] - {"hash":"944d86d40b9ae089","prefixes":{"":879}}, // [Vorwerk Deutschland Stiftung & Co. KG] - {"hash":"ebd456010571b5ae","prefixes":{"":879}}, // [Vorwerk Deutschland Stiftung & Co. KG] - {"hash":"4456fa769be90ba2","prefixes":{"":65}}, // [Active Agent] - {"hash":"84d65f5782dd26d6","prefixes":{"":880}}, // [Chico Distribution Services, LLC] - {"hash":"a6185f46e2849f58","prefixes":{"":881}}, // [12Mnkys GmbH] - {"hash":"bf874935c6972629","prefixes":{"":881}}, // [12Mnkys GmbH] - {"hash":"094b301a712414f2","prefixes":{"":881}}, // [12Mnkys GmbH] - {"hash":"56ee8d67408b2316","prefixes":{"":881}}, // [12Mnkys GmbH] - {"hash":"d62d9c20c47ec863","prefixes":{"":881}}, // [12Mnkys GmbH] - {"hash":"bb716a8269a2e79c","prefixes":{"":882}}, // [Spacyz, Inc.] - {"hash":"5a6ff8a620bb8de1","prefixes":{"":882}}, // [Spacyz, Inc.] - {"hash":"61cf0a8426083e54","prefixes":{"":882}}, // [Spacyz, Inc.] - {"hash":"4cf4aa4b0e37fdfd","prefixes":{"":719}}, // [E-Plus Mobilfunk GmbH & Co. KG] - {"hash":"745acee8f5973f56","prefixes":{"":719}}, // [E-Plus Mobilfunk GmbH & Co. KG] - {"hash":"9a834a1cc05cb878","prefixes":{"":883}}, // [MBuy, Inc.] - {"hash":"73cd161c10ebcf69","prefixes":{"":884}}, // [FullSpeed Inc.] - {"hash":"75c72b5cc4c40c16","prefixes":{"":884}}, // [FullSpeed Inc.] - {"hash":"33c5c398130ddfec","prefixes":{"":884}}, // [FullSpeed Inc.] - {"hash":"1c0653651245a014","prefixes":{"":884}}, // [FullSpeed Inc.] - {"hash":"271d057b15f67166","prefixes":{"":884}}, // [FullSpeed Inc.] - {"hash":"e9723a229b8b9eeb","prefixes":{"":884}}, // [FullSpeed Inc.] - {"hash":"8e3c99c5cc10c217","prefixes":{"":355}}, // [New Allyes Information Technology (winmax)] - {"hash":"07141bfb96faa93c","prefixes":{"":885}}, // [Madhouse Co. Limited (OptiMad)] - {"hash":"1d7294abbaa26e26","prefixes":{"":885}}, // [Madhouse Co. Limited (OptiMad)] - {"hash":"6872272e097d2cd9","prefixes":{"":885}}, // [Madhouse Co. Limited (OptiMad)] - {"hash":"66ec8aa560cf2231","prefixes":{"":885}}, // [Madhouse Co. Limited (OptiMad)] - {"hash":"17cd9dcba702b7e6","prefixes":{"":885}}, // [Madhouse Co. Limited (OptiMad)] - {"hash":"2347e9e88f6ead88","prefixes":{"":886}}, // [Nano Interactive / Audiencemanager] - {"hash":"7f19740ff86dc642","prefixes":{"":886}}, // [Nano Interactive / Audiencemanager] - {"hash":"18f553670e23734b","prefixes":{"":886}}, // [Nano Interactive / Audiencemanager] - {"hash":"ca862d199926ad1c","prefixes":{"":886}}, // [Nano Interactive / Audiencemanager] - {"hash":"2238776218564026","prefixes":{"":886}}, // [Nano Interactive / Audiencemanager] - {"hash":"d7e222c8d7ba68d8","prefixes":{"*":887}}, // [Youtube, LLC] - {"hash":"edff9009064b3fa4","prefixes":{"*":887}}, // [Youtube, LLC] - {"hash":"0d60795f6997311e","prefixes":{"":888}}, // [Omnibus co. Ltd] - {"hash":"f32008e13be0f96e","prefixes":{"":888}}, // [Omnibus co. Ltd] - {"hash":"dd5533aa2d49cc10","prefixes":{"":889}}, // [Omnibus co. Ltd.] - {"hash":"3bcd500b684d5142","prefixes":{"":888}}, // [Omnibus co. Ltd] - {"hash":"14eef63a2889c8da","prefixes":{"":888}}, // [Omnibus co. Ltd] - {"hash":"53f31e427551538d","prefixes":{"":888}}, // [Omnibus co. Ltd] - {"hash":"5296819de63f3444","prefixes":{"":888}}, // [Omnibus co. Ltd] - {"hash":"d8389fea989da8d7","prefixes":{"":41}}, // [Airpush, Inc.] - {"hash":"e251b17a002b679b","prefixes":{"":890}}, // [Education Management Corporation] - {"hash":"ec35188a522b7db9","prefixes":{"*":891}}, // [Screen6 (s6.io)] - {"hash":"8ee1cf907bbca914","prefixes":{"":892}}, // [LINK Marketing Services AG] - {"hash":"18a5decf96e8c80d","prefixes":{"":893}}, // [TiqIQ] - {"hash":"8927e5364d1bf1d1","prefixes":{"":893}}, // [TiqIQ] - {"hash":"86292c524ac79685","prefixes":{"":893}}, // [TiqIQ] - {"hash":"52562df3f7208c8f","prefixes":{"":893}}, // [TiqIQ] - {"hash":"a7d6fe32ab078e86","prefixes":{"":893}}, // [TiqIQ] - {"hash":"497857c899894a0f","prefixes":{"":893}}, // [TiqIQ] - {"hash":"0d8c5ad133d4c83e","prefixes":{"":894}}, // [Ibibo Group Private Limited] - {"hash":"a8c10e41d2d4b31a","prefixes":{"":894}}, // [Ibibo Group Private Limited] - {"hash":"9699d29520d1b9b8","prefixes":{"":894}}, // [Ibibo Group Private Limited] - {"hash":"103308c15ca4c459","prefixes":{"":889}}, // [Omnibus co. Ltd.] - {"hash":"d7a1b3fc5e1252ce","prefixes":{"":889}}, // [Omnibus co. Ltd.] - {"hash":"ee40bf7c9bae2361","prefixes":{"":895}}, // [justAd TV LTD] - {"hash":"8755d187f8ffb5e9","prefixes":{"":895}}, // [justAd TV LTD] - {"hash":"1bdf54cf79d18915","prefixes":{"":895}}, // [justAd TV LTD] - {"hash":"ed61950d43d99717","prefixes":{"":895}}, // [justAd TV LTD] - {"hash":"aff928fc58637cb3","prefixes":{"":896}}, // [Appier Inc.] - {"hash":"797f76218537ad5d","prefixes":{"*":896}}, // [Appier Inc.] - {"hash":"6bdcdb03ff6173af","prefixes":{"*":896}}, // [Appier Inc.] - {"hash":"6a3dec8b5a467987","prefixes":{"*":896}}, // [Appier Inc.] - {"hash":"a3d5f58bbde56b12","prefixes":{"":896}}, // [Appier Inc.] - {"hash":"7d0adb9089ff21ca","prefixes":{"":896}}, // [Appier Inc.] - {"hash":"c241f36263ea7c84","prefixes":{"":897}}, // [Kate Spade Saturday] - {"hash":"79dd595e79611766","prefixes":{"":898}}, // [Teufel GmbH] - {"hash":"df829b8e840c11e1","prefixes":{"":898}}, // [Teufel GmbH] - {"hash":"c3004b48539587e5","prefixes":{"":898}}, // [Teufel GmbH] - {"hash":"08eb365a3723ee17","prefixes":{"":898}}, // [Teufel GmbH] - {"hash":"71bc88c0bd0dc170","prefixes":{"":898}}, // [Teufel GmbH] - {"hash":"fb208e41a565430a","prefixes":{"":899}}, // [Aitarget LLC] - {"hash":"6121d39232612f56","prefixes":{"":899}}, // [Aitarget LLC] - {"hash":"0ef54a01a72fdbaf","prefixes":{"*":676}}, // [Rackspace, US Inc.] - {"hash":"532febae5cf9194f","prefixes":{"":900}}, // [Engage Lab Ltd.] - {"hash":"fbfff8f12503c208","prefixes":{"":901}}, // [Signal Digital, Inc dba Signal] - {"hash":"14afd1fc2da14d71","prefixes":{"":902}}, // [Motrixi Media Group LLC] - {"hash":"aa674bd59dee4716","prefixes":{"":902}}, // [Motrixi Media Group LLC] - {"hash":"7fd4fa8a06589cab","prefixes":{"":902}}, // [Motrixi Media Group LLC] - {"hash":"56f94b2620357eb5","prefixes":{"":902}}, // [Motrixi Media Group LLC] - {"hash":"32ddc8b6874f030b","prefixes":{"":903}}, // [WHITE HOUSE | BLACK MARKET, Chico Brands, Inc.] - {"hash":"fea26de6b70aafb9","prefixes":{"":904}}, // [Liftoff Mobile, Inc.] - {"hash":"4687be4922e9d22e","prefixes":{"":905}}, // [etracker GmbH] - {"hash":"b41f235329fba677","prefixes":{"*":906}}, // [MezzoMedia] - {"hash":"3362cc82ba37c826","prefixes":{"":906}}, // [MezzoMedia] - {"hash":"7c5cbde65c1f0a61","prefixes":{"":907}}, // [Eyeota Limited] - {"hash":"d85ebb0a3c0bdef4","prefixes":{"":908}}, // [Beijing PageChoice Network Technology co., Ltd.] - {"hash":"52d134e034c55e1d","prefixes":{"":908}}, // [Beijing PageChoice Network Technology co., Ltd.] - {"hash":"9d9805e510965156","prefixes":{"":908}}, // [Beijing PageChoice Network Technology co., Ltd.] - {"hash":"5ed6426649a5142e","prefixes":{"":908}}, // [Beijing PageChoice Network Technology co., Ltd.] - {"hash":"e629bb2b2df81562","prefixes":{"":908}}, // [Beijing PageChoice Network Technology co., Ltd.] - {"hash":"37a1384fb3fc6090","prefixes":{"":270}}, // [Intelliad] - {"hash":"9d931008b4067d12","prefixes":{"":909}}, // [Padopolis, Inc.] - {"hash":"2e0eb051f10f4fe7","prefixes":{"":909}}, // [Padopolis, Inc.] - {"hash":"e4e066ee012dd820","prefixes":{"":910}}, // [Republic Project, Inc.] - {"hash":"6bda23da60f6517c","prefixes":{"":910}}, // [Republic Project, Inc.] - {"hash":"8af361ef2f58932e","prefixes":{"":910}}, // [Republic Project, Inc.] - {"hash":"9cf685ac4bd3c1a0","prefixes":{"":910}}, // [Republic Project, Inc.] - {"hash":"ceb2ae18496c4062","prefixes":{"":910}}, // [Republic Project, Inc.] - {"hash":"a1c74be312c1e501","prefixes":{"":911}}, // [C8 Network] - {"hash":"e2305a44231103a0","prefixes":{"":911}}, // [C8 Network] - {"hash":"e9ea3f2053ff0ac5","prefixes":{"":911}}, // [C8 Network] - {"hash":"18f65ffdee8f8402","prefixes":{"":911}}, // [C8 Network] - {"hash":"dc902603963cc6c8","prefixes":{"":911}}, // [C8 Network] - {"hash":"6addef0844f148c5","prefixes":{"":911}}, // [C8 Network] - {"hash":"c2889776656d63b3","prefixes":{"":911}}, // [C8 Network] - {"hash":"1a1ac1eeae181fe7","prefixes":{"":911}}, // [C8 Network] - {"hash":"fa03396d4a303d45","prefixes":{"":911}}, // [C8 Network] - {"hash":"f632f69c34840122","prefixes":{"":911}}, // [C8 Network] - {"hash":"082e62ce70aa66fc","prefixes":{"":911}}, // [C8 Network] - {"hash":"775c7d89c606cd3c","prefixes":{"":911}}, // [C8 Network] - {"hash":"74b282bb3c686219","prefixes":{"":911}}, // [C8 Network] - {"hash":"a8e1956c97315aaf","prefixes":{"":911}}, // [C8 Network] - {"hash":"26dc9f78ec72cc0c","prefixes":{"":911}}, // [C8 Network] - {"hash":"c4ef7b6eeeda12ce","prefixes":{"":911}}, // [C8 Network] - {"hash":"e78c2b0997dfa249","prefixes":{"":912}}, // [Data Artist Inc.] - {"hash":"41045058078214c7","prefixes":{"":912}}, // [Data Artist Inc.] - {"hash":"c3b23588f42fedff","prefixes":{"":912}}, // [Data Artist Inc.] - {"hash":"5e94c742d87aa747","prefixes":{"":912}}, // [Data Artist Inc.] - {"hash":"52632067f5495750","prefixes":{"":913}}, // [AirFrance] - {"hash":"0b27c948dba1fda0","prefixes":{"":914}}, // [YDigital Media] - {"hash":"7e30f931dbb7180a","prefixes":{"":915}}, // [Zentrick] - {"hash":"b3e2285b6fc03e5c","prefixes":{"":915}}, // [Zentrick] - {"hash":"e2f247795ad2ad87","prefixes":{"":915}}, // [Zentrick] - {"hash":"1c9e4d1810e659fa","prefixes":{"":915}}, // [Zentrick] - {"hash":"57511b10e2ed4785","prefixes":{"":915}}, // [Zentrick] - {"hash":"c012faa898c62e0b","prefixes":{"":916}}, // [FreeBit Co. Ltd.] - {"hash":"41b7f33c961162f8","prefixes":{"":916}}, // [FreeBit Co. Ltd.] - {"hash":"ffac4f6c5fa1aa82","prefixes":{"":916}}, // [FreeBit Co. Ltd.] - {"hash":"a23bae62ab25e2c1","prefixes":{"":916}}, // [FreeBit Co. Ltd.] - {"hash":"ae08459757b76c12","prefixes":{"":916}}, // [FreeBit Co. Ltd.] - {"hash":"83efb7fcb7770789","prefixes":{"":917}}, // [Dennoo Inc.] - {"hash":"042456e26a44a268","prefixes":{"":917}}, // [Dennoo Inc.] - {"hash":"cb0b16dd50ba9d7d","prefixes":{"":917}}, // [Dennoo Inc.] - {"hash":"541a895df01d2b2c","prefixes":{"":917}}, // [Dennoo Inc.] - {"hash":"8f01887bb7405ab5","prefixes":{"":918}}, // [Adbrain] - {"hash":"33734a86766d25a9","prefixes":{"*":919}}, // [MSI-ACI Europe BV] - {"hash":"f66543f279b3e1e7","prefixes":{"":920}}, // [A.Mob] - {"hash":"74ee8c0f2fb31813","prefixes":{"":920}}, // [A.Mob] - {"hash":"584db20b69521f40","prefixes":{"":921}}, // [Toys R Us] - {"hash":"6a001e8bc99fede6","prefixes":{"":922}}, // [Geniee,Inc] - {"hash":"b8ff7bd604535ea9","prefixes":{"":922}}, // [Geniee,Inc] - {"hash":"3e005c1dfdc23488","prefixes":{"":923}}, // [Adsecure] - {"hash":"ddef7bb2f5930121","prefixes":{"":923}}, // [Adsecure] - {"hash":"424399e643b329b8","prefixes":{"":923}}, // [Adsecure] - {"hash":"23df92e04ec66406","prefixes":{"":923}}, // [Adsecure] - {"hash":"06f0caf4a4fe6ed1","prefixes":{"":923}}, // [Adsecure] - {"hash":"9697a0818cbd79bd","prefixes":{"":923}}, // [Adsecure] - {"hash":"ec7f61f2511ae20e","prefixes":{"":923}}, // [Adsecure] - {"hash":"fb4e7ecb8304ade4","prefixes":{"":924}}, // [Kimia Solutions SL] - {"hash":"84f5d0f934bd60f1","prefixes":{"":924}}, // [Kimia Solutions SL] - {"hash":"b3fcc22fcd382f3b","prefixes":{"":924}}, // [Kimia Solutions SL] - {"hash":"4eca8c3218b372ae","prefixes":{"":925}}, // [Sojern] - {"hash":"9636bfb4feaef839","prefixes":{"":925}}, // [Sojern] - {"hash":"a525b67906a4cb94","prefixes":{"":926}}, // [Where 2 Get It, Inc.] - {"hash":"6c959985d7181026","prefixes":{"":926}}, // [Where 2 Get It, Inc.] - {"hash":"7843db8d760e28cb","prefixes":{"":927}}, // [Tomoko Cloud] - {"hash":"eb7b756fc1f88aa1","prefixes":{"":927}}, // [Tomoko Cloud] - {"hash":"ed4d672687c27603","prefixes":{"":927}}, // [Tomoko Cloud] - {"hash":"b156b34e4d693e49","prefixes":{"":927}}, // [Tomoko Cloud] - {"hash":"16e146e87e7d0383","prefixes":{"":928}}, // [RevenueMantra] - {"hash":"1457bcbc098b2864","prefixes":{"":928}}, // [RevenueMantra] - {"hash":"450eb4f9273a5014","prefixes":{"":929}}, // [Automobile Ltd.] - {"hash":"436e4eaa95b23fcb","prefixes":{"":929}}, // [Automobile Ltd.] - {"hash":"a47ee9f00ca4f855","prefixes":{"":929}}, // [Automobile Ltd.] - {"hash":"889ab5643c83668a","prefixes":{"":929}}, // [Automobile Ltd.] - {"hash":"4ea96f1e0388da92","prefixes":{"":930}}, // [Big Mobile Group Pty Ltd] - {"hash":"ae895a18cc8c416e","prefixes":{"":930}}, // [Big Mobile Group Pty Ltd] - {"hash":"bbb46a7f5062448e","prefixes":{"":931}}, // [ADMIZED AG] - {"hash":"0b4e26a40cc2e647","prefixes":{"":932}}, // [Sparks47 s.r.l.] - {"hash":"745030cd7ac80402","prefixes":{"":933}}, // [Between Digital dba Intency DSP] - {"hash":"55db959f7c533183","prefixes":{"":933}}, // [Between Digital dba Intency DSP] - {"hash":"15b9a82ccbf2d7ff","prefixes":{"":933}}, // [Between Digital dba Intency DSP] - {"hash":"c63156716e201b52","prefixes":{"":933}}, // [Between Digital dba Intency DSP] - {"hash":"801d43da2c97866b","prefixes":{"":933}}, // [Between Digital dba Intency DSP] - {"hash":"d9f810d72012d4c4","prefixes":{"":934}}, // [eprofessional GmbH] - {"hash":"23c665bb68cc30cc","prefixes":{"":934}}, // [eprofessional GmbH] - {"hash":"8439b0c8f73daf02","prefixes":{"":934}}, // [eprofessional GmbH] - {"hash":"6a17378968b598f2","prefixes":{"":934}}, // [eprofessional GmbH] - {"hash":"4e157cd578931a97","prefixes":{"":935}}, // [Channel Factory, LLC] - {"hash":"9cdf7d74bee2159f","prefixes":{"":935}}, // [Channel Factory, LLC] - {"hash":"58c684b764b4dcab","prefixes":{"":935}}, // [Channel Factory, LLC] - {"hash":"85218b0017b8f452","prefixes":{"":935}}, // [Channel Factory, LLC] - {"hash":"9f790c0c14fa8f4d","prefixes":{"":935}}, // [Channel Factory, LLC] - {"hash":"e7518acf5c44d0a4","prefixes":{"":935}}, // [Channel Factory, LLC] - {"hash":"ec763282f8f41299","prefixes":{"":935}}, // [Channel Factory, LLC] - {"hash":"41a0870b895e73b4","prefixes":{"":936}}, // [ConvertStar Incorporated] - {"hash":"f0134a17d368f3d8","prefixes":{"":936}}, // [ConvertStar Incorporated] - {"hash":"58f6c00f44fb69a4","prefixes":{"":936}}, // [ConvertStar Incorporated] - {"hash":"ff266e088e3010a8","prefixes":{"":937}}, // [VisualDNA (Imagini)] - {"hash":"9748c01f8dcd17ee","prefixes":{"":937}}, // [VisualDNA (Imagini)] - {"hash":"8e588a0818c4fcfe","prefixes":{"":937}}, // [VisualDNA (Imagini)] - {"hash":"b499a754cc7d2c9b","prefixes":{"":937}}, // [VisualDNA (Imagini)] - {"hash":"12d363fa8ee6c4d3","prefixes":{"":938}}, // [Avocet] - {"hash":"a8bc7b6f66702489","prefixes":{"":938}}, // [Avocet] - {"hash":"5166d9515e755f19","prefixes":{"":939}}, // [Auction.com, LLC] - {"hash":"3adf8560ada830b8","prefixes":{"":939}}, // [Auction.com, LLC] - {"hash":"f24f87c799e92ae5","prefixes":{"":939}}, // [Auction.com, LLC] - {"hash":"a60dedada0fd44b6","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"5d0f226850d2e68c","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"e4c703070b535b17","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"bc640a9c42502b35","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"defb06aab760992e","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"9bb069524856b34b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ef253518584f013f","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"e100ba963ad8ecf2","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"05b625ee62d8685f","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"5a88fb5abffcefc9","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a31c0b568cc8ade8","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"3498894fbeac8341","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"1b82605c41d8b709","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"8138ad98ed0394e9","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"751c9ebb221bb5f9","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"8e3793804d9cdab5","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a6ecf1a95c24b6a3","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"d27fe0e9834e648b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"2a0c1b5a904b36c1","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"f0d6c35febee8008","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"3c01a29e3c9c7264","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"4a74fdfd9b816fe8","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"aac8184f18b04958","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"368da19a90db3ad7","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"116d46403c6e95b7","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"808bb76ca34e4d1b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"b19f61f6e35aaa6a","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"9afc01573e38d021","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"11d7bce194c54912","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a28be439cad08ebb","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"968792ffe85f5d2d","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"d3bc9343531e6c0c","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a11d6b37b0382222","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"4bf65fa3bba0c55b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"4e1d992a76cf0b41","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"b09978a8e01fdbca","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"7394b2453f854b55","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a76d4645267ff0be","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"7e60860df0c8945b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ed9d4b0db5598264","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"466d1d16f3a935fb","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"21ac8963bf026e1a","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"e04f72461a1fdf7d","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"549860a6933e906e","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ddc7313241f77312","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"d89759a9687ad368","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"343ad98c7f4d71ed","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"2cb5047919d88ce8","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"e9739ef3b0403f17","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"2b8ce30f483d90da","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"1d1295f1cdac7503","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"57fd3c09e35ca17e","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"050dd41acc3ac3be","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"1b853fb5c08c8727","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"1c3db6034ad5c3e7","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"4b87160668f51e71","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"d0875bc7510fbaa1","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a46257724ca67bc0","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"c93d397e8d1ed4fe","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"2cc9ae96e6b56fc3","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"051e72fcceee74d6","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"c24b55da2a7095df","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"88981afd076ecf48","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"1ac94ef8a4d9ad5b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a57f0b4ed1a21743","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ff1f4076329cb91c","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"1ea1f1044008a0b2","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"5a9b4bfa6d03c94b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"16bcde8365ddf2a8","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"7d3732d8588ffedb","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"fb6a6a06e4463001","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"e357e4b19a11617f","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ee8aa73feac66773","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"8aa426bd808ed75a","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"e5eed224f946f11b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"3b812696a50c7061","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a04facc89859d775","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"37413c9d9331953b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"d5713942d78606a2","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"52e5d6a64f47f9e8","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"bb49554e8d0ea27b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"9943d790e614f45a","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"b318da3bafca323e","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"8a6b3193ebf16daf","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"9a4e890c561b1f49","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"38f8c78460d0c171","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ceb269cfcbfb3737","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"6fc86b42fa2194c7","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"948868ae3f88e79e","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"f78a9daa44503acf","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"f065b37e2901ab44","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"03f33a24e5022801","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"db26cef88e3879aa","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"496f2ff2ad72eb56","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a33121c16f5e4d20","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"1b5947476f3297ae","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ebaf8ad16d676e4c","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"5ec7e2368a41e7d1","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"f69cbc017c2bee73","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"e2095571b8a4a9ea","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"8e8d9589ff612ccb","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"4acd7b14907d2eab","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"00b864b32ca6962f","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"0e7fd376997e0ce6","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"8947ab8c7604a712","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"12ae4c62954fb4d7","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"5c70a05c3b20c460","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"7920d4521fc31742","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"14d71824a114fae5","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"5c58f35ad11ea36d","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"c68ff69ac1086025","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"bfb4d21e0325cd27","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"1c5328ce70a7d781","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"3a27002f3a51c391","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"15c536b247ddda9b","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"b8dfd3383dc85e16","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"d834384f74b37311","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"33ad4a447e65fe4c","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"5f92334372103621","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"3b36a0673402ddea","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"b8e5c9de8614511a","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"d6f7d0951a76b20f","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"dc731362e0a22c79","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"c37ce29a12797b87","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"b7f8903cecd3c417","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"e9aea7737083ea26","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ca849c611df2249c","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"eba969c354d1a2b6","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"828d340d34a88e86","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"18a6bfdbc9b1999c","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"ea6de81ee5ad1594","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"46f992eaa836ef3e","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"a6c162781ee3a889","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"926d3b6bfe6e1943","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"7e15147781f2b0b4","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"221d9d61291eac74","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"d1c16d1fc4dc5998","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"25596ab30bae8f45","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"6f03c0e16553edcf","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"af7ab58246b4c2a5","prefixes":{"":940}}, // [White Ops, Inc.] - {"hash":"82a102dc6f580854","prefixes":{"":773}}, // [Extreme Reach Digital (ER Digital)] - {"hash":"35f665efa0e2a55c","prefixes":{"":773}}, // [Extreme Reach Digital (ER Digital)] - {"hash":"3a48f18bf340e74a","prefixes":{"":773}}, // [Extreme Reach Digital (ER Digital)] - {"hash":"ee365433c62a5c89","prefixes":{"":773}}, // [Extreme Reach Digital (ER Digital)] - {"hash":"abe6370bd088dcf4","prefixes":{"":941}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] - {"hash":"92dba1cdcba82d8a","prefixes":{"":941}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] - {"hash":"ee0ccb57505565f7","prefixes":{"":941}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] - {"hash":"f25f5e4909b9792c","prefixes":{"":941}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] - {"hash":"8a9e6f7beeb084c5","prefixes":{"":941}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] - {"hash":"94542c85cf399cbd","prefixes":{"":323}}, // [FinanceGenerator] - {"hash":"46ea052a92233562","prefixes":{"":942}}, // [Admetrics] - {"hash":"ea5d2271464ca35f","prefixes":{"":942}}, // [Admetrics] - {"hash":"286ad0ac5bca5d03","prefixes":{"":943}}, // [Admetrics GmbH] - {"hash":"ff1ffc67b7e3f33a","prefixes":{"":944}}, // [Amobee Inc. d/b/a Gradient X] - {"hash":"72cbc5ba1ec93b34","prefixes":{"":944}}, // [Amobee Inc. d/b/a Gradient X] - {"hash":"a6fc31f82c22f31e","prefixes":{"":25}}, // [Action Exchange, Inc.] - {"hash":"1ba64e5bb4889fc8","prefixes":{"":25}}, // [Action Exchange, Inc.] - {"hash":"1d75cb11df01626e","prefixes":{"":25}}, // [Action Exchange, Inc.] - {"hash":"91e1e972e2691a51","prefixes":{"":25}}, // [Action Exchange, Inc.] - {"hash":"23c68afb7d193b79","prefixes":{"":25}}, // [Action Exchange, Inc.] - {"hash":"63d64a70ef1c102e","prefixes":{"":792}}, // [Recruit Co., Ltd. Communications] - {"hash":"5e5fd982d3646780","prefixes":{"":793}}, // [RECRUIT Communications] - {"hash":"405787e06ebc6f4c","prefixes":{"dc":945}}, // [Datalicious Pty Ltd] - {"hash":"cddbf4fe7458433c","prefixes":{"":945}}, // [Datalicious Pty Ltd] - {"hash":"1c6b7ff16564ebc9","prefixes":{"":945}}, // [Datalicious Pty Ltd] - {"hash":"8b00b076af37f543","prefixes":{"":945}}, // [Datalicious Pty Ltd] - {"hash":"688a0b3764e1741b","prefixes":{"":946}}, // [Intent Media] - {"hash":"04bba28fa6468b07","prefixes":{"":947}}, // [AdNear Pte Ltd.] - {"hash":"c10761fe93bddd5e","prefixes":{"":948}}, // [Zhejiang MediaAdx Network Technology Co., Ltd] - {"hash":"3b0fa4efa209ebfa","prefixes":{"":948}}, // [Zhejiang MediaAdx Network Technology Co., Ltd] - {"hash":"e1b43b2549b03858","prefixes":{"":949}}, // [Harris Interactive] - {"hash":"6ade6d73b73295b2","prefixes":{"":949}}, // [Harris Interactive] - {"hash":"63efa9155999d096","prefixes":{"":950}}, // [BuzzCity Pte Ltd] - {"hash":"564ef19eef7254cf","prefixes":{"":950}}, // [BuzzCity Pte Ltd] - {"hash":"6bbdf3ce8eec6f52","prefixes":{"":950}}, // [BuzzCity Pte Ltd] - {"hash":"436cc9ab781b357d","prefixes":{"":950}}, // [BuzzCity Pte Ltd] - {"hash":"e8cc8c7d43c80259","prefixes":{"":951}}, // [Sputnyx] - {"hash":"2150e4c3d0f193b2","prefixes":{"":951}}, // [Sputnyx] - {"hash":"54d8f3466d5879bc","prefixes":{"":951}}, // [Sputnyx] - {"hash":"b930b780e12735dc","prefixes":{"":951}}, // [Sputnyx] - {"hash":"5817597124959a0f","prefixes":{"":952}}, // [CyberZ, Inc] - {"hash":"52a82bf3bc55f753","prefixes":{"":953}}, // [Spark Networks USA, LLC] - {"hash":"de87290af2710dce","prefixes":{"*":954}}, // [Ezakus] - {"hash":"9fa7220a9c513b82","prefixes":{"":955}}, // [V4x SAS] - {"hash":"cdea2314328db64c","prefixes":{"":955}}, // [V4x SAS] - {"hash":"9ab122baee7ffef0","prefixes":{"":955}}, // [V4x SAS] - {"hash":"5b7763887f62f0e2","prefixes":{"":956}}, // [Tapjoy Inc.] - {"hash":"c476f40a640ceb50","prefixes":{"":12}}, // [Madeleine Mode GmbH] - {"hash":"b03d8c48bc51a903","prefixes":{"":12}}, // [Madeleine Mode GmbH] - {"hash":"6ccfc9d681f1cce4","prefixes":{"":12}}, // [Madeleine Mode GmbH] - {"hash":"519e706c784712cd","prefixes":{"":12}}, // [Madeleine Mode GmbH] - {"hash":"4b99f5579a32d2ac","prefixes":{"":957}}, // [EXEBID] - {"hash":"7bb686f653faf750","prefixes":{"":957}}, // [EXEBID] - {"hash":"59bac04b09eb1850","prefixes":{"":957}}, // [EXEBID] - {"hash":"c7c593cac7252275","prefixes":{"":957}}, // [EXEBID] - {"hash":"9a2642b29d01ad5a","prefixes":{"":957}}, // [EXEBID] - {"hash":"19e7f2b697047d0f","prefixes":{"":957}}, // [EXEBID] - {"hash":"9ccde0a8ec3f9703","prefixes":{"":957}}, // [EXEBID] - {"hash":"a81ee6b474d31f53","prefixes":{"":957}}, // [EXEBID] - {"hash":"3b743b2fda71dd5f","prefixes":{"":957}}, // [EXEBID] - {"hash":"6378b3090ceafcf5","prefixes":{"*":562}}, // [Bannerflow AB] - {"hash":"09de5d9c8a08a419","prefixes":{"":958}}, // [Exposebox Ltd] - {"hash":"d88faf97f4f01be0","prefixes":{"":958}}, // [Exposebox Ltd] - {"hash":"1b7a8075387597c8","prefixes":{"":958}}, // [Exposebox Ltd] - {"hash":"42127ec758c4891f","prefixes":{"":959}}, // [MotoMiner] - {"hash":"f5fe6be8ffc65b45","prefixes":{"":960}}, // [BuySellAds.com Inc.] - {"hash":"4b9190a98d317014","prefixes":{"":960}}, // [BuySellAds.com Inc.] - {"hash":"3b55d9e306cf9e3c","prefixes":{"":960}}, // [BuySellAds.com Inc.] - {"hash":"a9da15f40498a6f3","prefixes":{"":961}}, // [Viking River Cruises] - {"hash":"e2a8d45a4c55dae6","prefixes":{"":962}}, // [Sittercity Incorporated] - {"hash":"5dd667d71a34b766","prefixes":{"":963}}, // [Facetz] - {"hash":"00776ec1c54574db","prefixes":{"":964}}, // [YOOSE Pte. Ltd.] - {"hash":"f8c3b8f01c62da3c","prefixes":{"":965}}, // [AdYapper, Inc.] - {"hash":"6c1804db5b6679a9","prefixes":{"":965}}, // [AdYapper, Inc.] - {"hash":"a361de3d2fcba609","prefixes":{"":965}}, // [AdYapper, Inc.] - {"hash":"1f805f58db63a12d","prefixes":{"":965}}, // [AdYapper, Inc.] - {"hash":"7090e79d9ec26768","prefixes":{"":965}}, // [AdYapper, Inc.] - {"hash":"727b3d1100b7d374","prefixes":{"":965}}, // [AdYapper, Inc.] - {"hash":"17b396141b96c236","prefixes":{"":799}}, // [Webtrekk GmbH] - {"hash":"b0149857f43862d9","prefixes":{"":144}}, // [Spark Flow S.A.] - {"hash":"f9c00c55ede792e2","prefixes":{"":144}}, // [Spark Flow S.A.] - {"hash":"8eb24e3340b4c707","prefixes":{"":966}}, // [Clickky LLP DBA Clickky] - {"hash":"2d27319a70fb6262","prefixes":{"":966}}, // [Clickky LLP DBA Clickky] - {"hash":"b140173de591dc64","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"7e2351ba05fefcce","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"88a66ce20c2df5e5","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"705049ea1378adbd","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"eadabb19d68f0e02","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"684343b8f00b5425","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"fedb0d981d151071","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"eb26cb8958b84143","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"b4f42222ff48d611","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"3ef4c3e15c3889aa","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"346ac90e489a0d27","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"f0685c9d889a4dcb","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"d2ae279176821009","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"98746e3eb9075cc5","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"f4e7d8ca3f73252e","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"145b69a5570cf0e6","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"b96e6d8a02aabe0f","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"5f7def46ec1776af","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"075bdf0eaa8a2d8a","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"374ddadb8b59c656","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"e75f11f13cf01278","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"fa9f043b6f9f0225","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"51046da25aac136a","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"e3d9117eacb00b04","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"2291e11d2ca518c2","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"208af3ccccac3a8a","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"8a7e7a7c9510a539","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"d62dab4e5ba19318","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"9faead99b5b9e15b","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"21ad4e7e6897e7be","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"5f6e332f4f7ad081","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"28c87295fd99d9b2","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"ec4d1f8447f0f153","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"bcdb0c5a6199a121","prefixes":{"":967}}, // [eRate Online Measurement Solutions Ltd.] - {"hash":"dc1424820d5085bc","prefixes":{"":967}}, // [eRate Online Measurement Solutions Ltd.] - {"hash":"d110aae732b1c80d","prefixes":{"":967}}, // [eRate Online Measurement Solutions Ltd.] - {"hash":"d461527c3648da49","prefixes":{"":968}}, // [Baumann Ber Rivnay] - {"hash":"da8fb21c56d3c224","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"abb93e258191198a","prefixes":{"":13}}, // [TripAdvisor LLC] - {"hash":"67ce1b0bfa972cc4","prefixes":{"":969}}, // [TUI Connect GmbH] - {"hash":"63c1158e18a5ea27","prefixes":{"":970}}, // [INFOnline GmbH] - {"hash":"46a40e7a328ffee7","prefixes":{"":971}}, // [Adizio] - {"hash":"c5ca269d09b7c25f","prefixes":{"*":972}}, // [Joystick Interactive] - {"hash":"93d3775dcb79f65b","prefixes":{"":973}}, // [EngageClick Inc] - {"hash":"0af466da8ca75d0b","prefixes":{"":973}}, // [EngageClick Inc] - {"hash":"111ffa6b19238149","prefixes":{"":974}}, // [GeeeN, Inc.] - {"hash":"46313b1a2b164e11","prefixes":{"":974}}, // [GeeeN, Inc.] - {"hash":"edeaec47d1406cde","prefixes":{"":974}}, // [GeeeN, Inc.] - {"hash":"e4fa03e71e81c717","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"b05d395ad43a6851","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"babf6252b4c60056","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"db86fc0d97ddf866","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"ea61f7fa94bc2f36","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"495d58c312a856b7","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"ce854368c8ba8c24","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"7c1ebbe2aa48388d","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"0ac9c5956a237913","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"5e22bf81f1721d99","prefixes":{"":975}}, // [Arbigo Inc.] - {"hash":"ad1ab56f3a151112","prefixes":{"":976}}, // [FlxOne BV] - {"hash":"6c833f73351e1f63","prefixes":{"":976}}, // [FlxOne BV] - {"hash":"82f8d328de710cff","prefixes":{"":976}}, // [FlxOne BV] - {"hash":"95c33537789a441f","prefixes":{"":976}}, // [FlxOne BV] - {"hash":"712c817d06c0d8c6","prefixes":{"":976}}, // [FlxOne BV] - {"hash":"63446ae2652818ab","prefixes":{"":10}}, // [eBay] - {"hash":"45b598cd3bc42672","prefixes":{"":977}}, // [ExtendTV, Inc.] - {"hash":"372fde25faa1c878","prefixes":{"":977}}, // [ExtendTV, Inc.] - {"hash":"db1da0ef7de3e1e6","prefixes":{"":978}}, // [momentM, Inc] - {"hash":"5710705dc9e3a971","prefixes":{"":978}}, // [momentM, Inc] - {"hash":"4ee3d75e96c8e3aa","prefixes":{"":978}}, // [momentM, Inc] - {"hash":"b329682f26e4fcc6","prefixes":{"":978}}, // [momentM, Inc] - {"hash":"71215c2931d79d35","prefixes":{"":978}}, // [momentM, Inc] - {"hash":"667a4fe4b59238b9","prefixes":{"":978}}, // [momentM, Inc] - {"hash":"a7fbeec4fb43f9d9","prefixes":{"*":979}}, // [OnCard Marketing dba RevTrax] - {"hash":"48a825a8c5ef9edd","prefixes":{"*":980}}, // [Youtube - API] - {"hash":"19459fb447ce4de4","prefixes":{"*":981}}, // [Thirdpresence Ltd] - {"hash":"f079f0a998c56593","prefixes":{"":981}}, // [Thirdpresence Ltd] - {"hash":"108dd21ceba53008","prefixes":{"":982}}, // [Visual IQ, Inc.] - {"hash":"7b0dec2d8c1ec967","prefixes":{"":982}}, // [Visual IQ, Inc.] - {"hash":"b5c3f2f3c1c37850","prefixes":{"":982}}, // [Visual IQ, Inc.] - {"hash":"ba9b4eca15987843","prefixes":{"":982}}, // [Visual IQ, Inc.] - {"hash":"4e521b3e57d76700","prefixes":{"":982}}, // [Visual IQ, Inc.] - {"hash":"29ab2715e00761b0","prefixes":{"":982}}, // [Visual IQ, Inc.] - {"hash":"688071d177180274","prefixes":{"":982}}, // [Visual IQ, Inc.] - {"hash":"4e682c7f7f2ebda9","prefixes":{"":983}}, // [Appreciate] - {"hash":"4b6451297bd74247","prefixes":{"":983}}, // [Appreciate] - {"hash":"95d75ced1262ecda","prefixes":{"":983}}, // [Appreciate] - {"hash":"ac94d70ffcad5c63","prefixes":{"":983}}, // [Appreciate] - {"hash":"578591a9eb9652f2","prefixes":{"":983}}, // [Appreciate] - {"hash":"5528666c72c7c71c","prefixes":{"":983}}, // [Appreciate] - {"hash":"3deb1cd39233765c","prefixes":{"":983}}, // [Appreciate] - {"hash":"4e05303df9f07532","prefixes":{"":983}}, // [Appreciate] - {"hash":"99936e7e455c77b7","prefixes":{"":983}}, // [Appreciate] - {"hash":"f0e5986dc65416fd","prefixes":{"":983}}, // [Appreciate] - {"hash":"5095285a4ab05444","prefixes":{"":983}}, // [Appreciate] - {"hash":"670fdb8ea86ee233","prefixes":{"":983}}, // [Appreciate] - {"hash":"ad2de628bc6fd483","prefixes":{"":984,"lodeo-":985}}, // [CyberAgent d/b/a GameLogic] [Lodeo] - {"hash":"66e26f9703f2dd67","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"80451e302607b653","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"bb7f7e8ab7b99724","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"51dbfc01b5f86601","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"2a8926ec16272a9b","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"4e2334aabf3b7af7","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"1bbabf05900a894f","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"a45a66788779b9bc","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"c4d101005526e2ef","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"7f43400e381d40ff","prefixes":{"":984}}, // [CyberAgent d/b/a GameLogic] - {"hash":"e323697c02e1a841","prefixes":{"":986}}, // [CyberAgent d/b/a Dynalyst] - {"hash":"11c505e6e21eefdb","prefixes":{"":986}}, // [CyberAgent d/b/a Dynalyst] - {"hash":"afc246433e1284d8","prefixes":{"":986}}, // [CyberAgent d/b/a Dynalyst] - {"hash":"c7322cd1ad0df953","prefixes":{"":986}}, // [CyberAgent d/b/a Dynalyst] - {"hash":"a090f61c3ee5e028","prefixes":{"":986}}, // [CyberAgent d/b/a Dynalyst] - {"hash":"846ea0c461560421","prefixes":{"":985}}, // [Lodeo] - {"hash":"b433db4fe5f6ed8d","prefixes":{"":987}}, // [LTD "RTB-MEDIA"] - {"hash":"e5b814009c134495","prefixes":{"":987}}, // [LTD "RTB-MEDIA"] - {"hash":"ce6b2c64ce23d14b","prefixes":{"":988}}, // [Maverick., inc.] - {"hash":"32ef8d9c49285e19","prefixes":{"":988}}, // [Maverick., inc.] - {"hash":"ac99e3b588f1d521","prefixes":{"":989}}, // [FuelX] - {"hash":"da9e2cf59b85610c","prefixes":{"":989}}, // [FuelX] - {"hash":"d86ff177f1095173","prefixes":{"":989}}, // [FuelX] - {"hash":"7853aedd1a267ffb","prefixes":{"":989}}, // [FuelX] - {"hash":"5f505217689c7174","prefixes":{"":989}}, // [FuelX] - {"hash":"27be294cbea039f9","prefixes":{"":989}}, // [FuelX] - {"hash":"bae6c7f50fff772d","prefixes":{"":989}}, // [FuelX] - {"hash":"63f51428712678ec","prefixes":{"":324}}, // [Telstra Corporation] - {"hash":"f1d920cc5a16b3a9","prefixes":{"":986}}, // [CyberAgent d/b/a Dynalyst] - {"hash":"12a7d7495eb7a91f","prefixes":{"":986}}, // [CyberAgent d/b/a Dynalyst] - {"hash":"6ca28320c4765235","prefixes":{"":986}}, // [CyberAgent d/b/a Dynalyst] - {"hash":"aeb171f5cad96320","prefixes":{"":990}}, // [Tutor.com] - {"hash":"5de1c09366f03919","prefixes":{"":164}}, // [wayStorm Co., Ltd.] - {"hash":"9977349a2c52c1b6","prefixes":{"":164}}, // [wayStorm Co., Ltd.] - {"hash":"3304d732f88c9ebe","prefixes":{"":164}}, // [wayStorm Co., Ltd.] - {"hash":"8b1b0e966a643448","prefixes":{"":164}}, // [wayStorm Co., Ltd.] - {"hash":"852a445ec03b1224","prefixes":{"":991}}, // [Rebelmouse] - {"hash":"ebef84a02e75b037","prefixes":{"*":991}}, // [Rebelmouse] - {"hash":"6ff0ad7b4d4ee2ef","prefixes":{"":992}}, // [Adviator DSP] - {"hash":"c28458e1bac3bb75","prefixes":{"":992}}, // [Adviator DSP] - {"hash":"041403702e0ae9f0","prefixes":{"":992}}, // [Adviator DSP] - {"hash":"31890d74ba2e0eca","prefixes":{"":525}}, // [abilicom GmbH] - {"hash":"16f2a07aea3c767b","prefixes":{"":993}}, // [Acens Technologies, S.L.] - {"hash":"4d628548402f2933","prefixes":{"":994}}, // [Yashi, Inc] - {"hash":"6f97eb7dc578c1d3","prefixes":{"":677}}, // [Taptica] - {"hash":"843cc26d64f855e3","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"7f81eef44c27fceb","prefixes":{"":495}}, // [Miaozhen Systems] - {"hash":"f36b5e4d896bf9fc","prefixes":{"":995}}, // [Flaminem Inc] - {"hash":"ec10935d7141abb2","prefixes":{"":996}}, // [Mediahead AG] - {"hash":"d074e5017286d25a","prefixes":{"":996}}, // [Mediahead AG] - {"hash":"e7aa39274c05edf9","prefixes":{"":996}}, // [Mediahead AG] - {"hash":"06d3dae2f13a3dcb","prefixes":{"":996}}, // [Mediahead AG] - {"hash":"1cc1098f16d364e0","prefixes":{"":996}}, // [Mediahead AG] - {"hash":"15ee5953d4c74d48","prefixes":{"":996}}, // [Mediahead AG] - {"hash":"b434b094029a84a8","prefixes":{"":997}}, // [Admixer] - {"hash":"766b3c24fb448820","prefixes":{"":998}}, // [Communication Services Tele2 GmbH] - {"hash":"35e369c14d37cb1c","prefixes":{"*":999}}, // [99click] - {"hash":"c5b7e1faef274865","prefixes":{"":1000}}, // [Ströer Mobile Media GmbH] - {"hash":"dc01aead4f784e4d","prefixes":{"":1000}}, // [Ströer Mobile Media GmbH] - {"hash":"39525c5c19a8a044","prefixes":{"":1000}}, // [Ströer Mobile Media GmbH] - {"hash":"397c77c97b7945f8","prefixes":{"":1000}}, // [Ströer Mobile Media GmbH] - {"hash":"3097a79ab689b320","prefixes":{"":1000}}, // [Ströer Mobile Media GmbH] - {"hash":"c27b66085df0c0da","prefixes":{"":1001}}, // [LLC "Life Style"] - {"hash":"3a7913ff0ce0cc21","prefixes":{"":1001}}, // [LLC "Life Style"] - {"hash":"9d1661dae6ff9304","prefixes":{"":1001}}, // [LLC "Life Style"] - {"hash":"cdd266d878d3e9d5","prefixes":{"":1001}}, // [LLC "Life Style"] - {"hash":"d3c08bf02865217e","prefixes":{"":1002}}, // [2Y Media SAS (adserverpub)] - {"hash":"bbfe70399cd3fcb3","prefixes":{"":1002}}, // [2Y Media SAS (adserverpub)] - {"hash":"a17b33aa94234849","prefixes":{"":1002}}, // [2Y Media SAS (adserverpub)] - {"hash":"8e073ea44074ec93","prefixes":{"":1002}}, // [2Y Media SAS (adserverpub)] - {"hash":"12afa442e09a07c9","prefixes":{"":1002}}, // [2Y Media SAS (adserverpub)] - {"hash":"7fa5ca05a3a0b474","prefixes":{"":1003}}, // [Platform IQ] - {"hash":"f6f0f65947909d69","prefixes":{"":1003}}, // [Platform IQ] - {"hash":"77b586602e5dec3f","prefixes":{"":1003}}, // [Platform IQ] - {"hash":"bba327f348a4693e","prefixes":{"":1003}}, // [Platform IQ] - {"hash":"a544a3b25c4d5113","prefixes":{"":1004}}, // [Silveredge Inc] - {"hash":"037cb4c9598870ee","prefixes":{"":1004}}, // [Silveredge Inc] - {"hash":"2a6ed7c1b6a78509","prefixes":{"":1004}}, // [Silveredge Inc] - {"hash":"06d23751a7963710","prefixes":{"":1005}}, // [SMADEX] - {"hash":"0a84ed2865a74b8a","prefixes":{"":1005}}, // [SMADEX] - {"hash":"e97a828dc740d645","prefixes":{"":1005}}, // [SMADEX] - {"hash":"aa964a10a9ccda5b","prefixes":{"":1005}}, // [SMADEX] - {"hash":"d7675b344c153db3","prefixes":{"":1005}}, // [SMADEX] - {"hash":"76eaf06fcd95e8e8","prefixes":{"":1006}}, // [Suzu Muchi] - {"hash":"fcf26d7eec95d72d","prefixes":{"":342}}, // [LOKA Research inc.] - {"hash":"3b238a232cbc26bd","prefixes":{"":342}}, // [LOKA Research inc.] - {"hash":"a544c76e7ec01862","prefixes":{"":342}}, // [LOKA Research inc.] - {"hash":"7922cb2f11972c85","prefixes":{"":342}}, // [LOKA Research inc.] - {"hash":"613551ec99bec944","prefixes":{"":1007}}, // [PlannTo Technologies Private Limited] - {"hash":"bb07eb5de3d95038","prefixes":{"":1007}}, // [PlannTo Technologies Private Limited] - {"hash":"729165b2ebbc6996","prefixes":{"":1007}}, // [PlannTo Technologies Private Limited] - {"hash":"187629571e0338cf","prefixes":{"":1007}}, // [PlannTo Technologies Private Limited] - {"hash":"bdd2e20221d7e507","prefixes":{"":1008}}, // [Viator, Inc] - {"hash":"1caf0cb7a05c5941","prefixes":{"":474}}, // [Xrost] - {"hash":"4c6513300966da08","prefixes":{"":1009}}, // [NODDINGTON TECHNOLOGIES LIMITED] - {"hash":"2c1d6a203788af2b","prefixes":{"":1010}}, // [Plan Blue Ltd] - {"hash":"3c0da2d4356e18a5","prefixes":{"":1010}}, // [Plan Blue Ltd] - {"hash":"2568d0917d238964","prefixes":{"":1011}}, // [Addictive Mobility] - {"hash":"f79c822ab2cca8ee","prefixes":{"":1011}}, // [Addictive Mobility] - {"hash":"c610b5ae3df923cd","prefixes":{"":1011}}, // [Addictive Mobility] - {"hash":"a614ab43bf2d906b","prefixes":{"":1011}}, // [Addictive Mobility] - {"hash":"747eeca86822f19c","prefixes":{"":1012}}, // [A6 Corporation] - {"hash":"cbdfdaeff8b7d933","prefixes":{"":1013}}, // [Mobusi Mobile Advertising] - {"hash":"5b2d082c7811bead","prefixes":{"":1014}}, // [Wishabi] - {"hash":"c811726b56670631","prefixes":{"":1014}}, // [Wishabi] - {"hash":"09d3b27d380d43a0","prefixes":{"":1014}}, // [Wishabi] - {"hash":"eecab68f555d1f9f","prefixes":{"":1015}}, // [onAd GmbH] - {"hash":"d05b20f38e3bb984","prefixes":{"":1016}}, // [Media Decision GmbH] - {"hash":"4a9b8dddd4c7567c","prefixes":{"":1016}}, // [Media Decision GmbH] - {"hash":"5121142c1b8676fd","prefixes":{"":1016}}, // [Media Decision GmbH] - {"hash":"e083b93d5a87c743","prefixes":{"":1017}}, // [Unitymedia NRW] - {"hash":"c665046a783daa9d","prefixes":{"":1018}}, // [Nowspots INC, dba Perfect Audience] - {"hash":"663d5fdbc3a52729","prefixes":{"":1018}}, // [Nowspots INC, dba Perfect Audience] - {"hash":"991e96310871571b","prefixes":{"":1018}}, // [Nowspots INC, dba Perfect Audience] - {"hash":"0503d775704af4a2","prefixes":{"":1018}}, // [Nowspots INC, dba Perfect Audience] - {"hash":"2c55a4ef41531200","prefixes":{"":1019}}, // [Avis] - {"hash":"62817a7d196cbdcf","prefixes":{"":1020}}, // [Comcast] - {"hash":"2e4bb1f4d07f3180","prefixes":{"":1021}}, // [Jack Spade] - {"hash":"e7d369c24971784a","prefixes":{"":1022}}, // [Dollar General] - {"hash":"a8c9b93aae571ed5","prefixes":{"":1023}}, // [Care.com] - {"hash":"713775b7bc737116","prefixes":{"":1024}}, // [Clickagy, LLC] - {"hash":"8b6b45c1c86cd84b","prefixes":{"":1024}}, // [Clickagy, LLC] - {"hash":"34376185507c9ae0","prefixes":{"":1025}}, // [GlobalWebIndex] - {"hash":"8b17254892b590e3","prefixes":{"":1026}}, // [King.com] - {"hash":"8516f044f19bb3ae","prefixes":{"":1026}}, // [King.com] - {"hash":"d811179454fad251","prefixes":{"":1026}}, // [King.com] - {"hash":"946611fa2f4b067a","prefixes":{"":1027}}, // [Proquire LLC - Accenture] - {"hash":"cfa0e9025fcd5712","prefixes":{"":1028}}, // [Dynamic Yield] - {"hash":"b01c0239af74e72e","prefixes":{"":1028}}, // [Dynamic Yield] - {"hash":"e480d03a980d3bfc","prefixes":{"":1028}}, // [Dynamic Yield] - {"hash":"f22fe7e17c58b562","prefixes":{"":1028}}, // [Dynamic Yield] - {"hash":"f8da77c2fdcad106","prefixes":{"":1028}}, // [Dynamic Yield] - {"hash":"cea3bf4afe576984","prefixes":{"":1029}}, // [Charter business] - {"hash":"f8029c009186163f","prefixes":{"":1030}}, // [The ADEX] - {"hash":"61b09f5d40722e69","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"aa63151485efa109","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"ed37e98f45f10404","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"f9fda879239cf155","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"2dfe9124b3439d80","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"93a9e29ac238b380","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"5e080b13877f5de7","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"eb22d59d4594e6d3","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"a28e072f804db6a0","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"945f948eacea62c9","prefixes":{"":1031}}, // [OneDigitalAd Technologies] - {"hash":"b85074a47da33b83","prefixes":{"":1032}}, // [ADJUST] - {"hash":"9f369deb4f045364","prefixes":{"":1032}}, // [ADJUST] - {"hash":"2d6ceeb3eef38c27","prefixes":{"":1032}}, // [ADJUST] - {"hash":"65240dfe37410021","prefixes":{"":1032}}, // [ADJUST] - {"hash":"8d3dfcbf705f1191","prefixes":{"":1032}}, // [ADJUST] - {"hash":"a2d35afc9c960448","prefixes":{"":1033}}, // [ADmantX, SPA] - {"hash":"3d3bc7f00b0ef8d3","prefixes":{"":1034}}, // [Sogou.com] - {"hash":"9eb0737a130a6f08","prefixes":{"":1035}}, // [xAd, Inc.] - {"hash":"a9ff22250a3c29a1","prefixes":{"":1036}}, // [VideoBlocks] - {"hash":"61f27a00f3821730","prefixes":{"":1037}}, // [GraphicStocks] - {"hash":"f0792974945e7184","prefixes":{"":1038}}, // [Microsoft Advertising] - {"hash":"81f27cb33d47894b","prefixes":{"":1039}}, // [Rontar LTD] - {"hash":"e8ae13fee2796336","prefixes":{"":1039}}, // [Rontar LTD] - {"hash":"d979ed3048a9dc3d","prefixes":{"":1039}}, // [Rontar LTD] - {"hash":"187c7e948b323b3a","prefixes":{"dsp":1039}}, // [Rontar LTD] - {"hash":"1a5cfb3f08b31682","prefixes":{"":1040}}, // [Torrential, Inc.] - {"hash":"99bb29582234b47b","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"14a0b2d821feb403","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"4bfb576f457b3ad4","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"d2e32e6a4c73344a","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"3c8aa320c0f5fb9d","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"8c3cae9472fcd43a","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"18d673cc147eaba5","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"fb4f3dd591cba22f","prefixes":{"bid":1041}}, // [AMoAd, Inc.] - {"hash":"49aef0b488ab5024","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"2b7a17458f44228d","prefixes":{"":1041}}, // [AMoAd, Inc.] - {"hash":"7458cceedc9f812a","prefixes":{"":1042}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] - {"hash":"dd47f178c7a055b8","prefixes":{"":1042}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] - {"hash":"db975b73da86c8ce","prefixes":{"":1042}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] - {"hash":"6216946891299e23","prefixes":{"":1042}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] - {"hash":"52ee7c22862a74b6","prefixes":{"":1042}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] - {"hash":"3cc39e801c92ea89","prefixes":{"":1043}}, // [Jampp/Devego S.A.] - {"hash":"31fb8bdf6d2b85d2","prefixes":{"":1044}}, // [Placed] - {"hash":"a6ff57032773fe41","prefixes":{"*":1045}}, // [Digitas Health] - {"hash":"caeb7a071a866e17","prefixes":{"*":1045}}, // [Digitas Health] - {"hash":"6e946f5fac72b0b4","prefixes":{"":1046}}, // [Answer Media, LLC] - {"hash":"31c3d7ef1499fd26","prefixes":{"":1046}}, // [Answer Media, LLC] - {"hash":"d681f06391c7f861","prefixes":{"":1047}}, // [1000mercis] - {"hash":"1549387212aaa6a3","prefixes":{"":1048}}, // [Upstart Network, Inc.] - {"hash":"e48f63d27d0c24c7","prefixes":{"":1049}}, // [Forensiq, LLC] - {"hash":"7579b1341b228f93","prefixes":{"":1049}}, // [Forensiq, LLC] - {"hash":"f2e570ecd2540399","prefixes":{"":1050}}, // [LoopMe Ltd] - {"hash":"7d4e3b90001a044e","prefixes":{"":1050}}, // [LoopMe Ltd] - {"hash":"528167a286d8ccf8","prefixes":{"":1051}}, // [Bannerlink (Liquidus)] - {"hash":"0b2d528c0dce5bba","prefixes":{"":1051}}, // [Bannerlink (Liquidus)] - {"hash":"758cc459053712cf","prefixes":{"":1051}}, // [Bannerlink (Liquidus)] - {"hash":"57951d4b34add1a9","prefixes":{"":1051}}, // [Bannerlink (Liquidus)] - {"hash":"3d93ddd2b1dc8083","prefixes":{"":1051}}, // [Bannerlink (Liquidus)] - {"hash":"88f0b992c4b99c65","prefixes":{"":1052}}, // [Dell Inc.] - {"hash":"f8820430b42f89f8","prefixes":{"":1053}}, // [My Perfect Resume] - {"hash":"6f018db6d7a12f5e","prefixes":{"":1054}}, // [Ajillion Max Ltd] - {"hash":"b591ab0baccecd52","prefixes":{"":1054}}, // [Ajillion Max Ltd] - {"hash":"35d86d0121dbd41a","prefixes":{"":1055}}, // [Swelen France SA.] - {"hash":"25b09dd0e8d2977a","prefixes":{"":1055}}, // [Swelen France SA.] - {"hash":"f5cb853eba61193a","prefixes":{"":1056}}, // [Sleeptrain] - {"hash":"c620109c65610aeb","prefixes":{"":1057}}, // [Sleepcountry] - {"hash":"9c6554ebebdbfa08","prefixes":{"":1058}}, // [Hoover] - {"hash":"f34ddb2b65330794","prefixes":{"":1059}}, // [Bidtellect] - {"hash":"5d9eb2a8dc1a1687","prefixes":{"":1059}}, // [Bidtellect] - {"hash":"e2207dc37f1b89e8","prefixes":{"":1059}}, // [Bidtellect] - {"hash":"682ec1b47db930b9","prefixes":{"":1059}}, // [Bidtellect] - {"hash":"638a2c2dc53d2ab7","prefixes":{"":1060}}, // [Mocoplex Inc.] - {"hash":"ee330ed89897a55c","prefixes":{"":1060}}, // [Mocoplex Inc.] - {"hash":"60ab035f28a35e30","prefixes":{"":1060}}, // [Mocoplex Inc.] - {"hash":"5929f2c289d9e073","prefixes":{"":778}}, // [Gruvi Ltd.] - {"hash":"c16c02248018a374","prefixes":{"":778}}, // [Gruvi Ltd.] - {"hash":"e6eb2d1c03d2be83","prefixes":{"":778}}, // [Gruvi Ltd.] - {"hash":"95fa4396cadc336e","prefixes":{"":778}}, // [Gruvi Ltd.] - {"hash":"2604a7d96f168c84","prefixes":{"":1061}}, // [Brightsparc Technologies Pty Ltd trading as Native] - {"hash":"294fdebfd41771a8","prefixes":{"":1061}}, // [Brightsparc Technologies Pty Ltd trading as Native] - {"hash":"c6beb451fe4c58c2","prefixes":{"":1061}}, // [Brightsparc Technologies Pty Ltd trading as Native] - {"hash":"b0f5f51210e6f2af","prefixes":{"":1061}}, // [Brightsparc Technologies Pty Ltd trading as Native] - {"hash":"ab3da2eb1a35cba6","prefixes":{"":1062}}, // [Verengo Solar] - {"hash":"c3bf5a10743772ec","prefixes":{"*":1063}}, // [Mobile Professionals BV] - {"hash":"d22325b6d6b04918","prefixes":{"*":1064}}, // [APNIC Pty Ltd] - {"hash":"27966131215e508a","prefixes":{"*":1064}}, // [APNIC Pty Ltd] - {"hash":"80022b9d8ddaffde","prefixes":{"*":1064}}, // [APNIC Pty Ltd] - {"hash":"9352d77ba286c03d","prefixes":{"":1065}}, // [Dun and Bradstreet Corporation] - {"hash":"74b7ef01f42d93ee","prefixes":{"":1066}}, // [UpToLike] - {"hash":"f2a78bb802dc4abd","prefixes":{"":1066}}, // [UpToLike] - {"hash":"f885798eaaace647","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"3bc813f113908f06","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"e1a6852c86da418c","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"0343b65d010db4b2","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"a9b74e1bcb339dd0","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"e8d325d7be0f046b","prefixes":{"*":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"f7ccb1753b9ae2ed","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"4a96db729cb9dd47","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"afde8c2ebcc380c8","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"05840eb3ff4432a8","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"3e5f0f83b0e34391","prefixes":{"":1067}}, // [Mobile360 Sdn Bhd] - {"hash":"f79fe1474c475ade","prefixes":{"":1068}}, // [Elite Fixtures] - {"hash":"b8b63e29130d798a","prefixes":{"":1069}}, // [Advanse Ads] - {"hash":"662442a875d5ae62","prefixes":{"":1069}}, // [Advanse Ads] - {"hash":"d8487e4c88a21c7f","prefixes":{"":1069}}, // [Advanse Ads] - {"hash":"37694729baf29aa5","prefixes":{"":1070}}, // [Insurance Step] - {"hash":"0153c16587a0c97c","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"c7bcca7e8dc820fe","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"2176b8c755bbab42","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"1979b33f68eb0ded","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"32a9ea6936225587","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"16fec67040bf3ff7","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"ed89b325a7c3ac6d","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"ae3bc4690ef89e8d","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"ed9a2a4f49ad4647","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"398d36669a77aca6","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"5bf9c0f450f9eb14","prefixes":{"":1071}}, // [Causal Impact] - {"hash":"30e7de1e277c7698","prefixes":{"":145}}, // [Aarki, Inc.] - {"hash":"888d9446d621d82f","prefixes":{"":145}}, // [Aarki, Inc.] - {"hash":"3061a4cd653ea2dc","prefixes":{"*":1072}}, // [Pixnet] - {"hash":"06daa5b82ebfa413","prefixes":{"":1073}}, // [Zapp360] - {"hash":"6f0f9a90c798715d","prefixes":{"":1074}}, // [Kijiji] - {"hash":"0259cba89f8f9676","prefixes":{"":1075}}, // [Spotad LTD.] - {"hash":"0ca2babbaeeca3bf","prefixes":{"":1075}}, // [Spotad LTD.] - {"hash":"e9a9d4fd1b468042","prefixes":{"":1075}}, // [Spotad LTD.] - {"hash":"6a79ac93cddc0cc5","prefixes":{"":1075}}, // [Spotad LTD.] - {"hash":"685b17838f2d9b82","prefixes":{"":1076}}, // [Go Daddy] - {"hash":"aa66f01be33256d0","prefixes":{"":1077}}, // [Bilendi] - {"hash":"3b8dfd79ab921d9b","prefixes":{"":1078}}, // [Hitokuse] - {"hash":"1992e397e0dff18d","prefixes":{"":1078}}, // [Hitokuse] - {"hash":"03eb230cd9f67c73","prefixes":{"":1078}}, // [Hitokuse] - {"hash":"0a75c26f5c580b3a","prefixes":{"*":1079}}, // [MGID Inc.] - {"hash":"4466d73d5d5dce19","prefixes":{"*":1079}}, // [MGID Inc.] - {"hash":"10bfb2327400e2ea","prefixes":{"":1079}}, // [MGID Inc.] - {"hash":"121b77f4b4412c85","prefixes":{"":1079}}, // [MGID Inc.] - {"hash":"e46ec8aa62f3f432","prefixes":{"":1080}}, // [AreaOne] - {"hash":"9849db4d4a694f16","prefixes":{"t":1081,"s":1081,"sna":1081}}, // [DynAd] [DynAd] [DynAd] - {"hash":"5fc0aef37d5fffca","prefixes":{"":1082}}, // [Loop Pay] - {"hash":"4cc75be32553222a","prefixes":{"":1083}}, // [Remerge GmbH] - {"hash":"b1a90d2aaa32932c","prefixes":{"":1083}}, // [Remerge GmbH] - {"hash":"3bea81bc90640751","prefixes":{"":1084}}, // [Audience Trading Platform LTD] - {"hash":"d193540bb7540e5c","prefixes":{"":1085}}, // [Whisla] - {"hash":"abd1c2a37733128b","prefixes":{"":1085}}, // [Whisla] - {"hash":"9546fe15199cbce1","prefixes":{"":1085}}, // [Whisla] - {"hash":"f43c40d1949f2c52","prefixes":{"":1086}}, // [Bridgevine] - {"hash":"28577f7c17c55b0f","prefixes":{"":1087}}, // [ADgraph DMP] - {"hash":"fa0c51dc55faddc6","prefixes":{"":1088}}, // [Gravity4 Inc.] - {"hash":"6dea119c4c95aaa8","prefixes":{"":1088}}, // [Gravity4 Inc.] - {"hash":"79b4752426dedd83","prefixes":{"":1089}}, // [Hipmunk] - {"hash":"ccba9f3966eabe6b","prefixes":{"*":1090}}, // [VideoHub DSP] - {"hash":"a6baef75f126450e","prefixes":{"":1091}}, // [DuMedia] - {"hash":"cf45efd3feda1ba3","prefixes":{"":1091}}, // [DuMedia] - {"hash":"efebf8c1ea6bab87","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"a866ff2bfe3133e7","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"49b89555382dac9e","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"e2d2b1537271ff61","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"5804e7cf2cdbbda0","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"db0ef2b4eff25274","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"d960535fd30704f8","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"258915df4406fd06","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"b34a1e3fd51aadb9","prefixes":{"":1092}}, // [F@N Communications, Inc.] - {"hash":"c0ad7ec6e1d46299","prefixes":{"":1093}}, // [VIVALU GmbH] - {"hash":"65c049a517720b5c","prefixes":{"":1093}}, // [VIVALU GmbH] - {"hash":"b60b2426028baba5","prefixes":{"":1094}}, // [Advertising Technologies LTD] - {"hash":"6d2df95e2bf52046","prefixes":{"":1094}}, // [Advertising Technologies LTD] - {"hash":"b4ad6ea6ca336f55","prefixes":{"":1094}}, // [Advertising Technologies LTD] - {"hash":"b750311cbd5c4013","prefixes":{"":1095}}, // [Authenticated Digital Inc] - {"hash":"aa634a3d8862b3be","prefixes":{"":1095}}, // [Authenticated Digital Inc] - {"hash":"361eefdfa29ada10","prefixes":{"":1096}}, // [PaeDae, Inc., DBA The Mobile Majority] - {"hash":"8a5c2c8d21fc6dd4","prefixes":{"":1096}}, // [PaeDae, Inc., DBA The Mobile Majority] - {"hash":"13b3d2e0ed78e518","prefixes":{"":1097}}, // [METROPCS] - {"hash":"c9b29fe277a47b8d","prefixes":{"":1098}}, // [PapayaMobile Inc.] - {"hash":"60f32eaa9335e4b9","prefixes":{"":1098}}, // [PapayaMobile Inc.] - {"hash":"c8f49035cb273e42","prefixes":{"":1098}}, // [PapayaMobile Inc.] - {"hash":"92d9e360f16ecdc9","prefixes":{"":1098}}, // [PapayaMobile Inc.] - {"hash":"2d3b5566afb5350b","prefixes":{"":1098}}, // [PapayaMobile Inc.] - {"hash":"0ca3b2088de2514c","prefixes":{"":1098}}, // [PapayaMobile Inc.] - {"hash":"67da96d5ae99faa0","prefixes":{"":1098}}, // [PapayaMobile Inc.] - {"hash":"347e898491d027ee","prefixes":{"":1099}}, // [Raumfeld] - {"hash":"ae3f27ad4a4f2f7d","prefixes":{"":1099}}, // [Raumfeld] - {"hash":"cd0c8a5dd793ee52","prefixes":{"":1099}}, // [Raumfeld] - {"hash":"451f4083f03028e0","prefixes":{"":1099}}, // [Raumfeld] - {"hash":"e1b048e4fa37eef2","prefixes":{"":1099}}, // [Raumfeld] - {"hash":"837ae92d9cf6b933","prefixes":{"":1099}}, // [Raumfeld] - {"hash":"6ae82aac87db94d3","prefixes":{"":1099}}, // [Raumfeld] - {"hash":"84ddfa5a2e93cb69","prefixes":{"":1100}}, // [GREE Ads DSP] - {"hash":"a7201f5a80824242","prefixes":{"":1100}}, // [GREE Ads DSP] - {"hash":"2ec22ca05310362a","prefixes":{"":1101}}, // [Tender Industries AB] - {"hash":"d159abbaeda22e7c","prefixes":{"":1102}}, // [BySide] - {"hash":"426783d6fe8d039e","prefixes":{"":1103}}, // [Sentrant Security Inc.] - {"hash":"d270ad50bb53fb01","prefixes":{"":1103}}, // [Sentrant Security Inc.] - {"hash":"49c693058534be83","prefixes":{"":1104}}, // [Locon Solutions Pvt. Ltd.] - {"hash":"a2b9f627da8737de","prefixes":{"":1105}}, // [Interrogare GmbH] - {"hash":"13b73169540642e5","prefixes":{"":1105}}, // [Interrogare GmbH] - {"hash":"afaa106d11846fa4","prefixes":{"*":1106}}, // [ChannelAdvisor] - {"hash":"b8b4eadeb5d3a123","prefixes":{"":1107}}, // [VideoAmp] - {"hash":"43546e9f6ff5bf2a","prefixes":{"":1107}}, // [VideoAmp] - {"hash":"61a9edb9af3769cf","prefixes":{"":1107}}, // [VideoAmp] - {"hash":"f76b3fe7048e93e2","prefixes":{"":1107}}, // [VideoAmp] - {"hash":"57263424c0e88a92","prefixes":{"track":1108}}, // [The Bridge] - {"hash":"516e9fc5ede4af0d","prefixes":{"":1108}}, // [The Bridge] - {"hash":"6bc0303a4262cb67","prefixes":{"":1108}}, // [The Bridge] - {"hash":"30a32fe2a89dccd8","prefixes":{"":1109}}, // [Pharmaca Integrative Pharmacy] - {"hash":"7881da604383a640","prefixes":{"":673}}, // [Videology DSP] - {"hash":"dec02c663bb06094","prefixes":{"":1110}}, // [Scrutineer] - {"hash":"6e2098af577c671d","prefixes":{"":1111}}, // [TF1 - FR] - {"hash":"60774075776e2612","prefixes":{"":1112}}, // [Core Digital] - {"hash":"79a775eea6a902e6","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"c0576db5cf4ba20b","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"d90fda985e3fb6bf","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"ee49dcf9326e853f","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"5b3c6abcd44de720","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"f97a320480541a27","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"57fc36302e0bde3c","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"736a8a8a0cef7bdf","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"36263b4a0d607a71","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"226480bf58f4fd1c","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"58379bd623597d70","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"f68e3a544a7397be","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"0f2880d71794e516","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"4ef52741fc1ade73","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"97d6a93635d72af0","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"ab75a46dc8bf2c1e","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"efeb8f966db03b87","prefixes":{"":110}}, // [Bonzai Digital Pvt. Ltd] - {"hash":"2a4d804c0fb397c1","prefixes":{"":1113}}, // [Adventive, Inc.] - {"hash":"29d370caa59b4ecc","prefixes":{"":1113}}, // [Adventive, Inc.] - {"hash":"a06632ec1be82319","prefixes":{"":1114}}, // [Turner Sports Interactive, Inc.] - {"hash":"bf10e40bae047225","prefixes":{"":1114}}, // [Turner Sports Interactive, Inc.] - {"hash":"67dbd4ebfca8ed7c","prefixes":{"":1114}}, // [Turner Sports Interactive, Inc.] - {"hash":"3ccffb010a6151f8","prefixes":{"":1115}}, // [SnappyTV] - {"hash":"7d838005a39552d8","prefixes":{"":1116}}, // [Target Media Partners] - {"hash":"003b7537bd9a7cfe","prefixes":{"":1116}}, // [Target Media Partners] - {"hash":"26b33a79482ab7ab","prefixes":{"":1117}}, // [KAIZEN platform Inc.] - {"hash":"15cdc1963890a6a2","prefixes":{"":1117}}, // [KAIZEN platform Inc.] - {"hash":"1f36b920b7ac4ebb","prefixes":{"":1118}}, // [Bidstalk Pte Ltd] - {"hash":"a59afaa1821362bd","prefixes":{"":1119}}, // [ESPN] - {"hash":"e0edcd5fafde3e7a","prefixes":{"":1119}}, // [ESPN] - {"hash":"7bd48d6255130db4","prefixes":{"":1120}}, // [Online Media Group] - {"hash":"88c41fbe0db9a483","prefixes":{"":1120}}, // [Online Media Group] - {"hash":"fc1080dbf4fae3f9","prefixes":{"":1120}}, // [Online Media Group] - {"hash":"37fc205007f7a040","prefixes":{"":1120}}, // [Online Media Group] - {"hash":"60c02a2ea01a737e","prefixes":{"":1121}}, // [Luxury Link] - {"hash":"24108d30b52b9587","prefixes":{"":1122}}, // [ESKIMI] - {"hash":"9de510a2d7f81a6e","prefixes":{"":1122}}, // [ESKIMI] - {"hash":"d0e18c7e72b51bf3","prefixes":{"":1122}}, // [ESKIMI] - {"hash":"6dbe16f5894e20af","prefixes":{"":1123}}, // [AdsYolo Media] - {"hash":"5292ef4ba83623a8","prefixes":{"":1124}}, // [Arrivalist] - {"hash":"b94285ae50bcb48e","prefixes":{"":1124}}, // [Arrivalist] - {"hash":"cd7b432729ab016a","prefixes":{"":1124}}, // [Arrivalist] - {"hash":"ae0442a300ebce47","prefixes":{"":1124}}, // [Arrivalist] - {"hash":"3c8cc56d5b514ec5","prefixes":{"":1124}}, // [Arrivalist] - {"hash":"794b2a51546b4f7f","prefixes":{"":1125}}, // [MobileWebAdz Ltd] - {"hash":"223eacf97254c888","prefixes":{"":1125}}, // [MobileWebAdz Ltd] - {"hash":"a3c5eeef285517f3","prefixes":{"":1125}}, // [MobileWebAdz Ltd] - {"hash":"b222f9498fcdb24c","prefixes":{"":1126}}, // [Demand Side Science, Inc.] - {"hash":"87af9974b30c5872","prefixes":{"":1126}}, // [Demand Side Science, Inc.] - {"hash":"db4cd3e487418898","prefixes":{"":1126}}, // [Demand Side Science, Inc.] - {"hash":"3fca6191597f44f2","prefixes":{"":1127}}, // [SOCIETE FRANCAISE DU RADIOTELEPHONE] - {"hash":"6873487b11c1952e","prefixes":{"":1127}}, // [SOCIETE FRANCAISE DU RADIOTELEPHONE] - {"hash":"d4b4ed3e1d07b00e","prefixes":{"":1127}}, // [SOCIETE FRANCAISE DU RADIOTELEPHONE] - {"hash":"e647f502df0429f5","prefixes":{"":1128}}, // [Mobitrans FZ LLC] - {"hash":"34f8fc43e14bad03","prefixes":{"":1128}}, // [Mobitrans FZ LLC] - {"hash":"5f43507f5a877784","prefixes":{"":1128}}, // [Mobitrans FZ LLC] - {"hash":"1466e6f77352194a","prefixes":{"":1128}}, // [Mobitrans FZ LLC] - {"hash":"e097497d606d17ed","prefixes":{"":1128}}, // [Mobitrans FZ LLC] - {"hash":"05c9b2eb92c8b5ba","prefixes":{"":1128}}, // [Mobitrans FZ LLC] - {"hash":"ce3bfbe09ddeb858","prefixes":{"":1128}}, // [Mobitrans FZ LLC] - {"hash":"3aa96b751d2c0d1c","prefixes":{"":1128}}, // [Mobitrans FZ LLC] - {"hash":"289e565c9c686515","prefixes":{"":1129}}, // [CoCo Reef] - {"hash":"0b6740b395a65857","prefixes":{"":1130}}, // [Kumma DP LTD] - {"hash":"c00365ed4d1c464c","prefixes":{"":1131}}, // [Cablato Limited] - {"hash":"9ee2a07ebdef206f","prefixes":{"":1131}}, // [Cablato Limited] - {"hash":"e270375f27c5f31c","prefixes":{"":1131}}, // [Cablato Limited] - {"hash":"0ebf573ebb6b30e1","prefixes":{"":1131}}, // [Cablato Limited] - {"hash":"e28b4c253b1f854f","prefixes":{"":1132}}, // [EURO DISNEY SCA] - {"hash":"3c8900c851ba9b28","prefixes":{"":1133}}, // [Norstat] - {"hash":"39518e51adbe0cd7","prefixes":{"":1133}}, // [Norstat] - {"hash":"2c5efd1ea532f0eb","prefixes":{"":1134}}, // [John Varvatos] - {"hash":"5679368e6b2bb34c","prefixes":{"":1135}}, // [Spritz Technology, Inc.] - {"hash":"60be7d8a6a76b62f","prefixes":{"":1135}}, // [Spritz Technology, Inc.] - {"hash":"cefbe9240a377d95","prefixes":{"":1136}}, // [Wix.com] - {"hash":"a56824227bb496f7","prefixes":{"*":1137}}, // [TapTap Networks] - {"hash":"41f2ae7d2d6939ae","prefixes":{"":1137}}, // [TapTap Networks] - {"hash":"51fd8b1d5b983b31","prefixes":{"":1138}}, // [Permodo] - {"hash":"12c3d246c6d4e6b1","prefixes":{"":1138}}, // [Permodo] - {"hash":"69da2bfa4e414e2c","prefixes":{"":1138}}, // [Permodo] - {"hash":"67bb1e3460d8e2eb","prefixes":{"":1138}}, // [Permodo] - {"hash":"38912558a11ff73e","prefixes":{"":1138}}, // [Permodo] - {"hash":"eb4d36cf5e4909c1","prefixes":{"":1139}}, // [Gaiam, Inc.] - {"hash":"16578371f24a5e90","prefixes":{"":1140}}, // [Plusing interactive co.,Ltd] - {"hash":"ecdb68bd2622aa9e","prefixes":{"":42}}, // [Clinch.co] - {"hash":"8adccb3847cc8b0f","prefixes":{"":42}}, // [Clinch.co] - {"hash":"2b5433780334b542","prefixes":{"":42}}, // [Clinch.co] - {"hash":"c853c92986d0cc90","prefixes":{"":42}}, // [Clinch.co] - {"hash":"388da33e7f0adca1","prefixes":{"":42}}, // [Clinch.co] - {"hash":"03d7a65aa1bc87bc","prefixes":{"":1141}}, // [Paypersale] - {"hash":"176eb71c0938f4a5","prefixes":{"":111}}, // [Epic Combo Malta Ltd.] - {"hash":"4366ec3ae9f41f7c","prefixes":{"":111}}, // [Epic Combo Malta Ltd.] - {"hash":"2e9617ef727ff0b5","prefixes":{"":1142}}, // [OCP Collective Corp. (d/b/a AdCade)] - {"hash":"11d6d13e3eb48a86","prefixes":{"":1142}}, // [OCP Collective Corp. (d/b/a AdCade)] - {"hash":"20e14ef573248fec","prefixes":{"":1142}}, // [OCP Collective Corp. (d/b/a AdCade)] - {"hash":"234345fe3c0da842","prefixes":{"":1142}}, // [OCP Collective Corp. (d/b/a AdCade)] - {"hash":"30fb60c9c450221d","prefixes":{"":1142}}, // [OCP Collective Corp. (d/b/a AdCade)] - {"hash":"69ddc7852224e18f","prefixes":{"":1143}}, // [ESV Digital] - {"hash":"240174bad001108d","prefixes":{"":1144}}, // [RevJet LLC.] - {"hash":"5250a7a3f2e4f73a","prefixes":{"":1144}}, // [RevJet LLC.] - {"hash":"590feb6b793ba20a","prefixes":{"":1144}}, // [RevJet LLC.] - {"hash":"23db13cb66f769e3","prefixes":{"":1145}}, // [GET IT Mobile, Inc] - {"hash":"cd7cd6c16f714024","prefixes":{"":1145}}, // [GET IT Mobile, Inc] - {"hash":"00ea35d76275dbeb","prefixes":{"":1145}}, // [GET IT Mobile, Inc] - {"hash":"5239cdae00ff02ea","prefixes":{"":1146}}, // [IBM] - {"hash":"e2c539bef8d2970c","prefixes":{"":1147}}, // [Lucid Holdings, LLC] - {"hash":"7078be8707b5ec93","prefixes":{"":1147}}, // [Lucid Holdings, LLC] - {"hash":"d65fe9ed9f23afbd","prefixes":{"":1148}}, // [Stratio Big Data Inc.] - {"hash":"9ccfc7f2ecfbaeec","prefixes":{"":1148}}, // [Stratio Big Data Inc.] - {"hash":"54e80254bbf4e20b","prefixes":{"":1149}}, // [Tripping.com] - {"hash":"4aaf263b4bae6cd1","prefixes":{"":1150}}, // [Vidible] - {"hash":"b782d6c10170e732","prefixes":{"":1150}}, // [Vidible] - {"hash":"ceef29c52329c405","prefixes":{"":1150}}, // [Vidible] - {"hash":"f14df6ca2e300bb9","prefixes":{"":1150}}, // [Vidible] - {"hash":"514c355026c95637","prefixes":{"":1150}}, // [Vidible] - {"hash":"b2529b7ebc0cbb27","prefixes":{"":1150}}, // [Vidible] - {"hash":"14ebf9b0cbed945c","prefixes":{"":1150}}, // [Vidible] - {"hash":"048147eea378ee03","prefixes":{"":1150}}, // [Vidible] - {"hash":"7b61440491bd2a65","prefixes":{"":1151}}, // [MiMTiD Corp] - {"hash":"036455e7801e853e","prefixes":{"":1151}}, // [MiMTiD Corp] - {"hash":"e631f0342d59176b","prefixes":{"":1152}}, // [Moloco, Inc.] - {"hash":"87938522c669c682","prefixes":{"":1152}}, // [Moloco, Inc.] - {"hash":"a102c87f9921beb6","prefixes":{"":1153}}, // [MEC SP. Z O.O] - {"hash":"f9133e4a069a0f4f","prefixes":{"":1153}}, // [MEC SP. Z O.O] - {"hash":"6c7737c0ecbf0d91","prefixes":{"":1153}}, // [MEC SP. Z O.O] - {"hash":"a82a0c18f6dfb959","prefixes":{"":1153}}, // [MEC SP. Z O.O] - {"hash":"e1bea08ea38dc600","prefixes":{"":1154}}, // [Abudantia LLC] - {"hash":"d60e1ada8dfa48fa","prefixes":{"":1154}}, // [Abudantia LLC] - {"hash":"b57f7b99f9deda72","prefixes":{"":1154}}, // [Abudantia LLC] - {"hash":"63f93976910e4aff","prefixes":{"":1154}}, // [Abudantia LLC] - {"hash":"93a75f3f5174e472","prefixes":{"":1154}}, // [Abudantia LLC] - {"hash":"ba1500537064494c","prefixes":{"":1155}}, // [Realzeit] - {"hash":"ce1c3e183853dec6","prefixes":{"":1156}}, // [Shanghai DigitalMatrix Information Technology Co.] - {"hash":"697384881bfb9a85","prefixes":{"":1156}}, // [Shanghai DigitalMatrix Information Technology Co.] - {"hash":"3b5e1e5d71bb095f","prefixes":{"":1156}}, // [Shanghai DigitalMatrix Information Technology Co.] - {"hash":"a55289ec43a1fd1c","prefixes":{"":1156}}, // [Shanghai DigitalMatrix Information Technology Co.] - {"hash":"0ea122741b5a296d","prefixes":{"":1156}}, // [Shanghai DigitalMatrix Information Technology Co.] - {"hash":"139c147db4a66d45","prefixes":{"":1156}}, // [Shanghai DigitalMatrix Information Technology Co.] - {"hash":"91a98ad80489e2aa","prefixes":{"":1156}}, // [Shanghai DigitalMatrix Information Technology Co.] - {"hash":"276f5a9d0ed20d4a","prefixes":{"":1156}}, // [Shanghai DigitalMatrix Information Technology Co.] - {"hash":"8408cf6c93d2f24f","prefixes":{"":1157}}, // [Alkemics] - {"hash":"658222dab2aa692d","prefixes":{"":1157}}, // [Alkemics] - {"hash":"8835156415ef5c70","prefixes":{"":1157}}, // [Alkemics] - {"hash":"7d76d0b7c2c49e1c","prefixes":{"":1158}}, // [Sodel Software Solutions Pvt. Ltd.] - {"hash":"26b315a19bf762cb","prefixes":{"":1159}}, // [Clearstream.TV, Inc] - {"hash":"80ce05889af3bb0e","prefixes":{"":1159}}, // [Clearstream.TV, Inc] - {"hash":"2d9798f06f3cd0f8","prefixes":{"":1159}}, // [Clearstream.TV, Inc] - {"hash":"4724e51f601e7bfa","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"e0f93294ef7c3cb7","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"ed1e0f5237be934f","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"4a0011b21f8d95f9","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"1af9efa73c3b3b29","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"49eea06f0c13da7f","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"584459c8cfe5595f","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"3b72d8e306588e71","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"f8d6e6d127d9c894","prefixes":{"":1161}}, // [Adluxe] - {"hash":"93d864f5e43a3230","prefixes":{"":1161}}, // [Adluxe] - {"hash":"793d5c9c5c35b0d9","prefixes":{"":1161}}, // [Adluxe] - {"hash":"7b9c5864364d5f5e","prefixes":{"*":1162}}, // [NinthDecimal] - {"hash":"7c86152a561c4b4b","prefixes":{"*":1162}}, // [NinthDecimal] - {"hash":"ec998efdf99b0c51","prefixes":{"*":1163}}, // [RICH MEDIA STUDIO] - {"hash":"e83c01ec741a261e","prefixes":{"":1164}}, // [TenMax Co., Ltd.] - {"hash":"e333d70ac6d1acda","prefixes":{"":1165}}, // [twiago GmbH] - {"hash":"bb81d5fd8a052b7f","prefixes":{"":1166}}, // [Ad Dynamo International (Pty) Ltd] - {"hash":"55b2516509252adb","prefixes":{"":1166}}, // [Ad Dynamo International (Pty) Ltd] - {"hash":"59e4e15b4fe6dccd","prefixes":{"":1166}}, // [Ad Dynamo International (Pty) Ltd] - {"hash":"f5005d7a14ac0a47","prefixes":{"":1166}}, // [Ad Dynamo International (Pty) Ltd] - {"hash":"ecf3dd9926cbe25e","prefixes":{"":1166}}, // [Ad Dynamo International (Pty) Ltd] - {"hash":"2ccdffccbbf20e3f","prefixes":{"":1167}}, // [Swarm Enterprises Inc] - {"hash":"bf0dd6fdbc53dfee","prefixes":{"":112}}, // [Quixey] - {"hash":"ac6a75f10f9f0391","prefixes":{"":1168}}, // [Media Forum] - {"hash":"2b5f5e731d08116b","prefixes":{"":1168}}, // [Media Forum] - {"hash":"8bf2fe7f00b3c760","prefixes":{"":1169}}, // [Beeswax.io] - {"hash":"480bd3bc762861ae","prefixes":{"":1170}}, // [Varick Media Management] - {"hash":"d9faa36410c39273","prefixes":{"":1170}}, // [Varick Media Management] - {"hash":"6114cbf7f45fd639","prefixes":{"":1171}}, // [JD] - {"hash":"739568f083f383e2","prefixes":{"":1171}}, // [JD] - {"hash":"1fc63d36c1d330fa","prefixes":{"":1171}}, // [JD] - {"hash":"bdb07c050de0bbd2","prefixes":{"":1171}}, // [JD] - {"hash":"6b6a891a61b05435","prefixes":{"":1172}}, // [Lotlinx Inc.] - {"hash":"b7831e02a1168f7b","prefixes":{"":1172}}, // [Lotlinx Inc.] - {"hash":"78f18ca9103e6031","prefixes":{"":1173}}, // [Lotlinx Inc] - {"hash":"9a1f751c74a01145","prefixes":{"":40}}, // [F# Inc.] - {"hash":"09ddf4e3268e6597","prefixes":{"":1174}}, // [Ingenio, LLC] - {"hash":"c5dd0849ac2febc6","prefixes":{"":1175}}, // [MVMT Watches] - {"hash":"3b471f8f08410883","prefixes":{"":1176}}, // [C1X Inc] - {"hash":"1bb74ab211a41c9c","prefixes":{"":1177}}, // [Vitro Agency] - {"hash":"96c0acfcd81b2dd4","prefixes":{"":1178}}, // [Kabbage] - {"hash":"0e5b93a6bd6f704b","prefixes":{"":1179}}, // [Redbranch, Inc. (dba Fraudlogix)] - {"hash":"a810be7236bf948f","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"ffe13e4342fd9a35","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"91d97869f45df6e0","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"c9f1716ec9d29c4b","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"4a9148e717cb46a9","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"9b57b296c2edd01c","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"1db72ea880eb28dc","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"61889d0078dd40a7","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"fd8f5971bf862430","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"d0da5b57fc3b9611","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"00449172b70d520c","prefixes":{"":1180}}, // [AutoWeb, Inc.] - {"hash":"7b3027f5a6cb4f20","prefixes":{"":1181}}, // [Aimee Soft Ltd.] - {"hash":"c29f58ba8b9fccc8","prefixes":{"":1181}}, // [Aimee Soft Ltd.] - {"hash":"e8cb0d7824f08de4","prefixes":{"":1181}}, // [Aimee Soft Ltd.] - {"hash":"97efe77626897346","prefixes":{"":1181}}, // [Aimee Soft Ltd.] - {"hash":"78afe44858076c81","prefixes":{"":1181}}, // [Aimee Soft Ltd.] - {"hash":"f5162869e573dcd7","prefixes":{"":1182}}, // [SAS Azameo] - {"hash":"3d35f5eb2971f5fe","prefixes":{"":17}}, // [iJento] - {"hash":"b98ddad6854c87b1","prefixes":{"":1183}}, // [Keyade] - {"hash":"65aab53cd46ccc7f","prefixes":{"":1184}}, // [Digital To Store (DTS)] - {"hash":"e1e5e1a4c7857257","prefixes":{"":1184}}, // [Digital To Store (DTS)] - {"hash":"c9f6517091430433","prefixes":{"":1184}}, // [Digital To Store (DTS)] - {"hash":"76d9ee307388af98","prefixes":{"":1184}}, // [Digital To Store (DTS)] - {"hash":"8fdb48d3b5466119","prefixes":{"":1184}}, // [Digital To Store (DTS)] - {"hash":"bd53e2fe0c663545","prefixes":{"":1185}}, // [Media iQ Digital] - {"hash":"58b54a485703f6ba","prefixes":{"":1185}}, // [Media iQ Digital] - {"hash":"35616fb4ea0feb94","prefixes":{"":1185}}, // [Media iQ Digital] - {"hash":"0a1563142096ed40","prefixes":{"":1186}}, // [Ingenious Technologies] - {"hash":"427d170b486c74e6","prefixes":{"":1186}}, // [Ingenious Technologies] - {"hash":"79480a2b5de32430","prefixes":{"":1186}}, // [Ingenious Technologies] - {"hash":"aaf44b046fb5c10d","prefixes":{"":1187}}, // [Sonicmoov co.,ltd] - {"hash":"e5d30ca87b591280","prefixes":{"":1187}}, // [Sonicmoov co.,ltd] - {"hash":"9ec9f8d7f0fc8c4a","prefixes":{"":1187}}, // [Sonicmoov co.,ltd] - {"hash":"dd0bf16db1e39f59","prefixes":{"":1187}}, // [Sonicmoov co.,ltd] - {"hash":"b32a4da2bc7363a0","prefixes":{"":1188}}, // [Bonadza LLC] - {"hash":"66a81343ae0223df","prefixes":{"":1188}}, // [Bonadza LLC] - {"hash":"ae76287d25b463d2","prefixes":{"":1188}}, // [Bonadza LLC] - {"hash":"7429b926177d09a6","prefixes":{"":1188}}, // [Bonadza LLC] - {"hash":"170cc20a9af99287","prefixes":{"":1189}}, // [Axis Shift Limited] - {"hash":"6e94333e674450ec","prefixes":{"":1190}}, // [TreSensa, Inc.] - {"hash":"8fac335d75d968fd","prefixes":{"":1190}}, // [TreSensa, Inc.] - {"hash":"eec936c95af6b31f","prefixes":{"":1190}}, // [TreSensa, Inc.] - {"hash":"3c744b206f15c46d","prefixes":{"":1190}}, // [TreSensa, Inc.] - {"hash":"049a71c722403987","prefixes":{"":1191}}, // [Media Logic Group LLC dba AerisWeather LLC] - {"hash":"efa6686cf4b6b73c","prefixes":{"":57}}, // [DeinDeal AG] - {"hash":"847219545b169784","prefixes":{"":1192}}, // [Fugumobile] - {"hash":"790ab0ec39724b71","prefixes":{"":1193}}, // [Localstars Ltd] - {"hash":"0133a556a80fd808","prefixes":{"":1194}}, // [Fluidads] - {"hash":"fc8901303bada188","prefixes":{"":1194}}, // [Fluidads] - {"hash":"ba549c39b1f43523","prefixes":{"*":1195}}, // [Wayfair LLC] - {"hash":"5d0b84261f7b6d5c","prefixes":{"*":1195}}, // [Wayfair LLC] - {"hash":"d48dde21b5917906","prefixes":{"*":1195}}, // [Wayfair LLC] - {"hash":"337dabe98702ae52","prefixes":{"*":1195}}, // [Wayfair LLC] - {"hash":"efad84d0a4dd4e46","prefixes":{"*":1195}}, // [Wayfair LLC] - {"hash":"019c3ee24427b355","prefixes":{"*":1195}}, // [Wayfair LLC] - {"hash":"ec79c848ccf2d648","prefixes":{"*":1195}}, // [Wayfair LLC] - {"hash":"ccf7fd5647b92698","prefixes":{"*":1195}}, // [Wayfair LLC] - {"hash":"3b03a24eea2a6345","prefixes":{"":1196}}, // [VCCORP CORPORATION] - {"hash":"f7f53c88139460cf","prefixes":{"":1197}}, // [IGAWorks] - {"hash":"93b9518285f54887","prefixes":{"":1198}}, // [Cellcom Ltd] - {"hash":"f59e6427783c3da0","prefixes":{"*":1199}}, // [Upsolver] - {"hash":"adb55d98bb4aef6e","prefixes":{"":1199}}, // [Upsolver] - {"hash":"7efba976eeea2d41","prefixes":{"":1199}}, // [Upsolver] - {"hash":"cdde375c1240991d","prefixes":{"":935}}, // [Channel Factory, LLC] - {"hash":"79c4dc57905857aa","prefixes":{"":1200}}, // [Phluidmedia, Inc.] - {"hash":"4948ea141d1a72e1","prefixes":{"":1200}}, // [Phluidmedia, Inc.] - {"hash":"e4aed5bf5c295c3a","prefixes":{"":1201}}, // [Roy Morgan Research Ltd] - {"hash":"547734f17effda77","prefixes":{"":1202}}, // [TapSense, Inc.] - {"hash":"96289aeef06d0a9f","prefixes":{"":1203}}, // [Southwest Airlines] - {"hash":"de497c6fbed4588d","prefixes":{"":794}}, // [Hatena Co., Ltd] - {"hash":"91412d534d321cb4","prefixes":{"":1204}}, // [Market Points, Inc.] - {"hash":"97aab20dd873dd2c","prefixes":{"":1205}}, // [UberMedia Inc.] - {"hash":"73cadeea8e1bd9e8","prefixes":{"":1205}}, // [UberMedia Inc.] - {"hash":"0e1bd2938a06a2e6","prefixes":{"":1206}}, // [Kadam SIA] - {"hash":"aa9658253aa81e62","prefixes":{"":1206}}, // [Kadam SIA] - {"hash":"75f1eab33abfa4f9","prefixes":{"":1206}}, // [Kadam SIA] - {"hash":"59f64b09b8e430ae","prefixes":{"":1206}}, // [Kadam SIA] - {"hash":"61947a042af2e3e8","prefixes":{"":1207}}, // [Alveo Platform (VMM own bidder)] - {"hash":"62c095e8dfbb0458","prefixes":{"":1208}}, // [Adbalancer EDV-DienstleistungsgesellschaftgmbH] - {"hash":"5cc57103c26df76f","prefixes":{"":1208}}, // [Adbalancer EDV-DienstleistungsgesellschaftgmbH] - {"hash":"53685a43fc711ea4","prefixes":{"":1209}}, // [JWPlayer] - {"hash":"53bdf03af7a44e2c","prefixes":{"":1209}}, // [JWPlayer] - {"hash":"eaff21a1a9591dab","prefixes":{"":1209}}, // [JWPlayer] - {"hash":"1d5626ba7394e6a1","prefixes":{"":1209}}, // [JWPlayer] - {"hash":"2e106bd1d1d064c6","prefixes":{"":1209}}, // [JWPlayer] - {"hash":"9f7691306b0805ea","prefixes":{"":1209}}, // [JWPlayer] - {"hash":"909df8f91bd33085","prefixes":{"*":1210}}, // [Adiquity Technologies Pvt Ltd] - {"hash":"d1eca614d3cc8883","prefixes":{"":1211}}, // [Smart Digital GmbH] - {"hash":"53d8de6b018c1793","prefixes":{"":1212}}, // [Auditorius LLC] - {"hash":"fd9a420ade305e17","prefixes":{"":1213}}, // [Treepodia] - {"hash":"c9fd06f055d4af40","prefixes":{"":1213}}, // [Treepodia] - {"hash":"6eb24d470a6f1018","prefixes":{"":1213}}, // [Treepodia] - {"hash":"e35bb07367786d4c","prefixes":{"":1214}}, // [Adamatic] - {"hash":"3943df18ad20915d","prefixes":{"":1214}}, // [Adamatic] - {"hash":"341d248dc25e4ea7","prefixes":{"":1214}}, // [Adamatic] - {"hash":"ad1f1681bab5cc6b","prefixes":{"":1215}}, // [SAS Web2ROI] - {"hash":"532b5bfa711f405e","prefixes":{"":1216}}, // [Cardlytics] - {"hash":"799c283a6f1c8ece","prefixes":{"":1217}}, // [MyFonts Inc.] - {"hash":"4448bee927dde7e6","prefixes":{"*":1218}}, // [Bluecore, Inc.] - {"hash":"a0bd7b7a099833b6","prefixes":{"":1218}}, // [Bluecore, Inc.] - {"hash":"fa3e77058c021551","prefixes":{"*":1218}}, // [Bluecore, Inc.] - {"hash":"4761d233715098ce","prefixes":{"":1219}}, // [EverQuote, Inc.] - {"hash":"35694b7df8059b22","prefixes":{"":1219}}, // [EverQuote, Inc.] - {"hash":"0740880077130a2d","prefixes":{"":1220}}, // [Optimize LCC D.B.A Genius Monkey] - {"hash":"bfe2c1b41278b804","prefixes":{"":1220}}, // [Optimize LCC D.B.A Genius Monkey] - {"hash":"6dd3f6dcd500314e","prefixes":{"":1220}}, // [Optimize LCC D.B.A Genius Monkey] - {"hash":"2f3f7cacd3fe0763","prefixes":{"*":1221}}, // [EverString Technology Ltd] - {"hash":"bca80e9becb74dcb","prefixes":{"*":1221}}, // [EverString Technology Ltd] - {"hash":"46edf29e9ba4ff96","prefixes":{"":1222}}, // [Axonix Limited] - {"hash":"98961093c86583f5","prefixes":{"":1223}}, // [Ubimo Ltd.] - {"hash":"1110ef291b2bd2e8","prefixes":{"":1224}}, // [gskinner.com,Inc] - {"hash":"e36b044e9f38bad6","prefixes":{"":1225}}, // [PlaceIQ, Inc.] - {"hash":"fe5b89fe7dbbd534","prefixes":{"":1226}}, // [DataBerries] - {"hash":"ddf083a57d484592","prefixes":{"":1227}}, // [Otto (GmbH & Co KG)] - {"hash":"7975f7864f4fa759","prefixes":{"":1228}}, // [Beijing ADirects Technology Corporation Limited] - {"hash":"657d40c4d7322985","prefixes":{"":1229}}, // [MagicGroup Asia Pte. Ltd.] - {"hash":"f5559eee02dd33ad","prefixes":{"":1229}}, // [MagicGroup Asia Pte. Ltd.] - {"hash":"e81aa2815c378230","prefixes":{"":1229}}, // [MagicGroup Asia Pte. Ltd.] - {"hash":"49269f9273c66c00","prefixes":{"":1230}}, // [Local Content, Inc.] - {"hash":"210fa7d8376be47a","prefixes":{"":1230}}, // [Local Content, Inc.] - {"hash":"e4ba8ed4e3abc143","prefixes":{"":1231}}, // [Flavia] - {"hash":"269e0d49e8a941b8","prefixes":{"":43}}, // [Transout Inc.] - {"hash":"b9cd0135bbe2cf40","prefixes":{"":43}}, // [Transout Inc.] - {"hash":"e4b01f2271c4171a","prefixes":{"":44}}, // [ADZIP] - {"hash":"e5b4daaa6c4ae231","prefixes":{"":44}}, // [ADZIP] - {"hash":"cfb3a28bf24f27aa","prefixes":{"":44}}, // [ADZIP] - {"hash":"db9ed30c0cbece36","prefixes":{"":44}}, // [ADZIP] - {"hash":"dd0d7f22eaea16dd","prefixes":{"":44}}, // [ADZIP] - {"hash":"4f3fc6e6fa75b1e8","prefixes":{"":44}}, // [ADZIP] - {"hash":"17a3e1cbc6818d71","prefixes":{"*":1232}}, // [Webtype] - {"hash":"39697008117e49f0","prefixes":{"":1233}}, // [Aditor] - {"hash":"50bd67909486f9f5","prefixes":{"":1233}}, // [Aditor] - {"hash":"d58ef2e47a1b5b3b","prefixes":{"":1234}}, // [YCmedia] - {"hash":"fc8e2c447edda6c9","prefixes":{"":1235}}, // [Addwish Aps] - {"hash":"d5ac956c9f185ddc","prefixes":{"":1236}}, // [AIDO TECHNOLOGY INC.] - {"hash":"24015d44b9900d67","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"460c0600751cbd60","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"77050f5bbccffdb1","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"cdf17fb674f06ff0","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"8fb430c8ece16b19","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"c9d79e6f1f2694cd","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"80c75e37374a2ef0","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"5f9ca3a706ea1a32","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"2839cda166e75a72","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"762d13ee90c23c96","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"c0ea3a6ac5a15ff4","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"70c0b65944772c14","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"06d5db93059745e1","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"ea0e52e08087883d","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"dd48f2110336efda","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"bac37ceef6479f6b","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"a16d986f3ad09790","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"44addae7b850b89f","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"ef08ba4268518e6e","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"1f2560020ac4a5dd","prefixes":{"":1237}}, // [Herolens Group LLC] - {"hash":"67339d8f9c205d19","prefixes":{"":1238}}, // [Fuisz Media, Inc.] - {"hash":"b3ebdb16675afb79","prefixes":{"":1238}}, // [Fuisz Media, Inc.] - {"hash":"33e8b1a10d6cf9bb","prefixes":{"":1238}}, // [Fuisz Media, Inc.] - {"hash":"33c97b2942fbfb43","prefixes":{"":1239}}, // [Bucksense, Inc.] - {"hash":"f7be89ec6f521ed4","prefixes":{"":664}}, // [Adiant] - {"hash":"8ad32c44e7f009d9","prefixes":{"":1240}}, // [Taylor Nelson Sofres Ukraine LLC] - {"hash":"9913ba5c46add495","prefixes":{"*":1241}}, // [MotionLead] - {"hash":"ed341f6ed9722885","prefixes":{"":1242}}, // [CJSC Recomendatsii tovarov i uslug] - {"hash":"b84298a2bc76c877","prefixes":{"":1242}}, // [CJSC Recomendatsii tovarov i uslug] - {"hash":"d7bd450b5571f32e","prefixes":{"":1242}}, // [CJSC Recomendatsii tovarov i uslug] - {"hash":"788739abab822f20","prefixes":{"":1242}}, // [CJSC Recomendatsii tovarov i uslug] - {"hash":"5119113b90253a71","prefixes":{"":1242}}, // [CJSC Recomendatsii tovarov i uslug] - {"hash":"8391f54ef2dd8235","prefixes":{"":1243}}, // [Adobe Edge] - {"hash":"ecbec0da60be727e","prefixes":{"":1243}}, // [Adobe Edge] - {"hash":"45df05b3b1fd2039","prefixes":{"":1243}}, // [Adobe Edge] - {"hash":"7f8b63980ada138f","prefixes":{"":1244}}, // [Bizible] - {"hash":"fcff0eb6c45bdf72","prefixes":{"":1245}}, // [Adludio Limited] - {"hash":"58ae271664061630","prefixes":{"":1245}}, // [Adludio Limited] - {"hash":"19795a3ce2a7e620","prefixes":{"":1245}}, // [Adludio Limited] - {"hash":"aa3822d449c78db7","prefixes":{"":1245}}, // [Adludio Limited] - {"hash":"b1269cde121b05a1","prefixes":{"":1245}}, // [Adludio Limited] - {"hash":"1581c4afe0d48fa2","prefixes":{"":1246}}, // [Ally Financial] - {"hash":"2e171be7640cf4cd","prefixes":{"":1247}}, // [AIAd Ltd.] - {"hash":"ae7e973c225c0a01","prefixes":{"":1248}}, // [Petplan] - {"hash":"78db497564cd4bf9","prefixes":{"bis":1249,"":1249,"openrtb":1249}}, // [Vidazoo Ltd.] [Vidazoo Ltd.] [Vidazoo Ltd.] - {"hash":"c0fe1ea55404bcbb","prefixes":{"bis":1249}}, // [Vidazoo Ltd.] - {"hash":"f7fbe362db50024a","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"55abab5eb4ff10bd","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"e542a9dea283be31","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"a49f5bf572a22bae","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"7e02810393b6fe89","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"89c0cbd03222de92","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"31a98e1d5bba9ae8","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"15010236a7d3b5a4","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"9f927848d0417d12","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"7a2f5d3665e9a31a","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"02fb27611632d82c","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"0b95d00c8ef095ae","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"2d42c2d0a5ab84ae","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"5e405c19a402590e","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"d037ae4b54d7e7ec","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"fa3f8d822f471c8e","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"1fd9bf19c5906c01","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"f989a4e87da3e6d9","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"26937f461d535e88","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"755c357326a37b3f","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"aac0686686b2d53c","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"3ce944fa0cf0291a","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"7302f12cc4ace16b","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"52816f3d4542b887","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"59bf7df499fa52b5","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"ebabd6c7d62c634c","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"53ac53e2f538f2e2","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"3ed73abadb9c898a","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"9574295460ce8d22","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"d591fdb6045eb90f","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"8b39993907ef0e90","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"fff9c91a1d02e69d","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"d086a625516bdfc4","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"dedb3c41790f0b92","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"d9d28815b7351be9","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"0eceb2a8a6aa5014","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"c3cd28d4911901ef","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"906300510974cf1a","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"6bbcc411e84396ce","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"18f5beb4c7518ce0","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"445bbe1950cddd7b","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"2b7cc0f2bdc9e984","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"2e1ee42fbe53923e","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"0d75de8359eb1e90","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"5439763db9182a6c","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"924a269d7fa953ef","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"56592bc98da7add7","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"e350307b97e81944","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"62d2d232adb7f5ce","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"9dbd1f71df681f87","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"60aa872cca3ea408","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"574290c159276582","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"520b03b31ba99ffe","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"8927dc4db3ffd9a0","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"cea75e5e1bcb3e75","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"00fc1e5ba5cf11e1","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"241443df62a11bdb","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"958852e67d999446","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"d7dde4fdd1b42ac2","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"b9a6386a13d1f59c","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"1b00c21eb7dca0ba","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"3b5a0569a91c1f27","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"07b3c413dcf0891e","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"4b15aef39cd29476","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"25743e7a87ec9352","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"2fe16a518eee6e23","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"a154647a02beff09","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"73a06806ded66bc0","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"a82c6297f56276f8","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"514b9e5dcf88791d","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"80e10fe664c9676b","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"788de2248f446546","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"e1e58f0b49a0e5c7","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"c56afa9dadaace8c","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"4084c6a05941d212","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"fafe943a3ce4b828","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"00cfc10d1f469dfc","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"cdb86fef4fddecdd","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"97b3473c7082a4c8","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"99973a84d3bda612","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"ba67aff057d8dc5d","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"a3c0da72dbefc878","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"5925a6291dd2ab40","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"34129d9457495ffd","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"e2505c6002184e33","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"9bcac0e9fd77ddc6","prefixes":{"":1249}}, // [Vidazoo Ltd.] - {"hash":"737c1868e55d5b37","prefixes":{"":1250}}, // [RTBstar LLC] - {"hash":"302ec6d6755746d6","prefixes":{"*":1251}}, // [Chalk Media Holdings] - {"hash":"202f611d1eab4195","prefixes":{"*":1251}}, // [Chalk Media Holdings] - {"hash":"a6ab491761f03a1c","prefixes":{"":1252}}, // [Oxford Biochronometrics] - {"hash":"3f8968d2bc62134f","prefixes":{"":1252}}, // [Oxford Biochronometrics] - {"hash":"fbe6ab3bc9d21558","prefixes":{"":1252}}, // [Oxford Biochronometrics] - {"hash":"769529a9ad14a9b6","prefixes":{"":1253}}, // [SGN Games, Inc.] - {"hash":"f1a29a10c1ad80be","prefixes":{"*":1254}}, // [Crutchfield New Media, LLC] - {"hash":"930e98a126b97c02","prefixes":{"":113}}, // [YOOX NET-A-PORTER GROUP SPA] - {"hash":"915e8620d61e782c","prefixes":{"":113}}, // [YOOX NET-A-PORTER GROUP SPA] - {"hash":"0492037eab3835e9","prefixes":{"":113}}, // [YOOX NET-A-PORTER GROUP SPA] - {"hash":"5eee79daa9337871","prefixes":{"":1255}}, // [Laserlike Inc] - {"hash":"3262121ae456ec62","prefixes":{"":1256}}, // [Adtile Technologies Inc.] - {"hash":"eefeb24c8c0df59f","prefixes":{"":1256}}, // [Adtile Technologies Inc.] - {"hash":"2f77e3292a158b3c","prefixes":{"":1256}}, // [Adtile Technologies Inc.] - {"hash":"ed04984091cd6edc","prefixes":{"":1257}}, // [Adgravity] - {"hash":"63c7ce8ea1112262","prefixes":{"":1257}}, // [Adgravity] - {"hash":"842d35d55c520fe2","prefixes":{"":1257}}, // [Adgravity] - {"hash":"3fb91c004caeeba4","prefixes":{"":1257}}, // [Adgravity] - {"hash":"4673bf1b8fb41012","prefixes":{"":1257}}, // [Adgravity] - {"hash":"60c5047efc444089","prefixes":{"":1257}}, // [Adgravity] - {"hash":"9daf201eb861bd5a","prefixes":{"":1257}}, // [Adgravity] - {"hash":"8fb045042ba8dab7","prefixes":{"":1257}}, // [Adgravity] - {"hash":"b5c5f11aa533f8f8","prefixes":{"":1257}}, // [Adgravity] - {"hash":"e69760fd18a7e847","prefixes":{"":1257}}, // [Adgravity] - {"hash":"afdb50efeb58adb3","prefixes":{"":1257}}, // [Adgravity] - {"hash":"54ade0c2fc33d554","prefixes":{"":1257}}, // [Adgravity] - {"hash":"980a9356a0a0604b","prefixes":{"":1257}}, // [Adgravity] - {"hash":"0d064f41dd06b071","prefixes":{"":1257}}, // [Adgravity] - {"hash":"4e9cc6d069d0700f","prefixes":{"":1257}}, // [Adgravity] - {"hash":"abc81c5ff7313f61","prefixes":{"":1257}}, // [Adgravity] - {"hash":"4d8e54db683834b5","prefixes":{"":1257}}, // [Adgravity] - {"hash":"958c891ea7a22760","prefixes":{"":1257}}, // [Adgravity] - {"hash":"02b0aa7dbed4dc70","prefixes":{"":1257}}, // [Adgravity] - {"hash":"cd655ca6f3e7c5b4","prefixes":{"":1257}}, // [Adgravity] - {"hash":"117f6d5c385c0fbf","prefixes":{"":1257}}, // [Adgravity] - {"hash":"974b3e895eb889a1","prefixes":{"":1257}}, // [Adgravity] - {"hash":"2315e038cabe7cf3","prefixes":{"":1257}}, // [Adgravity] - {"hash":"256e6db135713c95","prefixes":{"":1257}}, // [Adgravity] - {"hash":"44b4921e1dd83428","prefixes":{"":1257}}, // [Adgravity] - {"hash":"2ececb465fbab022","prefixes":{"":1257}}, // [Adgravity] - {"hash":"b3a40f1c056c981d","prefixes":{"":1257}}, // [Adgravity] - {"hash":"c4f8f640f24e3592","prefixes":{"":1257}}, // [Adgravity] - {"hash":"0a6e85210f1d99c3","prefixes":{"":1257}}, // [Adgravity] - {"hash":"d12573269a748046","prefixes":{"":1257}}, // [Adgravity] - {"hash":"deabc924d996a1ca","prefixes":{"":1257}}, // [Adgravity] - {"hash":"cfae73045c7b29a6","prefixes":{"":1257}}, // [Adgravity] - {"hash":"c97db9d5ee0b71c7","prefixes":{"":1257}}, // [Adgravity] - {"hash":"ca6bb3442fa58646","prefixes":{"":1257}}, // [Adgravity] - {"hash":"f38fbdc9759c0ef4","prefixes":{"":1257}}, // [Adgravity] - {"hash":"6db47cd08bed3122","prefixes":{"":1257}}, // [Adgravity] - {"hash":"86c0634020121d1e","prefixes":{"":1257}}, // [Adgravity] - {"hash":"98c1045cd61f979d","prefixes":{"":1257}}, // [Adgravity] - {"hash":"7bdc1a49cf0ccc1d","prefixes":{"":1257}}, // [Adgravity] - {"hash":"cdceb4fead43eafb","prefixes":{"":1257}}, // [Adgravity] - {"hash":"b8a7ac4ace0ce6e9","prefixes":{"":1257}}, // [Adgravity] - {"hash":"081ccef35be56b36","prefixes":{"":1257}}, // [Adgravity] - {"hash":"e29de726004b6e06","prefixes":{"":1257}}, // [Adgravity] - {"hash":"3044eb15f0757788","prefixes":{"":1258}}, // [Protected Media LTD] - {"hash":"675f561d3f31df38","prefixes":{"":1258}}, // [Protected Media LTD] - {"hash":"506fa56f8b748a67","prefixes":{"":1259}}, // [Media Detect GmbH] - {"hash":"d8795d8df3c0b355","prefixes":{"":1260}}, // [Centro CDN] - {"hash":"92690e6cf2068609","prefixes":{"":1261}}, // [DeltaX] - {"hash":"bcb296520399e855","prefixes":{"":1261}}, // [DeltaX] - {"hash":"eeba4d486f2a193f","prefixes":{"":1261}}, // [DeltaX] - {"hash":"36ba79aa125ace1d","prefixes":{"":1261}}, // [DeltaX] - {"hash":"755e0fb3a6d853e7","prefixes":{"":1261}}, // [DeltaX] - {"hash":"926c7d8c47cdc6a4","prefixes":{"":1261}}, // [DeltaX] - {"hash":"112f8b5a749d05e9","prefixes":{"":1261}}, // [DeltaX] - {"hash":"9a9421d022c231d0","prefixes":{"":1262}}, // [jQuery] - {"hash":"7ed84366898b6e95","prefixes":{"":1263}}, // [SoMo Audience Corp.] - {"hash":"7b7b8f4fea812106","prefixes":{"":1263}}, // [SoMo Audience Corp.] - {"hash":"cd27a627925b605e","prefixes":{"":1263}}, // [SoMo Audience Corp.] - {"hash":"8de4d0c2e5a785d3","prefixes":{"":1263}}, // [SoMo Audience Corp.] - {"hash":"dd090795aa41d39d","prefixes":{"":1263}}, // [SoMo Audience Corp.] - {"hash":"a792d2d9ef8b7d44","prefixes":{"":1264}}, // [Distribute Ltd] - {"hash":"a41d8bffbb4dde29","prefixes":{"c":1264}}, // [Distribute Ltd] - {"hash":"cb03110aa3eafbc3","prefixes":{"":1264}}, // [Distribute Ltd] - {"hash":"f8d5c1c3c0137b95","prefixes":{"":1264}}, // [Distribute Ltd] - {"hash":"fdbd7675a0a4e4d2","prefixes":{"":1264}}, // [Distribute Ltd] - {"hash":"c152daf3639bfd61","prefixes":{"":1265}}, // [Poppin] - {"hash":"77afc197a9ed3c55","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"f000d51ba83c9b81","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"e6b0bd562a8a6c44","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"b67bb576e01ff3b8","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"b74cae0d407627c6","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"d02ca334243aeff6","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"27c4aebe8feb1d61","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"cffac58b1fe6a120","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"060b85b3672a94b9","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"993dbbd9a4428116","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"5d08c05fd8f3e6b6","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"733a8e48aa3496c3","prefixes":{"":1266}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] - {"hash":"6dfff15240319d9a","prefixes":{"":1267}}, // [Art of Click Pte. Ltd] - {"hash":"3771e4f81d75a524","prefixes":{"":45}}, // [Adways SAS] - {"hash":"b2d089ed26249e9c","prefixes":{"":45}}, // [Adways SAS] - {"hash":"6cb0920968e42462","prefixes":{"":45}}, // [Adways SAS] - {"hash":"779587babf9a845a","prefixes":{"":45}}, // [Adways SAS] - {"hash":"21fd838e467abf9e","prefixes":{"":45}}, // [Adways SAS] - {"hash":"f35db5d8e41be046","prefixes":{"":1268}}, // [Quantasy LLC] - {"hash":"fb881e61783803db","prefixes":{"":1269}}, // [Wavenet Technology Co., Ltd.] - {"hash":"4a5a7154d849b6df","prefixes":{"":1270}}, // [ENVISIONX LTD] - {"hash":"4c0fc55e8fe8be51","prefixes":{"":1271}}, // [Adhood] - {"hash":"62b3383734c496fb","prefixes":{"":1271}}, // [Adhood] - {"hash":"27e40c1642b59b0e","prefixes":{"":1271}}, // [Adhood] - {"hash":"b7bcec8f502b24cf","prefixes":{"":1271}}, // [Adhood] - {"hash":"b8885905bf9e213f","prefixes":{"":1271}}, // [Adhood] - {"hash":"8238d8729bb3748c","prefixes":{"":1272}}, // [Telogical Systems, LLC] - {"hash":"92dbc0e618f7816a","prefixes":{"":1272}}, // [Telogical Systems, LLC] - {"hash":"d3be8ee2ff24c8dd","prefixes":{"":1272}}, // [Telogical Systems, LLC] - {"hash":"0767b016186f9908","prefixes":{"":1273}}, // [twyn group IT solutions & marketing services G] - {"hash":"1bf7b44093f4c0b5","prefixes":{"":1274}}, // [Marchex Sales, LLC] - {"hash":"b60099c5a3ff5579","prefixes":{"":1275}}, // [SmartyAds LLP] - {"hash":"e1e5cbc69ff27126","prefixes":{"":1275}}, // [SmartyAds LLP] - {"hash":"01e34dcf6356f25b","prefixes":{"":1275}}, // [SmartyAds LLP] - {"hash":"0587980840cbad28","prefixes":{"":1275}}, // [SmartyAds LLP] - {"hash":"c13574923293d7c7","prefixes":{"":1276}}, // [Reach150 Social, Inc.] - {"hash":"e87358da2b6ad070","prefixes":{"":1277}}, // [Leadmill ApS] - {"hash":"2a24bd7dfe371075","prefixes":{"":1277}}, // [Leadmill ApS] - {"hash":"3f6b6ba536add909","prefixes":{"":1278}}, // [TapHeaven, Inc.] - {"hash":"050f2328d34461a7","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"a66968f05d34f468","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"2c309ae9bbbe9384","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"fec9fa0c1f3ffe6e","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"49989970caa95c3d","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"32eb6e39519c6d65","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"5ecf91c30b7a0a6a","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"6394e1f93c660614","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"3c6f41d29f5df8e7","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"cda46d94d7efda5c","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"164782d64f732408","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"af8c779439f17da0","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"0394176d4d575a93","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"c9a808631e01ad1a","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"1478be442db9d539","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"171f00354c5c6b43","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"521c6ad185dff69d","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"a7c6cb92086909f4","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"efb2ad70cb64944c","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"35e50ce841196503","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"683154a56472f3e2","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"6664d0e4be1a2011","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"efefa03759c8eb89","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"20477544dc10c916","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"922d2397edef5dbd","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"a9591e706ed0da18","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"c073c895bf061908","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"8a7804498832f633","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"45c2a9f766453c46","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"2a167f8c5d41c9de","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"0992810376313bba","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"e4588caa0c232726","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"73fbee089a438309","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"48743ee797fa8c98","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"22561af78d8aeebe","prefixes":{"":1279}}, // [spring GmbH & Co. KG] - {"hash":"ee6f381082694c53","prefixes":{"":1280}}, // [Roq.ad GmbH] - {"hash":"606e92e46118e68c","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"5dd554237e590e7a","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"47c55cdc2352963b","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"698477bb42f577d9","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"4d35613bf3c5608c","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"8bbcbb68e24aba75","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"b5d289baee77f3bb","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"1eab99893ccb6812","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"2d38a36747a2e4c8","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"2aaee44de4212440","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"720594080864ccd0","prefixes":{"":1281}}, // [AdKernel LLC] - {"hash":"8cf8db96c95ad703","prefixes":{"":1282}}, // [Uprise Technologies] - {"hash":"bdc43b963f1225e2","prefixes":{"":1283}}, // [Sled, Inc.] - {"hash":"bd39b2a3b45ba87e","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"987e96fd53ac3377","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"ae49bd17dd4ead54","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"8d132f34a86f51b3","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"4210564496763546","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"c395c961fb4a60ae","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"2f37a86d54dbe4f9","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"04730e3fb984e716","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"8e040ece0573e008","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"a2aafd8d622f674d","prefixes":{"":1284}}, // [Pengtai Interactive Advertising Co.Ltd] - {"hash":"4da566f98e41e44f","prefixes":{"":1285}}, // [Adello Group AG] - {"hash":"469779ba0b2d7340","prefixes":{"":1286}}, // [BitGravity] - {"hash":"bfa6d675fa0c17c3","prefixes":{"":1287}}, // [44 Interactive] - {"hash":"ac3b5f670d7fd934","prefixes":{"*":1288}}, // [KeyCDN] - {"hash":"1058dbbc2bdc3042","prefixes":{"*":1288}}, // [KeyCDN] - {"hash":"d36cc93ea8fd1701","prefixes":{"":1289}}, // [Shopalyst Technologies Pvt Ltd] - {"hash":"95ad856091f4bc84","prefixes":{"":1289}}, // [Shopalyst Technologies Pvt Ltd] - {"hash":"848239e92ef8cdab","prefixes":{"":1290}}, // [Total Access Communication Public Company Limited] - {"hash":"0607ff309fec62de","prefixes":{"":1291}}, // [TACTIC Real-time marketing AS] - {"hash":"abcc33d7fb9de6d9","prefixes":{"":1292}}, // [Mobilewalla, Inc] - {"hash":"c7bbc2be78119074","prefixes":{"":1293}}, // [Nuviad Ltd] - {"hash":"63da29d5b072ec2a","prefixes":{"*":1294}}, // [AmberData LLC] - {"hash":"6c2f34d23d272f4b","prefixes":{"":1295}}, // [Aedgency] - {"hash":"2da837b490e2d01c","prefixes":{"":1296}}, // [MobPartner, Inc.] - {"hash":"900c7f6444e6ccdd","prefixes":{"":1297}}, // [AdTriba GmbH] - {"hash":"9110b2853fbfc1f4","prefixes":{"":1297}}, // [AdTriba GmbH] - {"hash":"0a9e29016a6f697e","prefixes":{"":1298}}, // [DISH Network L.L.C.] - {"hash":"8c7746484c824908","prefixes":{"":1299}}, // [Monotype Imaging Inc.] - {"hash":"11826b3490b70597","prefixes":{"":1299}}, // [Monotype Imaging Inc.] - {"hash":"55b9261efb54de8c","prefixes":{"":1299}}, // [Monotype Imaging Inc.] - {"hash":"57f3bae0dc69937f","prefixes":{"":1299}}, // [Monotype Imaging Inc.] - {"hash":"b5158a795c80a5a2","prefixes":{"":1300}}, // [MEDIAN Ltd.] - {"hash":"e79098d30fde1a28","prefixes":{"":1301}}, // [ClickForce Inc.] - {"hash":"36739070f3c0ff16","prefixes":{"":1301}}, // [ClickForce Inc.] - {"hash":"40febfb412ba6ddb","prefixes":{"":1302}}, // [Zemanta Inc.] - {"hash":"80e8d865a27bacac","prefixes":{"":237}}, // [Hostelworld.com Limited] - {"hash":"2a3acfae7d4bdd9b","prefixes":{"":861}}, // [Barometric] - {"hash":"a48dde8fab3a9ca1","prefixes":{"*":1303}}, // [jsdelivr.com] - {"hash":"82b544dbc2fdd536","prefixes":{"*":1304}}, // [Adssets AB] - {"hash":"46106972e3ebaddb","prefixes":{"":1305}}, // [Sellpoints Inc.] - {"hash":"bbc8c43681364c14","prefixes":{"":46}}, // [Opera Mediaworks Inc.] - {"hash":"a3ff6df6612f21df","prefixes":{"":46}}, // [Opera Mediaworks Inc.] - {"hash":"759162b5823db802","prefixes":{"":1306}}, // [HockeyCurve Growth Solutions Pvt Ltd] - {"hash":"b3f9f03fb2c77a95","prefixes":{"":1307}}, // [HockeyCurve Growth Solutions Pyt Ltd] - {"hash":"0dcbeed5fe35278e","prefixes":{"":1307}}, // [HockeyCurve Growth Solutions Pyt Ltd] - {"hash":"e6898006d78eca08","prefixes":{"":1308}}, // [Umeng Plus Beijing Technology Limited Company] - {"hash":"c2ecb9f34950ae64","prefixes":{"":1308}}, // [Umeng Plus Beijing Technology Limited Company] - {"hash":"e530de1f6ff8ed8c","prefixes":{"":1308}}, // [Umeng Plus Beijing Technology Limited Company] - {"hash":"ef9efb2dfd9b223f","prefixes":{"":1308}}, // [Umeng Plus Beijing Technology Limited Company] - {"hash":"8dad79c9e968f7ca","prefixes":{"":1309}}, // [Survata, Inc.] - {"hash":"7a5110db80c0493f","prefixes":{"":1309}}, // [Survata, Inc.] - {"hash":"e8cb0ee7c430e881","prefixes":{"":1310}}, // [JustWatch GmbH] - {"hash":"c85404f2f558dbd2","prefixes":{"":1310}}, // [JustWatch GmbH] - {"hash":"b44b63d6e81094e4","prefixes":{"":1310}}, // [JustWatch GmbH] - {"hash":"e1614140e3cef4fd","prefixes":{"":1310}}, // [JustWatch GmbH] - {"hash":"2cfd0204e2673c60","prefixes":{"":1311}}, // [Interactive Tracker Next] - {"hash":"1c5f8f88ce98b360","prefixes":{"":1312}}, // [Unisport A/S] - {"hash":"fbac71ab5dbe5025","prefixes":{"":1312}}, // [Unisport A/S] - {"hash":"4e08c60f61b1ac87","prefixes":{"":1312}}, // [Unisport A/S] - {"hash":"3f4bfa67b5290ada","prefixes":{"":1312}}, // [Unisport A/S] - {"hash":"a9b4af4145bf90b2","prefixes":{"":1312}}, // [Unisport A/S] - {"hash":"d0d20b9b53b41e58","prefixes":{"":1312}}, // [Unisport A/S] - {"hash":"ff37598c00132a2a","prefixes":{"":1312}}, // [Unisport A/S] - {"hash":"daae3b285b4abeee","prefixes":{"":1312}}, // [Unisport A/S] - {"hash":"20cdb1f9a8066fe2","prefixes":{"":1313}}, // [OPTDCOM TEKNOLOJİ YATIRIM ANONİM ŞİRKETİ] - {"hash":"a53281df69681314","prefixes":{"":1313}}, // [OPTDCOM TEKNOLOJİ YATIRIM ANONİM ŞİRKETİ] - {"hash":"f25344f6f85b0f61","prefixes":{"":1313}}, // [OPTDCOM TEKNOLOJİ YATIRIM ANONİM ŞİRKETİ] - {"hash":"b544d72a6726ee1d","prefixes":{"":1314}}, // [Softcube Inc.] - {"hash":"d924c399f5813397","prefixes":{"":1315}}, // [YOptima Media Solutions Pvt. Ltd] - {"hash":"3405e57b3b784a16","prefixes":{"":1316}}, // [ZMAGS INC] - {"hash":"8a5251fd38cf1d3b","prefixes":{"":1316}}, // [ZMAGS INC] - {"hash":"039987610c50338a","prefixes":{"":1316}}, // [ZMAGS INC] - {"hash":"0f7d3c301f70dfd0","prefixes":{"":1316}}, // [ZMAGS INC] - {"hash":"77f81cf76bb9bd07","prefixes":{"":1316}}, // [ZMAGS INC] - {"hash":"ec6a93785f45f030","prefixes":{"":1316}}, // [ZMAGS INC] - {"hash":"3fcf5b9a36bb776b","prefixes":{"":1316}}, // [ZMAGS INC] - {"hash":"b81945950b163ad1","prefixes":{"":1307}}, // [HockeyCurve Growth Solutions Pyt Ltd] - {"hash":"36c7ea48fdc42362","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"a3bfc331d20ba3e8","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"64a6cb69505f47a8","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"e7d85f73b7514aa9","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"0263af098607c6d6","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"5c911e8fdb9b3507","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"665d12aaa10c1858","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"206d02e9bc6dc66f","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"681210fb48272cc7","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"f42b80ceeefbc2e4","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"9b951656519bc7f0","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"f615949643fdcebb","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"814934c13235b868","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"d451d289ed990ba0","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"b2de6bb8607f747e","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"fa9dba37389c8a9e","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"fe776ee0016175b8","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"1a561a0db871620b","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"84440122e5725865","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"f98baedc7354d85b","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"cedef135a07c2d1b","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"b7196f696d55352b","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"90c8f9118f9c2653","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"7ec460729f94a446","prefixes":{"":1317}}, // [Realtag] - {"hash":"c84baed94e3d3a6c","prefixes":{"":1318}}, // [Bitsngo.net] - {"hash":"88c72a0edf15af94","prefixes":{"":1318}}, // [Bitsngo.net] - {"hash":"8ffa9e7ae82fdaf3","prefixes":{"":1318}}, // [Bitsngo.net] - {"hash":"b2b5e138d12de438","prefixes":{"":1318}}, // [Bitsngo.net] - {"hash":"1249b88e4ce20439","prefixes":{"":1318}}, // [Bitsngo.net] - {"hash":"9555623f08217ce6","prefixes":{"":1318}}, // [Bitsngo.net] - {"hash":"0358d164ffcdc51b","prefixes":{"":1318}}, // [Bitsngo.net] - {"hash":"d27b1458c8f91a54","prefixes":{"":1319}}, // [AdCanvas] - {"hash":"5fd4bc3f3699146f","prefixes":{"":1319}}, // [AdCanvas] - {"hash":"62f35089b9876ae5","prefixes":{"":1319}}, // [AdCanvas] - {"hash":"92d701b4810ee717","prefixes":{"":1319}}, // [AdCanvas] - {"hash":"3f927191adac8a66","prefixes":{"":1319}}, // [AdCanvas] - {"hash":"051c9e70f0515e04","prefixes":{"":1320}}, // [Happyfication, Inc.] - {"hash":"13580fc82945639c","prefixes":{"":1320}}, // [Happyfication, Inc.] - {"hash":"914acd1211445b65","prefixes":{"":1320}}, // [Happyfication, Inc.] - {"hash":"046e11a0cbc2124d","prefixes":{"":1320}}, // [Happyfication, Inc.] - {"hash":"df5483bdd241c0b5","prefixes":{"":258}}, // [HQ GmbH] - {"hash":"8ee2f36b1789e47d","prefixes":{"":1321,"*":1322}}, // [Cinarra Systems Japan株式会社] [Cinarra Systems Japan] - {"hash":"92d76828c2bc5e86","prefixes":{"":959}}, // [MotoMiner] - {"hash":"014c99288a655e93","prefixes":{"":1323}}, // [CUBED Attribution] - {"hash":"34d44a28a42c30fc","prefixes":{"":1323}}, // [CUBED Attribution] - {"hash":"6525e4a55892b035","prefixes":{"":1324}}, // [StackAdapt Inc.] - {"hash":"9108433b6be75366","prefixes":{"":1324}}, // [StackAdapt Inc.] - {"hash":"ca5914cdc3f95c5a","prefixes":{"":1324}}, // [StackAdapt Inc.] - {"hash":"e25d7e6b7298b655","prefixes":{"":1325}}, // [Statiq Ltd] - {"hash":"63a988f83e509eca","prefixes":{"":47}}, // [DistroScale Inc.] - {"hash":"e6c8188134efb49f","prefixes":{"":47}}, // [DistroScale Inc.] - {"hash":"eab29bab9fd4b969","prefixes":{"":47}}, // [DistroScale Inc.] - {"hash":"c24f6dcb16cfd4e4","prefixes":{"":47}}, // [DistroScale Inc.] - {"hash":"e6a95c6bb8d6efd4","prefixes":{"":1326}}, // [Tagular Analytics, LLC] - {"hash":"9c2dcc6473dee219","prefixes":{"":1326}}, // [Tagular Analytics, LLC] - {"hash":"986af10dd127ff07","prefixes":{"":1326}}, // [Tagular Analytics, LLC] - {"hash":"f74107ff21d433e9","prefixes":{"":1326}}, // [Tagular Analytics, LLC] - {"hash":"ce5c1378f1f941f6","prefixes":{"":1327}}, // [ComboTag Technologies Ltd.] - {"hash":"879f914a15df34e0","prefixes":{"":1328}}, // [Juice Mobile] - {"hash":"17e1300dbcc39afb","prefixes":{"":1328}}, // [Juice Mobile] - {"hash":"d22e60c27d37cebc","prefixes":{"":1328}}, // [Juice Mobile] - {"hash":"80d91748976f3124","prefixes":{"":1328}}, // [Juice Mobile] - {"hash":"74478806654499c0","prefixes":{"":1328}}, // [Juice Mobile] - {"hash":"1b02f5c5bfc0039d","prefixes":{"":1328}}, // [Juice Mobile] - {"hash":"ee644eb69f62cdec","prefixes":{"":1329}}, // [Bebe] - {"hash":"8fb13ead8b0754fb","prefixes":{"":1330}}, // [Augur] - {"hash":"cfbc4ef83efc3949","prefixes":{"*":1331}}, // [Seracast Digital LLC] - {"hash":"a8285782e9e5e7e8","prefixes":{"*":1331}}, // [Seracast Digital LLC] - {"hash":"3a9b5e5bcd74ba27","prefixes":{"":1332}}, // [AerServ LLC] - {"hash":"dd4e82ee18499196","prefixes":{"":1332}}, // [AerServ LLC] - {"hash":"2163ccdaf0c7ac04","prefixes":{"*":1321}}, // [Cinarra Systems Japan株式会社] - {"hash":"e16e6171a3e50ef3","prefixes":{"":1333}}, // [PT. Tokopedia] - {"hash":"74b1225f4470815b","prefixes":{"":1334}}, // [Terapeak, Inc.] - {"hash":"7eb7af632c622616","prefixes":{"*":1335}}, // [OpenStreetMap France] - {"hash":"63c3a3e6536f0b7f","prefixes":{"*":1336}}, // [Front Porch, Inc] - {"hash":"2c6aaec84cacd716","prefixes":{"*":1336}}, // [Front Porch, Inc] - {"hash":"6f469a516e0ea586","prefixes":{"":1337}}, // [Vertamedia] - {"hash":"d06485ea48298b7e","prefixes":{"":1338}}, // [Codewise (VoluumDSP)] - {"hash":"7a43b35e42bcef39","prefixes":{"":1339}}, // [Virool Inc.] - {"hash":"4ff7c90f1d23d419","prefixes":{"":1340}}, // [SpringServe LLC] - {"hash":"7f06352affc07566","prefixes":{"":1340}}, // [SpringServe LLC] - {"hash":"96ea998589a3f7ab","prefixes":{"":1340}}, // [SpringServe LLC] - {"hash":"32ec7c3af8d6ee95","prefixes":{"":1340}}, // [SpringServe LLC] - {"hash":"294e9804817557ed","prefixes":{"":1340}}, // [SpringServe LLC] - {"hash":"e9c681d4af45ad2c","prefixes":{"":1341}}, // [Intimate Merger] - {"hash":"68cc07724e870731","prefixes":{"":1341}}, // [Intimate Merger] - {"hash":"60f3c72e07bc4431","prefixes":{"":1342}}, // [Telecoming Connectivity S.A.] - {"hash":"22081b94e890803c","prefixes":{"":1343}}, // [Webssup] - {"hash":"00e22da76abf1e12","prefixes":{"":1343}}, // [Webssup] - {"hash":"498495a391901f22","prefixes":{"":1344}}, // [INCUBIQ Solutions Ltd] - {"hash":"606c712ed1507693","prefixes":{"":1344}}, // [INCUBIQ Solutions Ltd] - {"hash":"a9af6bbd9a022211","prefixes":{"*":1336}}, // [Front Porch, Inc] - {"hash":"5ad9370da8c17992","prefixes":{"":1345}}, // [ADSpend] - {"hash":"fdd6cbfd0c4ad857","prefixes":{"":1346}}, // [AdTradr Corporation] - {"hash":"81b9923f85553957","prefixes":{"":1346}}, // [AdTradr Corporation] - {"hash":"a145ad19f618929c","prefixes":{"":1346}}, // [AdTradr Corporation] - {"hash":"b1930e60745f4b68","prefixes":{"":1346}}, // [AdTradr Corporation] - {"hash":"57f97d99aaf44ad1","prefixes":{"":1346}}, // [AdTradr Corporation] - {"hash":"99143cae9fe8dcb7","prefixes":{"":1347}}, // [Quint Growth Inc.] - {"hash":"eb5cead7881dc3da","prefixes":{"":1348}}, // [ZAPR] - {"hash":"2fe0bb9349d1959b","prefixes":{"":1348}}, // [ZAPR] - {"hash":"b959bccbc132d322","prefixes":{"":20}}, // [On Device Research Ltd.] - {"hash":"8a8c554522b206ad","prefixes":{"":1349}}, // [1trn LLC] - {"hash":"45dfdbe509539a3b","prefixes":{"":1349}}, // [1trn LLC] - {"hash":"6265ceee0906cac8","prefixes":{"":1350}}, // [Matiro] - {"hash":"279177f20ce276af","prefixes":{"":1350}}, // [Matiro] - {"hash":"b90e8e5f5f114049","prefixes":{"":1350}}, // [Matiro] - {"hash":"68519953a094dc0c","prefixes":{"":1350}}, // [Matiro] - {"hash":"2c9356fdc6a3d512","prefixes":{"":1350}}, // [Matiro] - {"hash":"486312e316f3d301","prefixes":{"":1350}}, // [Matiro] - {"hash":"0dea0840245a2dd2","prefixes":{"*":1351}}, // [Inspired Mobile Ltd.] - {"hash":"e1e444909b156b50","prefixes":{"*":1351}}, // [Inspired Mobile Ltd.] - {"hash":"ba50b6623ecf06b7","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"bb2433b4d9ceaf93","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"752e7bd32ca03e2b","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"fecd6600c1d54571","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"e2f5a8707a39a63f","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"6e7cd3eb7639d09a","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"e0afc2f779238a59","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"51c4028642a5ca4c","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"1640e1eeb06df964","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"6c8c2f363cf647b5","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"d9f31e1a90e8fb75","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"845fb9c6387bef97","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"0459341df70cc072","prefixes":{"":1352}}, // [Selectmedia International LTD.] - {"hash":"be68419b316ae4c5","prefixes":{"":1353}}, // [Internet Billboard, a. s] - {"hash":"ba9be76fe6cba556","prefixes":{"":1353}}, // [Internet Billboard, a. s] - {"hash":"ec3aa5f11c754de6","prefixes":{"":1353}}, // [Internet Billboard, a. s] - {"hash":"5023d333c14740b4","prefixes":{"":1353}}, // [Internet Billboard, a. s] - {"hash":"eab4b496bc09743b","prefixes":{"":1354}}, // [Volvelle] - {"hash":"2afdaf8d1bad3cb9","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"c2367faa0f667908","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"ed58e98b46833296","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"e0249363e0c7a5fa","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"3f6dfa5fad816543","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"0daca079a0ddd2c5","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"eb2192ea666d6dfe","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"6a1269cf9dab66ee","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"39079f1183b1a260","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"9455a8e3fa7e9a01","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"bebc1faa9d73b7b0","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"c5069619b84c740e","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"8effee3cd53dcfe8","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"8251bfe6accaab04","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"f1019f4619cbfe25","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"825efa7e6eea91b6","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"cdeee95936c4dbc6","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"812ac20217e10e22","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"a20318efcc942b0c","prefixes":{"":1355}}, // [StreamRail Ltd.] - {"hash":"7ba3379a648cbc77","prefixes":{"":1356}}, // [E-Contenta] - {"hash":"faca10de01f9747f","prefixes":{"":1356}}, // [E-Contenta] - {"hash":"c4bada2290c2b41f","prefixes":{"":1356}}, // [E-Contenta] - {"hash":"7e8290508a81ecfd","prefixes":{"":1356}}, // [E-Contenta] - {"hash":"415fbdfa7cf95914","prefixes":{"":1356}}, // [E-Contenta] - {"hash":"97322010e9179343","prefixes":{"":1356}}, // [E-Contenta] - {"hash":"07549a667fcd0003","prefixes":{"":1356}}, // [E-Contenta] - {"hash":"f5359f09115e5a08","prefixes":{"":1357}}, // [Bath and Body Works] - {"hash":"93931937deeacde2","prefixes":{"":1160}}, // [KuaiziTech] - {"hash":"885efbcb155efd68","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"8515f5e803de693b","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"7990fea9b4cdbdf9","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"a6632e804a3dc853","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"ae4745b0fd104fbb","prefixes":{"":691}}, // [Aniview LTD.] - {"hash":"956dc47eeadd36f7","prefixes":{"":1358}}, // [WooTag Pte Ltd] - {"hash":"091b478316a2c133","prefixes":{"":1358}}, // [WooTag Pte Ltd] - {"hash":"dc8fa04b592b8307","prefixes":{"":1358}}, // [WooTag Pte Ltd] - {"hash":"fe0e326ac7dc8d7c","prefixes":{"":1358}}, // [WooTag Pte Ltd] - {"hash":"410a107d660ab1cd","prefixes":{"":1359}}, // [Cint AB] - {"hash":"b294fa0e55fcd2f4","prefixes":{"":1359}}, // [Cint AB] - {"hash":"5519fa5da5e57ad1","prefixes":{"":1360}}, // [Stein Mart] - {"hash":"7277d1fc5bc3c416","prefixes":{"":1361}}, // [Sift Media, Inc.] - {"hash":"4b89510469e15939","prefixes":{"":1362}}, // [StartApp Inc.] - {"hash":"d3b5600174198250","prefixes":{"":1362}}, // [StartApp Inc.] - {"hash":"5597070a34018ed8","prefixes":{"":1362}}, // [StartApp Inc.] - {"hash":"17f290970639d124","prefixes":{"":1363}}, // [Pxene - DSP] - {"hash":"263ee2028bc08b39","prefixes":{"":1363}}, // [Pxene - DSP] - {"hash":"da0f3a897355dd67","prefixes":{"":1363}}, // [Pxene - DSP] - {"hash":"9ca84ba93389421b","prefixes":{"":1363}}, // [Pxene - DSP] - {"hash":"4d5f045a10baa819","prefixes":{"":1364}}, // [Integral Marketing] - {"hash":"50bc395b6101613b","prefixes":{"":1364}}, // [Integral Marketing] - {"hash":"3affd533937e3000","prefixes":{"":48}}, // [Enzymic Consultancy] - {"hash":"971d313a32301476","prefixes":{"":48}}, // [Enzymic Consultancy] - {"hash":"c5351f32e51be2fe","prefixes":{"":48}}, // [Enzymic Consultancy] - {"hash":"4d91e6834270bed5","prefixes":{"":48}}, // [Enzymic Consultancy] - {"hash":"5d662bce285c4f62","prefixes":{"":48}}, // [Enzymic Consultancy] - {"hash":"acf96648fc83494b","prefixes":{"":48}}, // [Enzymic Consultancy] - {"hash":"ea62a03194f9fd95","prefixes":{"":1365}}, // [Expedia, Inc.] - {"hash":"d3ff1633dded1329","prefixes":{"":1366}}, // [DeepIntent, Inc] - {"hash":"2b5d6ad4574423ab","prefixes":{"":1366}}, // [DeepIntent, Inc] - {"hash":"dd8c5b641bb2871c","prefixes":{"":1366}}, // [DeepIntent, Inc] - {"hash":"4ba3d981f5073f12","prefixes":{"":1366}}, // [DeepIntent, Inc] - {"hash":"92f845f3cbfc83ca","prefixes":{"":181}}, // [Rakuten Attribution] - {"hash":"ff93f30e29dd79ce","prefixes":{"":1367}}, // [OmniVirt] - {"hash":"1219256c57d362c2","prefixes":{"":1367}}, // [OmniVirt] - {"hash":"588a30ada75d3716","prefixes":{"":1367}}, // [OmniVirt] - {"hash":"f99b3a1ad217ec24","prefixes":{"":1367}}, // [OmniVirt] - {"hash":"cffe29c52b1b73c5","prefixes":{"":1367}}, // [OmniVirt] - {"hash":"f03d942c23c65a90","prefixes":{"":1367}}, // [OmniVirt] - {"hash":"272d4c035a088de6","prefixes":{"":1367}}, // [OmniVirt] - {"hash":"724568d0c3fc08a4","prefixes":{"":1368}}, // ["Index20" LLC] - {"hash":"be108323d132726d","prefixes":{"":1369}}, // [Conversion Logic, Inc.] - {"hash":"9fc3b91614502933","prefixes":{"":1370}}, // [Collegehumor] - {"hash":"bb96895ad0b3fb46","prefixes":{"":1370}}, // [Collegehumor] - {"hash":"830074aed1a53d22","prefixes":{"":1370}}, // [Collegehumor] - {"hash":"db32b2c0136553c9","prefixes":{"":775}}, // [Netflix, Inc.] - {"hash":"47df197461b802d5","prefixes":{"":775}}, // [Netflix, Inc.] - {"hash":"acbf0ad8badc6915","prefixes":{"":775}}, // [Netflix, Inc.] - {"hash":"c0b6c8d5b1736dc3","prefixes":{"":775}}, // [Netflix, Inc.] - {"hash":"05dd2a6c687d34c0","prefixes":{"":1371}}, // [Silence Media Limited] - {"hash":"cea7d6ed17648fbf","prefixes":{"":1371}}, // [Silence Media Limited] - {"hash":"ad7149f316532231","prefixes":{"":1372}}, // [Zuuvi Aps] - {"hash":"693751faddbb5801","prefixes":{"":1372}}, // [Zuuvi Aps] - {"hash":"8da87b729d579d09","prefixes":{"":1372}}, // [Zuuvi Aps] - {"hash":"ab5cc3951d346c77","prefixes":{"":1372}}, // [Zuuvi Aps] - {"hash":"f41a4b1ac6a3bfb2","prefixes":{"":1373}}, // [SoftBank Corp.] - {"hash":"1d0977195dc18825","prefixes":{"":1374}}, // [ConnectOM, Inc.] - {"hash":"912365bfc81f780a","prefixes":{"":1374}}, // [ConnectOM, Inc.] - {"hash":"7434a888611cfaf2","prefixes":{"":1374}}, // [ConnectOM, Inc.] - {"hash":"1d73404a4d6b3cda","prefixes":{"":49}}, // [Fluct Inc.] - {"hash":"938fa93131d63045","prefixes":{"":49}}, // [Fluct Inc.] - {"hash":"67f35499d1bf5216","prefixes":{"":1375}}, // [Skillup Video Technologies Corporation] - {"hash":"c8e2c0c42c388ceb","prefixes":{"":1375}}, // [Skillup Video Technologies Corporation] - {"hash":"0800074e9a8aa9e0","prefixes":{"":1375}}, // [Skillup Video Technologies Corporation] - {"hash":"f6853fba1d5c8366","prefixes":{"":1376}}, // [Adara Impact Analytics] - {"hash":"7d5aa0875b5b03ee","prefixes":{"":1376}}, // [Adara Impact Analytics] - {"hash":"e1a55205d635d049","prefixes":{"":1376}}, // [Adara Impact Analytics] - {"hash":"b5ea51bb0aec174c","prefixes":{"":1376}}, // [Adara Impact Analytics] - {"hash":"e4140b78afda4fb4","prefixes":{"":1376}}, // [Adara Impact Analytics] - {"hash":"a4c74032025e0589","prefixes":{"":1377}}, // [TabMo SAS] - {"hash":"d9638d78485cff41","prefixes":{"":1377}}, // [TabMo SAS] - {"hash":"6ea3e003ed64d138","prefixes":{"":58}}, // [Sixt Leasing SE] - {"hash":"22ae0beb131c4b8c","prefixes":{"":1378}}, // [Casale Media] - {"hash":"29d1aaca6b9e6edd","prefixes":{"":1378}}, // [Casale Media] - {"hash":"7d1380d196e0f347","prefixes":{"":1379}}, // [StickyADStv] - {"hash":"147ac7b767355a32","prefixes":{"":1380}}, // [Teads Technology SAS] - {"hash":"4b60266ba860a4c5","prefixes":{"":1381}}, // [Anomaly Communications, LLC] - {"hash":"296893a9570ca935","prefixes":{"*":1382}}, // [ClickTicker, LTD] - {"hash":"094df71f27ebd6f1","prefixes":{"":1383}}, // [Tremour] - {"hash":"a1119d262b86de34","prefixes":{"":1384}}, // [Roket Media LTD] - {"hash":"b1ed20b79d8a43cb","prefixes":{"":152}}, // [e-Planning] - {"hash":"5bd05c801bb32096","prefixes":{"":1385}}, // [Video Jam (MauDau LTD)] - {"hash":"dacf613410c17f5e","prefixes":{"":1385}}, // [Video Jam (MauDau LTD)] - {"hash":"561f104ba38d6e64","prefixes":{"":1385}}, // [Video Jam (MauDau LTD)] - {"hash":"a43509d6a08d1777","prefixes":{"":1386}}, // [MINIMOB (CY) LTD] - {"hash":"b66514ac6530a3fd","prefixes":{"":1387}}, // [Snitcher B.V.] - {"hash":"79b3660bde132ba1","prefixes":{"":1387}}, // [Snitcher B.V.] - {"hash":"aaf7781b800509ba","prefixes":{"":1387}}, // [Snitcher B.V.] - {"hash":"8fec6c672f5b204a","prefixes":{"":1387}}, // [Snitcher B.V.] - {"hash":"825e7df4dfb9bae0","prefixes":{"":1387}}, // [Snitcher B.V.] - {"hash":"379899626d07c3b5","prefixes":{"":1388}}, // [Analights] - {"hash":"664ce6265f73ec6a","prefixes":{"":1388}}, // [Analights] - {"hash":"ab33f6650feb44c2","prefixes":{"":1388}}, // [Analights] - {"hash":"557404c787c545cb","prefixes":{"":1388}}, // [Analights] - {"hash":"0c881f692e8ec0de","prefixes":{"":1388}}, // [Analights] - {"hash":"36c7264c1d06cc29","prefixes":{"":1388}}, // [Analights] - {"hash":"2f7b75786675821b","prefixes":{"":1388}}, // [Analights] - {"hash":"714fd218f12fce7a","prefixes":{"":1388}}, // [Analights] - {"hash":"d140a5f747a666f1","prefixes":{"":1388}}, // [Analights] - {"hash":"cbcfd9d592f2d19d","prefixes":{"":1388}}, // [Analights] - {"hash":"a214f6ac537cafa2","prefixes":{"":1389}}, // [Romir Panel Ltd.] - {"hash":"84fe21a8c3ba3818","prefixes":{"":1390}}, // [Dievision] - {"hash":"c709bff0d2284ecc","prefixes":{"":1391}}, // [Ignite Technologies] - {"hash":"3510f10ff5914868","prefixes":{"*":1392}}, // [Navegg] - {"hash":"ea594377060cafc6","prefixes":{"":1393}}, // [Adalyser] - {"hash":"631a68a191554bae","prefixes":{"":1393}}, // [Adalyser] - {"hash":"8d9638d9355607ba","prefixes":{"":1393}}, // [Adalyser] - {"hash":"d6e288fc5cf14c83","prefixes":{"":1393}}, // [Adalyser] - {"hash":"90ec5cb29381e1ca","prefixes":{"":1393}}, // [Adalyser] - {"hash":"3debdfa22ee00230","prefixes":{"":1393}}, // [Adalyser] - {"hash":"45852dff86c0a7e0","prefixes":{"":1394}}, // [Nordic Factory International Inc.] - {"hash":"063e04585364c0e5","prefixes":{"":719}}, // [E-Plus Mobilfunk GmbH & Co. KG] - {"hash":"aa4bfba1bb15cb76","prefixes":{"":719}}, // [E-Plus Mobilfunk GmbH & Co. KG] - {"hash":"3f43b05864c28c77","prefixes":{"":1395}}, // [Addition Plus Ltd] - {"hash":"9e1057ca77b6610c","prefixes":{"":1395}}, // [Addition Plus Ltd] - {"hash":"a6278ebdf3ca7bce","prefixes":{"":1396}}, // [Karmatech Mediaworks Pvt Ltd] - {"hash":"954044969f047c6c","prefixes":{"":1396}}, // [Karmatech Mediaworks Pvt Ltd] - {"hash":"ca32fcf3a3101ca0","prefixes":{"":1397}}, // [Mediatropy Pte Ltd] - {"hash":"54a24d41044c0730","prefixes":{"":26}}, // [FSN ASIA PRIVATE LIMITED] - {"hash":"b3302c4e4fc23cb8","prefixes":{"":856}}, // [Adman Interactive SL] - {"hash":"328eed54ff330e78","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"98ed7d7c1ae050b7","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"226e8c98a3e2e091","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"9e777471181caa70","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"9df613858a859407","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"37af06459214925d","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"0fdc04aa3cd4e402","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"f33f14a479b17a13","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"535ad6947981986b","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"0db16a6e891b1ff5","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"11b3c502d4eb1fa3","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"001bd6970eef2bbe","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"93505db1f9bf4a47","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"a1c9ac37054fc828","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"f4f375e269f34a60","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"e61b3b137beecfbc","prefixes":{"":1398}}, // [Wayve Limited] - {"hash":"07bf22fe56be8d20","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"619e27568b55894c","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"374b60567f3c7f8b","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"af11cb377f269b3b","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"6bd694da478fa87b","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"75c4aedcbc945c91","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"71e8a51762fe81b6","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"5412f8017660207b","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"ba29363e3e139066","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"05de1e7e9f3271c3","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"5d73b4b9b8705846","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"711d9c4ce084aa7d","prefixes":{"*":1399}}, // [Kreditech Holding SSL GmbH] - {"hash":"1ed6fde4c65ef6f7","prefixes":{"*":269}}, // [DePauli AG] - {"hash":"4a6b1490dc059d0b","prefixes":{"*":269}}, // [DePauli AG] - {"hash":"cab81dbe20c0957f","prefixes":{"*":269}}, // [DePauli AG] - {"hash":"af1f3ce39e3b4862","prefixes":{"*":269}}, // [DePauli AG] - {"hash":"19e97b139ae15342","prefixes":{"*":269}}, // [DePauli AG] - {"hash":"fb46fee1ceef4719","prefixes":{"*":269}}, // [DePauli AG] - {"hash":"798bda76da15abeb","prefixes":{"*":21}}, // [SuperVista AG] - {"hash":"38bf7a598ad0c498","prefixes":{"*":21}}, // [SuperVista AG] - {"hash":"48651b09d312b814","prefixes":{"*":21}}, // [SuperVista AG] - {"hash":"bb2482d6022a94ba","prefixes":{"*":21}}, // [SuperVista AG] - {"hash":"d58f8af9ff4e691e","prefixes":{"":1400}}, // [Yengo] - {"hash":"8e9bee1bb54d54ee","prefixes":{"":1400}}, // [Yengo] - {"hash":"06689ed650e2b8c3","prefixes":{"":1401}}, // [DANtrack] - {"hash":"96c97b30a03579ac","prefixes":{"":1401}}, // [DANtrack] - {"hash":"407face2b2c27397","prefixes":{"":1402}}, // [Integral Markt- und Meinungsforschungsges.m.b.H.] - {"hash":"184f80ef53a0f178","prefixes":{"":1402}}, // [Integral Markt- und Meinungsforschungsges.m.b.H.] - {"hash":"076a460590b496bf","prefixes":{"":1403}}, // [Millemedia GmbH] - {"hash":"9febeed138f5f2ce","prefixes":{"":1404}}, // [Hiro-Media] - {"hash":"dd35fc0a5f5fa07b","prefixes":{"":1404}}, // [Hiro-Media] - {"hash":"b22687a0d4bd341f","prefixes":{"":1404}}, // [Hiro-Media] - {"hash":"1625640192348305","prefixes":{"*":1405}}, // [Simplaex Gmbh] - {"hash":"444356542461e7d2","prefixes":{"":1405}}, // [Simplaex Gmbh] - {"hash":"b9edac442e488ddf","prefixes":{"":1405}}, // [Simplaex Gmbh] - {"hash":"afed9502218fc1cd","prefixes":{"":1406}}, // [Telekom Deutschland GmbH] - {"hash":"286d994374ce8a57","prefixes":{"":1407}}, // [Infernotions Technologies Limited] - {"hash":"385225c96e123b3f","prefixes":{"":1408}}, // [Goldfish Media LLC] - {"hash":"19c11b9ad47802f9","prefixes":{"":1365}}, // [Expedia, Inc.] - {"hash":"d710ea66b904cd82","prefixes":{"":1409}}, // [Smartology Limited] - {"hash":"24bbec831a84a114","prefixes":{"":1409}}, // [Smartology Limited] - {"hash":"9c3279986e25304c","prefixes":{"":1409}}, // [Smartology Limited] - {"hash":"8ce94299572dc9b8","prefixes":{"":1409}}, // [Smartology Limited] - {"hash":"e51f784e66ceb1cb","prefixes":{"":1410}}, // [GroupM] - {"hash":"72bd4ec98dea633b","prefixes":{"":1410}}, // [GroupM] - {"hash":"0836c753510a4fd6","prefixes":{"*":1411}}, // [Haystagg Inc.] - {"hash":"4a13b1d65c372006","prefixes":{"*":1411}}, // [Haystagg Inc.] - {"hash":"14ec018232a87385","prefixes":{"":1412}}, // [Digital Turbine Media, Inc] - {"hash":"fdabfb7535e313a7","prefixes":{"":1412}}, // [Digital Turbine Media, Inc] - {"hash":"cddef42a99b1f0e2","prefixes":{"":1413}}, // [MDSP INC] - {"hash":"1bcc84762c0f2df8","prefixes":{"":1414}}, // [Quadas (YiDong Data Inc.)] - {"hash":"6c46a69428837566","prefixes":{"":1415}}, // [Recruit Career Co., Ltd.] - {"hash":"17319e35b3b89b68","prefixes":{"":1416}}, // [R2Net Inc.] - {"hash":"0825a9da5dd17373","prefixes":{"":1416}}, // [R2Net Inc.] - {"hash":"aaa00ce722ea0d35","prefixes":{"":1416}}, // [R2Net Inc.] - {"hash":"40b683cc8c061f6f","prefixes":{"":1417}}, // [Madberry OY] - {"hash":"ea76804ad8af9b5c","prefixes":{"":1417}}, // [Madberry OY] - {"hash":"985cce26aeab90d8","prefixes":{"":1417}}, // [Madberry OY] - {"hash":"d67d98cbb9553a81","prefixes":{"":1417}}, // [Madberry OY] - {"hash":"ee1c2ae2ffdaa754","prefixes":{"":1417}}, // [Madberry OY] - {"hash":"8c162379b63896e6","prefixes":{"":1418}}, // [QVC] - {"hash":"4c23d567b98e413f","prefixes":{"*":1419}}, // [Micro Cube Digital Limited] - {"hash":"9e74e9e50b7e6116","prefixes":{"":1420}}, // [egg.de GmbH] - {"hash":"8242efdacea6ca14","prefixes":{"":1421}}, // [Headway Mexico] - {"hash":"82b90117a5beb869","prefixes":{"":1422}}, // [RTBiQ Inc.] - {"hash":"7d4bc30e9d495d00","prefixes":{"":1194}}, // [Fluidads] - {"hash":"44305a81af328854","prefixes":{"":1194}}, // [Fluidads] - {"hash":"f5d44df23b5b7f0a","prefixes":{"":1423}}, // [SCIBIDS TECHNOLOGY S.A.S.] - {"hash":"e86f56889ef213b9","prefixes":{"*":1424}}, // [Kyocera Communication Systems] - {"hash":"e4f3abfdd94fbb95","prefixes":{"":1425}}, // [Cortex Media Group] - {"hash":"7525243b1d665de2","prefixes":{"":1426}}, // [appTV] - {"hash":"f27453a19aa2370e","prefixes":{"":1426}}, // [appTV] - {"hash":"8156979d25af9817","prefixes":{"":1427}}, // [ProgSol, Programmatic Solution, s.r.o.] - {"hash":"888e465d14dbfeb5","prefixes":{"":1427}}, // [ProgSol, Programmatic Solution, s.r.o.] - {"hash":"6bcdb8e4cf28b730","prefixes":{"":1427}}, // [ProgSol, Programmatic Solution, s.r.o.] - {"hash":"cc229228113699d8","prefixes":{"":1428}}, // [Rezonence] - {"hash":"fd7695df1dfe9f20","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"fbee76565472bc95","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"e53efe28417732fd","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"4f76dc8807a1b25a","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"b7cd9e7666d175e1","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"ba124e354a70dcb2","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"8c9e63d24d4fbe6b","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"ea6157fcb7c5c718","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"6063abef39fb89be","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"8a3b3647f6fae243","prefixes":{"":1429}}, // [LKQD Platform] - {"hash":"25b652ead7abb3b0","prefixes":{"":1059}}, // [Bidtellect] - {"hash":"ff03eb40846eaa12","prefixes":{"":1059}}, // [Bidtellect] - {"hash":"649ada5375fff936","prefixes":{"":1059}}, // [Bidtellect] - {"hash":"79b86a55d8f149ef","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"a6e4ebed7d417fec","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"e7a27d73288188b6","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"24028ce468194c75","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"d7f89ed8a6431a69","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"079f75aa60b736a9","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"55ab38530ebcb2e7","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"244a99d8deb4343c","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"02afdba2d61c6d76","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"50fcadafe2e2734b","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"0a5d998860b7968b","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"ff1ef9a23c2bdf51","prefixes":{"":1430}}, // [target value GmbH] - {"hash":"b7be02479c9268a9","prefixes":{"":1431}}, // [Firecracker] - {"hash":"2841b43a0e0ddb37","prefixes":{"":1431}}, // [Firecracker] - {"hash":"ea77ccf69493306c","prefixes":{"":1431}}, // [Firecracker] - {"hash":"786060a6ba793b32","prefixes":{"":1431}}, // [Firecracker] - {"hash":"af58243a5817c930","prefixes":{"":1431}}, // [Firecracker] - {"hash":"b90ffee242259e26","prefixes":{"":1432}}, // [MADGIC] - {"hash":"037e84a783130203","prefixes":{"":1433}}, // [Digiseg] - {"hash":"724c85c96cc72a57","prefixes":{"":1433}}, // [Digiseg] - {"hash":"df235ed188c82f5b","prefixes":{"":1433}}, // [Digiseg] - {"hash":"df21bf354a2570e1","prefixes":{"":1433}}, // [Digiseg] - {"hash":"52a9b224dd3e1b02","prefixes":{"":1433}}, // [Digiseg] - {"hash":"ef749fa483d558a9","prefixes":{"":1433}}, // [Digiseg] - {"hash":"a6b20df6c2ce3063","prefixes":{"":1433}}, // [Digiseg] - {"hash":"7e7e73dfe402883c","prefixes":{"":1433}}, // [Digiseg] - {"hash":"1cafe416eb8d746c","prefixes":{"":1433}}, // [Digiseg] - {"hash":"45c0ef64ecfb6525","prefixes":{"":1434}}, // [Bulbit Inc.] - {"hash":"d3710c2900e90f84","prefixes":{"":1434}}, // [Bulbit Inc.] - {"hash":"fed8a97b9e855887","prefixes":{"":1435}}, // [Lost My Name] - {"hash":"5c18434d5d721456","prefixes":{"":1436}}, // [People Media] - {"hash":"fd4a671d34b4c76c","prefixes":{"":1436}}, // [People Media] - {"hash":"181b385a8ea5a687","prefixes":{"":1437}}, // [Platform.io] - {"hash":"00393673ca5c1d8a","prefixes":{"":1437}}, // [Platform.io] - {"hash":"6479d1c5a241da2f","prefixes":{"":1438}}, // [Bidspeaker] - {"hash":"f30e44d5503f39c4","prefixes":{"":1439}}, // [YouGov PLC] - {"hash":"4f07a6c89151a2eb","prefixes":{"":1440}}, // [SAP SE] - {"hash":"519e46f12c141c48","prefixes":{"":1440}}, // [SAP SE] - {"hash":"413cdfaa8215b8f5","prefixes":{"":1440}}, // [SAP SE] - {"hash":"dd8e150b503eb12f","prefixes":{"":1441}}, // [Adchex] - {"hash":"58e19248782ce688","prefixes":{"":1442}}, // [Smart Bid Limited] - {"hash":"8ccc1dd45e305817","prefixes":{"":1443}}, // [UAd Exchange] - {"hash":"9543cb4330fa1d6e","prefixes":{"":1444}}, // [UAd Exchange - IBV] - {"hash":"fc1a7604d6ced7c3","prefixes":{"":1445}}, // [esc mediagroup GmbH] - {"hash":"90c354743de0b811","prefixes":{"":1446}}, // [defacto smart reach GmbH] - {"hash":"0d8f41278d702251","prefixes":{"":1447}}, // [Research and Analysis of Media in Sweden AB] - {"hash":"607c4919b3c8d5cd","prefixes":{"":1447}}, // [Research and Analysis of Media in Sweden AB] - {"hash":"d0d7647dc2ea9dd9","prefixes":{"":1448}}, // [OnAudience.com] - {"hash":"3c0c99d90a16804f","prefixes":{"":1449}}, // [OneTag] - {"hash":"a50e8a4129f160c8","prefixes":{"":1449}}, // [OneTag] - {"hash":"62105b8204658950","prefixes":{"":1449}}, // [OneTag] - {"hash":"8069afc9a94b92b9","prefixes":{"*":1349}}, // [1trn LLC] - {"hash":"c9cc947471099a0e","prefixes":{"":59}}, // [Air Berlin] - {"hash":"516352d734faa684","prefixes":{"":1450}}, // [Fiverr International Ltd.] - {"hash":"562a3da1a81ae4ec","prefixes":{"":1450}}, // [Fiverr International Ltd.] - {"hash":"1ba262fef7c9b94b","prefixes":{"":1451}}, // [Adikteev] - {"hash":"cd63e73725fa2b8d","prefixes":{"":1451}}, // [Adikteev] - {"hash":"f24819f2edd93c69","prefixes":{"":1451}}, // [Adikteev] - {"hash":"e3b1538bced5ec72","prefixes":{"":1451}}, // [Adikteev] - {"hash":"341df4e708fbbc07","prefixes":{"":1452}}, // [CenterPoint Media] - {"hash":"1f984187723f0399","prefixes":{"":1452}}, // [CenterPoint Media] - {"hash":"6ed35b33de5147be","prefixes":{"":1452}}, // [CenterPoint Media] - {"hash":"d3f04891cabd56f8","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"8429b5e7c7905de9","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"eadb7778a29b9617","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"15595532c38662ea","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"ce365566d66a72ee","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"60ef24e820a6a881","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"176be2434ca2f68b","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"3f72f2b4ec7f816e","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"bcc519027914bd79","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"86628b5f01332c66","prefixes":{"":1453}}, // [Digital Trace] - {"hash":"1c34c62a587e6c1e","prefixes":{"":1454}}, // [Pure Cobalt] - {"hash":"a30f9c646772cf0f","prefixes":{"":60}}, // [Aegon ESPAÑA, S.A. de Seguros y Reaseguros, Uniper] - {"hash":"b23d960b2ea25b51","prefixes":{"":1455}}, // [Cedato] - {"hash":"030115e352ac8e99","prefixes":{"":1455}}, // [Cedato] - {"hash":"77da2d98a4da970f","prefixes":{"":1455}}, // [Cedato] - {"hash":"aa0555e19cf5d6cc","prefixes":{"":1455}}, // [Cedato] - {"hash":"badc121e99935aa5","prefixes":{"":1455}}, // [Cedato] - {"hash":"115df78aa77ac9c4","prefixes":{"":1455}}, // [Cedato] - {"hash":"04713a193ed96cc0","prefixes":{"s":1455}}, // [Cedato] - {"hash":"fd6fd1f29de71285","prefixes":{"":1455}}, // [Cedato] - {"hash":"c5de6239a6efe818","prefixes":{"":1455}}, // [Cedato] - {"hash":"4c0be33a1c14428d","prefixes":{"":1455}}, // [Cedato] - {"hash":"09eaafed5beae0e1","prefixes":{"":1455}}, // [Cedato] - {"hash":"ff987bad618809e7","prefixes":{"":1455}}, // [Cedato] - {"hash":"2b50033d06870385","prefixes":{"":1455}}, // [Cedato] - {"hash":"8b399204d27d07c0","prefixes":{"s":1455}}, // [Cedato] - {"hash":"5f78d380fa9514b1","prefixes":{"":1455}}, // [Cedato] - {"hash":"c4912c3d8f04b948","prefixes":{"":1455}}, // [Cedato] - {"hash":"361fa6df6bb3216e","prefixes":{"":1455}}, // [Cedato] - {"hash":"b18ab9dfb513f02b","prefixes":{"":1455}}, // [Cedato] - {"hash":"de511a44653105ea","prefixes":{"":1455}}, // [Cedato] - {"hash":"f990d873e1622766","prefixes":{"":1455}}, // [Cedato] - {"hash":"f53618fe52956dbf","prefixes":{"s":1455}}, // [Cedato] - {"hash":"96ed69252978cab9","prefixes":{"cdn":1455}}, // [Cedato] - {"hash":"f38b31b7e204355c","prefixes":{"":943}}, // [Admetrics GmbH] - {"hash":"895a9c8a3b52c95f","prefixes":{"":943}}, // [Admetrics GmbH] - {"hash":"62ba002fe91498c3","prefixes":{"":943}}, // [Admetrics GmbH] - {"hash":"30fe524e700bd89f","prefixes":{"":943}}, // [Admetrics GmbH] - {"hash":"6ce86e7a47d6dfbc","prefixes":{"":1456}}, // [Jonsden Properties Limited] - {"hash":"8bed3db3f252b657","prefixes":{"":1457}}, // [Realytics] - {"hash":"c2dcbb4796436e89","prefixes":{"":1458}}, // [Twinpine] - {"hash":"97a8b5033c272f3c","prefixes":{"":1458}}, // [Twinpine] - {"hash":"3d39eae9e2370c3e","prefixes":{"":1459}}, // [Mopedo AB] - {"hash":"38a9bfae76c9b2f1","prefixes":{"*":1460}}, // [Netsales] - {"hash":"9b1148932500d0b5","prefixes":{"":1461}}, // [ViewersLogic LTD] - {"hash":"901b2f7c0272451b","prefixes":{"":1461}}, // [ViewersLogic LTD] - {"hash":"7d45cf386f5c3f27","prefixes":{"":1462}}, // [ADMAN] - {"hash":"e68e2f7630af032b","prefixes":{"":1462}}, // [ADMAN] - {"hash":"79dc640f62208944","prefixes":{"":1462}}, // [ADMAN] - {"hash":"4d6e312d6aba1117","prefixes":{"":1462}}, // [ADMAN] - {"hash":"29e85c5af35e3751","prefixes":{"":1462}}, // [ADMAN] - {"hash":"e5f2b6b92cc6daa3","prefixes":{"":1463}}, // [Hyper] - {"hash":"f867e04c6dde165d","prefixes":{"":1464}}, // [Hurra Communications] - {"hash":"8353a34ee35ff30c","prefixes":{"":1464}}, // [Hurra Communications] - {"hash":"cc7f69fa55a7518f","prefixes":{"":1465}}, // [Groundhog TW] - {"hash":"95ba2d10b1b0675c","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"e8f62a52edbdf4bd","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"321a7e08d4b4fd55","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"c6f272ed0a206999","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"bd34d2733467c1af","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"1b49f6113458dc63","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"58b83e3b3a97943e","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"814bcd9589533387","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"9465fc7c156593f4","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"7c1c219e78b2f270","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"9d376bb5982319b0","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"12ec9a0b2888a4a9","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"f3d7dfcb60972b0c","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"460cac48ec149c6d","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"2614d3ffa05e19a5","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"1f9b770c08378ced","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"054864ff1578657a","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"a5505306000e20f2","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"12cae8bbd22d8673","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"d7724f8a49b96fa6","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"3013078a26f31e2f","prefixes":{"":1466}}, // [ImediaMax] - {"hash":"80193dea6eb67372","prefixes":{"":1467}}, // [Flixbus] - {"hash":"02c439b9cc7e33b5","prefixes":{"":1468}}, // [AudienceTV] - {"hash":"fcb4adbb2bc559e7","prefixes":{"":1469}}, // [b34106183_test] - {"hash":"b08446a676a287b4","prefixes":{"":1469}}, // [b34106183_test] - {"hash":"12105a6cc17c32e7","prefixes":{"":1470}}, // [Netscore] - {"hash":"036d15c3ad4a0735","prefixes":{"":1470}}, // [Netscore] - {"hash":"c0c40e6f63813be1","prefixes":{"":1471}}, // [Chu Technology Limited] - {"hash":"4c434a52325c0b6e","prefixes":{"":1472}}, // [SmartyAds LLC] - {"hash":"825a99c46b61053a","prefixes":{"":1473}}, // [dbupdate1] - {"hash":"31ab5366866e8622","prefixes":{"":1473}}, // [dbupdate1] - {"hash":"32a21f3a47cb8ddc","prefixes":{"":128}}, // [ConvertMedia Ltd.] - {"hash":"44a7af6d373bd713","prefixes":{"":985}}, // [Lodeo] - {"hash":"36d8680483227de8","prefixes":{"":985}}, // [Lodeo] - {"hash":"9709c6b69d2e3e90","prefixes":{"":985}}, // [Lodeo] - {"hash":"5efefb78ec9b146f","prefixes":{"":1474}}, // [The Big Willow Inc.] - {"hash":"3e5b3c0010f7172f","prefixes":{"":1475}}, // [LiveIntent Inc] - {"hash":"07bf09263d039040","prefixes":{"":1476}}, // [OpenLedger ApS] - {"hash":"db879c71876741d0","prefixes":{"":1477}}, // [Whichit] - {"hash":"4a6caded0a4565d9","prefixes":{"":1477}}, // [Whichit] - {"hash":"05e0b02a9af28e89","prefixes":{"":1477}}, // [Whichit] - {"hash":"3aca7ac4649f240c","prefixes":{"":1477}}, // [Whichit] - {"hash":"81abe9587250fab1","prefixes":{"":1477}}, // [Whichit] - {"hash":"a3b6bac891e57819","prefixes":{"":1478}}, // [ParkDIA] - {"hash":"ddc3507bc6f79de4","prefixes":{"":1479}}, // [Atedra Inc.] - {"hash":"881c2002027de2ec","prefixes":{"":1479}}, // [Atedra Inc.] - {"hash":"28084881f4e51f6c","prefixes":{"":1480}}, // [Digital Forest OÜ] - {"hash":"d563745e0fed92f4","prefixes":{"":1481}}, // [Isobar Werbeagentur GmbH] - {"hash":"d6d13ce525771ba4","prefixes":{"":1482}}, // [Valuepotion Pte. Ltd.] - {"hash":"446766a73edf76a2","prefixes":{"":1483}}, // [Vuble Inc] - {"hash":"0753aa310a2a75aa","prefixes":{"":1484}}, // [adlocal.net] - {"hash":"772284fa4bfb8e5a","prefixes":{"":1484}}, // [adlocal.net] - {"hash":"0dd444b76d1727ab","prefixes":{"":1484}}, // [adlocal.net] - {"hash":"1db1fc299e2dc73a","prefixes":{"":1484}}, // [adlocal.net] - {"hash":"169447ab5f0b2b32","prefixes":{"":1485}}, // [Freckle IoT] - {"hash":"143b45f2e52d871c","prefixes":{"":1486}}, // [Personalization LLC] - {"hash":"bdc55693a2c62858","prefixes":{"":1487}}, // [ThoughtLeadr Inc.] - {"hash":"e69058fe073a8d07","prefixes":{"":1487}}, // [ThoughtLeadr Inc.] - {"hash":"ded94145085d34ed","prefixes":{"":1487}}, // [ThoughtLeadr Inc.] - {"hash":"ef66f2e1bb6a3605","prefixes":{"":1487}}, // [ThoughtLeadr Inc.] - {"hash":"5f62ecd0eec6b82a","prefixes":{"":1487}}, // [ThoughtLeadr Inc.] - {"hash":"45281cea83398309","prefixes":{"":1488,"web":1488,"app":1488,"cdn":1488,"api":1488,"p":1488,"mobpages":1488}}, // [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] - {"hash":"7ea0c7c8484fb7b4","prefixes":{"cdn":1488}}, // [Noqoush Mobile Media Group FZ-LLC] - {"hash":"6839ebcbc8dce175","prefixes":{"":1488,"p":1488}}, // [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] - {"hash":"c338016e1aa2a5f7","prefixes":{"":1489}}, // [G-Core Labs] - {"hash":"3bb879b5ab17cbeb","prefixes":{"":1490}}, // [Haensel AMS GmbH] - {"hash":"26627407df51f73d","prefixes":{"t":1490,"d":1490,"s":1490}}, // [Haensel AMS GmbH] [Haensel AMS GmbH] [Haensel AMS GmbH] - {"hash":"44e07e9341e20fd0","prefixes":{"":1491}}, // [LemonPI] - {"hash":"71d7abda3a6093b3","prefixes":{"":1491}}, // [LemonPI] - {"hash":"f57717e6df039cd1","prefixes":{"":1491}}, // [LemonPI] - {"hash":"0ae0087cfa7b79c3","prefixes":{"":1491}}, // [LemonPI] - {"hash":"59afc0d091a22f58","prefixes":{"":1492}}, // [4Info, Inc.] - {"hash":"fd299e0138ad90c1","prefixes":{"":1492}}, // [4Info, Inc.] - {"hash":"1e408d2e9033a6e2","prefixes":{"":1492}} // [4Info, Inc.] + {"hash":"20d393bb0e5c4de5","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"583365b5f9d44cae","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"dcde9b6a7ed731d0","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"0b618d2d6d12ff65","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"5ca7dfe6f0741d1b","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"f10aac9be30b9153","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"a29e3fb3581debcc","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"586a404a027dd51d","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"b772cd4d229b926f","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"5f153f573d601ed5","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"d08d00723aa41a8b","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"154c00442038842c","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"bb5191c2744e5157","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"24f8ee8d41010340","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"dde97a25d741083b","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"c40e51f0d4308aef","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"dafae404fe4bf9e0","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"2965d60c41f24692","prefixes":{"*":{"product":2}}}, // [AdGenie] + {"hash":"947df9bf1ccc5af0","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"5874ab2040fd92e5","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"12c9a3fc47eec655","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"ff950154b65538b5","prefixes":{"":{"product":5}}}, // [Conversant Ad Server] + {"hash":"cca88c18c3a955c3","prefixes":{"*":{"product":6}}}, // [Napster Luxemburg SARL] + {"hash":"cf583a4f1b85a63d","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"b1f24fb9f4e82bc4","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"7ed3ef3bd9b7964c","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"802185637db97f57","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"69ac52e6452dcdcc","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"99d6e276a9f3dce4","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"20cea422f633f132","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"abac0e92b7c09189","prefixes":{"":{"product":12}}}, // [Madeleine Mode GmbH] + {"hash":"ab6d5bcfcb13c98c","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"17c760570121cb8e","prefixes":{"":{"product":14}}}, // [Telefonica UK / O2 UK] + {"hash":"cca991b6ab7e6463","prefixes":{"":{"product":15}}}, // [Tamome] + {"hash":"b24aab8ad8822e3b","prefixes":{"":{"product":15}}}, // [Tamome] + {"hash":"e068190951dbb5ef","prefixes":{"":{"product":15}}}, // [Tamome] + {"hash":"c5a5fd495c44b8e2","prefixes":{"":{"product":15}}}, // [Tamome] + {"hash":"95305154969e81b9","prefixes":{"":{"product":15}}}, // [Tamome] + {"hash":"f40a7549c998fd96","prefixes":{"":{"product":16}}}, // [TUI UK Limited] + {"hash":"96eeaa2c9afb8955","prefixes":{"":{"product":16}}}, // [TUI UK Limited] + {"hash":"bfeca1a08eac1f5e","prefixes":{"*":{"product":17}}}, // [iJento] + {"hash":"cfbb894fdba5489a","prefixes":{"":{"product":18}}}, // [McCann Erikson] + {"hash":"4ce21296b7adb20d","prefixes":{"":{"product":19}}}, // [Tribes Research Limited] + {"hash":"72b12a834f93bbd1","prefixes":{"":{"product":20}}}, // [On Device Research Ltd.] + {"hash":"5a885783941e6540","prefixes":{"*":{"product":21}}}, // [SuperVista AG] + {"hash":"382734d54ddf7100","prefixes":{"":{"product":22}}}, // [National Lottery] + {"hash":"397eccbf74cd0328","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"bf0f9d6566d29693","prefixes":{"*":{"product":0}}}, // [1&1 Internet AG] + {"hash":"8fd259996da5f3d8","prefixes":{"":{"product":23}}}, // [AppNexus Open AdStream] + {"hash":"56837fe2597a4f9e","prefixes":{"":{"product":23}}}, // [AppNexus Open AdStream] + {"hash":"fb4ddfafadb387cf","prefixes":{"":{"product":23}}}, // [AppNexus Open AdStream] + {"hash":"07a11d9d007c60f8","prefixes":{"":{"product":23}}}, // [AppNexus Open AdStream] + {"hash":"9f36fc0bb2911046","prefixes":{"":{"product":23}}}, // [AppNexus Open AdStream] + {"hash":"57c7af60819b3f3e","prefixes":{"":{"product":23}}}, // [AppNexus Open AdStream] + {"hash":"c3363b1ccf0e2ab6","prefixes":{"":{"product":24}}}, // [Interworks Media, Inc.] + {"hash":"9d16bd99e929eccd","prefixes":{"":{"product":24}}}, // [Interworks Media, Inc.] + {"hash":"211d3b9ea9bf8f2e","prefixes":{"":{"product":25}}}, // [Action Exchange, Inc.] + {"hash":"414adfe007d3587f","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"4486e584990f9603","prefixes":{"":{"product":26}}}, // [FSN ASIA PRIVATE LIMITED] + {"hash":"8cfb66b1b7bbe43b","prefixes":{"":{"product":27}}}, // [iPromote] + {"hash":"fba2a0cd626a5289","prefixes":{"":{"product":27}}}, // [iPromote] + {"hash":"113620ff3bb3ea60","prefixes":{"":{"product":27}}}, // [iPromote] + {"hash":"b5f6b4570a07977c","prefixes":{"*":{"product":28}}}, // [33Across Inc.] + {"hash":"877f3b2ffe34bf4a","prefixes":{"*":{"product":29}}}, // [8thBridge] + {"hash":"bc5882cc43d24a0b","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"5506a3935677620c","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"4a8f0380d8c0ee22","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"54e1d163948509ad","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"534e3733e4e5309d","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"c7db8e0e25c7df2d","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"222afdeb29be30ca","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"a082f434d9ec1f91","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"e00092cf7fb8c74a","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"6b3557e54073f90d","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"b825724682d4bd41","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"62a0c6b3cd3ed567","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"7e5f80275a654f93","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"426c5ded1145b847","prefixes":{"*":{"product":1}}}, // [A9] + {"hash":"14f8bb92c4036403","prefixes":{"*":{"product":1}}}, // [A9] + {"hash":"8aaada51ee8ebb86","prefixes":{"*":{"product":1}}}, // [A9] + {"hash":"38eac8937b706b6f","prefixes":{"*":{"product":1}}}, // [A9] + {"hash":"7e1d4edc3530e448","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"c0bf2b011199054e","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"b4dd68c5d9b68922","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"8d943586f8f86d04","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"f60b51325cdf4f80","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"080dea15fa602a8c","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"a01c9f1f745acb3b","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"eae49b84950db230","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"dabd2bc95093ab29","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"8912c7bdde481292","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"c735e4adfc754475","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"297bba8cd045b252","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"b4ee04e1cb1b0cb0","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"1f9925b611b3db5e","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"e63f21a01153ba8c","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"5df92c4776ddb318","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"995e4fe402d230d4","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"f27bb2795a31311c","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"7ef41a846501bb38","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"f69c9d0a6726d8a2","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"fbe82cac507c4685","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"b30108dc4fef7d3d","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"3db91f22497fff29","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"8c93f288d1c9adaa","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"dd3abfa7756f945e","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"562f4260ff4f65a6","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"94d8ddce749b5392","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"031269afdaee38ca","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"0c0ba1aaf374eac4","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"cb1dbab4bb9d139b","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"144dac73c5669441","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"b6b232e5fbf868f0","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"3c15562b6abc1319","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"722c811d70136506","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"78a792effa9e4967","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"a59637a7ca9dc579","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"6b3ce5ea793ecbbf","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"2b5bd0c505a178d0","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"c41cefa39852e538","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"e572b55cf0c6834b","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"80d0b8e46094b1c9","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"343036881041aa23","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"2559ed3163479794","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"95acf3d42f565375","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"a3fe80607786880e","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"b7629e7fab881e7a","prefixes":{"":{"product":31}}}, // [Retailigence] + {"hash":"03ec1a841574bd4d","prefixes":{"":{"product":32}}}, // [Logly DSP] + {"hash":"7ff5ce1768464e16","prefixes":{"":{"product":32}}}, // [Logly DSP] + {"hash":"10fb9dcb49e1abe6","prefixes":{"":{"product":33}}}, // [App-CM Inc.] + {"hash":"6d3713ad37a0966c","prefixes":{"":{"product":33}}}, // [App-CM Inc.] + {"hash":"41670ff004a04f19","prefixes":{"":{"product":33}}}, // [App-CM Inc.] + {"hash":"cd681b4891935464","prefixes":{"":{"product":34}}}, // [Yahoo! Japan Corporation] + {"hash":"b97383320b594f18","prefixes":{"":{"product":34}}}, // [Yahoo! Japan Corporation] + {"hash":"e2fff6e598b3035e","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"cb3ceda4f0c38995","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"a218e065250d793c","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"deb1ce911dfb392b","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"de5ead15f1d0f171","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"92615d394883ce3c","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"6cd842b12d20b208","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"dc6acfacad6c1026","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"81aa25ff76990996","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"1254067a80cf3d7b","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"31e2549e82b92770","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"be41bb940608434e","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"05a4ab4a11bd1fbf","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"05677aca712e9b65","prefixes":{"":{"product":1}}}, // [A9] + {"hash":"00620801050945d1","prefixes":{"":{"product":35}}}, // [Oggifinogi] + {"hash":"c0ceae7cfc4144c6","prefixes":{"":{"product":36}}}, // [PaperG] + {"hash":"996c3990310cfc19","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"14210f0f0a8d71cb","prefixes":{"":{"product":38}}}, // [The Trade Desk Inc.] + {"hash":"0f07ec261c73a8fb","prefixes":{"*":{"product":39},"":{"product":40}}}, // [Amazon] [F# Inc.] + {"hash":"736b87f504b5e113","prefixes":{"":{"product":41}}}, // [Airpush, Inc.] + {"hash":"0bcef81372eadfe9","prefixes":{"":{"product":42}}}, // [Clinch.co] + {"hash":"9ea6769e672968e9","prefixes":{"":{"product":40}}}, // [F# Inc.] + {"hash":"0fde612c20c42c6f","prefixes":{"":{"product":43}}}, // [Transout Inc.] + {"hash":"5ed995763ce85029","prefixes":{"":{"product":44}}}, // [ADZIP] + {"hash":"497082972c1c0dfd","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"6d7e4a203b969d9e","prefixes":{"":{"product":46}}}, // [Opera Mediaworks Inc.] + {"hash":"d3a9057528c14a56","prefixes":{"":{"product":47}}}, // [DistroScale Inc.] + {"hash":"479d2f8d9b7a2efe","prefixes":{"":{"product":48}}}, // [Enzymic Consultancy] + {"hash":"6f7f9fb0065f745f","prefixes":{"":{"product":48}}}, // [Enzymic Consultancy] + {"hash":"893d967f4540bb44","prefixes":{"":{"product":49}}}, // [Fluct Inc.] + {"hash":"3830f0b6ca460fe9","prefixes":{"*":{"product":50}}}, // [Acuity Ads] + {"hash":"e917237ee7b0ad79","prefixes":{"":{"product":50}}}, // [Acuity Ads] + {"hash":"e0792ffd5eca94d8","prefixes":{"":{"product":50}}}, // [Acuity Ads] + {"hash":"29beee316a393584","prefixes":{"":{"product":50}}}, // [Acuity Ads] + {"hash":"ce9e7bf17bba4aae","prefixes":{"":{"product":50}}}, // [Acuity Ads] + {"hash":"fcf604f90166bb66","prefixes":{"":{"product":50}}}, // [Acuity Ads] + {"hash":"f11d9059950b4a07","prefixes":{"":{"product":50}}}, // [Acuity Ads] + {"hash":"7d142a85f84f3402","prefixes":{"":{"product":50}}}, // [Acuity Ads] + {"hash":"83c0bb224cb5622f","prefixes":{"*":{"product":51}}}, // [iLead] + {"hash":"27841e5ac1581a67","prefixes":{"":{"product":52}}}, // [Canned Banners LLC] + {"hash":"83fbdce4f2519684","prefixes":{"":{"product":52}}}, // [Canned Banners LLC] + {"hash":"db7bac94f2b2d2b7","prefixes":{"":{"product":53}}}, // [AdBroker GmbH] + {"hash":"e0de5430253205fc","prefixes":{"":{"product":53}}}, // [AdBroker GmbH] + {"hash":"bde57b6011499edf","prefixes":{"":{"product":53}}}, // [AdBroker GmbH] + {"hash":"077ad042e29c79fb","prefixes":{"":{"product":53}}}, // [AdBroker GmbH] + {"hash":"d3c6fc5ff77f0d5c","prefixes":{"":{"product":53}}}, // [AdBroker GmbH] + {"hash":"b88fb01ba215894e","prefixes":{"*":{"product":54}}}, // [InMind Opinion Media] + {"hash":"3d509f557aeca6de","prefixes":{"*":{"product":55}}}, // [Adcentric] + {"hash":"3a9959c8cadd20af","prefixes":{"*":{"product":56}}}, // [AdClear GmbH] + {"hash":"e8adb806a39f661d","prefixes":{"":{"product":57}}}, // [DeinDeal AG] + {"hash":"da7343c16a51f1d4","prefixes":{"":{"product":58}}}, // [Sixt Leasing SE] + {"hash":"5b4d4595453d5b23","prefixes":{"":{"product":59}}}, // [Air Berlin] + {"hash":"b5085d3a3ede7503","prefixes":{"":{"product":60}}}, // [Aegon ESPAÑA, S.A. de Seguros y Reaseguros, Uniper] + {"hash":"076e39b7aa354c83","prefixes":{"":{"product":56}}}, // [AdClear GmbH] + {"hash":"61c6a6d15b340f45","prefixes":{"":{"product":61}}}, // [Adform DSP] + {"hash":"36f5cb213a4d1469","prefixes":{"":{"product":61}}}, // [Adform DSP] + {"hash":"95b61c012402be18","prefixes":{"":{"product":61}}}, // [Adform DSP] + {"hash":"34e291794974e891","prefixes":{"":{"product":61}}}, // [Adform DSP] + {"hash":"1d0d393655ae7ce4","prefixes":{"*":{"product":62}}}, // [Adconion Media Group] + {"hash":"dbeb328acc5a5a14","prefixes":{"*":{"product":62}}}, // [Adconion Media Group] + {"hash":"9e6e5f626bc3d43c","prefixes":{"":{"product":62}}}, // [Adconion Media Group] + {"hash":"66659e79ff807f39","prefixes":{"":{"product":62}}}, // [Adconion Media Group] + {"hash":"ce965bb06760436e","prefixes":{"":{"product":62}}}, // [Adconion Media Group] + {"hash":"ca8e04a5cdd8fd1f","prefixes":{"":{"product":62}}}, // [Adconion Media Group] + {"hash":"1084f1e2b04f5f93","prefixes":{"":{"product":62}}}, // [Adconion Media Group] + {"hash":"04b3778457a3ba99","prefixes":{"*":{"product":62}}}, // [Adconion Media Group] + {"hash":"1b843f2f1a39c98b","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"8c7beead1ea37382","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"2b59e3efe59d97d5","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"518308907f5f69a2","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"07d4474fae9c6b06","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"47c590566f0c869a","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"409240a38e5e72b9","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"98421a6d07f5517b","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"4f00fb001635132a","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"c6ba94708f6d7d53","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"caae109e8a3bec4f","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"9137b5cdfcb54ca8","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"ae96d818a0674db2","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"f67fecc339ee273d","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"763cc84e4a6c6fce","prefixes":{"":{"product":63}}}, // [Adform] + {"hash":"170707627205d14d","prefixes":{"*":{"product":64}}}, // [Adition] + {"hash":"4b3783127a962269","prefixes":{"":{"product":64}}}, // [Adition] + {"hash":"457cb5e9c72500bd","prefixes":{"":{"product":64}}}, // [Adition] + {"hash":"0f529dc32c1b9057","prefixes":{"":{"product":64}}}, // [Adition] + {"hash":"e63845bad50263e0","prefixes":{"":{"product":65}}}, // [Active Agent] + {"hash":"08b8d8e397848984","prefixes":{"":{"product":65}}}, // [Active Agent] + {"hash":"146ac0ff21924e7d","prefixes":{"*":{"product":64}}}, // [Adition] + {"hash":"a51da6855de3a256","prefixes":{"":{"product":66}}}, // [mov.ad GmbH] + {"hash":"150498b9d10f053f","prefixes":{"*":{"product":64}}}, // [Adition] + {"hash":"fa1803e00adafe6b","prefixes":{"":{"product":66}}}, // [mov.ad GmbH] + {"hash":"ba820e9bd3ccd291","prefixes":{"":{"product":66}}}, // [mov.ad GmbH] + {"hash":"73f70045beea25cc","prefixes":{"":{"product":66}}}, // [mov.ad GmbH] + {"hash":"14725cb7b6e3ed94","prefixes":{"":{"product":66}}}, // [mov.ad GmbH] + {"hash":"fd2740cbee10696e","prefixes":{"":{"product":66}}}, // [mov.ad GmbH] + {"hash":"e150d6342c6a3952","prefixes":{"*":{"product":67}}}, // [AdJug] + {"hash":"50b8b93a8b536983","prefixes":{"*":{"product":68}}}, // [AdJuggler] + {"hash":"85335d83efc9839b","prefixes":{"*":{"product":68}}}, // [AdJuggler] + {"hash":"08f0bb45326cffe3","prefixes":{"*":{"product":69}}}, // [AdKeeper] + {"hash":"5e2b2393dad3f5eb","prefixes":{"*":{"product":69}}}, // [AdKeeper] + {"hash":"da818bcf61df9002","prefixes":{"*":{"product":69}}}, // [AdKeeper] + {"hash":"846fc96edd68e2f8","prefixes":{"*":{"product":70}}}, // [AdKnife] + {"hash":"914d48df3d7aeaae","prefixes":{"*":{"product":71}}}, // [Adku] + {"hash":"8a5b58174cc938bf","prefixes":{"*":{"product":72}}}, // [AdLantic Online Advertising] + {"hash":"218eb49612488266","prefixes":{"":{"product":73}}}, // [Adloox] + {"hash":"85c9a55ea5b2a32a","prefixes":{"data":{"product":73},"am":{"product":73}}}, // [Adloox] [Adloox] + {"hash":"9827ba4725b53982","prefixes":{"":{"product":73}}}, // [Adloox] + {"hash":"bfbbf6b81b109b36","prefixes":{"*":{"product":74}}}, // [adMarketplace] + {"hash":"006d6718806b7562","prefixes":{"*":{"product":75}}}, // [AdMotion] + {"hash":"43871f2ce46890ca","prefixes":{"":{"product":76}}}, // [AdMotion USA Inc.] + {"hash":"2648f20c5fa7ee09","prefixes":{"":{"product":76}}}, // [AdMotion USA Inc.] + {"hash":"63e0b0f085a8b214","prefixes":{"*":{"product":77}}}, // [Digilant] + {"hash":"7288a07f340911e5","prefixes":{"":{"product":78}}}, // [Adnologies GmbH] + {"hash":"a9fe843107d85e5c","prefixes":{"":{"product":78}}}, // [Adnologies GmbH] + {"hash":"230c59512261d73d","prefixes":{"cdn-":{"product":78}}}, // [Adnologies GmbH] + {"hash":"8ca4fb13619b77f1","prefixes":{"*":{"product":79}}}, // [Adometry by Google] + {"hash":"18652cba5445b34f","prefixes":{"":{"product":80}}}, // [Adperium BV] + {"hash":"6d09f83aefc4501e","prefixes":{"*":{"product":81}}}, // [AdPredictive] + {"hash":"70732e61af9adb0a","prefixes":{"":{"product":82}}}, // [Usemax] + {"hash":"b4ede094fe706ace","prefixes":{"*":{"product":83}}}, // [Adrime] + {"hash":"b19cfc95f67d0fa7","prefixes":{"*":{"product":83}}}, // [Adrime] + {"hash":"a6e49b6248014fa6","prefixes":{"*":{"product":84}}}, // [AdRiver] + {"hash":"4a53a59490c4a38e","prefixes":{"*":{"product":85}}}, // [Adroit Interactive] + {"hash":"683e22cafa5f3bd9","prefixes":{"":{"product":85}}}, // [Adroit Interactive] + {"hash":"02e8a15ca61fbc33","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"a3c3a62c565da599","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"73e23ad84157f244","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"b8bd71aaaa715a18","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"34049e798c80bbab","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"838eef1c1fe8af0c","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"3a7e3dfea00e8162","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"0173cd7bab46f6fd","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"c14663d78cbafed8","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"308a1884ba632254","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"0e219f63d92e9f36","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"f01c179c5f1e71ec","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"37523eaeb9b1fe5b","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"65eb7e97acb7c098","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"3caa5b10b768447a","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"c8ca517332beacb1","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"8af902c81b793154","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"38ed924c01545a3b","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"5b57e356a62d6828","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"4281a0a413796f1b","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"66e356358c42a9dd","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"ebe26069a0940456","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"9d2283ab81bb7a35","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"d5e05919c2d72fad","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"519afd00add32a74","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"88d48f3ceb8d02bb","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"d93b7d2660301b12","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"6a71009da4358f28","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"71f627786ab80897","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"f1b3147102656b41","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"1a3040f79f3eda5d","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"1c81402568ca4c6f","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"5a0f681f5d3714ed","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"fb3d4d280f621627","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"6811c6e8c717b25b","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"2b559615c990392b","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"d8447dd40d9ce63f","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"98a32e82bd6110c6","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"e3ff44ab0bbc3669","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"d8a244effa85d1e6","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"fa65d62c20cc94bb","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"1d55dfbdf9f318d1","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"e06269227a02ae45","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"2d2bf9a9cb6e9f86","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"0adbf6085946bbf6","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"61d2d4f25972fccd","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"d9506dcf3ab8ec68","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"61448b088aea7146","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"12b1ed92371498e2","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"b8f763d46b3d230a","prefixes":{"":{"product":88}}}, // [Integral Ad Science Firewall] + {"hash":"d4033ecbe069d318","prefixes":{"":{"product":87}}}, // [Campaign Monitor] + {"hash":"b45ea1eb9c9290a1","prefixes":{"*":{"product":89}}}, // [AdShuffle] + {"hash":"333bed71fe872c88","prefixes":{"":{"product":90}}}, // [ADSOVO] + {"hash":"ad77e48d841a15a8","prefixes":{"":{"product":90}}}, // [ADSOVO] + {"hash":"f822525b349866b4","prefixes":{"*":{"product":91}}}, // [ADTECH GmbH] + {"hash":"6cda8dd1b92f6c35","prefixes":{"*":{"product":91}}}, // [ADTECH GmbH] + {"hash":"4d592efbff40197f","prefixes":{"*":{"product":92}}}, // [Adtelligence] + {"hash":"0f57dc611a2e469e","prefixes":{"":{"product":93}}}, // [Adverline] + {"hash":"ef6cb8337223cbd6","prefixes":{"*":{"product":93}}}, // [Adverline] + {"hash":"1902c407bbf1add8","prefixes":{"*":{"product":94}}}, // [AOL Advertising.com] + {"hash":"c0956c63084023a8","prefixes":{"":{"product":95}}}, // [Adap.tv Inc. (AdX)] + {"hash":"f029d231882ed252","prefixes":{"":{"product":95}}}, // [Adap.tv Inc. (AdX)] + {"hash":"ee175aa478efb9b3","prefixes":{"*":{"product":94}}}, // [AOL Advertising.com] + {"hash":"c0fff6b8232710c4","prefixes":{"*":{"product":94}}}, // [AOL Advertising.com] + {"hash":"a846e3340ac27ada","prefixes":{"":{"product":96}}}, // [Convertro Inc] + {"hash":"548e9e34dbc275b6","prefixes":{"":{"product":97}}}, // [Tacoda] + {"hash":"e9695f56eaa054e2","prefixes":{"":{"product":94}}}, // [AOL Advertising.com] + {"hash":"f7f6bc8c4345347a","prefixes":{"*":{"product":98}}}, // [Digital Control GmbH (Advolution)] + {"hash":"484a656ace404140","prefixes":{"":{"product":98}}}, // [Digital Control GmbH (Advolution)] + {"hash":"a224f896601ec717","prefixes":{"":{"product":98}}}, // [Digital Control GmbH (Advolution)] + {"hash":"3a79a6d9af12ef9a","prefixes":{"*":{"product":99}}}, // [AdXcel] + {"hash":"b3dec77d20a55dcb","prefixes":{"*":{"product":99}}}, // [AdXcel] + {"hash":"1f25b55eca65221c","prefixes":{"":{"product":99}}}, // [AdXcel] + {"hash":"f531a655b1982ee7","prefixes":{"":{"product":100}}}, // [Alenty S.A.S] + {"hash":"b884f71dddd78a28","prefixes":{"":{"product":101}}}, // [DoubleClick Bid Manager] + {"hash":"839eb75d0e9ef128","prefixes":{"":{"product":102}}}, // [BigaBid Media Ltd.] + {"hash":"ad920773d5c3e050","prefixes":{"":{"product":103}}}, // [Cogo Labs, Inc.] + {"hash":"6cbe6bc5d5fed35c","prefixes":{"":{"product":104}}}, // [AudienceProject] + {"hash":"98bc51a23a37253c","prefixes":{"*":{"product":39}}}, // [Amazon] + {"hash":"78cfcb50606e44f8","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"37f3aa55bd48760c","prefixes":{"":{"product":106}}}, // [PocketMath] + {"hash":"fa92c520ca116999","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"26d760f54a265d93","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"c046bec428f602d5","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"7fbe2c9290629394","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"16d261a6c03c3de9","prefixes":{"":{"product":108}}}, // [Immedium, Inc.] + {"hash":"1787c49522ce0278","prefixes":{"":{"product":109}}}, // [Fractional Media, LLC] + {"hash":"45ccf99ac2ed3af1","prefixes":{"":{"product":109}}}, // [Fractional Media, LLC] + {"hash":"8c431268cc5c116b","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"645dd2a279a77bf5","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"53aaf4b4f2f2cc8b","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"a6175c31fc587fa2","prefixes":{"":{"product":111}}}, // [Epic Combo Malta Ltd.] + {"hash":"c0e7c2ad3d5d28a0","prefixes":{"":{"product":111}}}, // [Epic Combo Malta Ltd.] + {"hash":"b6a3ce0dea65f762","prefixes":{"":{"product":112}}}, // [Quixey] + {"hash":"29e07de898fa18f4","prefixes":{"":{"product":112}}}, // [Quixey] + {"hash":"1959efaae117c9fa","prefixes":{"":{"product":113}}}, // [YOOX NET-A-PORTER GROUP SPA] + {"hash":"0a59c33253f15970","prefixes":{"":{"product":114}}}, // [TapTap Networks S.L.] + {"hash":"383161133467d12b","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"e2e18e4ba75c8e58","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"1f9da694ae6dd7cf","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"5038b4de783848d4","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"3542b95dcaa08c84","prefixes":{"":{"product":115}}}, // [ComScore (AdXpose)] + {"hash":"7bbbdfeb3d7b8ad3","prefixes":{"*":{"product":116}}}, // [AdYard] + {"hash":"2ff57c503c5f110d","prefixes":{"":{"product":117}}}, // [Affectv] + {"hash":"7558d4293841e1f0","prefixes":{"":{"product":117}}}, // [Affectv] + {"hash":"a8adad05a6ff5945","prefixes":{"":{"product":117}}}, // [Affectv] + {"hash":"cd1a91dee22478d6","prefixes":{"":{"product":117}}}, // [Affectv] + {"hash":"ebca243af085210f","prefixes":{"":{"product":117}}}, // [Affectv] + {"hash":"bc7fe0dc5bf3e869","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"c894a0e3f6a6b627","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"bfdba513f8cb5b8b","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"1227c609f898a707","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"4f884065e1e88de2","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"93d5950ea0bfe437","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"560c5b8e23e29733","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"28d74bfc94c9635f","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"8f7f83e4f5f9e2c0","prefixes":{"":{"product":3}}}, // [Affilinet GmbH] + {"hash":"fd2b5d860fd44749","prefixes":{"*":{"product":3}}}, // [Affilinet GmbH] + {"hash":"1d0953961a14d2fc","prefixes":{"*":{"product":118}}}, // [SET.tv] + {"hash":"5edf63353d8e0fcd","prefixes":{"":{"product":118}}}, // [SET.tv] + {"hash":"e4e5eb11299f703e","prefixes":{"*":{"product":119}}}, // [Adroit Digital Solutions (ADS)] + {"hash":"e5cf3445847fef93","prefixes":{"*":{"product":119}}}, // [Adroit Digital Solutions (ADS)] + {"hash":"8441a2216167c3d4","prefixes":{"at":{"product":100}}}, // [Alenty S.A.S] + {"hash":"b9dab57457cf6477","prefixes":{"":{"product":100}}}, // [Alenty S.A.S] + {"hash":"94b55596aadb2893","prefixes":{"":{"product":100}}}, // [Alenty S.A.S] + {"hash":"16b31027fd40c9ad","prefixes":{"":{"product":100}}}, // [Alenty S.A.S] + {"hash":"bb4adabba7b0b6b8","prefixes":{"*":{"product":120}}}, // [AppNexus Inc] + {"hash":"3b2105469f3563e3","prefixes":{"*":{"product":121}}}, // [Adfusion] + {"hash":"2f29d38052b5a8a6","prefixes":{"":{"product":122}}}, // [ARC Media Group] + {"hash":"c133787bd9e605a7","prefixes":{"":{"product":123}}}, // [Semasio GmbH] + {"hash":"b9a83d8bd4f31543","prefixes":{"":{"product":124}}}, // [Mojiva] + {"hash":"2a1b04ad13f24350","prefixes":{"":{"product":124}}}, // [Mojiva] + {"hash":"a3c6b5ffd4fe1547","prefixes":{"":{"product":125}}}, // [Mocean mobile, Inc.] + {"hash":"0d52aafa33281228","prefixes":{"":{"product":126}}}, // [Phluant] + {"hash":"655e31ecb8719994","prefixes":{"":{"product":126}}}, // [Phluant] + {"hash":"dca17bd2f3edb917","prefixes":{"":{"product":126}}}, // [Phluant] + {"hash":"53de58004f88a1c2","prefixes":{"*":{"product":127}}}, // [AdSpirit] + {"hash":"6a1ebf671194b458","prefixes":{"*":{"product":127}}}, // [AdSpirit] + {"hash":"6ae5a4877aab564f","prefixes":{"":{"product":127}}}, // [AdSpirit] + {"hash":"9dcf8d17ea42466e","prefixes":{"*":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"32e0acbf3d2ecfcf","prefixes":{"*":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"708d275466f5230b","prefixes":{"*":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"7532d83428e05417","prefixes":{"*":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"3258e49c5f504307","prefixes":{"*":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"2c381faaa3144a37","prefixes":{"*":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"776a64cc3a9c6f81","prefixes":{"*":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"d38247a8e8d14a7c","prefixes":{"*":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"50e5ca70dbb69c03","prefixes":{"":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"c1dfde405c173e3b","prefixes":{"":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"5bce52b0f221aac2","prefixes":{"":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"5a0e5730145156e2","prefixes":{"":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"c081c626ad2c0f1f","prefixes":{"":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"a31f53c0c58e67c0","prefixes":{"":{"product":129}}}, // [ATK Media GmbH] + {"hash":"514b4f19cbdabb49","prefixes":{"":{"product":129}}}, // [ATK Media GmbH] + {"hash":"264493bb5046087f","prefixes":{"":{"product":129}}}, // [ATK Media GmbH] + {"hash":"f4e40d6832493420","prefixes":{"":{"product":129}}}, // [ATK Media GmbH] + {"hash":"126b6492c0b1b53a","prefixes":{"*":{"product":130}}}, // [Atlas] + {"hash":"0493e6c92c59fd4d","prefixes":{"":{"product":130}}}, // [Atlas] + {"hash":"d6d40b4417fefcf6","prefixes":{"":{"product":130}}}, // [Atlas] + {"hash":"0b16a7cf8dcb70ee","prefixes":{"*":{"product":131}}}, // [AudienceScience] + {"hash":"edbae80f2449aaee","prefixes":{"":{"product":132}}}, // [BeWebMedia] + {"hash":"c1196b1418bf9de9","prefixes":{"*":{"product":133}}}, // [Bidalpha Inc.] + {"hash":"87016c0f34162bc6","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"2db3b8adb9c37590","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"a10756ccfb034a50","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"1ea7188c5cc22ccf","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"c70dfe98c3735972","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"05aa49d024d6d67c","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"27c08401857f47aa","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"9a569266b426d074","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"5a764c1f870e4e38","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"64b993405c35034f","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"afe660eca2d01217","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"cd398b1d3fe6caf4","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"bb2cf5a27552ba10","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"b79ee8778f5b56d6","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"cae97638b357a30e","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"2c552297aa3abdf1","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"75bbf901ed30d891","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"365bc710167be24c","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"49849f3497cc7ff5","prefixes":{"":{"product":134}}}, // [AdGear Technologies Inc.] + {"hash":"14bb8555907973f5","prefixes":{"":{"product":135}}}, // [BlueKai] + {"hash":"db6cb381e20a210a","prefixes":{"":{"product":135}}}, // [BlueKai] + {"hash":"8e4e07437ae4b61c","prefixes":{"*":{"product":136}}}, // [Bluestreak] + {"hash":"4bb6a17c8b5ec2e7","prefixes":{"":{"product":137}}}, // [blurbIQ] + {"hash":"4aefb106f857a0de","prefixes":{"":{"product":137}}}, // [blurbIQ] + {"hash":"a7b4452428f93120","prefixes":{"":{"product":137}}}, // [blurbIQ] + {"hash":"38f01d050feb50e8","prefixes":{"":{"product":137}}}, // [blurbIQ] + {"hash":"d4ab70ba7caf1ed7","prefixes":{"":{"product":138}}}, // [Brand.net] + {"hash":"94655d9cadd8e829","prefixes":{"":{"product":138}}}, // [Brand.net] + {"hash":"f3347a75843fd9c6","prefixes":{"":{"product":138}}}, // [Brand.net] + {"hash":"60e2856d32c1da23","prefixes":{"*":{"product":139}}}, // [Brandscreen Inc.] + {"hash":"16460b1ac6e3b229","prefixes":{"*":{"product":140}}}, // [BrightRoll Inc.] + {"hash":"814fef93d6498b58","prefixes":{"*":{"product":140}}}, // [BrightRoll Inc.] + {"hash":"72fc1d7c40b9af03","prefixes":{"":{"product":141}}}, // [Brightroll Inc.] + {"hash":"80f8944423cc936c","prefixes":{"*":{"product":142}}}, // [Vindico] + {"hash":"bf2db20b09960c7b","prefixes":{"":{"product":142}}}, // [Vindico] + {"hash":"e180a56826bf4ba6","prefixes":{"*":{"product":143}}}, // [Edgesuite] + {"hash":"1e90c625da6683a5","prefixes":{"":{"product":144}}}, // [Spark Flow S.A.] + {"hash":"d385e514fe92e6d9","prefixes":{"":{"product":145}}}, // [Aarki, Inc.] + {"hash":"4646e46d55455ffe","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"a8c5f49831b36ab9","prefixes":{"*":{"product":147}}}, // [Burst Media LLC d/b/a AdConductor] + {"hash":"f22276edaf005231","prefixes":{"*":{"product":148}}}, // [Advertising.com Dynamic Retargeter] + {"hash":"220db526eb84992d","prefixes":{"*":{"product":149}}}, // [NetAffiliation] + {"hash":"cda3d9612026866c","prefixes":{"*":{"product":149}}}, // [NetAffiliation] + {"hash":"9f8018bcbf15a592","prefixes":{"*":{"product":150}}}, // [C3 Metrics Inc.] + {"hash":"78ec342986654215","prefixes":{"":{"product":150}}}, // [C3 Metrics Inc.] + {"hash":"e4ad94837095a9bc","prefixes":{"*":{"product":150}}}, // [C3 Metrics Inc.] + {"hash":"32401b2118eff57a","prefixes":{"":{"product":151}}}, // [CAPITALDATA (SARL)] + {"hash":"a5f9db92cb5aeb3e","prefixes":{"":{"product":151}}}, // [CAPITALDATA (SARL)] + {"hash":"ae12db7e0621ec0b","prefixes":{"*":{"product":151}}}, // [CAPITALDATA (SARL)] + {"hash":"d7c4970fde31fd62","prefixes":{"*":{"product":152}}}, // [e-Planning] + {"hash":"20cadab7abdae752","prefixes":{"*":{"product":153}}}, // [Chango Inc.] + {"hash":"38363f7ac872a916","prefixes":{"":{"product":154}}}, // [Chango] + {"hash":"114215c6a5b66e37","prefixes":{"":{"product":154}}}, // [Chango] + {"hash":"1ab7b29d135c11fc","prefixes":{"":{"product":155}}}, // [Channel Intelligence] + {"hash":"6e8e21cf50387d5b","prefixes":{"":{"product":155}}}, // [Channel Intelligence] + {"hash":"cbd8c46855691ac9","prefixes":{"*":{"product":155}}}, // [Channel Intelligence] + {"hash":"8bde0d3ba731b24b","prefixes":{"":{"product":155}}}, // [Channel Intelligence] + {"hash":"d415664f4794184e","prefixes":{"":{"product":155}}}, // [Channel Intelligence] + {"hash":"bc8edad5208e36aa","prefixes":{"*":{"product":155}}}, // [Channel Intelligence] + {"hash":"00900e4af3795b7d","prefixes":{"":{"product":156}}}, // [Chartbeat Inc] + {"hash":"dddd416675de98d6","prefixes":{"":{"product":156}}}, // [Chartbeat Inc] + {"hash":"085835cbbea3af5b","prefixes":{"":{"product":156}}}, // [Chartbeat Inc] + {"hash":"0a5fd555ed989cf1","prefixes":{"":{"product":156}}}, // [Chartbeat Inc] + {"hash":"38e6dfa04599d610","prefixes":{"":{"product":156}}}, // [Chartbeat Inc] + {"hash":"282ab213b57d8196","prefixes":{"":{"product":156}}}, // [Chartbeat Inc] + {"hash":"465922959fd197de","prefixes":{"*":{"product":157}}}, // [Choicestream Inc.] + {"hash":"07b64c6d94aaeede","prefixes":{"*":{"product":158}}}, // [AddThis, Inc] + {"hash":"8285ed75ebbab1bf","prefixes":{"*":{"product":158}}}, // [AddThis, Inc] + {"hash":"397da235f9c6c4dc","prefixes":{"*":{"product":159}}}, // [Platform 161] + {"hash":"3e4380669a97a9da","prefixes":{"":{"product":159}}}, // [Platform 161] + {"hash":"0cf7c3db283e4fde","prefixes":{"":{"product":160}}}, // [ClickPoint] + {"hash":"e3f610966ee4998e","prefixes":{"":{"product":161}}}, // [Cobalt Group] + {"hash":"5458332ef0eff5a0","prefixes":{"":{"product":161}}}, // [Cobalt Group] + {"hash":"755391831ca3c171","prefixes":{"*":{"product":161}}}, // [Cobalt Group] + {"hash":"82d7785d13750576","prefixes":{"*":{"product":161}}}, // [Cobalt Group] + {"hash":"f1b10b28e8a1e8a9","prefixes":{"*":{"product":161}}}, // [Cobalt Group] + {"hash":"ca9dc80141cf19b5","prefixes":{"*":{"product":162}}}, // [Cognitive Match Limited] + {"hash":"d02173b857f779a2","prefixes":{"*":{"product":162}}}, // [Cognitive Match Limited] + {"hash":"987bec8a1c07a02f","prefixes":{"*":{"product":162}}}, // [Cognitive Match Limited] + {"hash":"da18ae94f7dd99ab","prefixes":{"":{"product":163}}}, // [Tagtoo Tech Limited] + {"hash":"5e1256c5e919daf9","prefixes":{"":{"product":164}}}, // [wayStorm Co., Ltd.] + {"hash":"9e92efa06a26b83d","prefixes":{"*":{"product":162}}}, // [Cognitive Match Limited] + {"hash":"cf3c8c24a0eb24a6","prefixes":{"*":{"product":162}}}, // [Cognitive Match Limited] + {"hash":"eac24a30a92872f1","prefixes":{"":{"product":162}}}, // [Cognitive Match Limited] + {"hash":"a508105d922876fe","prefixes":{"":{"product":162}}}, // [Cognitive Match Limited] + {"hash":"b7f1551e6c1615df","prefixes":{"":{"product":162}}}, // [Cognitive Match Limited] + {"hash":"e56a388aae6f063e","prefixes":{"*":{"product":165}}}, // [Collective Media LLC] + {"hash":"adc75d0087df3e89","prefixes":{"":{"product":166}}}, // [ComScore Campaign Essentials (CE)] + {"hash":"6f0ca09cdc147fb0","prefixes":{"":{"product":166}}}, // [ComScore Campaign Essentials (CE)] + {"hash":"11f6ca20d229d8ad","prefixes":{"":{"product":166}}}, // [ComScore Campaign Essentials (CE)] + {"hash":"e4d55634b8d3126d","prefixes":{"":{"product":166}}}, // [ComScore Campaign Essentials (CE)] + {"hash":"6100cc0a622faa8e","prefixes":{"":{"product":167}}}, // [ComScore Validated Campaign Essentials (vCE)] + {"hash":"a30e99f000f781c7","prefixes":{"":{"product":167}}}, // [ComScore Validated Campaign Essentials (vCE)] + {"hash":"1ee7cb55ae2cb071","prefixes":{"":{"product":166}}}, // [ComScore Campaign Essentials (CE)] + {"hash":"7dfd981d4c018953","prefixes":{"":{"product":167}}}, // [ComScore Validated Campaign Essentials (vCE)] + {"hash":"5011cb94579baba5","prefixes":{"*":{"product":168}}}, // [VoiceFive (ComScore)] + {"hash":"a0cff89b286fc309","prefixes":{"":{"product":168}}}, // [VoiceFive (ComScore)] + {"hash":"4782e75d195d3144","prefixes":{"":{"product":168}}}, // [VoiceFive (ComScore)] + {"hash":"e63472329b04aab9","prefixes":{"":{"product":168}}}, // [VoiceFive (ComScore)] + {"hash":"5814e7da11d65131","prefixes":{"":{"product":168}}}, // [VoiceFive (ComScore)] + {"hash":"26c9b653ef6bfb53","prefixes":{"*":{"product":169}}}, // [Connexity LLC] + {"hash":"eb38b3659a232a56","prefixes":{"":{"product":169}}}, // [Connexity LLC] + {"hash":"08abcdb9184877f9","prefixes":{"":{"product":169}}}, // [Connexity LLC] + {"hash":"65a439ff09c9f7dc","prefixes":{"":{"product":169}}}, // [Connexity LLC] + {"hash":"b3afc505f1f5487b","prefixes":{"":{"product":169}}}, // [Connexity LLC] + {"hash":"b35a41328f2a6830","prefixes":{"*":{"product":170}}}, // [Constant Contact] + {"hash":"e7a596af94ad559e","prefixes":{"":{"product":171}}}, // [ContextWeb Inc.] + {"hash":"25ccc25c112607e9","prefixes":{"":{"product":171}}}, // [ContextWeb Inc.] + {"hash":"44e652990824ddee","prefixes":{"":{"product":171}}}, // [ContextWeb Inc.] + {"hash":"8eac48fda4ccaec8","prefixes":{"":{"product":171}}}, // [ContextWeb Inc.] + {"hash":"0b4eee92ed72b991","prefixes":{"ant-":{"product":172}}}, // [Conversive BV] + {"hash":"dae78afe5d5cc699","prefixes":{"":{"product":172}}}, // [Conversive BV] + {"hash":"7729d423a7fc3adf","prefixes":{"*":{"product":96}}}, // [Convertro Inc] + {"hash":"cdff1e442509fafe","prefixes":{"":{"product":173}}}, // [IBM Digital Analytics] + {"hash":"a07829ef9a9d6762","prefixes":{"*":{"product":174}}}, // [Crimtan] + {"hash":"43c19f0523939982","prefixes":{"":{"product":174}}}, // [Crimtan] + {"hash":"2c4f3b76ebfafe80","prefixes":{"":{"product":174}}}, // [Crimtan] + {"hash":"078603692b0949b1","prefixes":{"":{"product":174}}}, // [Crimtan] + {"hash":"aa8844a1d3e7c3aa","prefixes":{"":{"product":174}}}, // [Crimtan] + {"hash":"15ef2aafaa03e60b","prefixes":{"":{"product":174}}}, // [Crimtan] + {"hash":"5f1ef71046a359f7","prefixes":{"":{"product":174}}}, // [Crimtan] + {"hash":"2fb5d0d81abd43be","prefixes":{"":{"product":174}}}, // [Crimtan] + {"hash":"75ce1d1edaf8f426","prefixes":{"":{"product":174}}}, // [Crimtan] + {"hash":"a7b80b09927034f7","prefixes":{"*":{"product":175}}}, // [Criteo] + {"hash":"3a96f385ff402ab6","prefixes":{"*":{"product":175}}}, // [Criteo] + {"hash":"189c3bab631d09cf","prefixes":{"":{"product":176}}}, // [D.A. Consortium Inc. (EffectiveOne)] + {"hash":"3d9109a2e81eee2d","prefixes":{"":{"product":176}}}, // [D.A. Consortium Inc. (EffectiveOne)] + {"hash":"e7170acff5d6fda1","prefixes":{"":{"product":176}}}, // [D.A. Consortium Inc. (EffectiveOne)] + {"hash":"e0496c88da22371f","prefixes":{"":{"product":176}}}, // [D.A. Consortium Inc. (EffectiveOne)] + {"hash":"59ab4e7744543242","prefixes":{"":{"product":177}}}, // [Platform One] + {"hash":"f6b2b21a0a0417b3","prefixes":{"":{"product":177}}}, // [Platform One] + {"hash":"4384a84e6276861a","prefixes":{"*":{"product":178}}}, // [Dapper Inc.] + {"hash":"1ccbc5b91f4c5fc9","prefixes":{"*":{"product":179}}}, // [DataXu Inc.] + {"hash":"4840192a8cb4b1ca","prefixes":{"*":{"product":179}}}, // [DataXu Inc.] + {"hash":"b50faf8c7e5fc38c","prefixes":{"":{"product":180}}}, // [Aperture] + {"hash":"bdcf8ed53f1b3802","prefixes":{"*":{"product":181}}}, // [Rakuten Attribution] + {"hash":"d31e7c4efd1a5191","prefixes":{"*":{"product":181}}}, // [Rakuten Attribution] + {"hash":"68318c6d8f72133b","prefixes":{"*":{"product":181}}}, // [Rakuten Attribution] + {"hash":"c0c4eb0ce58ef4b1","prefixes":{"*":{"product":181}}}, // [Rakuten Attribution] + {"hash":"ae713ee10231204e","prefixes":{"*":{"product":182}}}, // [AdAction] + {"hash":"e4282cef85ee5728","prefixes":{"":{"product":183}}}, // [Demandbase Inc.] + {"hash":"79f6c3d16c278e94","prefixes":{"":{"product":183}}}, // [Demandbase Inc.] + {"hash":"13e7a7b6c40ef05b","prefixes":{"":{"product":183}}}, // [Demandbase Inc.] + {"hash":"57e736fc045f8fbd","prefixes":{"":{"product":183}}}, // [Demandbase Inc.] + {"hash":"82137a4a2aa17f33","prefixes":{"*":{"product":184}}}, // [Omniture] + {"hash":"ddc305710749f5b7","prefixes":{"*":{"product":184}}}, // [Omniture] + {"hash":"3cd74441fc237226","prefixes":{"*":{"product":184}}}, // [Omniture] + {"hash":"5b40000c858b730d","prefixes":{"":{"product":185}}}, // [Rovion, Inc.] + {"hash":"47d66d29363322cd","prefixes":{"":{"product":186}}}, // [AdHui.com LLC] + {"hash":"10a0efb999eedf90","prefixes":{"":{"product":186}}}, // [AdHui.com LLC] + {"hash":"b1bf29ae383ea0cd","prefixes":{"":{"product":187}}}, // [Bidlab sp. z o.o] + {"hash":"baa26df04dbce196","prefixes":{"":{"product":187}}}, // [Bidlab sp. z o.o] + {"hash":"c3f807fb12d64ef3","prefixes":{"":{"product":188}}}, // [CyberAgent Inc.] + {"hash":"8caed248711d066a","prefixes":{"":{"product":188}}}, // [CyberAgent Inc.] + {"hash":"03524472b71baf6f","prefixes":{"":{"product":189}}}, // [Relay42 Technology B.V.] + {"hash":"c26a754afcf8496d","prefixes":{"":{"product":189}}}, // [Relay42 Technology B.V.] + {"hash":"fa7ea4c2400b9304","prefixes":{"":{"product":189}}}, // [Relay42 Technology B.V.] + {"hash":"f91d12ea09409395","prefixes":{"":{"product":189}}}, // [Relay42 Technology B.V.] + {"hash":"a3404139a451726c","prefixes":{"":{"product":190}}}, // [Nielsen Digital Ad Ratings (JS)] + {"hash":"072f7bf8cea1803c","prefixes":{"":{"product":190}}}, // [Nielsen Digital Ad Ratings (JS)] + {"hash":"8a6e324bbf4e02dc","prefixes":{"":{"product":190}}}, // [Nielsen Digital Ad Ratings (JS)] + {"hash":"e59faa8d364c3ad2","prefixes":{"":{"product":190}}}, // [Nielsen Digital Ad Ratings (JS)] + {"hash":"fe39de2cc1550ee8","prefixes":{"":{"product":190}}}, // [Nielsen Digital Ad Ratings (JS)] + {"hash":"41808f1a9bcd4cdf","prefixes":{"":{"product":190}}}, // [Nielsen Digital Ad Ratings (JS)] + {"hash":"1774fa7feee9a3ba","prefixes":{"":{"product":191}}}, // [Nielsen (Audience Measurement Platform)] + {"hash":"8bcfafa30bb9a496","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"04a15100eebd5238","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"37669b02a11574d6","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"b0e609cae76778ef","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"8602961190244c78","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"cf6160cc55083e3f","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"d8e0147c7067e033","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"fa4e638adbad854a","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"bfc104d8ae540578","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"a77b609ecb948524","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"5b2d6c6512b90826","prefixes":{"":{"product":193}}}, // [Nielsen (Sales Effect)] + {"hash":"3f0eefad5865a4ed","prefixes":{"":{"product":194}}}, // [Ohana Media India Private Limited] + {"hash":"ba149fc47bfe9e3c","prefixes":{"":{"product":194}}}, // [Ohana Media India Private Limited] + {"hash":"cfe9fa124d3faa0b","prefixes":{"*":{"product":195}}}, // [DisplayCDN] + {"hash":"3f8d8705aeaf2a69","prefixes":{"":{"product":196}}}, // [Avail Intelligence] + {"hash":"3aa373d150e55bb8","prefixes":{"":{"product":196}}}, // [Avail Intelligence] + {"hash":"01168e37c54407ec","prefixes":{"":{"product":196}}}, // [Avail Intelligence] + {"hash":"9d0088e875d03291","prefixes":{"":{"product":197}}}, // [Adobe Scene 7] + {"hash":"7892719f65634daf","prefixes":{"":{"product":198}}}, // [Asda] + {"hash":"76913c314e7299f2","prefixes":{"":{"product":199}}}, // [Adsfactor Limited] + {"hash":"ec3d301a1049a29c","prefixes":{"":{"product":200}}}, // [Trademob GmbH] + {"hash":"0407237113d1e01a","prefixes":{"":{"product":201}}}, // [Zamplus Technology Co., Ltd.] + {"hash":"3c2b08727db96f57","prefixes":{"":{"product":201}}}, // [Zamplus Technology Co., Ltd.] + {"hash":"28d8cdf30ea5e75d","prefixes":{"":{"product":201}}}, // [Zamplus Technology Co., Ltd.] + {"hash":"4ba6006c59a0afb3","prefixes":{"":{"product":201}}}, // [Zamplus Technology Co., Ltd.] + {"hash":"34b0d82da56a6e14","prefixes":{"":{"product":201}}}, // [Zamplus Technology Co., Ltd.] + {"hash":"c9293b8324f3a30f","prefixes":{"":{"product":201}}}, // [Zamplus Technology Co., Ltd.] + {"hash":"78ad5395bf22ddd5","prefixes":{"":{"product":201}}}, // [Zamplus Technology Co., Ltd.] + {"hash":"b0a5ecf647236d2a","prefixes":{"*":{"product":202}}}, // [Telemetry Limited] + {"hash":"7d7cd86e56968411","prefixes":{"*":{"product":202}}}, // [Telemetry Limited] + {"hash":"ccaf0946d1809711","prefixes":{"*":{"product":202}}}, // [Telemetry Limited] + {"hash":"d08bcd58e153f333","prefixes":{"*":{"product":203}}}, // [AdPilot] + {"hash":"c1ee73ca85937956","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"b3ee9fcc7f773476","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"0dededd99d2513e4","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"efd59a0198028c96","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"b43b6c20f3008842","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"5d96f09a039b26e6","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"039408a17f3d50d7","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"16d696fe52cae068","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"ebb9da6e533fc7e4","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"c8afb150eb1b6ac1","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"1e21c92ae4d08330","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"07ef420d707be72c","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"3d9fb95e3108b648","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"938e971e186be4f8","prefixes":{"":{"product":203}}}, // [AdPilot] + {"hash":"94682de65e11f128","prefixes":{"":{"product":204}}}, // [ebookers] + {"hash":"32548cc45dcf9e95","prefixes":{"":{"product":204}}}, // [ebookers] + {"hash":"baa9bf6209e38821","prefixes":{"":{"product":204}}}, // [ebookers] + {"hash":"25a263451425f7e3","prefixes":{"*":{"product":205}}}, // [Mashero GmbH] + {"hash":"4871ba81fe73b693","prefixes":{"":{"product":206}}}, // [4wMarketPlace Srl] + {"hash":"f95f038fea1148ab","prefixes":{"*":{"product":207}}}, // [Meetic Partners] + {"hash":"8c555d8aa0a96ecc","prefixes":{"*":{"product":208}}}, // [Adara Media] + {"hash":"a31f9bce31634750","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"bb0d6b4eac177896","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"02712dc83414bc52","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"03cfa92cdadd6c20","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"4d8640e955380082","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"42ab78ba8d28d0d9","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"113f35a595990303","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"9566d797c39bca8e","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"caab6878aaf1b900","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"13581af42d29c3fd","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"8ad4e9b59b6cfebf","prefixes":{"":{"product":209}}}, // [Adledge] + {"hash":"025223bda779fb50","prefixes":{"":{"product":210}}}, // [Adledge: Ad Swapping] + {"hash":"6c7970d8fe11d946","prefixes":{"":{"product":211}}}, // [LifeStreet Corportation] + {"hash":"226c377d19c24dca","prefixes":{"":{"product":211}}}, // [LifeStreet Corportation] + {"hash":"bc64475cc35aba69","prefixes":{"":{"product":211}}}, // [LifeStreet Corportation] + {"hash":"8a184297457cd4e1","prefixes":{"":{"product":211}}}, // [LifeStreet Corportation] + {"hash":"900f97a5320abcc7","prefixes":{"":{"product":212}}}, // [AdCirrus] + {"hash":"39b102bb2f01d1b4","prefixes":{"":{"product":213}}}, // [Dimestore] + {"hash":"a144c6442bbc8a0a","prefixes":{"":{"product":213}}}, // [Dimestore] + {"hash":"8020cac4e8fee023","prefixes":{"":{"product":213}}}, // [Dimestore] + {"hash":"2dcefe5fe106e478","prefixes":{"*":{"product":214}}}, // [GfK SE] + {"hash":"d8f9a95c0ea00853","prefixes":{"*":{"product":213}}}, // [Dimestore] + {"hash":"829fbeefc45bca87","prefixes":{"*":{"product":215}}}, // [Conversant CRM] + {"hash":"357a04f93d25ae56","prefixes":{"cdn":{"product":216},"log":{"product":216},"tps":{"product":216},"rtb":{"product":217}}}, // [DoubleVerify Inc.] [DoubleVerify Inc.] [DoubleVerify Inc.] [DoubleVerify Inc. (BrandShield): Ad Swapping] + {"hash":"e88f040d82035294","prefixes":{"":{"product":216}}}, // [DoubleVerify Inc.] + {"hash":"a378f9f7b3d5241c","prefixes":{"*":{"product":218}}}, // [Dynamic Video LLC] + {"hash":"665ab90fa95fb304","prefixes":{"":{"product":218}}}, // [Dynamic Video LLC] + {"hash":"1358623e271c4d67","prefixes":{"":{"product":218}}}, // [Dynamic Video LLC] + {"hash":"a11e7a579fa98970","prefixes":{"*":{"product":219}}}, // [Think RealTime, LLC] + {"hash":"9225aaedf058b2a2","prefixes":{"":{"product":220}}}, // [Effective Measure] + {"hash":"36703887ac49b84b","prefixes":{"":{"product":220}}}, // [Effective Measure] + {"hash":"eabfdf13f38b7671","prefixes":{"*":{"product":221}}}, // [Adobe Media Optimizer] + {"hash":"e6c60b5e8bab7589","prefixes":{"*":{"product":221}}}, // [Adobe Media Optimizer] + {"hash":"d4ca4e6c8a0bedf0","prefixes":{"*":{"product":221}}}, // [Adobe Media Optimizer] + {"hash":"2e2899c7688fb6b3","prefixes":{"":{"product":222}}}, // [Effiliation] + {"hash":"3a8f463e807ab596","prefixes":{"":{"product":222}}}, // [Effiliation] + {"hash":"4c49524999954857","prefixes":{"":{"product":222}}}, // [Effiliation] + {"hash":"99a4dc2c14dac648","prefixes":{"*":{"product":223}}}, // [EmediateAd] + {"hash":"eb4d633002c35102","prefixes":{"*":{"product":223}}}, // [EmediateAd] + {"hash":"a4155988849d9899","prefixes":{"":{"product":223}}}, // [EmediateAd] + {"hash":"a29dc15ab67e4109","prefixes":{"*":{"product":223}}}, // [EmediateAd] + {"hash":"244ed2f383a41c11","prefixes":{"*":{"product":223}}}, // [EmediateAd] + {"hash":"f1d41523d742a4c4","prefixes":{"*":{"product":224}}}, // [engage:BDR Inc.] + {"hash":"0c985c5bee46dcca","prefixes":{"":{"product":224}}}, // [engage:BDR Inc.] + {"hash":"6f92a4165360659f","prefixes":{"":{"product":224}}}, // [engage:BDR Inc.] + {"hash":"e65b14b1de797d5e","prefixes":{"":{"product":224}}}, // [engage:BDR Inc.] + {"hash":"8908058eec675b88","prefixes":{"*":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"dae357b5105fb541","prefixes":{"*":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"7e5c5286bc6286af","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"6459463a599e26cf","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"d49c14999963fa7c","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"f7508ecdfd1b76f8","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"747ac3c2114de916","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"5445e9650966f2c9","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"b701368b6964f28f","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"b8d4853208b3d665","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"a3c05ce1535a1bb0","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"4113e02fa7f80330","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"1ec6c8299f47c92d","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"6213db1c94ff78b4","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"afdce1f639f05293","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"c00800107f132ba6","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"f989cf5678a6cd73","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"7248d6bb2e2d2812","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"5decec320495d46c","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"ffffd176ae6095cd","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"a0ead46cc7797fc4","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"d1b36183709cf97c","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"f08e0f4d46762277","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"1082450fce471aec","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"0c44a72359ff962b","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"c6ced7239fa526b8","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"bcd4afcb16a1bf83","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"c4629483e0f61849","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"3a8a51dc5059eb82","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"10d4846e0b0f6213","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"8e03e96054cde307","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"d1c591453eeceb89","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"a9ee5e0b07924cbe","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"4909d0f68d7f7dd5","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"1954dcac7d36e9a9","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"855fab1b44b2cc38","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"9a977de69b69e767","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"32c7999b9a328d48","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"675aab76d51e4be4","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"454f35d478088b2f","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"af6f37ce228476e6","prefixes":{"*":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"dfa2565386557dbb","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"7bdee1b08b69b7fa","prefixes":{"*":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"259e3811976143d2","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"29633cedc9fc7e43","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"758fea4a1e3ad80f","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"cf9bd7435c526efc","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"913928e477b69d4d","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"391c770feaa49fc6","prefixes":{"":{"product":4}}}, // [Eulerian Technologies SARL] + {"hash":"9aea11673c37df0c","prefixes":{"*":{"product":225}}}, // [Ghostery Enterprise] + {"hash":"5efff70d6937c916","prefixes":{"*":{"product":226}}}, // [Experteer GmbH] + {"hash":"070f9075bd0072ca","prefixes":{"":{"product":227}}}, // [Action Allocator] + {"hash":"2a8c1779456949e3","prefixes":{"":{"product":228}}}, // [AdTraxx] + {"hash":"0d3994abeed42b53","prefixes":{"*":{"product":229}}}, // [eyeDemand] + {"hash":"f804a9a8eb6b2828","prefixes":{"*":{"product":230}}}, // [EyeReturn Marketing] + {"hash":"e7b16a7fe9d18804","prefixes":{"*":{"product":230}}}, // [EyeReturn Marketing] + {"hash":"9a826150ea3a7b3d","prefixes":{"*":{"product":231}}}, // [EyeView Inc.] + {"hash":"00f40aaf7abec691","prefixes":{"":{"product":232}}}, // [EyeView, Inc] + {"hash":"a47b1640e108c4e8","prefixes":{"":{"product":232}}}, // [EyeView, Inc] + {"hash":"73c2e89f340530ca","prefixes":{"*":{"product":233}}}, // [Eyewonder Inc.] + {"hash":"ab1869399e555ac8","prefixes":{"":{"product":234}}}, // [MLB Advanced Media, L.P.] + {"hash":"6792c3d3132bf7d7","prefixes":{"":{"product":234}}}, // [MLB Advanced Media, L.P.] + {"hash":"3d02d3a5042f5ad8","prefixes":{"":{"product":234}}}, // [MLB Advanced Media, L.P.] + {"hash":"c75b8641d2e585d7","prefixes":{"*":{"product":235}}}, // [Facilitate For Agencies (FFA)] + {"hash":"535ecf05ae26ef66","prefixes":{"*":{"product":235}}}, // [Facilitate For Agencies (FFA)] + {"hash":"2393220aafcd7f07","prefixes":{"*":{"product":235}}}, // [Facilitate For Agencies (FFA)] + {"hash":"ead2626488917d96","prefixes":{"*":{"product":235}}}, // [Facilitate For Agencies (FFA)] + {"hash":"82402af773d46078","prefixes":{"*":{"product":235}}}, // [Facilitate For Agencies (FFA)] + {"hash":"0afacf5097d4ca72","prefixes":{"*":{"product":236}}}, // [Flashtalking] + {"hash":"5215ee9300af8125","prefixes":{"":{"product":237}}}, // [Hostelworld.com Limited] + {"hash":"e8d6e54e3ce31711","prefixes":{"":{"product":238}}}, // [Knorex Pte. Ltd.] + {"hash":"a8ac5a522ae936d9","prefixes":{"":{"product":238}}}, // [Knorex Pte. Ltd.] + {"hash":"c88080a9090fa828","prefixes":{"":{"product":238}}}, // [Knorex Pte. Ltd.] + {"hash":"90727ba23e427206","prefixes":{"":{"product":238}}}, // [Knorex Pte. Ltd.] + {"hash":"d496e8a41b845c1e","prefixes":{"":{"product":238}}}, // [Knorex Pte. Ltd.] + {"hash":"e3463a0b0d9d0b84","prefixes":{"*":{"product":239}}}, // [Flite Inc.] + {"hash":"93eff417d773ad79","prefixes":{"*":{"product":239}}}, // [Flite Inc.] + {"hash":"48c727cb39130203","prefixes":{"*":{"product":239}}}, // [Flite Inc.] + {"hash":"1f8b889072fc177c","prefixes":{"*":{"product":240}}}, // [Forbes Media LLC] + {"hash":"830e0f5dd1181234","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"daf4edc7545d5166","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"f786c1dcc4eb883e","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"82534a9af0bc48e0","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"091c6487b80c3425","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"5250da9f53cd928c","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"9a8514c0fd5d6754","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"ad91bc98d2a8be55","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"91e5780ca48d75cc","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"481acfc271cd8a12","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"ef2a23dc677d8426","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"c6c8ce28b06bbda5","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"2bb2e1ea24146c0b","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"a0f3f5041ae3893c","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"4d6c5ce49301c775","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"747c952599211285","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"59f1e6a6110f9a68","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"f76895f67d1585b2","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"a2e125e235d1ffd4","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"17cbbfe7617725b3","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"1c118cc87c632e94","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"a48217ad66a56eb4","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"949fd73ca6fcc6f5","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"7d647d3164d8e6e2","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"e6d9d61b6785d883","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"aad9a81ea12d3c42","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"f0d9b5588abc50f9","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"dcb3db79fcab6476","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"1985d21c9c0b8366","prefixes":{"":{"product":242}}}, // [FreakOut Inc.] + {"hash":"a268d22dc85b4108","prefixes":{"*":{"product":243}}}, // [FreeWheel] + {"hash":"a796f300b3d2c84e","prefixes":{"*":{"product":244}}}, // [Fringe81 Inc.] + {"hash":"bb563c63d09f3d76","prefixes":{"":{"product":245}}}, // [Fringe81 - IBV] + {"hash":"c6ac10cc9ddfb3e7","prefixes":{"":{"product":245}}}, // [Fringe81 - IBV] + {"hash":"48983a0d5a27b120","prefixes":{"*":{"product":246}}}, // [FuseBox Inc.] + {"hash":"6f59caebdaac019e","prefixes":{"":{"product":246}}}, // [FuseBox Inc.] + {"hash":"8090c608651eca30","prefixes":{"*":{"product":247}}}, // [gemiusDirectEffect+] + {"hash":"e7b75209bd73a1d9","prefixes":{"*":{"product":248}}}, // [AdOcean Ltd] + {"hash":"24fb4ed27ae8cf88","prefixes":{"*":{"product":249}}}, // [Intomart GfK (GfK Daphne)] + {"hash":"91571a34ff0fcf44","prefixes":{"*":{"product":250}}}, // [Gigya] + {"hash":"bd66201f4f935a9d","prefixes":{"*":{"product":251}}}, // [Global Market Insite Inc.] + {"hash":"c0854a371610fbf2","prefixes":{"*":{"product":251}}}, // [Global Market Insite Inc.] + {"hash":"4a32fa997117f00d","prefixes":{"*":{"product":252},"":{"product":253}}}, // [DoubleClick Campaign Manager] [DoubleClick Bidder Pilot for Networks] + {"hash":"68d0356c33bd8ec4","prefixes":{"*":{"product":252},"":{"product":253}}}, // [DoubleClick Campaign Manager] [DoubleClick Bidder Pilot for Networks] + {"hash":"813ca9ac3483af55","prefixes":{"*":{"product":252},"":{"product":253}}}, // [DoubleClick Campaign Manager] [DoubleClick Bidder Pilot for Networks] + {"hash":"ee88383142da014d","prefixes":{"*":{"product":252},"":{"product":253}}}, // [DoubleClick Campaign Manager] [DoubleClick Bidder Pilot for Networks] + {"hash":"1332f3da43091ed3","prefixes":{"":{"product":252},"*":{"product":254}}}, // [DoubleClick Campaign Manager] [DoubleClick for Publishers Premium] + {"hash":"f04082d14282d452","prefixes":{"":{"product":252}}}, // [DoubleClick Campaign Manager] + {"hash":"c6ad6c580aef6ce5","prefixes":{"":{"product":252}}}, // [DoubleClick Campaign Manager] + {"hash":"642706b0b0335500","prefixes":{"":{"product":252}}}, // [DoubleClick Campaign Manager] + {"hash":"baea954b95731c68","prefixes":{"*":{"product":255}}}, // [Google CDN] + {"hash":"8e23699963552b18","prefixes":{"*":{"product":252}}}, // [DoubleClick Campaign Manager] + {"hash":"bafedfe69ed92305","prefixes":{"*":{"product":253}}}, // [DoubleClick Bidder Pilot for Networks] + {"hash":"f2b999c597cd97af","prefixes":{"*":{"product":253}}}, // [DoubleClick Bidder Pilot for Networks] + {"hash":"66399889a04f9513","prefixes":{"cdn":{"product":256},"ads":{"product":256},"db":{"product":256},"img":{"product":256},"ssl":{"product":256}}}, // [GroovinAds] [GroovinAds] [GroovinAds] [GroovinAds] [GroovinAds] + {"hash":"ea8510cdf6991d43","prefixes":{"":{"product":256}}}, // [GroovinAds] + {"hash":"fc2e03aac9426552","prefixes":{"":{"product":257}}}, // [Reddion] + {"hash":"6db39bcc7e0f7fed","prefixes":{"":{"product":258}}}, // [HQ GmbH] + {"hash":"4de0590ca954b13b","prefixes":{"*":{"product":259}}}, // [Performance Display Advertising] + {"hash":"44f4a1b16ff856e5","prefixes":{"":{"product":5}}}, // [Conversant Ad Server] + {"hash":"127f97cfaedd763a","prefixes":{"":{"product":5}}}, // [Conversant Ad Server] + {"hash":"508035bfa2d95844","prefixes":{"*":{"product":5}}}, // [Conversant Ad Server] + {"hash":"6ea225859ddfba18","prefixes":{"*":{"product":5}}}, // [Conversant Ad Server] + {"hash":"600a5a5f53775d42","prefixes":{"*":{"product":216}}}, // [DoubleVerify Inc.] + {"hash":"cabba15c5ee8f039","prefixes":{"*":{"product":260}}}, // [Avazu Inc.] + {"hash":"ec98881ed60ffe62","prefixes":{"":{"product":91}}}, // [ADTECH GmbH] + {"hash":"b1e5dca23b928251","prefixes":{"":{"product":91}}}, // [ADTECH GmbH] + {"hash":"87e919d72e293303","prefixes":{"*":{"product":261}}}, // [Aerify Media] + {"hash":"652d7fe0079512a8","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"8e1afef4fbf079f4","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"3f697fb2c06659ac","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"cb5ae4cc94e8cb57","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"3cd44a0f52d69fed","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"fd87dfe8678e2630","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"274e4476c8fbfe0c","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"e3f56a006c4f8fc8","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"11d582be18893073","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"54768ce9ed6664fc","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"6de580016869f523","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"7fc8a77e5f366b6d","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"06838afa0ce9cde5","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"d788f92161d75b72","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"bd27e9c073c7b133","prefixes":{"":{"product":262}}}, // [IClick Interactive Ltd.] + {"hash":"71096991891dc36f","prefixes":{"*":{"product":263}}}, // [iCrossing] + {"hash":"0a48c6448ca1af83","prefixes":{"":{"product":263}}}, // [iCrossing] + {"hash":"23cce2ff5ce35111","prefixes":{"*":{"product":264}}}, // [Impact Engine Inc.] + {"hash":"b63329d63303bcac","prefixes":{"*":{"product":265}}}, // [Inadco Inc.] + {"hash":"9dab6f7b066953b6","prefixes":{"":{"product":265}}}, // [Inadco Inc.] + {"hash":"83efdfb00e5e0bb5","prefixes":{"":{"product":266}}}, // [Innity Singapore Pte Ltd.] + {"hash":"5cdf5c58d4a95757","prefixes":{"":{"product":266}}}, // [Innity Singapore Pte Ltd.] + {"hash":"e84e4c75279b444c","prefixes":{"":{"product":266}}}, // [Innity Singapore Pte Ltd.] + {"hash":"0ad278b0f1bf83c7","prefixes":{"":{"product":267}}}, // [Insight Express (Cross Media - Ignite)] + {"hash":"75089fef153b99d9","prefixes":{"":{"product":267}}}, // [Insight Express (Cross Media - Ignite)] + {"hash":"17e48170b29e8c55","prefixes":{"":{"product":268}}}, // [intelliAd Media GmbH] + {"hash":"8ff3a159f3a3dd37","prefixes":{"":{"product":268}}}, // [intelliAd Media GmbH] + {"hash":"d5ad6ce37d416c20","prefixes":{"":{"product":268}}}, // [intelliAd Media GmbH] + {"hash":"829116e73bbd766e","prefixes":{"*":{"product":269}}}, // [DePauli AG] + {"hash":"58b6a73b811a6744","prefixes":{"":{"product":268}}}, // [intelliAd Media GmbH] + {"hash":"1b02753d1ebc5935","prefixes":{"":{"product":270}}}, // [Intelliad] + {"hash":"2d4767b28a0a3dc9","prefixes":{"":{"product":270}}}, // [Intelliad] + {"hash":"04e4138ad91eba43","prefixes":{"*":{"product":271}}}, // [Genome] + {"hash":"a80259001a08f5fe","prefixes":{"":{"product":271}}}, // [Genome] + {"hash":"bccc7d85fcbd6406","prefixes":{"*":{"product":272}}}, // [Intergi LLC dba PlaywireMedia] + {"hash":"c0e12f3f6483d539","prefixes":{"":{"product":273}}}, // [Intermundo Media LLC] + {"hash":"aa0d2346396993dc","prefixes":{"*":{"product":274}}}, // [Interpolls] + {"hash":"391f02c89579c41e","prefixes":{"*":{"product":275}}}, // [Intelligent Reach (Intuitive Search Technologies)] + {"hash":"dd7ece8c8b4dd4db","prefixes":{"*":{"product":275}}}, // [Intelligent Reach (Intuitive Search Technologies)] + {"hash":"51beebfd5a677c74","prefixes":{"":{"product":275}}}, // [Intelligent Reach (Intuitive Search Technologies)] + {"hash":"15a557d1be0b55bc","prefixes":{"*":{"product":276}}}, // [IPONWEB Limited] + {"hash":"ab800ebb45ab5e96","prefixes":{"":{"product":276}}}, // [IPONWEB Limited] + {"hash":"cf6003cf8be11c49","prefixes":{"*":{"product":276}}}, // [IPONWEB Limited] + {"hash":"bd97f2dac673ccef","prefixes":{"*":{"product":277}}}, // [JasperLabs Inc.] + {"hash":"833a1bb3e47002bf","prefixes":{"":{"product":278}}}, // [Jivox Corporation] + {"hash":"925afb63a81d22ae","prefixes":{"":{"product":278}}}, // [Jivox Corporation] + {"hash":"e3546ee6177ce1af","prefixes":{"":{"product":278}}}, // [Jivox Corporation] + {"hash":"a6e81a993b0dc090","prefixes":{"":{"product":278}}}, // [Jivox Corporation] + {"hash":"57cf72a6bcc6ed09","prefixes":{"":{"product":278}}}, // [Jivox Corporation] + {"hash":"aef508a87f7b9342","prefixes":{"":{"product":278}}}, // [Jivox Corporation] + {"hash":"a1a0984cf6e12596","prefixes":{"":{"product":278}}}, // [Jivox Corporation] + {"hash":"c1b82a9e7564881f","prefixes":{"*":{"product":279}}}, // [Joinville AB] + {"hash":"e20dd6e83cbd14d9","prefixes":{"":{"product":280}}}, // [KliKKicom Oy] + {"hash":"a1f03f455fc97850","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"fb87830648f1fe7c","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"93a6bcd7660ef88b","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"14fba951393c4cc1","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"96bf72901db53556","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"00ceea10fed6f827","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"5deb6288aeb4b20d","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"30a9e5cf761c2db9","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"b49fda322943ed64","prefixes":{"":{"product":281}}}, // [Komli Media Inc.] + {"hash":"30067241c16c9f88","prefixes":{"*":{"product":282}}}, // [Koninklijke Luchtvaart Maatschappij N.V.] + {"hash":"06076b672be3af43","prefixes":{"*":{"product":283}}}, // [J.D. Power O2O] + {"hash":"1e23ff61443f919a","prefixes":{"*":{"product":284}}}, // [Kwanzoo Inc.] + {"hash":"32327b1f6a21f5e3","prefixes":{"":{"product":285}}}, // [Legolas Media Inc.] + {"hash":"0a98bd3ecf6d4f39","prefixes":{"":{"product":285}}}, // [Legolas Media Inc.] + {"hash":"4141a8162aadd52a","prefixes":{"":{"product":285}}}, // [Legolas Media Inc.] + {"hash":"9562185c6a06e194","prefixes":{"*":{"product":286}}}, // [Undertone Ad System (UAS)] + {"hash":"d8be1c121ccbdb84","prefixes":{"":{"product":287}}}, // [LEVEL Studios] + {"hash":"1b274516ac601c1d","prefixes":{"*":{"product":288}}}, // [LinkedIn Corporation] + {"hash":"a9aceb9b28670518","prefixes":{"*":{"product":288}}}, // [LinkedIn Corporation] + {"hash":"f202e32e7503e766","prefixes":{"":{"product":288}}}, // [LinkedIn Corporation] + {"hash":"cf2094771b42b367","prefixes":{"*":{"product":288}}}, // [LinkedIn Corporation] + {"hash":"ad94f98bfc0092ce","prefixes":{"*":{"product":289}}}, // [Content Directions, Inc. dba Linkstorm] + {"hash":"2c97a2e1f7f59cde","prefixes":{"*":{"product":290}}}, // [Liverail Inc.] + {"hash":"562dd0a17776cfc3","prefixes":{"*":{"product":291}}}, // [Lotame Solutions Inc.] + {"hash":"85c409cdb78a6fec","prefixes":{"*":{"product":292}}}, // [LucidMedia Networks Inc.] + {"hash":"25cdc372f14aa636","prefixes":{"":{"product":293}}}, // [Magnetic Media Online Inc.] + {"hash":"490a692feb4f5f83","prefixes":{"":{"product":293}}}, // [Magnetic Media Online Inc.] + {"hash":"8cd182ca413a6770","prefixes":{"":{"product":293}}}, // [Magnetic Media Online Inc.] + {"hash":"891ff79862603ca1","prefixes":{"*":{"product":294}}}, // [Mate1.com] + {"hash":"ae4598324ee10cb2","prefixes":{"":{"product":295}}}, // [MaxPoint Interactive Inc.] + {"hash":"66eafecb064a6d3f","prefixes":{"*":{"product":296}}}, // [Media Armor Inc.] + {"hash":"d9921090d75b28f3","prefixes":{"*":{"product":297}}}, // [Xaxis, Inc] + {"hash":"4c897372b3205c67","prefixes":{"*":{"product":298}}}, // [Dstillery] + {"hash":"78172051b147ff71","prefixes":{"*":{"product":298}}}, // [Dstillery] + {"hash":"3e32e501635bb994","prefixes":{"*":{"product":299}}}, // [Rakuten MediaForge] + {"hash":"ab3637f7ff19f3be","prefixes":{"*":{"product":86}}}, // [MediaMath Inc.] + {"hash":"eacb348c2bfde7c6","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"6ec884dc33afd8e3","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"6a0b44c268e0ea34","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"5ec2ba6f16455240","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"f6a166105958340d","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"1bafa194e1b43948","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"c4c10492cf3f1e5c","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"fb29996f4dbe86a3","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"33cba419c0d99c36","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"1471e355753e333a","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"8445471e74aaf1ad","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"fe448fc8fa6792c7","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"7142d0f7c95e6c11","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"f71e7939a3a06b85","prefixes":{"":{"product":300}}}, // [Sizmek] + {"hash":"25c9493f4ceef0b3","prefixes":{"*":{"product":300}}}, // [Sizmek] + {"hash":"1c0486f5c9ddccc1","prefixes":{"":{"product":301}}}, // [MediaMind] + {"hash":"511f53552d7c07f9","prefixes":{"*":{"product":302}}}, // [Paypal] + {"hash":"c23b8169e8d6f935","prefixes":{"*":{"product":302}}}, // [Paypal] + {"hash":"4cb72fa17c1e6b21","prefixes":{"*":{"product":303}}}, // [ZEDO Inc.] + {"hash":"1f5866beb3a2da29","prefixes":{"":{"product":304}}}, // [Contobox] + {"hash":"ab4011df7412e85a","prefixes":{"":{"product":304}}}, // [Contobox] + {"hash":"4c55c99f7e731e4a","prefixes":{"":{"product":304}}}, // [Contobox] + {"hash":"b32dad95de4864bb","prefixes":{"":{"product":304}}}, // [Contobox] + {"hash":"e0636cdabf7592eb","prefixes":{"":{"product":305}}}, // [Melt Tecnologia e Informatica S.A] + {"hash":"9e011383339e65c3","prefixes":{"":{"product":305}}}, // [Melt Tecnologia e Informatica S.A] + {"hash":"c7f6b97c8a7defa6","prefixes":{"":{"product":305}}}, // [Melt Tecnologia e Informatica S.A] + {"hash":"ba29743e534631a5","prefixes":{"":{"product":305}}}, // [Melt Tecnologia e Informatica S.A] + {"hash":"df986bbd43ef130c","prefixes":{"":{"product":305}}}, // [Melt Tecnologia e Informatica S.A] + {"hash":"32e39349c0434e31","prefixes":{"":{"product":305}}}, // [Melt Tecnologia e Informatica S.A] + {"hash":"4b920fabf61fd6f3","prefixes":{"":{"product":305}}}, // [Melt Tecnologia e Informatica S.A] + {"hash":"068f3a0cd7e03112","prefixes":{"":{"product":305}}}, // [Melt Tecnologia e Informatica S.A] + {"hash":"95cce4f80c49239f","prefixes":{"*":{"product":306}}}, // [MeMo2 / Hottraffic] + {"hash":"7672c0e258e0a20b","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"065ac7f58737b759","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"f0c15855e3418345","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"45068fe6a5fe83c0","prefixes":{"*":{"product":308}}}, // [Mercado Livre.com Atividades de Internet Ltda] + {"hash":"a3fbfa83663a40a6","prefixes":{"*":{"product":308}}}, // [Mercado Livre.com Atividades de Internet Ltda] + {"hash":"5241d2ae86ff307f","prefixes":{"":{"product":309}}}, // [Merchenta Limited] + {"hash":"7b92690c1232eb23","prefixes":{"":{"product":309}}}, // [Merchenta Limited] + {"hash":"e5b1a15c3a7a1ee6","prefixes":{"":{"product":309}}}, // [Merchenta Limited] + {"hash":"f0b40488d9ecd9a2","prefixes":{"":{"product":309}}}, // [Merchenta Limited] + {"hash":"63600c9242f10b0d","prefixes":{"":{"product":309}}}, // [Merchenta Limited] + {"hash":"e89e81c1ed314cc1","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"297f0571158ef576","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"2228e5ce36c007c1","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"798bcbf9415873df","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"ab03f3b496037843","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"eca01f0508fbf30b","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"0d839898fade319a","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"4fc010b1e52ce252","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"31e7fb81bf0ec52f","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"a6edfa074ceff9c5","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"289dc75a912dcc97","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"5a8de8926c410758","prefixes":{"":{"product":310}}}, // [Metapeople GmbH] + {"hash":"79c798a9d8e32afc","prefixes":{"":{"product":311}}}, // [MetrixLab B.V.] + {"hash":"61617058e6206283","prefixes":{"":{"product":311}}}, // [MetrixLab B.V.] + {"hash":"cf945767a2fa79f3","prefixes":{"*":{"product":312}}}, // [Mixpo Inc.] + {"hash":"541f85cbc710f2b3","prefixes":{"":{"product":313}}}, // [Monsoon Ads Pvt. Ltd.] + {"hash":"c800e798e160fe89","prefixes":{"*":{"product":314}}}, // [Monster] + {"hash":"775035c4c49057eb","prefixes":{"":{"product":314}}}, // [Monster] + {"hash":"2a26221290e24a08","prefixes":{"":{"product":314}}}, // [Monster] + {"hash":"fda966b5c6ec2b2f","prefixes":{"":{"product":314}}}, // [Monster] + {"hash":"887d148a8ae98b2e","prefixes":{"*":{"product":315}}}, // [neckermann.de GmbH] + {"hash":"35c76c9165e3349f","prefixes":{"*":{"product":316}}}, // [Ad.agio] + {"hash":"d5c7d1240bf8989e","prefixes":{"":{"product":316}}}, // [Ad.agio] + {"hash":"1e5ea56690a0716d","prefixes":{"*":{"product":316}}}, // [Ad.agio] + {"hash":"a54661777ef51189","prefixes":{"*":{"product":317}}}, // [Netmining LLC] + {"hash":"cc673c90c8abe830","prefixes":{"*":{"product":318}}}, // [AdoTube] + {"hash":"5f4b0cbb95552b3f","prefixes":{"*":{"product":319}}}, // [Next Audience GmbH] + {"hash":"a78e1096b3c03fbc","prefixes":{"":{"product":319}}}, // [Next Audience GmbH] + {"hash":"582d973b69391ebe","prefixes":{"":{"product":319}}}, // [Next Audience GmbH] + {"hash":"18585076a5b0c6dd","prefixes":{"":{"product":319}}}, // [Next Audience GmbH] + {"hash":"1df4ee86fc680406","prefixes":{"":{"product":319}}}, // [Next Audience GmbH] + {"hash":"ecd80a6a46f6a1ce","prefixes":{"":{"product":319}}}, // [Next Audience GmbH] + {"hash":"8ee876aaaaf537e0","prefixes":{"":{"product":319}}}, // [Next Audience GmbH] + {"hash":"92bc4f011133f05a","prefixes":{"*":{"product":320}}}, // [Nextperf] + {"hash":"b10d35d6cde76082","prefixes":{"*":{"product":320}}}, // [Nextperf] + {"hash":"8e6e5d6dc4720f98","prefixes":{"":{"product":320}}}, // [Nextperf] + {"hash":"edc7f4e87a8ea4a3","prefixes":{"":{"product":320}}}, // [Nextperf] + {"hash":"f0c76e07985c56c5","prefixes":{"":{"product":320}}}, // [Nextperf] + {"hash":"cff1b3137482e5e3","prefixes":{"":{"product":320}}}, // [Nextperf] + {"hash":"33f28c5ebbd4525e","prefixes":{"":{"product":320}}}, // [Nextperf] + {"hash":"849c8324d4c32ea5","prefixes":{"*":{"product":321}}}, // [Calibex] + {"hash":"0bbcc61ed60a63c7","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"7da7b69cf130f867","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"524bf7ee34882325","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"0fc0858e8d42a096","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"94d1d06666f458b2","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"9515f9226eae0ee6","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"c610850a50287c6c","prefixes":{"":{"product":322}}}, // [ViziAds for Advertisers] + {"hash":"1324d12fd047205a","prefixes":{"":{"product":322}}}, // [ViziAds for Advertisers] + {"hash":"922df75b11e4bdb8","prefixes":{"":{"product":322}}}, // [ViziAds for Advertisers] + {"hash":"94be9a0ad636acc1","prefixes":{"":{"product":322}}}, // [ViziAds for Advertisers] + {"hash":"921f858655989bdb","prefixes":{"":{"product":322}}}, // [ViziAds for Advertisers] + {"hash":"0f44f6f2024007e3","prefixes":{"":{"product":323}}}, // [FinanceGenerator] + {"hash":"c5d545736ea3f40c","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"c5190fb3c11efd5c","prefixes":{"":{"product":324}}}, // [Telstra Corporation] + {"hash":"f8e19bba6ecb9d4d","prefixes":{"":{"product":324}}}, // [Telstra Corporation] + {"hash":"78cb9e34e18a70c6","prefixes":{"":{"product":324}}}, // [Telstra Corporation] + {"hash":"555b77a6dbc1077b","prefixes":{"":{"product":324}}}, // [Telstra Corporation] + {"hash":"04cd318d8c9e0aa1","prefixes":{"":{"product":325}}}, // [The Online Research Unit] + {"hash":"44b28a9d35489468","prefixes":{"":{"product":326}}}, // [Webling Pty Ltd] + {"hash":"ce2a97a2bc3975b0","prefixes":{"*":{"product":327}}}, // [Lasoo Pty Ltd] + {"hash":"70c03e717046730c","prefixes":{"":{"product":327}}}, // [Lasoo Pty Ltd] + {"hash":"a1956c55f379362a","prefixes":{"":{"product":327}}}, // [Lasoo Pty Ltd] + {"hash":"78761e0920b86d00","prefixes":{"":{"product":327}}}, // [Lasoo Pty Ltd] + {"hash":"fb49a17ebf575b4b","prefixes":{"":{"product":327}}}, // [Lasoo Pty Ltd] + {"hash":"3ba767e322876167","prefixes":{"":{"product":327}}}, // [Lasoo Pty Ltd] + {"hash":"db60b4edeb07e6e0","prefixes":{"*":{"product":328}}}, // [Salefinder Ltd.] + {"hash":"1a2b2619e15f81ed","prefixes":{"":{"product":329}}}, // [The Monkeys Pty Ltd] + {"hash":"d33c5423234580d2","prefixes":{"":{"product":330}}}, // [Louder Digital Pty Ltd] + {"hash":"35c305dfff8e956a","prefixes":{"":{"product":331}}}, // [Publisher’s Internationale Pty Ltd] + {"hash":"ab95fd39f34e1919","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"e394eac0c09e952a","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"d754759cedf098a4","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"469abb509832dcba","prefixes":{"":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"089ade7154cdafd2","prefixes":{"":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"7c4e77c7146bc27c","prefixes":{"":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"27943a61af56578c","prefixes":{"":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"5b9d47d01e06e207","prefixes":{"":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"efae037999b8354a","prefixes":{"":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"64b1c22f19588e65","prefixes":{"*":{"product":7}}}, // [Wize Commerce, Inc.] + {"hash":"1b500703c376dccf","prefixes":{"":{"product":332}}}, // [NowSpots] + {"hash":"0b8a5111da2a18e6","prefixes":{"":{"product":332}}}, // [NowSpots] + {"hash":"6cbd63b4b5a258e2","prefixes":{"":{"product":333}}}, // [Ocapi] + {"hash":"2ecb3e4de562d83f","prefixes":{"*":{"product":334}}}, // [Predicta] + {"hash":"b0032d1f28de6b8e","prefixes":{"*":{"product":334}}}, // [Predicta] + {"hash":"495b10f97369710c","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"07b876ce04abdfc9","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"bd1d888d58054387","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"deb9a6a0e78770f8","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"3d9897472cebdff7","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"a036e7b2680b1720","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"ae8e27c2a1195c9d","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"685a4ece024544f3","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"d705e49e2ecfc500","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"4fd0a4a032a34191","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"2640f60d445d4d0d","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"0067af133fbf162f","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"62e23e8e0a53b097","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"57643f79e10fdc91","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"fe70aef371b87e0d","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"c80b88907c4da662","prefixes":{"":{"product":335}}}, // [Comune SA] + {"hash":"3c027bef12167411","prefixes":{"":{"product":336}}}, // [EuroAds Group A/S] + {"hash":"9e7ef06636d0b122","prefixes":{"":{"product":337}}}, // [Hotwords Informação LTDA] + {"hash":"ac577d2ba001c2f9","prefixes":{"":{"product":337}}}, // [Hotwords Informação LTDA] + {"hash":"8baa23f77cec98d3","prefixes":{"":{"product":337}}}, // [Hotwords Informação LTDA] + {"hash":"86e7352358e7a0a1","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"461a9aacd5db8b62","prefixes":{"":{"product":42}}}, // [Clinch.co] + {"hash":"4523c8dd39cb10d8","prefixes":{"":{"product":338}}}, // [uMotion] + {"hash":"a7eaf4ae4152200d","prefixes":{"":{"product":338}}}, // [uMotion] + {"hash":"1874ed5aa610db51","prefixes":{"*":{"product":339}}}, // [Google Zoo] + {"hash":"0dc9eb6c8b1c77fa","prefixes":{"*":{"product":35}}}, // [Oggifinogi] + {"hash":"60d67cd6819e7de4","prefixes":{"*":{"product":340}}}, // [Audience Manager] + {"hash":"2ae524eac16b06f8","prefixes":{"":{"product":341}}}, // [ONDCP] + {"hash":"8210c1a27bdc1916","prefixes":{"*":{"product":143}}}, // [Edgesuite] + {"hash":"ca8cb9c623002dc9","prefixes":{"*":{"product":143}}}, // [Edgesuite] + {"hash":"3ba6cc7216a7d5ae","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"24267a212835b827","prefixes":{"*":{"product":143}}}, // [Edgesuite] + {"hash":"060ee4c312a8e977","prefixes":{"":{"product":342}}}, // [LOKA Research inc.] + {"hash":"c7e974006ec125b7","prefixes":{"*":{"product":343}}}, // [OpenX OnRamp] + {"hash":"a985fc121086ead8","prefixes":{"*":{"product":344}}}, // [OpenX Ad Server] + {"hash":"402ca9fbb308a1c5","prefixes":{"*":{"product":36}}}, // [PaperG] + {"hash":"53f1cd9b06aea643","prefixes":{"*":{"product":36}}}, // [PaperG] + {"hash":"36b3ccfb92c2fb71","prefixes":{"*":{"product":345}}}, // [Parship] + {"hash":"28832d626c0b0925","prefixes":{"*":{"product":346}}}, // [pauldirekt GmbH] + {"hash":"1d474301c170499d","prefixes":{"*":{"product":347}}}, // [Pictela Inc.] + {"hash":"fd1aa96507b2bf44","prefixes":{"":{"product":347}}}, // [Pictela Inc.] + {"hash":"7f890c0fd0c6a4d9","prefixes":{"":{"product":348}}}, // [Pilot 1/0 GmbH & Co KG] + {"hash":"549e294957d756d8","prefixes":{"":{"product":348}}}, // [Pilot 1/0 GmbH & Co KG] + {"hash":"5dc046f77f32adf1","prefixes":{"*":{"product":349}}}, // [Piximedia] + {"hash":"637b449ba51266a3","prefixes":{"*":{"product":349}}}, // [Piximedia] + {"hash":"013ba3dd6b18ee4a","prefixes":{"*":{"product":350}}}, // [Pointroll] + {"hash":"d96024cf9bafa9d0","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"4f0c9093d18b4ecd","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"d63f961e0c8cc056","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"1bcfb023adee2b08","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"6ed6fb838bd994d7","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"9fd07c030d45578d","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"5370f7b026779f44","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"c367910d58376fa0","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"670926f337b85492","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"77d7124c8aa87819","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"3726ef9236aeff59","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"84078980dc7d0310","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"496666ed92d32be6","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"3e8a19db4e7dd91a","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"33cfdc955bd4ab90","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"543046798ae8e38d","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"54537a7d4ce6fc35","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"242ab5d44fae500c","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"e81d4aa82d04d068","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"7c7f55d8a75cb9bc","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"e4a82caa59141478","prefixes":{"":{"product":351}}}, // [Beijing PinYou Interactive Information Technology] + {"hash":"caf06dc269e4cbd7","prefixes":{"":{"product":352}}}, // [YoYi Interactive] + {"hash":"70584cd7bcb88744","prefixes":{"":{"product":352}}}, // [YoYi Interactive] + {"hash":"c9cabfef49bb4ec1","prefixes":{"":{"product":352}}}, // [YoYi Interactive] + {"hash":"993089a2d306632e","prefixes":{"":{"product":352}}}, // [YoYi Interactive] + {"hash":"cf04aaaa5ae5f383","prefixes":{"":{"product":352}}}, // [YoYi Interactive] + {"hash":"a1cd6a668ae89bc8","prefixes":{"*":{"product":353}}}, // [AdMaster] + {"hash":"f72dd5293eba3ca3","prefixes":{"":{"product":353}}}, // [AdMaster] + {"hash":"e946e6b718e0e81a","prefixes":{"":{"product":353}}}, // [AdMaster] + {"hash":"01b5e469f3b6d1d9","prefixes":{"":{"product":354}}}, // [D.A.Consortium Beijing (Platform One China)] + {"hash":"f091b3175305cced","prefixes":{"":{"product":354}}}, // [D.A.Consortium Beijing (Platform One China)] + {"hash":"1a095d1901f0940b","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"f66f99ee20105c2c","prefixes":{"*":{"product":356}}}, // [PurposeLab] + {"hash":"86c250cf0da31481","prefixes":{"":{"product":356}}}, // [PurposeLab] + {"hash":"8f204c79d6b23736","prefixes":{"*":{"product":334}}}, // [Predicta] + {"hash":"43d06db6d6527876","prefixes":{"":{"product":357}}}, // [Proclivity Systems] + {"hash":"5f76990173953807","prefixes":{"":{"product":357}}}, // [Proclivity Systems] + {"hash":"7d3947ee9533bf30","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"fb241396adc3ebde","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"b8c5f46fb8945332","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"ade9cbc2becb390f","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"874cfa4e9fe27233","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"2cd6bee614e910f1","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"854f37cd1f444e62","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"19a83c7a2b673a6d","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"ca312e078723d178","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"4aa2d41fa2878d8f","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"8931cc3c2d6bbbff","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"e67e88dcc0afe050","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"3f6c08baf48fa0ae","prefixes":{"":{"product":37}}}, // [Public-Idées] + {"hash":"6303a58061eaabd1","prefixes":{"*":{"product":358}}}, // [Viewbix] + {"hash":"4fdc8f2ff122ce2e","prefixes":{"":{"product":358}}}, // [Viewbix] + {"hash":"ba78a984ad7a8c06","prefixes":{"":{"product":358}}}, // [Viewbix] + {"hash":"00885ce869b93eab","prefixes":{"":{"product":358}}}, // [Viewbix] + {"hash":"eb59f3a64b415670","prefixes":{"":{"product":359}}}, // [Quantcast Inc.] + {"hash":"243c70f61da9731c","prefixes":{"":{"product":359}}}, // [Quantcast Inc.] + {"hash":"ed6b204d6b8f351e","prefixes":{"":{"product":359}}}, // [Quantcast Inc.] + {"hash":"e6888b5be4ecca23","prefixes":{"":{"product":359}}}, // [Quantcast Inc.] + {"hash":"8034327c12c77172","prefixes":{"":{"product":359}}}, // [Quantcast Inc.] + {"hash":"d48f950e0036e4ee","prefixes":{"*":{"product":360}}}, // [QuinStreet] + {"hash":"6118e4e0e4001349","prefixes":{"*":{"product":361}}}, // [Quisma GmbH] + {"hash":"1b2f3dadf0889c1c","prefixes":{"":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"491acc9692437dcc","prefixes":{"":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"95be38e789f1c074","prefixes":{"":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"7d13c9a93867cfd5","prefixes":{"":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"aee8b719a85b0392","prefixes":{"":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"03c0d3572439e910","prefixes":{"":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"42c10737e9d2eb44","prefixes":{"":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"1ece70926dbb1e9c","prefixes":{"*":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"15b1b56db686d6b8","prefixes":{"":{"product":362}}}, // [Quismatch / Quisma Tracker] + {"hash":"6f2fb017c596785a","prefixes":{"*":{"product":363}}}, // [Qwobl Inc.] + {"hash":"45bd1c4bac827eb4","prefixes":{"*":{"product":364}}}, // [RadiumOne Inc.] + {"hash":"a4cc28a873663be4","prefixes":{"*":{"product":365}}}, // [Core Audience, Inc] + {"hash":"b1b347108c9875ae","prefixes":{"*":{"product":366}}}, // [The Reach Group] + {"hash":"9680c32132a31294","prefixes":{"":{"product":367}}}, // [revenue cloud] + {"hash":"4654f1b3d29f0a2a","prefixes":{"":{"product":367}}}, // [revenue cloud] + {"hash":"c8daf5a2befc762c","prefixes":{"":{"product":367}}}, // [revenue cloud] + {"hash":"cd7c04dcaab54cf3","prefixes":{"":{"product":366}}}, // [The Reach Group] + {"hash":"6a7ac3b20a8267da","prefixes":{"*":{"product":368}}}, // [Register.it] + {"hash":"24064a7ef37c0464","prefixes":{"":{"product":369}}}, // [ReleStar] + {"hash":"28a4e40561f9acae","prefixes":{"":{"product":369}}}, // [ReleStar] + {"hash":"3de1987cea55e4a5","prefixes":{"":{"product":369}}}, // [ReleStar] + {"hash":"827fd98c61df481c","prefixes":{"":{"product":369}}}, // [ReleStar] + {"hash":"eec1a8a2a7fb129b","prefixes":{"":{"product":369}}}, // [ReleStar] + {"hash":"6c1b7209cced7033","prefixes":{"*":{"product":370}}}, // [Research Horizons LLC dba Phoenix Marketing] + {"hash":"c946c0fe5eecd55f","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"2feb43f5f97da585","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"4847977384b78f1d","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"308241d0fe4d6897","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"2cb30990aa0823eb","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"2c25ebde178886b1","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"d4eeb4a4736e8358","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"12498108d00bd104","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"c7437019688a5c87","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"ebc599442a1e60e0","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"ab9c2a8a5ed26375","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"c9473e1a1e9213bc","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"a15bca05fb29201f","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"8c2336c1a87e9c7e","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"67f7079b53ce160f","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"a07c7ea743bca71c","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"ce0849726cb792fb","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"6d2feeb6316bf983","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"056132328ebc8a3b","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"c0dd251eb151bbdd","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"02e6b230a73d95aa","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"62cef89124c9831e","prefixes":{"*":{"product":8}}}, // [Research Now Limited] + {"hash":"1bb2fad94e9dfd9e","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"0bca341ab2881187","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"fb725c5783121bc4","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"6d442fcb13dbd9da","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"6020c585ae546ba6","prefixes":{"":{"product":8}}}, // [Research Now Limited] + {"hash":"bf994d767fc23e27","prefixes":{"":{"product":371}}}, // [Retention Media Inc.] + {"hash":"5c801bba74bf0884","prefixes":{"":{"product":372}}}, // [Suite 66] + {"hash":"f727f4c8af36c75c","prefixes":{"":{"product":373}}}, // [Dynamic Logic / Safecount (AdIndex)] + {"hash":"d0ffa461035bdf6a","prefixes":{"":{"product":373}}}, // [Dynamic Logic / Safecount (AdIndex)] + {"hash":"f1381513cbb99bf4","prefixes":{"":{"product":373}}}, // [Dynamic Logic / Safecount (AdIndex)] + {"hash":"2b675a4d048f6d58","prefixes":{"":{"product":373}}}, // [Dynamic Logic / Safecount (AdIndex)] + {"hash":"ff9153951d29d7eb","prefixes":{"":{"product":373}}}, // [Dynamic Logic / Safecount (AdIndex)] + {"hash":"9a2d88a64367054f","prefixes":{"":{"product":373}}}, // [Dynamic Logic / Safecount (AdIndex)] + {"hash":"22fc5e00cc71b2ca","prefixes":{"":{"product":373}}}, // [Dynamic Logic / Safecount (AdIndex)] + {"hash":"d680287680a3b47b","prefixes":{"*":{"product":374}}}, // [BridgeTrack] + {"hash":"6659ffe0ec0db1a1","prefixes":{"*":{"product":375}}}, // [adnanny.com GmbH] + {"hash":"cdef303f827a483a","prefixes":{"":{"product":375}}}, // [adnanny.com GmbH] + {"hash":"d37d706078f5ef0e","prefixes":{"":{"product":375}}}, // [adnanny.com GmbH] + {"hash":"cefa23f603f78274","prefixes":{"":{"product":375}}}, // [adnanny.com GmbH] + {"hash":"9beed54a2448f091","prefixes":{"*":{"product":376}}}, // [AdRoll] + {"hash":"131ff02cd8b2b585","prefixes":{"*":{"product":377}}}, // [AdExtent] + {"hash":"7b03a999ce62649a","prefixes":{"*":{"product":377}}}, // [AdExtent] + {"hash":"d888444955526bb9","prefixes":{"*":{"product":378}}}, // [ShareThis Inc.] + {"hash":"24bedaecde8de52e","prefixes":{"*":{"product":379}}}, // [Shirtinator] + {"hash":"b2227832d5aab97c","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"29d1a1af90fe4764","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"55d58888ac21e28e","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"15a0a565c098bb6c","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"c740f7042a936f94","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"83b6131729d98b9a","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"df61075a3761234c","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"31d664b6a0c86fc5","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"b3f1fda5ca08f66c","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"a505bd8c5fdbe348","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"ff4b6ade85c0fa47","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"55fbb05b573abfc0","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"7509d096b740a94e","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"3e05ad70e7b857f6","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"f3122b05b916b4d8","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"7df77439ca0af167","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"7b6f3b92d848f180","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"c50b3a11c75c1de2","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"42bd2bd016c36072","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"d964e110e03add5e","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"b2f31dc5aea81475","prefixes":{"":{"product":380}}}, // [Simplifi Holdings Inc.] + {"hash":"fd3e5505c689b1bc","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"e21937d0011ae5da","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"0f5e4a0353dbf996","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"0ece74eb8682685e","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"3d8ea3f0d5d389d5","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"b5602307e99e9538","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"c13decc96884eb9a","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"b815964fd4b63ffe","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"a49f2db509757639","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"a3ff80b45a2b0087","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"b1b2c1399cbf19be","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"ba44917903f4a6c0","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"ad5130e5aa2d0bcd","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"ceb371583c3948ee","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"5d5d4fe496a7b543","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"30e80084c1e7285e","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"d1d0fc6a034973b5","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"e2814abcd5399a68","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"9d4acd34336a73d4","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"dcbf7d7841f88317","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"c19fe37a92d8a1f4","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"dd502cbc700beb03","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"a76947b64ea18232","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"75875c5fcc471f0e","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"5323dbf9f56befc9","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"fd3df839224700c5","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"dc544506e0acf31c","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"fbadeeb25ac766a9","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"f344fa72c3acea98","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"e0d1929f5490ba3c","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"150d5c444034d16f","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"63c9992c37df81e6","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"9a2f65999a2602e7","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"b13d21bc9d60e3cf","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"36778688c2c8550d","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"7764641665318d7a","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"137839bac6b52f11","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"75b17e7427d84ec0","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"f576b8f6b5698927","prefixes":{"":{"product":381}}}, // [Centro DSP] + {"hash":"a0bef488713fd8ba","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"c4f4b5d7a3bc8772","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"652de2e0257cc6f9","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"e1eb84d2322dd53f","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"0759e6e60e23806b","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"b97f9d3a0092f5df","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"6f7d322aac5f6093","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"d6d2092af97f6672","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"700b4497422246d3","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"24758e00974c4842","prefixes":{"*":{"product":382}}}, // [Smart Adserver] + {"hash":"229fb1e217ec3254","prefixes":{"*":{"product":382}}}, // [Smart Adserver] + {"hash":"24f84ec66db45f3d","prefixes":{"":{"product":383}}}, // [smartclip Holding AG] + {"hash":"7bce0d8d711753ea","prefixes":{"":{"product":383}}}, // [smartclip Holding AG] + {"hash":"f748f5ce8e9c4b7d","prefixes":{"*":{"product":384}}}, // [Snap Technologies Inc.] + {"hash":"c17d7dedc318749c","prefixes":{"":{"product":385}}}, // [So-net Media Networks] + {"hash":"d3a65eea99debc9b","prefixes":{"":{"product":385}}}, // [So-net Media Networks] + {"hash":"f28c7fc4a06bcfa7","prefixes":{"*":{"product":386}}}, // [SocialMedia.com] + {"hash":"17591c31cbbe7cf8","prefixes":{"":{"product":387}}}, // [Content to Emotion (CTE)] + {"hash":"7c350f8bcf5a54b9","prefixes":{"":{"product":387}}}, // [Content to Emotion (CTE)] + {"hash":"e003938122f9ac4f","prefixes":{"":{"product":387}}}, // [Content to Emotion (CTE)] + {"hash":"6deac101ca463319","prefixes":{"static":{"product":387}}}, // [Content to Emotion (CTE)] + {"hash":"304e631663a87e67","prefixes":{"*":{"product":388}}}, // [Sociomantic.com] + {"hash":"e86474f9c6ad794d","prefixes":{"":{"product":389}}}, // [AdSpeed] + {"hash":"de0816aa39c8b153","prefixes":{"*":{"product":390}}}, // [Sophus3] + {"hash":"df8355613a445a85","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"a72f2b989760c8dc","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"fbdc1704c28873c8","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"d4674ccaf8fb7e31","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"f184677fb7e2abca","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"e7d695bf507e45d5","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"ea3f746342f05dbf","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"96ce83a8f0d84d99","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"62c6410f3e4000d8","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"b5172a6eee01acbe","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"36f9ba3a735ea443","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"f4d96ab5c7d0f720","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"7c8204a143e83abb","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"cb5b72c4fdc90b38","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"01c1382916f7a580","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"49505fd99e787062","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"a931030a5dbd7346","prefixes":{"":{"product":9}}}, // [Spartoo] + {"hash":"3a09b22266ba0b12","prefixes":{"*":{"product":391}}}, // [Specific Media Inc.] + {"hash":"2136a3b6f6600ad0","prefixes":{"*":{"product":391}}}, // [Specific Media Inc.] + {"hash":"600e429523014251","prefixes":{"*":{"product":391}}}, // [Specific Media Inc.] + {"hash":"e8b1c9274a662ad3","prefixes":{"":{"product":392}}}, // [Metrigo GmbH] + {"hash":"d0ffd80eda189780","prefixes":{"":{"product":392}}}, // [Metrigo GmbH] + {"hash":"e4ab3c31faef2b98","prefixes":{"":{"product":392}}}, // [Metrigo GmbH] + {"hash":"97db8dd1bcbc33b8","prefixes":{"*":{"product":393}}}, // [SpongeCell] + {"hash":"363a0f26e680d077","prefixes":{"":{"product":394}}}, // [SpngeCell LLC] + {"hash":"fbef9c1b28b0cb05","prefixes":{"*":{"product":393}}}, // [SpongeCell] + {"hash":"2218b5ce1e656e21","prefixes":{"":{"product":395}}}, // [SpotXchange] + {"hash":"a520f16cf6177ebd","prefixes":{"":{"product":395}}}, // [SpotXchange] + {"hash":"cda310d3654d07a5","prefixes":{"":{"product":395}}}, // [SpotXchange] + {"hash":"2fc5567578510af6","prefixes":{"":{"product":396}}}, // [Spotxchange] + {"hash":"abab2d20e98c266d","prefixes":{"":{"product":395}}}, // [SpotXchange] + {"hash":"0d78cf3b068120c3","prefixes":{"":{"product":395}}}, // [SpotXchange] + {"hash":"6da7c1b25ca845b3","prefixes":{"":{"product":395}}}, // [SpotXchange] + {"hash":"c824df0fb0fdf482","prefixes":{"":{"product":397}}}, // [Encore Attribution Platform] + {"hash":"9796e014709ccbec","prefixes":{"":{"product":397}}}, // [Encore Attribution Platform] + {"hash":"fced1a8e32fb989c","prefixes":{"*":{"product":398}}}, // [Steelhouse] + {"hash":"314523d1c217734f","prefixes":{"*":{"product":399}}}, // [Strike New Media Limited] + {"hash":"276a5e6003750c07","prefixes":{"*":{"product":400}}}, // [Struq Limited] + {"hash":"e5652beb11c7fceb","prefixes":{"*":{"product":401}}}, // [SundaySky Inc.] + {"hash":"173a426f6562928a","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"81a2745fc67a8ecf","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"0aea8cde58b4f14e","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"b73e77077b022d36","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"fcf0d2e1f5fa003b","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"361c645b41a96807","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"81df9375fed9172e","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"c1493a0cd3b358a5","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"bdedb9447527960a","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"1cb83aa94e1a92ed","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"2172731c879fab72","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"7dd683bf50ce5c95","prefixes":{"":{"product":402}}}, // [Symphony Advanced Media] + {"hash":"aa47b70219d2ee5e","prefixes":{"*":{"product":403}}}, // [TagMan Ltd.] + {"hash":"097edb88beda9a20","prefixes":{"":{"product":404}}}, // [Taobao] + {"hash":"bc65fc5f46cd88c9","prefixes":{"":{"product":404}}}, // [Taobao] + {"hash":"a4d1bf696dde4d0b","prefixes":{"":{"product":404}}}, // [Taobao] + {"hash":"c94f9318ace953f1","prefixes":{"":{"product":404}}}, // [Taobao] + {"hash":"9e2b38828859944c","prefixes":{"":{"product":404}}}, // [Taobao] + {"hash":"02319fc6286418de","prefixes":{"":{"product":404}}}, // [Taobao] + {"hash":"dacc570e07ffd91a","prefixes":{"img":{"product":404}}}, // [Taobao] + {"hash":"e8ccbc8f652e85a2","prefixes":{"*":{"product":404}}}, // [Taobao] + {"hash":"da1ba7cc516336c0","prefixes":{"":{"product":404}}}, // [Taobao] + {"hash":"41035e7733acf867","prefixes":{"":{"product":405}}}, // [Target.com] + {"hash":"0509bdf297db5010","prefixes":{"*":{"product":406}}}, // [Teadma] + {"hash":"69925c7888575612","prefixes":{"":{"product":407}}}, // [BannerConnect BV] + {"hash":"4d44c3cec23aea09","prefixes":{"":{"product":407}}}, // [BannerConnect BV] + {"hash":"447ea46d0797b32f","prefixes":{"*":{"product":408}}}, // [Teracent Corporation] + {"hash":"26ca7e33d194e46e","prefixes":{"":{"product":408}}}, // [Teracent Corporation] + {"hash":"b49b79e2e870c3e9","prefixes":{"":{"product":408}}}, // [Teracent Corporation] + {"hash":"3a555db76a46f881","prefixes":{"*":{"product":38}}}, // [The Trade Desk Inc.] + {"hash":"c4684dd3ef28687a","prefixes":{"*":{"product":409}}}, // [The Travelers Indemnity Company] + {"hash":"1c38e7ac2dcf5d58","prefixes":{"":{"product":410}}}, // [Videology] + {"hash":"4872cdec0944a698","prefixes":{"":{"product":410}}}, // [Videology] + {"hash":"d4e30248b2920988","prefixes":{"*":{"product":411}}}, // [TNS Custom Research Inc.] + {"hash":"48a3c2e049507f03","prefixes":{"*":{"product":412}}}, // [TrackingSoft LLC] + {"hash":"4c5b26902a10b85b","prefixes":{"*":{"product":412}}}, // [TrackingSoft LLC] + {"hash":"51a48ea23568f817","prefixes":{"*":{"product":413}}}, // [Tradedoubler] + {"hash":"14251708a577d8c0","prefixes":{"*":{"product":414}}}, // [Epic Marketplace] + {"hash":"20f50db7213cc9ee","prefixes":{"*":{"product":415}}}, // [Tribal Fusion] + {"hash":"37b92f46ce8d0fa5","prefixes":{"":{"product":415}}}, // [Tribal Fusion] + {"hash":"218cac8e116dcf7a","prefixes":{"":{"product":416}}}, // [Triggit] + {"hash":"135ee5dfe108d144","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"f6afa26369731900","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"600a58efa4b0a4fa","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"c274f6336d4a40c8","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"0476503f0fc5138a","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"f6364185e21bdfb4","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"3b746cbe2984928e","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"2966c06482ab340c","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"1320f91c88734ed7","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"49e73f2dfe503088","prefixes":{"":{"product":417}}}, // [True Ultimate Standards Everywhere Inc.] + {"hash":"345de3574a5cef61","prefixes":{"":{"product":418}}}, // [CarMax Business Services LLC] + {"hash":"8aba77355315b602","prefixes":{"":{"product":419}}}, // [Charter Communications] + {"hash":"f7e3882cd37d1a5b","prefixes":{"":{"product":419}}}, // [Charter Communications] + {"hash":"a62179d5dc8551f8","prefixes":{"":{"product":419}}}, // [Charter Communications] + {"hash":"ac3760dc99259bf9","prefixes":{"":{"product":420}}}, // [General Nutrition Centers Inc.] + {"hash":"e1c51fbf9b39950a","prefixes":{"":{"product":421}}}, // [Hamilton Beach] + {"hash":"68a80c829eb9c95e","prefixes":{"":{"product":422}}}, // [JacquieLawson.com] + {"hash":"a02b27c9af68c051","prefixes":{"":{"product":423}}}, // [TruEffect] + {"hash":"76b54dcac102ae32","prefixes":{"":{"product":423}}}, // [TruEffect] + {"hash":"f36743b8c729a3e5","prefixes":{"":{"product":424}}}, // [American Greetings] + {"hash":"0d479c4b1d2026b9","prefixes":{"":{"product":425}}}, // [BlueMountain] + {"hash":"c442eae221355ece","prefixes":{"":{"product":426}}}, // [Cardstore] + {"hash":"1277d75c5bace94e","prefixes":{"":{"product":427}}}, // [Chemistry.com] + {"hash":"2534adb635b0540f","prefixes":{"":{"product":428}}}, // [Donald J Pliner] + {"hash":"6e81f110e25ceba1","prefixes":{"*":{"product":429}}}, // [Gevalia] + {"hash":"58cb024039904795","prefixes":{"":{"product":430}}}, // [GSI Media] + {"hash":"36b7a450ddb92a81","prefixes":{"":{"product":431}}}, // [Match.com] + {"hash":"7f6a517c8eef7bf6","prefixes":{"":{"product":431}}}, // [Match.com] + {"hash":"b5aa9057f4ab3a79","prefixes":{"":{"product":431}}}, // [Match.com] + {"hash":"177eb410b9eee3c2","prefixes":{"":{"product":431}}}, // [Match.com] + {"hash":"5a75e9cff48a157c","prefixes":{"":{"product":431}}}, // [Match.com] + {"hash":"e561536f4b9b0f5e","prefixes":{"*":{"product":432}}}, // [Optimum Response] + {"hash":"3aaed06dec8e119b","prefixes":{"":{"product":433}}}, // [Oreck] + {"hash":"e7b19cb5471c10b3","prefixes":{"":{"product":434}}}, // [Tassimo] + {"hash":"5eca8c8aa7c01b0e","prefixes":{"*":{"product":435}}}, // [uSwitch] + {"hash":"974734b3e01eab8f","prefixes":{"*":{"product":436}}}, // [TubeMogul Inc.] + {"hash":"9f562a308fffc5c4","prefixes":{"*":{"product":437}}}, // [Tumri] + {"hash":"09845e50cbc70b8a","prefixes":{"*":{"product":182}}}, // [AdAction] + {"hash":"82ae5c9a96804ec8","prefixes":{"":{"product":102}}}, // [BigaBid Media Ltd.] + {"hash":"b91b0c7d1d3bb786","prefixes":{"":{"product":102}}}, // [BigaBid Media Ltd.] + {"hash":"2804070c2606fca7","prefixes":{"":{"product":102}}}, // [BigaBid Media Ltd.] + {"hash":"d601d362a989dcf9","prefixes":{"":{"product":438}}}, // [iMarker] + {"hash":"0a0084b896887917","prefixes":{"":{"product":438}}}, // [iMarker] + {"hash":"64596506e8398578","prefixes":{"":{"product":438}}}, // [iMarker] + {"hash":"4af3f67573bdbad8","prefixes":{"":{"product":438}}}, // [iMarker] + {"hash":"1348f7d07273dfcf","prefixes":{"":{"product":438}}}, // [iMarker] + {"hash":"4329a4ad527e4779","prefixes":{"":{"product":438}}}, // [iMarker] + {"hash":"5eca470b27725f28","prefixes":{"*":{"product":439}}}, // [Turn Inc.] + {"hash":"83807aae08d0747f","prefixes":{"*":{"product":439}}}, // [Turn Inc.] + {"hash":"a0542aaf969b6041","prefixes":{"*":{"product":440}}}, // [Twelvefold Media] + {"hash":"7837c4e8623f0409","prefixes":{"":{"product":440}}}, // [Twelvefold Media] + {"hash":"13c46ea92caeecf8","prefixes":{"":{"product":441}}}, // [Tynt] + {"hash":"5f7c83b68361da73","prefixes":{"*":{"product":442}}}, // [Unica an IBM Company] + {"hash":"bd242bcdc493f1d5","prefixes":{"*":{"product":443}}}, // [Unicast] + {"hash":"58ad481190b46e2e","prefixes":{"":{"product":444}}}, // [UniQlick] + {"hash":"7f5d2102ab11e53c","prefixes":{"":{"product":444}}}, // [UniQlick] + {"hash":"c9dd94a27435350b","prefixes":{"":{"product":444}}}, // [UniQlick] + {"hash":"4ba8d85cfc1bf5de","prefixes":{"*":{"product":445}}}, // [United Virtualities] + {"hash":"0e4c474511c6dfff","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"875bbc4dbaee8734","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"58b08ddd9e3cccf9","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"356603d457828911","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"77c85cb3240da7c5","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"010912cf400592bc","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"28e6d587198883be","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"e64d00be64f97a79","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"fa0271d4d3cf41f2","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"a227d04cd3bf04f7","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"b69205c5372d2357","prefixes":{"":{"product":446}}}, // [VideoHub] + {"hash":"da30259edef87286","prefixes":{"*":{"product":447}}}, // [Visible Measures Corp.] + {"hash":"46ff1406588e7ceb","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"32ca2bff73328a83","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"3fc1dc351d4952c8","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"80d2379485452f29","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"35183abb3e2c864c","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"3b235045cc005d15","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"34e6561d9300f451","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"08980f63c2ca7971","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"327755ab5ee91d4e","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"c3d3383046b5690f","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"d196d3b7e439011e","prefixes":{"":{"product":192}}}, // [Nielsen OBE (Vizu)] + {"hash":"09582bb8c4f0bdcd","prefixes":{"*":{"product":448}}}, // [Vizury Interactive Solutions Pvt. Ltd.] + {"hash":"ccbe4ed34be8ea01","prefixes":{"":{"product":449}}}, // [Markit On Demand (Adhesion)] + {"hash":"5c0c23d6d5b2aa7d","prefixes":{"":{"product":449}}}, // [Markit On Demand (Adhesion)] + {"hash":"6fe5393d68aec474","prefixes":{"":{"product":449}}}, // [Markit On Demand (Adhesion)] + {"hash":"24de99047e3e2592","prefixes":{"":{"product":449}}}, // [Markit On Demand (Adhesion)] + {"hash":"a1d810c22ef4c8c1","prefixes":{"":{"product":449}}}, // [Markit On Demand (Adhesion)] + {"hash":"a961c235191ccc5c","prefixes":{"":{"product":449}}}, // [Markit On Demand (Adhesion)] + {"hash":"30327e228a69909d","prefixes":{"":{"product":450}}}, // [WebMetro Inc] + {"hash":"1440114026d0b8a7","prefixes":{"*":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"178b8b7280deb93c","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"8398687ebe990cf1","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"5cad65c27ef8d2b4","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"f28574a110053221","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"64eae6bdfc8cc1be","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"36b0c30f5f967ffd","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"10c282544a39807b","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"ce2ed22191804ffa","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"bd56114f26a64020","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"54e283b8884709c3","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"313822ab7be9585a","prefixes":{"":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"d54943c8060334b5","prefixes":{"":{"product":452}}}, // [Weborama Campaign Manager] + {"hash":"7928274bf854b28b","prefixes":{"*":{"product":451}}}, // [Weborama Campaign Manager (former name AdPerf)] + {"hash":"80415fbb74db7704","prefixes":{"":{"product":453}}}, // [Shopping Network] + {"hash":"0840f634995e25fb","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"5fd40859cc837b00","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"c58731043a973263","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"ee286fbf0440d595","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"660d555331a8be68","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"c412040c18d978ce","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"667b34ab71cb2381","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"b888d9e69003de82","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"38dafaa727179ef7","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"78da2db9d3a51f98","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"94a731a76dd26bfc","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"c4ca9b6aebf488dc","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"0a42e116235fec6e","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"ac4d0bcf3017cc3d","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"6653125411676c31","prefixes":{"":{"product":454}}}, // [Shanghai WiseMedia Technology Co. Ltd.] + {"hash":"062b10abe02277c1","prefixes":{"":{"product":455}}}, // [XA.net] + {"hash":"b0e3a57e8f415aec","prefixes":{"":{"product":455}}}, // [XA.net] + {"hash":"6e1bb44d175a20af","prefixes":{"":{"product":455}}}, // [XA.net] + {"hash":"34984bab025a1372","prefixes":{"":{"product":455}}}, // [XA.net] + {"hash":"d9eb2f654e80ebc6","prefixes":{"*":{"product":456}}}, // [Xaxis LLC] + {"hash":"b86d63843110e02a","prefixes":{"*":{"product":457}}}, // [Net Edge] + {"hash":"8b526a29c7f2b3dc","prefixes":{"*":{"product":458}}}, // [Xplusone Solutions Inc.] + {"hash":"fe0ea2a1c212cfde","prefixes":{"":{"product":459}}}, // [Yahoo! Ad Exchange] + {"hash":"dc06a1403eb499df","prefixes":{"":{"product":459}}}, // [Yahoo! Ad Exchange] + {"hash":"5a36dbbd382aa12c","prefixes":{"":{"product":459}}}, // [Yahoo! Ad Exchange] + {"hash":"c8fd15f0a5fc19aa","prefixes":{"":{"product":459}}}, // [Yahoo! Ad Exchange] + {"hash":"ce63a8ab511ec5bb","prefixes":{"":{"product":459}}}, // [Yahoo! Ad Exchange] + {"hash":"a6d2abe55f73bc22","prefixes":{"":{"product":460}}}, // [Yahoo Ad Manager Plus] + {"hash":"707a11e6dcfa57db","prefixes":{"":{"product":460}}}, // [Yahoo Ad Manager Plus] + {"hash":"f75439bd7693fa84","prefixes":{"":{"product":460}}}, // [Yahoo Ad Manager Plus] + {"hash":"2f7feb27a1b93524","prefixes":{"":{"product":460}}}, // [Yahoo Ad Manager Plus] + {"hash":"f801c9750710d30a","prefixes":{"":{"product":460}}}, // [Yahoo Ad Manager Plus] + {"hash":"9b96f7700d727da9","prefixes":{"":{"product":461}}}, // [APT from Yahoo!] + {"hash":"e4de2f3b2c06e193","prefixes":{"":{"product":461}}}, // [APT from Yahoo!] + {"hash":"21aa62a45ef75fb2","prefixes":{"*":{"product":462}}}, // [Yieldr] + {"hash":"85bf1ed3ee6ee615","prefixes":{"":{"product":462}}}, // [Yieldr] + {"hash":"2cf45ce27d0b73c2","prefixes":{"":{"product":462}}}, // [Yieldr] + {"hash":"e8595ade1fd26e2f","prefixes":{"":{"product":462}}}, // [Yieldr] + {"hash":"5660279fe13cfe3b","prefixes":{"":{"product":462}}}, // [Yieldr] + {"hash":"ad26a995ee586915","prefixes":{"":{"product":462}}}, // [Yieldr] + {"hash":"23412da5a5a538cc","prefixes":{"":{"product":462}}}, // [Yieldr] + {"hash":"6b333dd78d119434","prefixes":{"":{"product":462}}}, // [Yieldr] + {"hash":"f3621ca5777d61d5","prefixes":{"":{"product":462}}}, // [Yieldr] + {"hash":"cafa71178e1de4db","prefixes":{"":{"product":352}}}, // [YoYi Interactive] + {"hash":"2ca490afaa90c2b3","prefixes":{"":{"product":352}}}, // [YoYi Interactive] + {"hash":"8f0dd7539cf77aa5","prefixes":{"":{"product":352}}}, // [YoYi Interactive] + {"hash":"005700973b435023","prefixes":{"":{"product":463}}}, // [YuMe Inc.] + {"hash":"bd83427751c351a9","prefixes":{"":{"product":463}}}, // [YuMe Inc.] + {"hash":"e504ebfcb6b2722e","prefixes":{"":{"product":463}}}, // [YuMe Inc.] + {"hash":"8080438b5792b260","prefixes":{"":{"product":464}}}, // [Ebuzzing] + {"hash":"274889e98fbfc776","prefixes":{"":{"product":464}}}, // [Ebuzzing] + {"hash":"b62ee5e77fde7ec7","prefixes":{"":{"product":464}}}, // [Ebuzzing] + {"hash":"cc6a7a565374c257","prefixes":{"":{"product":464}}}, // [Ebuzzing] + {"hash":"e02936a6a6a2cb94","prefixes":{"":{"product":465}}}, // [Ziff Davis] + {"hash":"223b94a95f6849cf","prefixes":{"":{"product":465}}}, // [Ziff Davis] + {"hash":"a8742ddae80b031b","prefixes":{"":{"product":466}}}, // [DataPoint Media Inc.] + {"hash":"fd4a16bf59718107","prefixes":{"":{"product":467}}}, // [Simplytics Limited] + {"hash":"a3d523521bc4ba12","prefixes":{"":{"product":467}}}, // [Simplytics Limited] + {"hash":"9da8a1153308d7f2","prefixes":{"":{"product":468}}}, // [Makazi] + {"hash":"d9cc216c8d482336","prefixes":{"":{"product":468}}}, // [Makazi] + {"hash":"166c65d7776be9f8","prefixes":{"":{"product":468}}}, // [Makazi] + {"hash":"55f15e5b7746999f","prefixes":{"":{"product":468}}}, // [Makazi] + {"hash":"213194bd445e551d","prefixes":{"":{"product":468}}}, // [Makazi] + {"hash":"31406bff4dc35eea","prefixes":{"":{"product":468}}}, // [Makazi] + {"hash":"ea9607b28f4238c2","prefixes":{"":{"product":468}}}, // [Makazi] + {"hash":"707d5310f27d0dd2","prefixes":{"":{"product":469}}}, // [Rich Relevance] + {"hash":"59fa19b8a48477fe","prefixes":{"":{"product":470}}}, // [MainADV] + {"hash":"efb9b464f6ebaa1d","prefixes":{"":{"product":470}}}, // [MainADV] + {"hash":"8848350bd85fffe4","prefixes":{"":{"product":470}}}, // [MainADV] + {"hash":"519275c83a5a1c92","prefixes":{"":{"product":470}}}, // [MainADV] + {"hash":"fc92bec8fbcb03ef","prefixes":{"":{"product":470}}}, // [MainADV] + {"hash":"91fa39bc27e156b4","prefixes":{"":{"product":470}}}, // [MainADV] + {"hash":"408d55e8995335ad","prefixes":{"":{"product":470}}}, // [MainADV] + {"hash":"d0efa7323c77b1a5","prefixes":{"":{"product":470}}}, // [MainADV] + {"hash":"baa5cf88a0176bc6","prefixes":{"":{"product":471}}}, // [MediaV Advertising] + {"hash":"3ea0854e74c32c87","prefixes":{"":{"product":471}}}, // [MediaV Advertising] + {"hash":"88a70c2ebfe38ffe","prefixes":{"":{"product":471}}}, // [MediaV Advertising] + {"hash":"dc0b1dfe6de6a967","prefixes":{"":{"product":471}}}, // [MediaV Advertising] + {"hash":"cb234228af27fa32","prefixes":{"":{"product":471}}}, // [MediaV Advertising] + {"hash":"c7abee58863b27a4","prefixes":{"":{"product":472}}}, // [MicroAd Inc.] + {"hash":"7e34b0d37ded49ed","prefixes":{"":{"product":472}}}, // [MicroAd Inc.] + {"hash":"d7255d3135b0294d","prefixes":{"":{"product":472}}}, // [MicroAd Inc.] + {"hash":"e21264db24be060c","prefixes":{"":{"product":472}}}, // [MicroAd Inc.] + {"hash":"466676eb4c2066ac","prefixes":{"":{"product":472}}}, // [MicroAd Inc.] + {"hash":"2505a55ead3f2266","prefixes":{"":{"product":472}}}, // [MicroAd Inc.] + {"hash":"e97b9e2d31ac962d","prefixes":{"":{"product":472}}}, // [MicroAd Inc.] + {"hash":"906215055f6880fa","prefixes":{"":{"product":472}}}, // [MicroAd Inc.] + {"hash":"df04046f11aea628","prefixes":{"*":{"product":473}}}, // [Platform ID Inc.] + {"hash":"9fe8e93d55562003","prefixes":{"":{"product":474}}}, // [Xrost] + {"hash":"6b29d401833f79d8","prefixes":{"":{"product":473}}}, // [Platform ID Inc.] + {"hash":"00928c6f847a4785","prefixes":{"":{"product":475}}}, // [Sociocast Networks LLC D/B/A Velos] + {"hash":"c3964f299adbe862","prefixes":{"":{"product":476}}}, // [Trend Research] + {"hash":"76b5ddb10f769c6b","prefixes":{"*":{"product":248}}}, // [AdOcean Ltd] + {"hash":"f52edd3926705507","prefixes":{"":{"product":477}}}, // [Black Swan Verification] + {"hash":"d63e38f4eba51f77","prefixes":{"":{"product":478}}}, // [Momentum K.K] + {"hash":"6c0f8db03b28099b","prefixes":{"":{"product":477}}}, // [Black Swan Verification] + {"hash":"248772911542d550","prefixes":{"*":{"product":479}}}, // [Betgenius Limited] + {"hash":"38a72a4924027d8f","prefixes":{"*":{"product":480}}}, // [AT Internet] + {"hash":"255b63d169bd9d10","prefixes":{"*":{"product":480}}}, // [AT Internet] + {"hash":"3a7fbbad166769ad","prefixes":{"":{"product":481}}}, // [DataLab] + {"hash":"fe64230a84967d3c","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"a91296d609efe0b4","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"947fa5564eccc0b2","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"89a64fe46687237f","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"ce1cd4220063df2d","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"1c489f83a79301c6","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"9dc25ef9b3ed3e5d","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"9898ab0c57d683ae","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"0229fe19979f88b0","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"6dedd3fca4d243ac","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"cbc23a15e9f02987","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"95119cbc05f9e42a","prefixes":{"":{"product":482}}}, // [Innovid Inc.] + {"hash":"784647d3eb0236dc","prefixes":{"*":{"product":483}}}, // [Adacado] + {"hash":"4be5d3939998bc39","prefixes":{"":{"product":483}}}, // [Adacado] + {"hash":"cec4d2059bbb1365","prefixes":{"*":{"product":484}}}, // [NetDNA, LLC] + {"hash":"87a9bf2f660b2673","prefixes":{"":{"product":478}}}, // [Momentum K.K] + {"hash":"33f6a4c9eb1313f6","prefixes":{"":{"product":478}}}, // [Momentum K.K] + {"hash":"38afcf5464d4a46e","prefixes":{"":{"product":478}}}, // [Momentum K.K] + {"hash":"4a13b10532de4033","prefixes":{"*":{"product":485}}}, // [Pipewave Inc.] + {"hash":"a703750c918980b9","prefixes":{"":{"product":486}}}, // [US Media Consulting] + {"hash":"77b351109047edb2","prefixes":{"":{"product":487}}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] + {"hash":"f9f554c96902397e","prefixes":{"":{"product":487}}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] + {"hash":"e8f2c7f5eb75983e","prefixes":{"":{"product":487}}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] + {"hash":"f8680fe819857e65","prefixes":{"image":{"product":487}}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] + {"hash":"fe12b35f78c433a6","prefixes":{"":{"product":487}}}, // [SKYTOUCH TECHNOLOGY CO., LIMITED] + {"hash":"531f5c369a189473","prefixes":{"":{"product":488}}}, // [Hanesbrands Direct LLC] + {"hash":"4bfbc25da5ec8116","prefixes":{"":{"product":489}}}, // [A1platform] + {"hash":"56cea5c2b408989a","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"2c24b521f38d28dd","prefixes":{"":{"product":10}}}, // [eBay] + {"hash":"d6449351ee8e33eb","prefixes":{"*":{"product":490}}}, // [Agency.com] + {"hash":"5e040f7b958d9ee5","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"379ed8ab1690a74c","prefixes":{"":{"product":10}}}, // [eBay] + {"hash":"6ed4dac9c333ef4b","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"b4a9e54654a6de08","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"687aa30619ec8e7f","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"5ac823af612c4492","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"ce4e70466c43ade3","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"c865cf822ab64a71","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"083c339aac7e418c","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"8d689bf60029c98f","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"19d0e1eba389dfe1","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"b789395149868329","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"25e9f5191f3d2397","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"82a743999425aaad","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"97a729e5b41fd4ed","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"0db7dee9d3c756cc","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"10981271cb66af25","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"8c86bc9ba67482c6","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"d36efad270e3e7c6","prefixes":{"*":{"product":10}}}, // [eBay] + {"hash":"b709ca3f38e77645","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"3cba207e4aa9d6c6","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"fd8da92d6de2518b","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"d9e4d4444ebd418f","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"4c1c4999d1501943","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"a635762b8125bba5","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"99818fe6fd4c4a9c","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"cae59de4e054a5a8","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"d1ec771fe9489a9f","prefixes":{"":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"d9effad74da2b097","prefixes":{"*":{"product":491}}}, // [Adap.tv Inc. (AdWords/YouTube)] + {"hash":"46b1fc5fe9da3681","prefixes":{"*":{"product":492}}}, // [Fjord Technologies S.A.S.] + {"hash":"e0b6d05a8e75e14a","prefixes":{"*":{"product":492}}}, // [Fjord Technologies S.A.S.] + {"hash":"11a89bdf6bc5cf28","prefixes":{"":{"product":493}}}, // [Refined Ads] + {"hash":"8e6414939f586d4c","prefixes":{"":{"product":493}}}, // [Refined Ads] + {"hash":"6462a1b97a382278","prefixes":{"":{"product":493}}}, // [Refined Ads] + {"hash":"9f99479854cea2e7","prefixes":{"":{"product":493}}}, // [Refined Ads] + {"hash":"72b40aacd2b4e226","prefixes":{"":{"product":493}}}, // [Refined Ads] + {"hash":"67401ef133d2ed76","prefixes":{"":{"product":494}}}, // [nugg.ad AG] + {"hash":"696757eb9e07845f","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"c84660fcf8a4c99c","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"df10c5e0e18fda2b","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"c71dfe726124b24c","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"f603a3b56996fe4c","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"149593d24f74e3e1","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"a1b9a8ff7b829f8f","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"37cb27fb57319c52","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"16e50e7e7b499ab7","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"c10f5df1adb6160f","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"df3ea734f608dc0d","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"3014b8652e4fd340","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"68034e41a59505be","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"4ff550d2f5a384d7","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"266c6cb54cc8f810","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"c174c4c7c469cbb9","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"19b8f26cb5987d86","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"0301de7ebef3ded7","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"5f6ccacecc90b6ab","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"cee43a3e7001e664","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"27d64efa61e9fb8c","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"05eae7c7a976d6b2","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"da375ad9bc86ad05","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"1a62d22c13d4f003","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"65f8018fc9a58ba7","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"c790c87ca2abbb6c","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"5aefca188560242d","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"eaa7adee1317cb7d","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"db5afb1288bacbff","prefixes":{"*":{"product":496}}}, // [Meetrics GmbH] + {"hash":"7d618e8de39f548c","prefixes":{"*":{"product":496}}}, // [Meetrics GmbH] + {"hash":"b5c30978f10b1f56","prefixes":{"s":{"product":496},"":{"product":497}}}, // [Meetrics GmbH] [Meetrics] + {"hash":"5c4a10a5bc0897c7","prefixes":{"":{"product":498}}}, // [Digital Control GmbH & Co. KG] + {"hash":"38d57267aeada6cc","prefixes":{"":{"product":498}}}, // [Digital Control GmbH & Co. KG] + {"hash":"7dc82de98d234295","prefixes":{"":{"product":499}}}, // [Dedicated Media] + {"hash":"98cac3ed2db221c5","prefixes":{"":{"product":499}}}, // [Dedicated Media] + {"hash":"74da617dd78c4c25","prefixes":{"":{"product":499}}}, // [Dedicated Media] + {"hash":"ad829d4fccaec076","prefixes":{"":{"product":499}}}, // [Dedicated Media] + {"hash":"93214296470961ea","prefixes":{"":{"product":500}}}, // [Accuen] + {"hash":"fb8e16c2a3413e04","prefixes":{"*":{"product":501}}}, // [Pulse 360, Inc.] + {"hash":"56de3497c2b187f9","prefixes":{"*":{"product":501}}}, // [Pulse 360, Inc.] + {"hash":"1763cb8cd4ecb455","prefixes":{"*":{"product":502}}}, // [ZANOX AG] + {"hash":"dd420134fd0b31f7","prefixes":{"*":{"product":502}}}, // [ZANOX AG] + {"hash":"f942b375b4c9d301","prefixes":{"":{"product":503}}}, // [Webgains Ltd] + {"hash":"03b41741559a5775","prefixes":{"":{"product":504}}}, // [Virgin Media Limited] + {"hash":"e01702c3ace07a42","prefixes":{"*":{"product":505}}}, // [MyBuys MyAds] + {"hash":"a09aa03b7ab4a6a0","prefixes":{"*":{"product":506}}}, // [VCCP Search LTD] + {"hash":"8ae53030752d70fd","prefixes":{"":{"product":507}}}, // [Conversant Media] + {"hash":"3f911ef887ffe5a7","prefixes":{"":{"product":507}}}, // [Conversant Media] + {"hash":"dd0195d7ab665db3","prefixes":{"":{"product":507}}}, // [Conversant Media] + {"hash":"6ac275e8e99e5fb3","prefixes":{"":{"product":507}}}, // [Conversant Media] + {"hash":"5960f055d64bd6fb","prefixes":{"":{"product":507}}}, // [Conversant Media] + {"hash":"d07a0c93a2f5da33","prefixes":{"":{"product":507}}}, // [Conversant Media] + {"hash":"b7e7f7ab4a600ced","prefixes":{"":{"product":508}}}, // [Up-Value GmbH & Co. KG] + {"hash":"e92a06c68de2a79d","prefixes":{"":{"product":509}}}, // [Unruly Media] + {"hash":"789659215cb1620d","prefixes":{"":{"product":509}}}, // [Unruly Media] + {"hash":"b61a48cd46cb7b06","prefixes":{"*":{"product":510}}}, // [Underdog Media LLC] + {"hash":"eea5fa46bce35e24","prefixes":{"":{"product":511}}}, // [SVG Media Pvt. Ltd.] + {"hash":"9b346c0d77f1fcf2","prefixes":{"*":{"product":512}}}, // [TRAFFIQ LLC] + {"hash":"51324f52aa5eb734","prefixes":{"*":{"product":513}}}, // [TellApart Inc.] + {"hash":"26e3ca8cc9a6afd8","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"2e9a57a3dcc61e3e","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"2e6202e113ae62e3","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"9a1d6d8445e2cce9","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"88015c5982735097","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"9cf10e72a2db48a9","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"cf5ba41c477d8aa5","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"fe50c8a7f665cf78","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"5c9fb160269ccb5c","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"366f4f435bfb9255","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"5dde0b5371aae6bb","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"ebd682e181b553a7","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"df61db1cec60b5a1","prefixes":{"":{"product":513}}}, // [TellApart Inc.] + {"hash":"bdf8043c482f8afd","prefixes":{"*":{"product":514}}}, // [Shopzilla Inc.] + {"hash":"83ce758172dc68fb","prefixes":{"*":{"product":514}}}, // [Shopzilla Inc.] + {"hash":"45cc9e8ea0e405cc","prefixes":{"*":{"product":514}}}, // [Shopzilla Inc.] + {"hash":"bc8d543772676fe4","prefixes":{"*":{"product":514}}}, // [Shopzilla Inc.] + {"hash":"6a51a8a28265901e","prefixes":{"*":{"product":515}}}, // [Reactivpub] + {"hash":"b03903f1abac9b82","prefixes":{"*":{"product":515}}}, // [Reactivpub] + {"hash":"14281bd870fa9e85","prefixes":{"*":{"product":515}}}, // [Reactivpub] + {"hash":"52c9dfb471edf4de","prefixes":{"*":{"product":515}}}, // [Reactivpub] + {"hash":"b9cf807aee694b90","prefixes":{"*":{"product":516}}}, // [Netseer Inc.] + {"hash":"720c7cdaeb3d3edf","prefixes":{"":{"product":516}}}, // [Netseer Inc.] + {"hash":"a7e82477ddc48f23","prefixes":{"*":{"product":517}}}, // [eBay Enterprise] + {"hash":"c35bcff10cd62a44","prefixes":{"":{"product":518}}}, // [Goodway Group] + {"hash":"a45b6c7292b7b560","prefixes":{"":{"product":518}}}, // [Goodway Group] + {"hash":"d76efc82324fb8d5","prefixes":{"":{"product":518}}}, // [Goodway Group] + {"hash":"5eb742c77c59dd05","prefixes":{"":{"product":518}}}, // [Goodway Group] + {"hash":"33f9a4e4a440eeda","prefixes":{"*":{"product":519}}}, // [Double Positive Marketing Group Inc.] + {"hash":"285da2e20d7ae651","prefixes":{"*":{"product":520}}}, // [Chitika Inc.] + {"hash":"9ac26bc76037dd72","prefixes":{"*":{"product":521}}}, // [Scigineer Inc.] + {"hash":"11971f02aee2996f","prefixes":{"*":{"product":522}}}, // [Sales Spider Inc.] + {"hash":"0dfb620c1f99f67a","prefixes":{"*":{"product":522}}}, // [Sales Spider Inc.] + {"hash":"dee182aa38b90802","prefixes":{"":{"product":523}}}, // [Rocket Fuel Inc.] + {"hash":"ee4bb512aa7bff3f","prefixes":{"":{"product":523}}}, // [Rocket Fuel Inc.] + {"hash":"051b6a2f5170871a","prefixes":{"":{"product":523}}}, // [Rocket Fuel Inc.] + {"hash":"ef2266eab416e344","prefixes":{"":{"product":523}}}, // [Rocket Fuel Inc.] + {"hash":"40701593c87a2a6d","prefixes":{"":{"product":523}}}, // [Rocket Fuel Inc.] + {"hash":"4dfd82e7fcbfe056","prefixes":{"":{"product":523}}}, // [Rocket Fuel Inc.] + {"hash":"2848c783f1d69118","prefixes":{"":{"product":523}}}, // [Rocket Fuel Inc.] + {"hash":"26fd0c0c1bbdf578","prefixes":{"":{"product":523}}}, // [Rocket Fuel Inc.] + {"hash":"9362d437c6649ac0","prefixes":{"":{"product":524}}}, // [RTBlab] + {"hash":"83cd554cdc78cc1c","prefixes":{"":{"product":524}}}, // [RTBlab] + {"hash":"e4e3ec12949d311d","prefixes":{"":{"product":524}}}, // [RTBlab] + {"hash":"9db675ca1ddbc25e","prefixes":{"":{"product":524}}}, // [RTBlab] + {"hash":"c407f5eb4837736a","prefixes":{"":{"product":524}}}, // [RTBlab] + {"hash":"27e52d41a00bf2fd","prefixes":{"":{"product":524}}}, // [RTBlab] + {"hash":"efa545dd9d577a82","prefixes":{"":{"product":525}}}, // [abilicom GmbH] + {"hash":"f690d50313c4d883","prefixes":{"":{"product":525}}}, // [abilicom GmbH] + {"hash":"e59063ed858c1ac6","prefixes":{"":{"product":525}}}, // [abilicom GmbH] + {"hash":"08ecd0f7d816b029","prefixes":{"":{"product":30}}}, // [Rakuten Display] + {"hash":"6321d1d2e3ff13cf","prefixes":{"":{"product":526}}}, // [Pulpo Media Inc] + {"hash":"68778ff49de5f6b7","prefixes":{"":{"product":526}}}, // [Pulpo Media Inc] + {"hash":"4fe62a295bf7110a","prefixes":{"*":{"product":527}}}, // [OneSpot] + {"hash":"0a03e9309eac6fef","prefixes":{"*":{"product":528}}}, // [MyThings UK Ltd] + {"hash":"aefee6cf21fb21d0","prefixes":{"*":{"product":528}}}, // [MyThings UK Ltd] + {"hash":"013cf49561a61d0a","prefixes":{"":{"product":529}}}, // [Exactag] + {"hash":"a5db93b0474bf88a","prefixes":{"":{"product":529}}}, // [Exactag] + {"hash":"8eaf146d58acaea3","prefixes":{"":{"product":353}}}, // [AdMaster] + {"hash":"62f9a92e2fa55157","prefixes":{"":{"product":353}}}, // [AdMaster] + {"hash":"2246fe417fb594d4","prefixes":{"":{"product":353}}}, // [AdMaster] + {"hash":"46be6b3d05c938b5","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"b588a450d30940dc","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"9013d3536e80f01a","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"705829297e5108ae","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"bfd58f70292d02ab","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"ff5875b2546d8499","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"ccc16a9e8656612f","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"a764b52c155c440b","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"a8a184b600fd3af1","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"6c106d279e66ed61","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"7c8663ad744aeec9","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"c3860e5673290a36","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"485e37ef683e6da8","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"03c59eef464ffa9d","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"e8d28d6ae9ed1064","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"1fc896b89972c4a3","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"89d45cf6716d92c4","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"c374b8ef2cc0a6d3","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"e0a60df6ec972762","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"dfd819866072d81c","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"dbaab38c6254fc44","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"25e404b1d7cfa7b3","prefixes":{"":{"product":530}}}, // [Kyocera Communication Systems Co. Ltd.] + {"hash":"f9b4a878a9f66629","prefixes":{"":{"product":531}}}, // [Beijing LangTaoJin Interactive] + {"hash":"5be19b5ec9da0217","prefixes":{"":{"product":531}}}, // [Beijing LangTaoJin Interactive] + {"hash":"c3dd7532c047b725","prefixes":{"":{"product":532}}}, // [Conversant Mobile Media] + {"hash":"fd89a621a7abcb21","prefixes":{"":{"product":532}}}, // [Conversant Mobile Media] + {"hash":"8b03c1ce3a37af3e","prefixes":{"":{"product":533}}}, // [MM1X.nl] + {"hash":"51f16f2963b5ab62","prefixes":{"":{"product":533}}}, // [MM1X.nl] + {"hash":"a34aa5b1e90b35d0","prefixes":{"":{"product":534}}}, // [Trivu Media Inc.] + {"hash":"f5fe0a711d905029","prefixes":{"":{"product":534}}}, // [Trivu Media Inc.] + {"hash":"284614e662fae11d","prefixes":{"*":{"product":535}}}, // [BlueCava Inc.] + {"hash":"cd9e8f6b8e3bb0bb","prefixes":{"":{"product":536}}}, // [Beijing Gridsum Technology Co. Ltd.] + {"hash":"97de03485c681efe","prefixes":{"":{"product":536}}}, // [Beijing Gridsum Technology Co. Ltd.] + {"hash":"6c624568b77c2f15","prefixes":{"":{"product":536}}}, // [Beijing Gridsum Technology Co. Ltd.] + {"hash":"2b74e65baabdf4a8","prefixes":{"":{"product":537}}}, // [Adsvana DSP] + {"hash":"fcd2a83347336f39","prefixes":{"":{"product":537}}}, // [Adsvana DSP] + {"hash":"7cadddba30cbf68b","prefixes":{"":{"product":367}}}, // [revenue cloud] + {"hash":"39fe4b58f8d0c85a","prefixes":{"":{"product":538}}}, // [Target Performance] + {"hash":"219259d49fac1ccb","prefixes":{"":{"product":539}}}, // [Content Spread] + {"hash":"ca8d76b63fdfd7e7","prefixes":{"":{"product":367}}}, // [revenue cloud] + {"hash":"ed86b10928624d27","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"205f80e614b45d0c","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"88c9d142721a0010","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"c918497df4612dee","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"ecc68ce18bc92eac","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"acd6e7d757515791","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"611fb7a1125a9f9d","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"b47f7e78adfcce8c","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"e7d6867de133fef3","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"022353c9ab0821d7","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"2571a0502f188936","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"f1efca13f787818a","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"1c982ec87cdd98c7","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"ffa62eae8604dfb9","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"8c7ba8485b195fda","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"188fb424b02d01b3","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"92807e8eb5ceed53","prefixes":{"":{"product":540}}}, // [Datamind Effective Media] + {"hash":"b01a0f9c920b194e","prefixes":{"":{"product":541}}}, // [ScaleOut Inc.] + {"hash":"da51b7d5383143f5","prefixes":{"":{"product":541}}}, // [ScaleOut Inc.] + {"hash":"acca27df7ac50ef3","prefixes":{"":{"product":541}}}, // [ScaleOut Inc.] + {"hash":"8a45863cac015863","prefixes":{"":{"product":541}}}, // [ScaleOut Inc.] + {"hash":"ae50046a864ceafd","prefixes":{"":{"product":541}}}, // [ScaleOut Inc.] + {"hash":"de57dfdaf9a2c48a","prefixes":{"":{"product":542}}}, // [Ensighten] + {"hash":"b1235d3bdac69ad1","prefixes":{"":{"product":542}}}, // [Ensighten] + {"hash":"b2717d6d54a069e3","prefixes":{"":{"product":542}}}, // [Ensighten] + {"hash":"d7152b8a7fbdcfd9","prefixes":{"":{"product":542}}}, // [Ensighten] + {"hash":"66d946b524ae15cb","prefixes":{"":{"product":542}}}, // [Ensighten] + {"hash":"cb87adb62a805e43","prefixes":{"":{"product":542}}}, // [Ensighten] + {"hash":"64b417a15cd2caba","prefixes":{"":{"product":542}}}, // [Ensighten] + {"hash":"fe5fbbb5f87923ec","prefixes":{"":{"product":542}}}, // [Ensighten] + {"hash":"7955dee10fac2b43","prefixes":{"":{"product":543}}}, // [Sony Electronics Inc.] + {"hash":"18b842a485714b88","prefixes":{"":{"product":543}}}, // [Sony Electronics Inc.] + {"hash":"5e881c11a6141a0e","prefixes":{"*":{"product":544}}}, // [Gravity Research and Development LTD] + {"hash":"eb0586aa7b44d1a7","prefixes":{"":{"product":545}}}, // [Project SunBlock] + {"hash":"e49a615e7e072e05","prefixes":{"":{"product":546}}}, // [Novem sp. z o.o.] + {"hash":"c36795d664275022","prefixes":{"*":{"product":547}}}, // [Econda GmbH] + {"hash":"1dafa12902626628","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"ddc49ed0d5adc099","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"43e36030afe70656","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"f4acd265af4a8b2c","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"2d00af9d31137b46","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"6330d2d69fac5592","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"07924db4fbf62ed8","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"40fa76b0dfe6cba9","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"cb5e5323270dac31","prefixes":{"":{"product":548}}}, // [AdMaxim LLC] + {"hash":"644395b50223d6f0","prefixes":{"":{"product":78}}}, // [Adnologies GmbH] + {"hash":"e816f89057c8ad52","prefixes":{"":{"product":78}}}, // [Adnologies GmbH] + {"hash":"8d2b1cab5958796f","prefixes":{"":{"product":549}}}, // [Alliance Health Networks] + {"hash":"3568ed34edbd0a3b","prefixes":{"":{"product":550}}}, // [Adsame Advertising Co., Ltd] + {"hash":"5fd75bdc8d48469a","prefixes":{"":{"product":550}}}, // [Adsame Advertising Co., Ltd] + {"hash":"f24f098ee46d7e52","prefixes":{"":{"product":550}}}, // [Adsame Advertising Co., Ltd] + {"hash":"695c924af8b7be90","prefixes":{"":{"product":551}}}, // [Guangzhou Shunfei Infomation Technology Corporatio] + {"hash":"8c21b1544107f44b","prefixes":{"":{"product":552}}}, // [PK Online Ventures Limited] + {"hash":"b709e5fa7a0a3284","prefixes":{"":{"product":553}}}, // [Bigpoint Gmbh] + {"hash":"54393b0479bf4296","prefixes":{"":{"product":553}}}, // [Bigpoint Gmbh] + {"hash":"0228e75fa4f7ba29","prefixes":{"":{"product":553}}}, // [Bigpoint Gmbh] + {"hash":"161b02c3430e8493","prefixes":{"":{"product":554}}}, // [Mirapodo GmbH] + {"hash":"cbfa294f671394f5","prefixes":{"":{"product":555}}}, // [Digitize New Media Ltd.] + {"hash":"126e76895e35030f","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"d1a3397960a0c962","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"27111f31c7f7870c","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"5066a54cd7ed6242","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"5447e7edfecbefc9","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"763e1c5a534cdce1","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"664f6761d6e1bdc5","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"85dc2dbb9cf8ba01","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"efd9939ccbcea7ca","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"5095b815554d166d","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"bbd5220662a5b37e","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"ad929f7cd3353264","prefixes":{"":{"product":556}}}, // [AdSage] + {"hash":"aebf7b2a7ee1ff12","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"01ef28aab34283cc","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"c075f26f4aa57d43","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"f651f54211351ca7","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"98e7e8f01b1e7221","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"7f46832cdf7b45db","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"4a37345bf96b65b5","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"ee9f377ce8711040","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"f6f6db9a07009087","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"37414a33ff3de4a0","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"7cc06f83f0710408","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"beb1894d6a5dd8d5","prefixes":{"":{"product":557}}}, // [Fingereach] + {"hash":"ab305fad96c94e68","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"abb33b12777d693c","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"cf57baa1fe60d65d","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"1e230cd699b878e6","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"0e9f493079696ce3","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"7d3350cd22011e53","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"f50b2ec037f5428b","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"c171c26c4996ef48","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"558975de73dc2727","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"ee6b8472f5558865","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"8183f3c56cedc5d3","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"9e46a78131a70a25","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"9805160753b78db8","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"9665241d465c8ced","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"0886098ece10411f","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"1b9aa72e21da1cf9","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"75eaf92a0259fced","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"6647ef959a9060b1","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"55bbd43420c048ba","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"769a3492166f2234","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"88bf787fe78aa617","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"62db61a5802ee5f9","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"ecc8b48b5b165bff","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"3b713a488717f579","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"7d501d2362243528","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"1e2c100fab457dce","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"7adb32ad76289644","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"3f4747021104b435","prefixes":{"":{"product":307}}}, // [DMA Institute dba Hottraffic] + {"hash":"55ade41cc24f1b54","prefixes":{"":{"product":558}}}, // [Calvin Klein] + {"hash":"210f025e6c6b8122","prefixes":{"":{"product":559}}}, // [Medialets Servo] + {"hash":"233555f3daf9abb0","prefixes":{"":{"product":559}}}, // [Medialets Servo] + {"hash":"eea93545b322651b","prefixes":{"":{"product":559}}}, // [Medialets Servo] + {"hash":"d9fe49eeaf35eb01","prefixes":{"":{"product":559}}}, // [Medialets Servo] + {"hash":"ea41698b95338591","prefixes":{"s-cdn-":{"product":559}}}, // [Medialets Servo] + {"hash":"5f4e90726b181c95","prefixes":{"":{"product":560}}}, // [Webmoblink Inc.] + {"hash":"9d51efd26ab44317","prefixes":{"":{"product":31}}}, // [Retailigence] + {"hash":"32440ed26b61d14a","prefixes":{"":{"product":31}}}, // [Retailigence] + {"hash":"8234d0306d8984eb","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"e35bf94930bc4d1a","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"aec3f56c7f99151f","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"ef395b96d922c238","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"5a053ab1bcfcfb41","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"2cf811bb8aa279b5","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"cd383e750e6cfcdb","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"c30fc77cd822188e","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"fb9678eef7ea227d","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"102facb286a88bbe","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"aa39a58ff4ed2114","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"e33908ca9604cfa4","prefixes":{"*":{"product":561},"":{"product":562}}}, // [Moat Inc.] [Bannerflow AB] + {"hash":"31902ac0cc3e362b","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"796535f10f1e1ea0","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"681a390e8faf08c1","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"1ef04ebc565a6f48","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"1461b89b1a4a5e39","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"a7c34fe979c7afe2","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"30142bb0f24faa5a","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"dc8fdd8b9daae8d5","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"80a2d23602bd08d7","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"e8713f2da85d0fff","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"08fe20173c2a758a","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"105d629b41621e8f","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"c2c8a87c65874f50","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"ecc3d954923aa593","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"313e3d29b4712d5d","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"bf332f1705b05b91","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"c2fac5215807677b","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"b1db7fe55b719fd0","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"c405962725b0a49f","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"f30974f87632ff65","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"c3a0f51c7a475e24","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"470beffefc05bd89","prefixes":{"":{"product":561}}}, // [Moat Inc.] + {"hash":"44c6bfd7fd3ae379","prefixes":{"*":{"product":561}}}, // [Moat Inc.] + {"hash":"ed26e1a4862cb993","prefixes":{"":{"product":563}}}, // [Batch Media Gmbh] + {"hash":"8cc35cd28ab69e7b","prefixes":{"":{"product":563}}}, // [Batch Media Gmbh] + {"hash":"b480ee3cdb39aba3","prefixes":{"":{"product":563}}}, // [Batch Media Gmbh] + {"hash":"42b8cf3c1fba5649","prefixes":{"":{"product":563}}}, // [Batch Media Gmbh] + {"hash":"9c59afa20426083f","prefixes":{"":{"product":564}}}, // [Twenga] + {"hash":"96fa4f0d4282b74b","prefixes":{"":{"product":564}}}, // [Twenga] + {"hash":"147ff4cde8f7b63b","prefixes":{"":{"product":564}}}, // [Twenga] + {"hash":"e6d43f305d0372aa","prefixes":{"":{"product":565}}}, // [TraceAd] + {"hash":"e2a94bb0b8f09309","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"7b17e990632021fb","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"c82cc94c761aff4f","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"9bb0143d6745a1fc","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"9906e8d23a282ab0","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"c29531bb7c2a5a95","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"53972f8a807a3522","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"d8acabec202014c0","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"4062ceb50584895d","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"776d48fd287f99c8","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"caf4c25a1be2f9cc","prefixes":{"":{"product":566}}}, // [Speed Shift Media] + {"hash":"ad66fb580dfc0f61","prefixes":{"*":{"product":567}}}, // [CCB Paris] + {"hash":"2cd1c0997e13f343","prefixes":{"":{"product":568}}}, // [Nielsen Digital Ad Ratings] + {"hash":"cc67f7e32922d026","prefixes":{"*":{"product":569}}}, // [OpenDSP] + {"hash":"37ac881f55f6b624","prefixes":{"":{"product":551}}}, // [Guangzhou Shunfei Infomation Technology Corporatio] + {"hash":"6eaeb2fb0379f719","prefixes":{"":{"product":551}}}, // [Guangzhou Shunfei Infomation Technology Corporatio] + {"hash":"0f37e190c3f7659c","prefixes":{"":{"product":551}}}, // [Guangzhou Shunfei Infomation Technology Corporatio] + {"hash":"cd5e1f475a9a9cb9","prefixes":{"":{"product":551}}}, // [Guangzhou Shunfei Infomation Technology Corporatio] + {"hash":"70dae8af3c6cb2af","prefixes":{"":{"product":551}}}, // [Guangzhou Shunfei Infomation Technology Corporatio] + {"hash":"60e90d920044304d","prefixes":{"":{"product":551}}}, // [Guangzhou Shunfei Infomation Technology Corporatio] + {"hash":"9d34d5e680004543","prefixes":{"":{"product":551}}}, // [Guangzhou Shunfei Infomation Technology Corporatio] + {"hash":"b4b546c24d350673","prefixes":{"":{"product":570}}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] + {"hash":"d0b7581f4a3ff9b8","prefixes":{"":{"product":570}}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] + {"hash":"0c865d217e1f18f3","prefixes":{"":{"product":570}}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] + {"hash":"6333a18d75125edb","prefixes":{"":{"product":570}}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] + {"hash":"ed4cdc36d8829848","prefixes":{"":{"product":570}}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] + {"hash":"3643c909d7cbe0b8","prefixes":{"":{"product":570}}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] + {"hash":"296c21a7001df348","prefixes":{"":{"product":570}}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] + {"hash":"97079bad5fb6ead8","prefixes":{"":{"product":570}}}, // [Beijing NetEase YouDao Computer System Co., Ltd.] + {"hash":"7b936711dd1e9a43","prefixes":{"":{"product":571}}}, // [Mediarithmics] + {"hash":"c1d09d8bf99adaae","prefixes":{"":{"product":572}}}, // [RUN, INC.] + {"hash":"c1a2559db78be890","prefixes":{"":{"product":572}}}, // [RUN, INC.] + {"hash":"99a2370c6cb03aad","prefixes":{"":{"product":572}}}, // [RUN, INC.] + {"hash":"58188c996594e74c","prefixes":{"":{"product":573}}}, // [Shanghai Menlo Network Technologies Co.,Ltd] + {"hash":"ae01937a71f3c8ec","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"bb20b492d8e895ee","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"a174c7ffc06f1430","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"b04d26682765cf0f","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"3d8976d50a72415a","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"0b937695e283332d","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"5a633d8ed4134176","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"a3d5abb1d31313bb","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"651fcbfafdaa1c3b","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"210045798925d1ec","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"cb9c94ed4430c0d7","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"eb42b6b78e25a31e","prefixes":{"":{"product":574}}}, // [Beijing Oasis Technology Co. Ltd (Mjoys)] + {"hash":"e21aeaa35d9e4534","prefixes":{"*":{"product":575}}}, // [NET-Metrix-Audit] + {"hash":"d563d7cb84148f5b","prefixes":{"":{"product":576}}}, // [New Allyes Information Technology (Quantone)] + {"hash":"881b6320fa6c5a13","prefixes":{"":{"product":576}}}, // [New Allyes Information Technology (Quantone)] + {"hash":"648b287843959e5a","prefixes":{"":{"product":576}}}, // [New Allyes Information Technology (Quantone)] + {"hash":"aa60743a7bfcd1af","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"afa7ee2a02f86074","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"3b21af5a3fa47754","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"e8f690279ec23223","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"8763304c40959465","prefixes":{"":{"product":576}}}, // [New Allyes Information Technology (Quantone)] + {"hash":"97be7bbbd0aa71f1","prefixes":{"":{"product":576}}}, // [New Allyes Information Technology (Quantone)] + {"hash":"1610ab8331e9fa67","prefixes":{"":{"product":576}}}, // [New Allyes Information Technology (Quantone)] + {"hash":"5cf6c3a758fb5a50","prefixes":{"":{"product":576}}}, // [New Allyes Information Technology (Quantone)] + {"hash":"753372717104cecb","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"9868ccc3384ca704","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"834fc9317bd55432","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"77a58434eda65f1d","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"d6c8f549569824c7","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"605f200bf3a42aec","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"24475a888daf195d","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"577626a5c9b3a87c","prefixes":{"":{"product":577}}}, // [Resonate Networks, Inc] + {"hash":"c258f6da0cd8b8cb","prefixes":{"":{"product":577}}}, // [Resonate Networks, Inc] + {"hash":"d9483fc3fe68e764","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"7caa53e1a6ec2bae","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"dcd74656fa3b323d","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"10fa95c5f15310d2","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"6f83daf020ab465f","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"d3d1a09a28024ddc","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"9b208cc58454fb78","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"4645c6078dc34d50","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"b238cbc325c6ef22","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"49deee4d32200263","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"773d9411487a25e3","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"0f6cf491f7eb2baa","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"8dfcd3664fa1302b","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"800bb9e7968e58aa","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"b05b363e5a0511e8","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"20c70723848457f0","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"66114e4ff94b609f","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"f2d188b48acaeb7f","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"409d1c85462adead","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"4d380a7d15d0a78a","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"d5449f5767bfafa7","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"bd39dc0af22a3152","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"f86c1368c7c5eb92","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"e8f74e435e9df1dd","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"9b5fa6e2f606ce7d","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"9a9e03501130b17a","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"2a12549f5c240468","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"215711257cc47c2c","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"77f3f7c79d95de48","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"6d31870e0469ddc6","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"d43e8e7f0b7db722","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"cf4d5bbc8b02dc1c","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"fa69e0b93ab494f2","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"5df810b59c15eb88","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"1bad3858e6b1db68","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"1f0663d5a32c3b7d","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"0accadb2b8868065","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"29fa86d0b3c09f26","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"6c3481a4787dc989","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"dd2cdf0ae459961c","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"b7ddbc93720778f5","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"65db5bf588a1552c","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"d63dadf02badb9bc","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"12a0ac899815d56b","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"4e73ffb09594e8a6","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"7bd28c9beb85782f","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"e5a3c52df181ad93","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"5db249180904a1af","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"f33943be4b14e2cc","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"3304828c6a87914b","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"0dee81e461b3c8bb","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"23282a1244ed862f","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"8b1d859efda195f4","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"e22129fd14c58ef7","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"c11b0627d1105ed8","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"fee7a2b84c23b6bb","prefixes":{"":{"product":11}}}, // [advanced STORE GmbH] + {"hash":"3f3d4a96d97bc12a","prefixes":{"":{"product":578}}}, // [Triple Lift, Inc.] + {"hash":"7e4d08be26af0913","prefixes":{"":{"product":578}}}, // [Triple Lift, Inc.] + {"hash":"ab1718aa8ada6ceb","prefixes":{"":{"product":578}}}, // [Triple Lift, Inc.] + {"hash":"00810432ca3a5adf","prefixes":{"":{"product":578}}}, // [Triple Lift, Inc.] + {"hash":"e597ba5088abb36a","prefixes":{"":{"product":578}}}, // [Triple Lift, Inc.] + {"hash":"d206640e7ca174cd","prefixes":{"":{"product":579}}}, // [Indie Ads] + {"hash":"d8ed39f1e603b870","prefixes":{"":{"product":580}}}, // [Ocean Park Interactive] + {"hash":"a241e76a04cef012","prefixes":{"":{"product":580}}}, // [Ocean Park Interactive] + {"hash":"dcc0a9d204aaa55d","prefixes":{"":{"product":580}}}, // [Ocean Park Interactive] + {"hash":"8372761b0392f8a9","prefixes":{"":{"product":580}}}, // [Ocean Park Interactive] + {"hash":"e7b8261c49b1fbf6","prefixes":{"":{"product":580}}}, // [Ocean Park Interactive] + {"hash":"d5094d4667f378a7","prefixes":{"":{"product":580}}}, // [Ocean Park Interactive] + {"hash":"4091c3043c2da68a","prefixes":{"":{"product":581}}}, // [Impulse Media Pvt. Ltd.] + {"hash":"6eb5dacfebab8b02","prefixes":{"":{"product":582}}}, // [CrowdTwist] + {"hash":"123db0626c48324c","prefixes":{"":{"product":582}}}, // [CrowdTwist] + {"hash":"4f356e0d187085b0","prefixes":{"":{"product":583}}}, // [Adzerk Inc.] + {"hash":"79c1600f16f0c0ad","prefixes":{"":{"product":583}}}, // [Adzerk Inc.] + {"hash":"170c4bddaa53e87c","prefixes":{"":{"product":584}}}, // [Adtarget.me] + {"hash":"b514da98fbd70da7","prefixes":{"":{"product":584}}}, // [Adtarget.me] + {"hash":"e585ef712f906ce4","prefixes":{"":{"product":584}}}, // [Adtarget.me] + {"hash":"ed5e6e621b9e9e9e","prefixes":{"":{"product":584}}}, // [Adtarget.me] + {"hash":"904a8c71f0ac4b5c","prefixes":{"":{"product":584}}}, // [Adtarget.me] + {"hash":"138d782ff8ef8b3d","prefixes":{"":{"product":585}}}, // [Recruit Marketing Partners Co.,Ltd] + {"hash":"8899c2499d762095","prefixes":{"":{"product":586}}}, // [Beijing Emar Online Technology Co.,Ltd] + {"hash":"29f87adefe40afd5","prefixes":{"":{"product":586}}}, // [Beijing Emar Online Technology Co.,Ltd] + {"hash":"1f57d0a967ca4f30","prefixes":{"":{"product":586}}}, // [Beijing Emar Online Technology Co.,Ltd] + {"hash":"378c4fbbd1d161e2","prefixes":{"":{"product":587}}}, // [AdMagnet] + {"hash":"bb42bdad4318ee55","prefixes":{"":{"product":587}}}, // [AdMagnet] + {"hash":"71d1cec71f53e140","prefixes":{"":{"product":587}}}, // [AdMagnet] + {"hash":"e3bad45f1f3d21a5","prefixes":{"":{"product":587}}}, // [AdMagnet] + {"hash":"aa63c60aa73e9d29","prefixes":{"":{"product":588}}}, // [Momondo A/S] + {"hash":"b34444583646725a","prefixes":{"*":{"product":588}}}, // [Momondo A/S] + {"hash":"cbf8a85fbf4c22e5","prefixes":{"*":{"product":589}}}, // [DKK Agency] + {"hash":"e775e327a7a42402","prefixes":{"":{"product":590}}}, // [OneScreen Inc.] + {"hash":"080fcbb984e6ecdd","prefixes":{"":{"product":590}}}, // [OneScreen Inc.] + {"hash":"8c1ae0a34a3df5d0","prefixes":{"":{"product":590}}}, // [OneScreen Inc.] + {"hash":"ba491e57276128df","prefixes":{"":{"product":591}}}, // [AdElement Media Solutions Pvt Ltd] + {"hash":"620a368e66b0d054","prefixes":{"":{"product":591}}}, // [AdElement Media Solutions Pvt Ltd] + {"hash":"f100a71a63d2ccfa","prefixes":{"":{"product":591}}}, // [AdElement Media Solutions Pvt Ltd] + {"hash":"636877d8cc1827f4","prefixes":{"":{"product":592}}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] + {"hash":"e066625a70a1d5c0","prefixes":{"":{"product":592}}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] + {"hash":"117aaf00ef5036e3","prefixes":{"":{"product":592}}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] + {"hash":"4f795b8396901621","prefixes":{"":{"product":592}}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] + {"hash":"b8da1e9feb0bc01d","prefixes":{"":{"product":592}}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] + {"hash":"433e8fe3ca1684d7","prefixes":{"":{"product":592}}}, // [Beijing Sheng Jin Lan Advertising Co., Ltd.] + {"hash":"f2fc51beddbeb4a9","prefixes":{"":{"product":593}}}, // [Adsit Media Advertising Ltd. Co., (AdMan)] + {"hash":"ba7f680031ef6ba6","prefixes":{"":{"product":594}}}, // [AdMan] + {"hash":"cba16dd5f4409e30","prefixes":{"":{"product":594}}}, // [AdMan] + {"hash":"3e32b1ab3e9c66b6","prefixes":{"":{"product":593}}}, // [Adsit Media Advertising Ltd. Co., (AdMan)] + {"hash":"59225b3e2dce506e","prefixes":{"":{"product":595}}}, // [MicroAd Inc. (China)] + {"hash":"8a3b315693f80801","prefixes":{"":{"product":595}}}, // [MicroAd Inc. (China)] + {"hash":"9ecae05881ad91ad","prefixes":{"":{"product":595}}}, // [MicroAd Inc. (China)] + {"hash":"86a1bc63ec059f25","prefixes":{"":{"product":596}}}, // [Adfonic] + {"hash":"ea28de00a6512e88","prefixes":{"":{"product":596}}}, // [Adfonic] + {"hash":"42bb8749687dee83","prefixes":{"":{"product":597}}}, // [OwnerIQ Inc.] + {"hash":"125539a4b58c9087","prefixes":{"":{"product":597}}}, // [OwnerIQ Inc.] + {"hash":"895ef87fbdd546df","prefixes":{"":{"product":598}}}, // [Mobile Space Ltd] + {"hash":"55e398544c3aeb40","prefixes":{"":{"product":598}}}, // [Mobile Space Ltd] + {"hash":"c5e19d61efa129d7","prefixes":{"ap":{"product":598},"e":{"product":598},"":{"product":599}}}, // [Mobile Space Ltd] [Mobile Space Ltd] [Jaduda GmbH] + {"hash":"3668b933b223ee50","prefixes":{"":{"product":600}}}, // [Omotenashi Banner] + {"hash":"209fb323c4db09ef","prefixes":{"":{"product":600}}}, // [Omotenashi Banner] + {"hash":"d95ee45ed75216b1","prefixes":{"":{"product":600}}}, // [Omotenashi Banner] + {"hash":"7fb039cd298570d4","prefixes":{"":{"product":600}}}, // [Omotenashi Banner] + {"hash":"a7d0a34772320297","prefixes":{"":{"product":600}}}, // [Omotenashi Banner] + {"hash":"a86ff9f748f6a5ab","prefixes":{"":{"product":600}}}, // [Omotenashi Banner] + {"hash":"046d69c93f0ab29e","prefixes":{"":{"product":601}}}, // [Infectious Media Ltd.] + {"hash":"735a86882c4fccc6","prefixes":{"":{"product":602}}}, // [One97 Communications Limited (Ad Works)] + {"hash":"c454c081e737b7e2","prefixes":{"":{"product":602}}}, // [One97 Communications Limited (Ad Works)] + {"hash":"1e8ed560dc22bbb5","prefixes":{"":{"product":602}}}, // [One97 Communications Limited (Ad Works)] + {"hash":"3a57d7982ec36d46","prefixes":{"":{"product":602}}}, // [One97 Communications Limited (Ad Works)] + {"hash":"282d5dfa72aee67e","prefixes":{"":{"product":603}}}, // [Walk Light Media Inc.] + {"hash":"350189fcd66d737d","prefixes":{"":{"product":604}}}, // [AdBrite Inc.] + {"hash":"727c2c9dbde23217","prefixes":{"":{"product":604}}}, // [AdBrite Inc.] + {"hash":"f0011932876966b7","prefixes":{"":{"product":605}}}, // [Hi-Media] + {"hash":"49a154fb3339d1c9","prefixes":{"":{"product":605}}}, // [Hi-Media] + {"hash":"bc9403b31553c2c3","prefixes":{"":{"product":605}}}, // [Hi-Media] + {"hash":"4a763539fb83a309","prefixes":{"":{"product":605}}}, // [Hi-Media] + {"hash":"ad517dcc393c2311","prefixes":{"":{"product":605}}}, // [Hi-Media] + {"hash":"ea321c60d183257c","prefixes":{"":{"product":606}}}, // [Adlabs] + {"hash":"44405675d1af2241","prefixes":{"":{"product":606}}}, // [Adlabs] + {"hash":"703f39c13b597e32","prefixes":{"":{"product":606}}}, // [Adlabs] + {"hash":"5bdae611f2bbe659","prefixes":{"":{"product":606}}}, // [Adlabs] + {"hash":"87862d3ee53095a7","prefixes":{"":{"product":606}}}, // [Adlabs] + {"hash":"376d93acf5e8d717","prefixes":{"":{"product":606}}}, // [Adlabs] + {"hash":"3f0e8ced0049c1d6","prefixes":{"":{"product":606}}}, // [Adlabs] + {"hash":"a04ea3bd56ab64bb","prefixes":{"":{"product":607}}}, // [Arth Salutions] + {"hash":"b6ce2c8ae8e9a415","prefixes":{"":{"product":608}}}, // [Konverta] + {"hash":"ebd466a03db9081b","prefixes":{"":{"product":608}}}, // [Konverta] + {"hash":"2df213fa126d1ca4","prefixes":{"":{"product":608}}}, // [Konverta] + {"hash":"901ec1b8062c3af6","prefixes":{"":{"product":608}}}, // [Konverta] + {"hash":"3cdfbabfa37b6bd8","prefixes":{"":{"product":608}}}, // [Konverta] + {"hash":"4a23a2c45d1e92c3","prefixes":{"":{"product":608}}}, // [Konverta] + {"hash":"72784278d856920c","prefixes":{"":{"product":608}}}, // [Konverta] + {"hash":"61aa8fff3eb4e893","prefixes":{"":{"product":103}}}, // [Cogo Labs, Inc.] + {"hash":"def94469bdc7241e","prefixes":{"":{"product":103}}}, // [Cogo Labs, Inc.] + {"hash":"f17d17eabacc3756","prefixes":{"":{"product":103}}}, // [Cogo Labs, Inc.] + {"hash":"8644dbe4c2172593","prefixes":{"":{"product":609}}}, // [Shopall] + {"hash":"4b55c6a7d9c2a9a2","prefixes":{"":{"product":609}}}, // [Shopall] + {"hash":"305615cf8dbc9be2","prefixes":{"":{"product":610}}}, // [targeting360 GmbH] + {"hash":"bed5b722b1bc1ba5","prefixes":{"":{"product":611}}}, // [xplosion interactive GmbH] + {"hash":"1528f8323f342e8b","prefixes":{"":{"product":611}}}, // [xplosion interactive GmbH] + {"hash":"3c0f20ff410483fa","prefixes":{"":{"product":611}}}, // [xplosion interactive GmbH] + {"hash":"929748002c9b8ecf","prefixes":{"":{"product":611}}}, // [xplosion interactive GmbH] + {"hash":"da8d84b561cc6dae","prefixes":{"":{"product":611}}}, // [xplosion interactive GmbH] + {"hash":"4d73c36d7413bcac","prefixes":{"":{"product":611}}}, // [xplosion interactive GmbH] + {"hash":"fa03c22fb3892bf3","prefixes":{"":{"product":611}}}, // [xplosion interactive GmbH] + {"hash":"9671a9ac26a91d0a","prefixes":{"":{"product":611}}}, // [xplosion interactive GmbH] + {"hash":"ca13edee3fffee4e","prefixes":{"":{"product":612}}}, // [Hubrus LLC] + {"hash":"e6c8579483f7b40c","prefixes":{"":{"product":612}}}, // [Hubrus LLC] + {"hash":"5426e12fbea3fce8","prefixes":{"":{"product":612}}}, // [Hubrus LLC] + {"hash":"e1b8d2118c9dc529","prefixes":{"":{"product":613}}}, // [Here Media Inc.] + {"hash":"6ab38da002eadbc3","prefixes":{"":{"product":614}}}, // [Silver Egg Technology Co., Ltd.] + {"hash":"8ef09da008fcfae7","prefixes":{"":{"product":614}}}, // [Silver Egg Technology Co., Ltd.] + {"hash":"f09f918279f1e9ac","prefixes":{"":{"product":614}}}, // [Silver Egg Technology Co., Ltd.] + {"hash":"ddfd965ee920e41c","prefixes":{"":{"product":614}}}, // [Silver Egg Technology Co., Ltd.] + {"hash":"47758f4c45a57ea4","prefixes":{"":{"product":614}}}, // [Silver Egg Technology Co., Ltd.] + {"hash":"9b402085270b3ed3","prefixes":{"":{"product":614}}}, // [Silver Egg Technology Co., Ltd.] + {"hash":"5a4e20d5da4d99f0","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"1a32d33420507504","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"c841d6764f10305d","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"c0be785071fbf1f8","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"a48b68d5e7caa5da","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"93764efbaea60a26","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"2d1e1dbc88160b0a","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"49c9a4898d7eda62","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"51f27880010fd487","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"1930e597dd2d66f9","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"9138d5d361766aa2","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"942211eb94fc3c91","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"ac957255586d114c","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"1174020d0986b7c7","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"1386eff7c87966ca","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"d3c3497dc1813ac6","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"ec8bb52d56c92778","prefixes":{"":{"product":615}}}, // [QuarticOn.com] + {"hash":"b999321725f081a5","prefixes":{"":{"product":616}}}, // [Kavanga LLC] + {"hash":"43f2f88cac5c1e66","prefixes":{"":{"product":617}}}, // [Vodafone D2 GmbH] + {"hash":"71f43d2f0436d908","prefixes":{"":{"product":617}}}, // [Vodafone D2 GmbH] + {"hash":"4b13b8a28b6556d3","prefixes":{"":{"product":617}}}, // [Vodafone D2 GmbH] + {"hash":"b9c154f4d363eebc","prefixes":{"":{"product":618}}}, // [Qubit Digital Ltd] + {"hash":"f3c32c6c7e1ac882","prefixes":{"":{"product":538},"ad":{"product":619},"tm":{"product":619}}}, // [Target Performance] [NEORY GmbH] [NEORY GmbH] + {"hash":"728524adefe77058","prefixes":{"static-":{"product":620}}}, // [Populis Ireland Limited] + {"hash":"7f78aff31bba38ad","prefixes":{"":{"product":620}}}, // [Populis Ireland Limited] + {"hash":"cacb07348e9e3ca3","prefixes":{"":{"product":620}}}, // [Populis Ireland Limited] + {"hash":"ce069e74d10aa948","prefixes":{"":{"product":621}}}, // [LiquidM Technology GmbH] + {"hash":"b6284e5bbeacee78","prefixes":{"":{"product":622}}}, // [Celtra Inc.] + {"hash":"f71102b0f2425eb8","prefixes":{"":{"product":622}}}, // [Celtra Inc.] + {"hash":"da76d0a397ab42fc","prefixes":{"":{"product":622}}}, // [Celtra Inc.] + {"hash":"d30b0fd7cd3dc387","prefixes":{"":{"product":622}}}, // [Celtra Inc.] + {"hash":"d3643510dc9a55a3","prefixes":{"":{"product":622}}}, // [Celtra Inc.] + {"hash":"f74f9b96eb974ad7","prefixes":{"":{"product":622}}}, // [Celtra Inc.] + {"hash":"538d09650aaff88b","prefixes":{"":{"product":622}}}, // [Celtra Inc.] + {"hash":"7533124cd83c8651","prefixes":{"":{"product":622}}}, // [Celtra Inc.] + {"hash":"8413a3afa447ddd6","prefixes":{"":{"product":623}}}, // [Adfox] + {"hash":"28b1ebc0a8b51b58","prefixes":{"":{"product":623}}}, // [Adfox] + {"hash":"0c6cea5dd49d0361","prefixes":{"sd":{"product":623},"rt":{"product":623}}}, // [Adfox] [Adfox] + {"hash":"2e3df7d60b00392d","prefixes":{"":{"product":623}}}, // [Adfox] + {"hash":"4cdee950d7417a19","prefixes":{"":{"product":623}}}, // [Adfox] + {"hash":"2060d81c3f4dbace","prefixes":{"":{"product":624}}}, // [Accordant Media LLC] + {"hash":"f2c07b1834365ee5","prefixes":{"":{"product":625}}}, // [Fiksu DSP] + {"hash":"c61068cc57f0f219","prefixes":{"":{"product":625}}}, // [Fiksu DSP] + {"hash":"a1f4b4a83add949d","prefixes":{"":{"product":625}}}, // [Fiksu DSP] + {"hash":"70b9063aeb27d88a","prefixes":{"":{"product":625}}}, // [Fiksu DSP] + {"hash":"f66693ef14e4cd79","prefixes":{"":{"product":625}}}, // [Fiksu DSP] + {"hash":"7f9c748aa884dba0","prefixes":{"":{"product":626}}}, // [MdotM, Inc.] + {"hash":"13c8f5f12f3b9ca6","prefixes":{"":{"product":626}}}, // [MdotM, Inc.] + {"hash":"c47f6172a03d7b4c","prefixes":{"":{"product":627}}}, // [TNS GALLUP ADFACT, ZAO] + {"hash":"f9a74c430e157e57","prefixes":{"":{"product":627}}}, // [TNS GALLUP ADFACT, ZAO] + {"hash":"534ec1b9f199f18a","prefixes":{"":{"product":628}}}, // [MicroAd Inc. (APAC)] + {"hash":"98bec02f53970649","prefixes":{"":{"product":628}}}, // [MicroAd Inc. (APAC)] + {"hash":"dab5b2f5150fe52c","prefixes":{"*":{"product":629}}}, // [ECRITEL SARL] + {"hash":"039caee3c590ef51","prefixes":{"*":{"product":629}}}, // [ECRITEL SARL] + {"hash":"56d4c8e77cf378ca","prefixes":{"*":{"product":629}}}, // [ECRITEL SARL] + {"hash":"ea054eef4a7b535d","prefixes":{"*":{"product":629}}}, // [ECRITEL SARL] + {"hash":"503f3e019cf902f7","prefixes":{"":{"product":104}}}, // [AudienceProject] + {"hash":"d07ac29d5418a5a2","prefixes":{"":{"product":104}}}, // [AudienceProject] + {"hash":"252a5f53fef3adef","prefixes":{"":{"product":630}}}, // [Gloto Corp.] + {"hash":"c45cf16c98289ef3","prefixes":{"":{"product":630}}}, // [Gloto Corp.] + {"hash":"43869a9fbc1e93ac","prefixes":{"":{"product":631}}}, // [OOO GPM-Digital] + {"hash":"4334255596024dac","prefixes":{"*":{"product":632}}}, // [adverserve digital advertising services] + {"hash":"c1cbc8c1a131b486","prefixes":{"":{"product":633}}}, // [Abstract] + {"hash":"f0a24e1beb2ff006","prefixes":{"":{"product":633}}}, // [Abstract] + {"hash":"5d7848291e6b2aac","prefixes":{"":{"product":633}}}, // [Abstract] + {"hash":"678875d741d45390","prefixes":{"":{"product":634}}}, // [Adscale GmbH] + {"hash":"bdf162f8a955792f","prefixes":{"":{"product":634}}}, // [Adscale GmbH] + {"hash":"e2aa93ef150b6625","prefixes":{"":{"product":634}}}, // [Adscale GmbH] + {"hash":"fbecc41bd388e9da","prefixes":{"":{"product":634}}}, // [Adscale GmbH] + {"hash":"33d677c1746e032b","prefixes":{"":{"product":634}}}, // [Adscale GmbH] + {"hash":"7b632e1d26173d9a","prefixes":{"":{"product":635}}}, // [Weebly, Inc.] + {"hash":"d60691434c15d686","prefixes":{"":{"product":636}}}, // [KPI Solutions Co.,Ltd.] + {"hash":"6bcb40c428dea82d","prefixes":{"*":{"product":636}}}, // [KPI Solutions Co.,Ltd.] + {"hash":"a7261ab41534a5a9","prefixes":{"":{"product":637}}}, // [LuckyBrand.com] + {"hash":"8ade692ce59ddf24","prefixes":{"":{"product":638}}}, // [Bedrijvenweb.nl] + {"hash":"4f925a01c20725e2","prefixes":{"":{"product":639}}}, // [CacheFly] + {"hash":"bd7deac394b9d8ec","prefixes":{"*":{"product":640}}}, // [EdgeCast Networks Inc.] + {"hash":"d14784769a5e3e32","prefixes":{"*":{"product":640}}}, // [EdgeCast Networks Inc.] + {"hash":"eeb0b9405b4e70a9","prefixes":{"*":{"product":640}}}, // [EdgeCast Networks Inc.] + {"hash":"799a922119603cf7","prefixes":{"":{"product":641}}}, // [AdFrontier] + {"hash":"b99f16bd08f23e1d","prefixes":{"":{"product":642}}}, // [NFQ] + {"hash":"55b741a8d993b7af","prefixes":{"":{"product":642}}}, // [NFQ] + {"hash":"7733707370fb05c7","prefixes":{"":{"product":642}}}, // [NFQ] + {"hash":"9a42cb5421abffa9","prefixes":{"":{"product":643}}}, // [DataLogix, Inc.] + {"hash":"47ee3c7f6cf3211f","prefixes":{"":{"product":643}}}, // [DataLogix, Inc.] + {"hash":"5e4b5dd08e9b52fd","prefixes":{"":{"product":643}}}, // [DataLogix, Inc.] + {"hash":"6bc3a667cd012222","prefixes":{"":{"product":643}}}, // [DataLogix, Inc.] + {"hash":"18656382f2827e60","prefixes":{"":{"product":643}}}, // [DataLogix, Inc.] + {"hash":"c73db0ed241d269e","prefixes":{"":{"product":643}}}, // [DataLogix, Inc.] + {"hash":"bc0f02a06b74b7a3","prefixes":{"":{"product":643}}}, // [DataLogix, Inc.] + {"hash":"0405bea2af551e19","prefixes":{"":{"product":643}}}, // [DataLogix, Inc.] + {"hash":"c35511ecd49cebb7","prefixes":{"":{"product":644}}}, // [Brightcove] + {"hash":"22bfe38898977ed4","prefixes":{"":{"product":644}}}, // [Brightcove] + {"hash":"c095755780478f7e","prefixes":{"":{"product":645}}}, // [Experian] + {"hash":"0f2014a09acf7abd","prefixes":{"*":{"product":646}}}, // [Facebook Connect] + {"hash":"b7c70898d90f5bb3","prefixes":{"*":{"product":646}}}, // [Facebook Connect] + {"hash":"18562cb1149fda1e","prefixes":{"*":{"product":647}}}, // [Disqus] + {"hash":"37b749b8858eb61b","prefixes":{"*":{"product":648}}}, // [Namecheap.com] + {"hash":"3d59be0f5f7101a0","prefixes":{"":{"product":649}}}, // [Polldaddy] + {"hash":"510cff549d2cb397","prefixes":{"":{"product":650}}}, // [Outbrain Inc.] + {"hash":"e23ebc9959ce4873","prefixes":{"":{"product":650}}}, // [Outbrain Inc.] + {"hash":"623fd0210b11d4fb","prefixes":{"":{"product":651}}}, // [Lijit Networks, Inc.] + {"hash":"686b7accbfa398fa","prefixes":{"":{"product":651}}}, // [Lijit Networks, Inc.] + {"hash":"85c44f0602598dd8","prefixes":{"":{"product":651}}}, // [Lijit Networks, Inc.] + {"hash":"a107b1fdcffae5b9","prefixes":{"*":{"product":484}}}, // [NetDNA, LLC] + {"hash":"7f37823a3f5c76b3","prefixes":{"*":{"product":652}}}, // [PubMatic] + {"hash":"6d306315a1fecfda","prefixes":{"":{"product":653}}}, // [Say Media Inc.] + {"hash":"c2568516b08f0d3d","prefixes":{"":{"product":653}}}, // [Say Media Inc.] + {"hash":"308d3a8d1ca52ff3","prefixes":{"":{"product":653}}}, // [Say Media Inc.] + {"hash":"59e488c5f151e7c3","prefixes":{"":{"product":653}}}, // [Say Media Inc.] + {"hash":"7efe6908db5a1d44","prefixes":{"":{"product":654}}}, // [Rubicon Project Turing, Inc. (SSP)] + {"hash":"c6ee8faaa7d6734c","prefixes":{"":{"product":655}}}, // [Rubicon Project Edison, Inc. (DSP)] + {"hash":"d93c07cc68e53d91","prefixes":{"":{"product":655}}}, // [Rubicon Project Edison, Inc. (DSP)] + {"hash":"86ae2b216def9790","prefixes":{"":{"product":655}}}, // [Rubicon Project Edison, Inc. (DSP)] + {"hash":"1267f5857549a6ab","prefixes":{"":{"product":655}}}, // [Rubicon Project Edison, Inc. (DSP)] + {"hash":"367db80c0776f6d8","prefixes":{"":{"product":655}}}, // [Rubicon Project Edison, Inc. (DSP)] + {"hash":"c4c762a7e666da1c","prefixes":{"":{"product":655}}}, // [Rubicon Project Edison, Inc. (DSP)] + {"hash":"bb66261f843823d5","prefixes":{"":{"product":656}}}, // [Wordpress Stats] + {"hash":"a0f3bb8dcd67010e","prefixes":{"":{"product":656}}}, // [Wordpress Stats] + {"hash":"4a423f1da960eda6","prefixes":{"*":{"product":657}}}, // [Twitter] + {"hash":"465806fbb3547c25","prefixes":{"*":{"product":657}}}, // [Twitter] + {"hash":"13c55ef8102cbdbb","prefixes":{"":{"product":658}}}, // [Yandex LLC] + {"hash":"db546baba3acb079","prefixes":{"":{"product":658}}}, // [Yandex LLC] + {"hash":"3dd0667dbad0af61","prefixes":{"":{"product":658}}}, // [Yandex LLC] + {"hash":"78e224c91aabe6de","prefixes":{"":{"product":659}}}, // [Rutarget / Segmento] + {"hash":"75acb8a5a60ef63d","prefixes":{"":{"product":659}}}, // [Rutarget / Segmento] + {"hash":"6f7720a054c19a2b","prefixes":{"":{"product":659}}}, // [Rutarget / Segmento] + {"hash":"dbd76c26579bf2b1","prefixes":{"":{"product":659}}}, // [Rutarget / Segmento] + {"hash":"2cd7f311595e164e","prefixes":{"":{"product":659}}}, // [Rutarget / Segmento] + {"hash":"b08380cf2fcb4415","prefixes":{"":{"product":659}}}, // [Rutarget / Segmento] + {"hash":"59ac14277edec497","prefixes":{"":{"product":659}}}, // [Rutarget / Segmento] + {"hash":"88893eeb26e546a0","prefixes":{"":{"product":659}}}, // [Rutarget / Segmento] + {"hash":"da151dbe8b815dfb","prefixes":{"":{"product":660}}}, // [Addoor LatinMarkets, SL] + {"hash":"b2a44f920e268749","prefixes":{"":{"product":661}}}, // [Kate Spade] + {"hash":"96a8c3ab1740bf2f","prefixes":{"":{"product":97}}}, // [Tacoda] + {"hash":"95e85e3cbd858779","prefixes":{"":{"product":662}}}, // [TARGUSinfo] + {"hash":"dbf2963c9bf26f55","prefixes":{"":{"product":663}}}, // [Adblade] + {"hash":"8064758f6c80afed","prefixes":{"":{"product":663}}}, // [Adblade] + {"hash":"b7aaa083e4151ca8","prefixes":{"":{"product":663}}}, // [Adblade] + {"hash":"f4d5f13eb7d1ba2b","prefixes":{"":{"product":663}}}, // [Adblade] + {"hash":"eca68f2e6cd8a07e","prefixes":{"":{"product":664}}}, // [Adiant] + {"hash":"30f4c3f682592add","prefixes":{"":{"product":665}}}, // [AddToAny] + {"hash":"39b76d24e28075d4","prefixes":{"*":{"product":666}}}, // [Bizo Inc] + {"hash":"dabe4d73219c06e0","prefixes":{"":{"product":667}}}, // [Google Analytics] + {"hash":"f8c6758a214299be","prefixes":{"":{"product":668}}}, // [Gravatar] + {"hash":"0a130612d187bc06","prefixes":{"":{"product":668}}}, // [Gravatar] + {"hash":"6802f0145385df51","prefixes":{"":{"product":669}}}, // [MediaGlu] + {"hash":"ccbeaf028d3c721b","prefixes":{"":{"product":669}}}, // [MediaGlu] + {"hash":"1d7daeed381c33aa","prefixes":{"":{"product":669}}}, // [MediaGlu] + {"hash":"038b4d8cab2d2a58","prefixes":{"":{"product":669}}}, // [MediaGlu] + {"hash":"bd4919274e19987e","prefixes":{"":{"product":669}}}, // [MediaGlu] + {"hash":"dcdf95e5abf7c100","prefixes":{"":{"product":670}}}, // [Tapstream Network Inc.] + {"hash":"1f751fd80b11ad3c","prefixes":{"":{"product":671}}}, // [HUNT Mobile Ads] + {"hash":"de1c8591006ce969","prefixes":{"":{"product":672}}}, // [Apsalar, Inc.] + {"hash":"9fd29903977b5176","prefixes":{"":{"product":672}}}, // [Apsalar, Inc.] + {"hash":"c3930fc2f4cd70df","prefixes":{"*":{"product":673}}}, // [Videology DSP] + {"hash":"8924b56175f6f114","prefixes":{"":{"product":674}}}, // [Videostrip] + {"hash":"f4b2d76af9987952","prefixes":{"":{"product":675}}}, // [Affinity – Hostway Corporation] + {"hash":"b11ec5ee5b26491a","prefixes":{"*":{"product":241},"":{"product":676}}}, // [Scene Stealer Ltd.] [Rackspace, US Inc.] + {"hash":"5177084498d58bac","prefixes":{"":{"product":676}}}, // [Rackspace, US Inc.] + {"hash":"3a0fe0aeaa847996","prefixes":{"*":{"product":676}}}, // [Rackspace, US Inc.] + {"hash":"2b344a3d6c766cb7","prefixes":{"":{"product":676}}}, // [Rackspace, US Inc.] + {"hash":"a0b6774e583d1787","prefixes":{"":{"product":677}}}, // [Taptica] + {"hash":"e6ad999a7fc77500","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"4056609d04bfec9c","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"d1f59501d08f217a","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"bd467d941e65225d","prefixes":{"":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"0de83b2d387d2a84","prefixes":{"*":{"product":241}}}, // [Scene Stealer Ltd.] + {"hash":"227a1699196d5009","prefixes":{"":{"product":678}}}, // [LiveRamp, Inc.] + {"hash":"2077744d64232ddc","prefixes":{"":{"product":678}}}, // [LiveRamp, Inc.] + {"hash":"5ff42e5327d8c926","prefixes":{"":{"product":679}}}, // [Plista GmbH] + {"hash":"1d36941de87ee056","prefixes":{"":{"product":679}}}, // [Plista GmbH] + {"hash":"0534ec41ce34cced","prefixes":{"":{"product":679}}}, // [Plista GmbH] + {"hash":"c5ca32bf780ff41c","prefixes":{"":{"product":680}}}, // [Netquest Ad Tracking] + {"hash":"fcb4d508b7c17d00","prefixes":{"":{"product":681}}}, // [Mediasmart Mobile S.L.] + {"hash":"32de5cdddc7878d9","prefixes":{"":{"product":681}}}, // [Mediasmart Mobile S.L.] + {"hash":"832d7e03cfa50775","prefixes":{"":{"product":682}}}, // [Netshelter Technology Media, Inc.] + {"hash":"5d0747bdde7700a5","prefixes":{"":{"product":682}}}, // [Netshelter Technology Media, Inc.] + {"hash":"6cda89e3ca547147","prefixes":{"":{"product":682}}}, // [Netshelter Technology Media, Inc.] + {"hash":"6825c9c2052b6d49","prefixes":{"":{"product":682}}}, // [Netshelter Technology Media, Inc.] + {"hash":"dac91e433fa392a8","prefixes":{"":{"product":682}}}, // [Netshelter Technology Media, Inc.] + {"hash":"5112708cd650c1bf","prefixes":{"":{"product":682}}}, // [Netshelter Technology Media, Inc.] + {"hash":"8c56edbaad9d7666","prefixes":{"":{"product":682}}}, // [Netshelter Technology Media, Inc.] + {"hash":"e3f867313a3a22ff","prefixes":{"*":{"product":683}}}, // [Mixmarket Affiliate Network] + {"hash":"337d4f2254b699bd","prefixes":{"":{"product":683}}}, // [Mixmarket Affiliate Network] + {"hash":"7dd6cb3709637812","prefixes":{"*":{"product":684}}}, // [Turbobytes] + {"hash":"b42ea914a5e25208","prefixes":{"":{"product":685}}}, // [Voodoo Video AG] + {"hash":"441e902171e2e03a","prefixes":{"*":{"product":255}}}, // [Google CDN] + {"hash":"ed45a52fc3b3ded7","prefixes":{"*":{"product":255}}}, // [Google CDN] + {"hash":"b70fc2e7ac8321a4","prefixes":{"*":{"product":255}}}, // [Google CDN] + {"hash":"a7ab005368d54a08","prefixes":{"*":{"product":255}}}, // [Google CDN] + {"hash":"b3808fd8fb0b9d9b","prefixes":{"*":{"product":255}}}, // [Google CDN] + {"hash":"273fe37ef5880422","prefixes":{"lh":{"product":255},"geo":{"product":255}}}, // [Google CDN] [Google CDN] + {"hash":"cf71755ff09e2183","prefixes":{"":{"product":255}}}, // [Google CDN] + {"hash":"3a32bf25eec1a95b","prefixes":{"*":{"product":686}}}, // [Full Performance] + {"hash":"783c5679a80d5c10","prefixes":{"":{"product":687}}}, // [eXelate Inc.] + {"hash":"40f709f76b1af3f1","prefixes":{"":{"product":687}}}, // [eXelate Inc.] + {"hash":"a97762efe739bb85","prefixes":{"*":{"product":688}}}, // [Oreck Canada] + {"hash":"f3460c4c9a5112cc","prefixes":{"":{"product":689}}}, // [Beijing WuShuang Technology Ltd. (AGrant)] + {"hash":"1c1f6d178312c71a","prefixes":{"*":{"product":690}}}, // [Limelight] + {"hash":"227010fe11d13050","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"5460e19d75ae0d01","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"f132b41fc3fed2c5","prefixes":{"":{"product":692}}}, // [Matomy Media] + {"hash":"930103c79d1886c9","prefixes":{"":{"product":693}}}, // [Mail.Ru Group] + {"hash":"befa931c2b772e6d","prefixes":{"":{"product":693}}}, // [Mail.Ru Group] + {"hash":"e1a46a5a294589c8","prefixes":{"":{"product":693}}}, // [Mail.Ru Group] + {"hash":"7aaf1724444529c4","prefixes":{"":{"product":693}}}, // [Mail.Ru Group] + {"hash":"a8e6cfd540b8a74b","prefixes":{"":{"product":693}}}, // [Mail.Ru Group] + {"hash":"4dd30b722c120fe4","prefixes":{"":{"product":693}}}, // [Mail.Ru Group] + {"hash":"5785365f3a4da9e4","prefixes":{"":{"product":693}}}, // [Mail.Ru Group] + {"hash":"b4cbadcc4ab58981","prefixes":{"":{"product":694}}}, // [LSi - Lionsoft Studios, spol. s r.o.] + {"hash":"a41a0099a363da99","prefixes":{"":{"product":695}}}, // [Adelphic Inc.] + {"hash":"9a9d3ceef9c489c7","prefixes":{"":{"product":695}}}, // [Adelphic Inc.] + {"hash":"8c652e1d454ab3a2","prefixes":{"":{"product":695}}}, // [Adelphic Inc.] + {"hash":"36e0703b50fb3eed","prefixes":{"":{"product":695}}}, // [Adelphic Inc.] + {"hash":"a437c97b4d64cafd","prefixes":{"":{"product":696}}}, // [Lincoln Technical Institute, Inc.] + {"hash":"f69a68a2d09d0720","prefixes":{"":{"product":697}}}, // [Smartstream.tv] + {"hash":"26dfb2b8bad92f4f","prefixes":{"":{"product":697}}}, // [Smartstream.tv] + {"hash":"dc67b62ecf5a9b08","prefixes":{"":{"product":697}}}, // [Smartstream.tv] + {"hash":"23b6f2b40a209645","prefixes":{"":{"product":697}}}, // [Smartstream.tv] + {"hash":"40536f7cc3f3bcdb","prefixes":{"":{"product":697}}}, // [Smartstream.tv] + {"hash":"7eab1c802c1cb708","prefixes":{"*":{"product":697}}}, // [Smartstream.tv] + {"hash":"8381e43653c976d7","prefixes":{"":{"product":697}}}, // [Smartstream.tv] + {"hash":"010611867004fb28","prefixes":{"":{"product":698}}}, // [Reklamport] + {"hash":"8c138e749437f931","prefixes":{"":{"product":698}}}, // [Reklamport] + {"hash":"735ef6cfbe0d0549","prefixes":{"":{"product":699}}}, // [Fattext LLC (DBA Moolah Media)] + {"hash":"7a17aa6bfdfb3de1","prefixes":{"":{"product":699}}}, // [Fattext LLC (DBA Moolah Media)] + {"hash":"3c593814e40d8a9e","prefixes":{"":{"product":700}}}, // [MoGo Marketing & Media Inc.] + {"hash":"0f36027c1644631a","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"ec8b441e84a18442","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"b923ca8213dfa24a","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"fe0b93fc6d0ad89a","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"b7164c05e2fb0d2f","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"6e085b0ec37b404f","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"d282ecf2666110dd","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"131054b9a54841da","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"556351c03849a4c1","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"bebf7033727205d4","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"e77b9add119d1d72","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"fbcb122264bcdbda","prefixes":{"":{"product":701}}}, // [SA Media, llc] + {"hash":"10f64b8265e0b1be","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"cc468e0676f36482","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"a445930c60af8d98","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"63e594feafaae08b","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"045d86072bc5dc10","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"ea9d0ea08d06528d","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"94bc93c21ca5d014","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"bcfa082cf700866c","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"17f352a8e88349c9","prefixes":{"":{"product":702}}}, // [Barons Media LLC] + {"hash":"8b18a83b14ef8906","prefixes":{"":{"product":703}}}, // [apprupt GmbH] + {"hash":"46ba596945892a31","prefixes":{"":{"product":703}}}, // [apprupt GmbH] + {"hash":"b0780943092b032a","prefixes":{"":{"product":703}}}, // [apprupt GmbH] + {"hash":"7d76822bfe9ffcd9","prefixes":{"":{"product":703}}}, // [apprupt GmbH] + {"hash":"87582b95561cf002","prefixes":{"":{"product":703}}}, // [apprupt GmbH] + {"hash":"eb0a0cd03ec7f57b","prefixes":{"":{"product":704}}}, // [PropellerADs media Ltd] + {"hash":"73009a8d32988e92","prefixes":{"*":{"product":705}}}, // [CJ Affiliate by Conversant] + {"hash":"698b1ec9df8d6759","prefixes":{"*":{"product":705}}}, // [CJ Affiliate by Conversant] + {"hash":"00e17f99243707e9","prefixes":{"":{"product":706}}}, // [Millennial Media Inc] + {"hash":"62ddc5a87d328e5a","prefixes":{"":{"product":706}}}, // [Millennial Media Inc] + {"hash":"0e02d11afea79a5e","prefixes":{"":{"product":707}}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] + {"hash":"56b1e984216b464b","prefixes":{"":{"product":707}}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] + {"hash":"815a3c2c1ed6f999","prefixes":{"":{"product":707}}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] + {"hash":"28175ac65441fbce","prefixes":{"":{"product":707}}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] + {"hash":"fe775b2f045dcced","prefixes":{"":{"product":707}}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] + {"hash":"d47b0a700d84bfba","prefixes":{"":{"product":707}}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] + {"hash":"1c34827419405aee","prefixes":{"":{"product":707}}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] + {"hash":"7e99ada6b2533f54","prefixes":{"":{"product":707}}}, // [BEIJING BEHE SCIENCE & TECHNOLOGY Co., Ltd] + {"hash":"2c5ac4af628363a6","prefixes":{"*":{"product":708}}}, // [Manage.com Group, Inc.] + {"hash":"f05737fe4b72d22f","prefixes":{"":{"product":709}}}, // [CloudFlare, Inc.] + {"hash":"1b728798f387160d","prefixes":{"":{"product":710}}}, // [MASSMOTIONMEDIA SARL] + {"hash":"fb80c91c77673741","prefixes":{"":{"product":711}}}, // [Brainworks Sp. z.o.o.] + {"hash":"6ff7dff7c1404e1b","prefixes":{"":{"product":711}}}, // [Brainworks Sp. z.o.o.] + {"hash":"bd6c259e3bb98101","prefixes":{"":{"product":711}}}, // [Brainworks Sp. z.o.o.] + {"hash":"0dcb02764319e823","prefixes":{"":{"product":711}}}, // [Brainworks Sp. z.o.o.] + {"hash":"258ec0f081f1c767","prefixes":{"":{"product":711}}}, // [Brainworks Sp. z.o.o.] + {"hash":"518594f7ef9754dc","prefixes":{"*":{"product":712}}}, // [Media Intelligence Platform (Aggregate Knowledge)] + {"hash":"223b1b79a6de9f04","prefixes":{"*":{"product":712}}}, // [Media Intelligence Platform (Aggregate Knowledge)] + {"hash":"848f45768ee0ad4e","prefixes":{"":{"product":713}}}, // [S4M] + {"hash":"77b7e5f4ec17a6e5","prefixes":{"":{"product":713}}}, // [S4M] + {"hash":"f965c1f8d2cce837","prefixes":{"":{"product":713}}}, // [S4M] + {"hash":"3e327352cea146b3","prefixes":{"":{"product":713}}}, // [S4M] + {"hash":"0658ca5e4baec224","prefixes":{"":{"product":713}}}, // [S4M] + {"hash":"f2e7a2604319636b","prefixes":{"":{"product":713}}}, // [S4M] + {"hash":"c825977950596abc","prefixes":{"":{"product":713}}}, // [S4M] + {"hash":"b55fc4c23b864e56","prefixes":{"":{"product":713}}}, // [S4M] + {"hash":"3eddbbcdb5a13a1b","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"f133f36dd9e9688d","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"06ead346af8ecc6d","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"16a442ad35246027","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"778068a948335ee6","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"2bb088766e0a77ce","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"f54b41faf3e29ddb","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"3d71c7985374fc62","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"de336679348d46a5","prefixes":{"":{"product":714}}}, // [Sobmag LTD] + {"hash":"6818fc4cb694f0d4","prefixes":{"*":{"product":715}}}, // [Netbooster] + {"hash":"5682fbd14ad230ca","prefixes":{"":{"product":716}}}, // [Musikhaus Thomann e.K.] + {"hash":"abbb14ff2ab0311d","prefixes":{"":{"product":716}}}, // [Musikhaus Thomann e.K.] + {"hash":"578726cfc508157e","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"a141aaa30beb2f05","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"9161fa28951da89a","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"2e52f74b27e9eb2f","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"0ecdef2f55913790","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"409546c7e7a9ce63","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"3dceafc40a01cd8c","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"e748a008fcd2910c","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"499e3537aead5990","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"aad99655e792b7af","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"df381181135b37f3","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"49fe77e073f907d5","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"fe445db4579e7177","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"f7038d35f22c32db","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"184913b8eec78f63","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"4db6f08af3d6cf66","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"0a71646e475ff9c1","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"7847dd8c5608a507","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"cde33b0511f42c7c","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"6a5ae5d2380647dc","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"0821cc1422e91328","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"0e157baa999502bc","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"cb93fe6df3fe4097","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"657c19e08d899173","prefixes":{"":{"product":146}}}, // [SiteScout AdServer] + {"hash":"e411fd988f243d89","prefixes":{"":{"product":717}}}, // [Trovit] + {"hash":"1ea512003540106b","prefixes":{"":{"product":717}}}, // [Trovit] + {"hash":"48255f09ec2021d3","prefixes":{"":{"product":717}}}, // [Trovit] + {"hash":"7b5b7cfa3d62a886","prefixes":{"":{"product":717}}}, // [Trovit] + {"hash":"1235b8279a106276","prefixes":{"":{"product":718}}}, // [O2online] + {"hash":"cf76706eea2f0be6","prefixes":{"":{"product":719}}}, // [E-Plus Mobilfunk GmbH & Co. KG] + {"hash":"766b747f8dec4417","prefixes":{"":{"product":720}}}, // [Meteora] + {"hash":"270677f86b7f115b","prefixes":{"":{"product":720}}}, // [Meteora] + {"hash":"0a500f94a8a562f0","prefixes":{"":{"product":721}}}, // [Madison Logic, Inc.] + {"hash":"764d3a17a9512438","prefixes":{"":{"product":721}}}, // [Madison Logic, Inc.] + {"hash":"38c3aac1b79f0478","prefixes":{"":{"product":721}}}, // [Madison Logic, Inc.] + {"hash":"babcc401d384553f","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"2ddae58a55ab72ef","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"db564708d2871d3a","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"a4199cfcbe70d7f5","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"e6f5851275bc1fb3","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"16e6d7e84ad7fa3a","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"58ae73a3bb4327d7","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"366a0ad35670aef5","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"5b30682b9dfa7060","prefixes":{"":{"product":722}}}, // [万相DSP(IZP Technologies)] + {"hash":"67128a22cdd1a7cd","prefixes":{"":{"product":23}}}, // [AppNexus Open AdStream] + {"hash":"db56992e1a1a3f90","prefixes":{"":{"product":723}}}, // [righTarget] + {"hash":"479ab75d77a40d72","prefixes":{"":{"product":723}}}, // [righTarget] + {"hash":"17bcd2af46fb1fe6","prefixes":{"":{"product":723}}}, // [righTarget] + {"hash":"45681f09a76fe33e","prefixes":{"":{"product":724}}}, // [e.QQ.com] + {"hash":"612f6aab80bc21df","prefixes":{"":{"product":724}}}, // [e.QQ.com] + {"hash":"ab7e80409519ca9d","prefixes":{"":{"product":724}}}, // [e.QQ.com] + {"hash":"a3ae7b8a84f28040","prefixes":{"":{"product":724}}}, // [e.QQ.com] + {"hash":"b25f21b316565014","prefixes":{"":{"product":724}}}, // [e.QQ.com] + {"hash":"cc9eeba2126e4261","prefixes":{"":{"product":724}}}, // [e.QQ.com] + {"hash":"9d4819b235a5272b","prefixes":{"":{"product":724}}}, // [e.QQ.com] + {"hash":"78bb032f283d8f48","prefixes":{"":{"product":725}}}, // [Shen Zhen ShiJi KaiXuan Technology Company Ltd.] + {"hash":"de11a5b150526c21","prefixes":{"":{"product":725}}}, // [Shen Zhen ShiJi KaiXuan Technology Company Ltd.] + {"hash":"bbd9e4a668c4e348","prefixes":{"":{"product":725}}}, // [Shen Zhen ShiJi KaiXuan Technology Company Ltd.] + {"hash":"c38a1d9eab08d787","prefixes":{"*":{"product":724}}}, // [e.QQ.com] + {"hash":"5b2c8d90176bd14e","prefixes":{"":{"product":724}}}, // [e.QQ.com] + {"hash":"62980dc2fa42e1e0","prefixes":{"*":{"product":724}}}, // [e.QQ.com] + {"hash":"9b37bab01bf28d39","prefixes":{"*":{"product":726}}}, // [Bannercockpit] + {"hash":"01ec60ee4671b195","prefixes":{"":{"product":727}}}, // [Outrigger Media Inc.] + {"hash":"4be86c0a73547960","prefixes":{"":{"product":727}}}, // [Outrigger Media Inc.] + {"hash":"e3ab574adac96838","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"91bb53a0f22994f8","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"fd65eb28a70ed09a","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"9f41f821ddcb5092","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"ee70168f83fb2cce","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"c15fc4a18b25e6d3","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"f5f66dfaf3d2d2a3","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"e6e8716836c8a21d","prefixes":{"":{"product":105}}}, // [Rockabox Media Ltd] + {"hash":"da37db68457e2782","prefixes":{"*":{"product":728}}}, // [shopLocal] + {"hash":"ab8c1ee046adfbb1","prefixes":{"*":{"product":729}}}, // [Impact Radius] + {"hash":"0e8c1ff7462c1084","prefixes":{"*":{"product":729}}}, // [Impact Radius] + {"hash":"8f228d8317e4718d","prefixes":{"":{"product":729}}}, // [Impact Radius] + {"hash":"29d7e31af4b1be0b","prefixes":{"":{"product":730}}}, // [Unister AdServer] + {"hash":"efeab3df1fe3849a","prefixes":{"":{"product":730}}}, // [Unister AdServer] + {"hash":"8f2fea31a1c79b34","prefixes":{"":{"product":730}}}, // [Unister AdServer] + {"hash":"9cda8eeaf1e150bf","prefixes":{"":{"product":730}}}, // [Unister AdServer] + {"hash":"714091c1b8581de0","prefixes":{"":{"product":731}}}, // [Unister Media GmbH] + {"hash":"c7002b2b9e4b5370","prefixes":{"":{"product":730}}}, // [Unister AdServer] + {"hash":"1802591342537dae","prefixes":{"":{"product":730}}}, // [Unister AdServer] + {"hash":"787cbd14bb54b647","prefixes":{"":{"product":730}}}, // [Unister AdServer] + {"hash":"084ef5666af6a26c","prefixes":{"":{"product":730}}}, // [Unister AdServer] + {"hash":"df72b5eb4be388d1","prefixes":{"":{"product":732}}}, // [TLV Media Online Ltd] + {"hash":"64ffcabaf6ec2bcf","prefixes":{"":{"product":733}}}, // [d3media AG] + {"hash":"a6d58332a182c9f4","prefixes":{"":{"product":733}}}, // [d3media AG] + {"hash":"0bbd108959983b32","prefixes":{"":{"product":734}}}, // [Innovative Metrics] + {"hash":"80eab7dfc5e4cc5f","prefixes":{"*":{"product":735}}}, // [AthenaHealth] + {"hash":"131ce36599fa0d6c","prefixes":{"":{"product":736}}}, // [TAPVALUE SAS] + {"hash":"9181181a015040bc","prefixes":{"":{"product":736}}}, // [TAPVALUE SAS] + {"hash":"fca830abc4829657","prefixes":{"":{"product":736}}}, // [TAPVALUE SAS] + {"hash":"e78333a733c1b05a","prefixes":{"":{"product":736}}}, // [TAPVALUE SAS] + {"hash":"3f82423bd28a526a","prefixes":{"":{"product":736}}}, // [TAPVALUE SAS] + {"hash":"f2da501e4086afa3","prefixes":{"":{"product":736}}}, // [TAPVALUE SAS] + {"hash":"719080d33b283abc","prefixes":{"":{"product":736}}}, // [TAPVALUE SAS] + {"hash":"3502e0d544b9b739","prefixes":{"":{"product":737}}}, // [Meteor Worldwide LLC.] + {"hash":"a6227ad6f805c298","prefixes":{"":{"product":737}}}, // [Meteor Worldwide LLC.] + {"hash":"0933cf49bb328213","prefixes":{"*":{"product":189}}}, // [Relay42 Technology B.V.] + {"hash":"bea1d6b2a32d3927","prefixes":{"*":{"product":189}}}, // [Relay42 Technology B.V.] + {"hash":"8d1313bfd522fcf6","prefixes":{"":{"product":738}}}, // [Audience2Media Limited] + {"hash":"ec4f449051680a17","prefixes":{"":{"product":738}}}, // [Audience2Media Limited] + {"hash":"9badeeb4c9cff3b3","prefixes":{"":{"product":739}}}, // [HRB Digital LLC.] + {"hash":"e0e362bb66a6c5dd","prefixes":{"*":{"product":740}}}, // [!NOOB] + {"hash":"e1e1866cde6f23c7","prefixes":{"":{"product":163}}}, // [Tagtoo Tech Limited] + {"hash":"60a41acd40c494b4","prefixes":{"":{"product":163}}}, // [Tagtoo Tech Limited] + {"hash":"4dbc45b703241615","prefixes":{"":{"product":163}}}, // [Tagtoo Tech Limited] + {"hash":"d11722ff46ba063b","prefixes":{"":{"product":163}}}, // [Tagtoo Tech Limited] + {"hash":"706cd1e56e4ca9f0","prefixes":{"":{"product":163}}}, // [Tagtoo Tech Limited] + {"hash":"eabe2f56a564ea71","prefixes":{"":{"product":125}}}, // [Mocean mobile, Inc.] + {"hash":"9ab3f80172f06a67","prefixes":{"":{"product":125}}}, // [Mocean mobile, Inc.] + {"hash":"19c44c882811c0bd","prefixes":{"":{"product":741}}}, // [OSV online] + {"hash":"1f888bdc491d19c5","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"3259bacc0853f3ce","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"4a7fe8ed74a69bb1","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"72680bf564258b75","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"d25a0b5dd4617071","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"4e53e370f5c0d9c8","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"2585ec8519ec2a03","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"9ebfdd26621d46c7","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"083fd3b939cbd105","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"3fd28ace41da6736","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"3f5d8ed1881e0849","prefixes":{"":{"product":742}}}, // [Addroid™] + {"hash":"4f0f13d0fae2b262","prefixes":{"":{"product":743}}}, // [AdAccess] + {"hash":"5a40dbdf15bc0d0c","prefixes":{"":{"product":743}}}, // [AdAccess] + {"hash":"0eff5ff163f074f0","prefixes":{"":{"product":743}}}, // [AdAccess] + {"hash":"f5ce32de4ea8770e","prefixes":{"":{"product":743}}}, // [AdAccess] + {"hash":"8a367cea803ead3b","prefixes":{"":{"product":743}}}, // [AdAccess] + {"hash":"960b38f18a31bf7f","prefixes":{"":{"product":743}}}, // [AdAccess] + {"hash":"45b03c5f8881301a","prefixes":{"":{"product":744}}}, // [Yabuka Media, Inc.] + {"hash":"c2875e9f5741c896","prefixes":{"":{"product":744}}}, // [Yabuka Media, Inc.] + {"hash":"46751092d6f82d66","prefixes":{"":{"product":744}}}, // [Yabuka Media, Inc.] + {"hash":"379f0fda3a2142b3","prefixes":{"*":{"product":745}}}, // [Highwinds CDN] + {"hash":"5033b8e8796bc944","prefixes":{"":{"product":746}}}, // [Bridgewell Incorporated] + {"hash":"fcbb4f6f5d116bcf","prefixes":{"":{"product":746}}}, // [Bridgewell Incorporated] + {"hash":"6d68bbc023aff7a8","prefixes":{"":{"product":746}}}, // [Bridgewell Incorporated] + {"hash":"98a17e63899d6f0f","prefixes":{"":{"product":746}}}, // [Bridgewell Incorporated] + {"hash":"1cae38f50bbcbe1a","prefixes":{"":{"product":746}}}, // [Bridgewell Incorporated] + {"hash":"df13ce542b6c0e84","prefixes":{"":{"product":747}}}, // [Bidtheatre AB] + {"hash":"f9e2fc82f27f7b60","prefixes":{"":{"product":747}}}, // [Bidtheatre AB] + {"hash":"0d324dc8c79d8b3c","prefixes":{"":{"product":747}}}, // [Bidtheatre AB] + {"hash":"1d9569d0ee02dcee","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"73bd54b1be5e9df1","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"dae6130e1d796d90","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"53335dc721cbee0b","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"ae279eee4c5b4941","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"684276719f3b5728","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"b7376659acddae09","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"aa7f623da51e1603","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"6785583c4297f7c5","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"20138e51a97d179b","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"cc3a77dca011bc78","prefixes":{"":{"product":748}}}, // [AdMoment] + {"hash":"6d441f7565cc5de4","prefixes":{"":{"product":749}}}, // [UDG München GmbH] + {"hash":"f8c1c6199f46e722","prefixes":{"":{"product":749}}}, // [UDG München GmbH] + {"hash":"151a96d84e31a957","prefixes":{"":{"product":749}}}, // [UDG München GmbH] + {"hash":"5d34fdb93148d891","prefixes":{"":{"product":750}}}, // [Pixalate, Inc.] + {"hash":"6855407f17ce6aec","prefixes":{"":{"product":750}}}, // [Pixalate, Inc.] + {"hash":"b9f9a074ce5d4d07","prefixes":{"":{"product":750}}}, // [Pixalate, Inc.] + {"hash":"41ddcdd6ba9e42fe","prefixes":{"":{"product":750}}}, // [Pixalate, Inc.] + {"hash":"c72923bea1fa64ce","prefixes":{"":{"product":751}}}, // [InMobi Inc.] + {"hash":"9085e035c617887b","prefixes":{"":{"product":751}}}, // [InMobi Inc.] + {"hash":"0f7037ee4e476493","prefixes":{"":{"product":751}}}, // [InMobi Inc.] + {"hash":"bdfbe6247c640d80","prefixes":{"":{"product":752}}}, // [Crisp Media Inc.] + {"hash":"2cb723bdc82533b2","prefixes":{"":{"product":752}}}, // [Crisp Media Inc.] + {"hash":"be72bf3ef3e3bd14","prefixes":{"":{"product":752}}}, // [Crisp Media Inc.] + {"hash":"3af5f91fedb0e598","prefixes":{"":{"product":752}}}, // [Crisp Media Inc.] + {"hash":"9feb6dbe57a5ab9b","prefixes":{"":{"product":752}}}, // [Crisp Media Inc.] + {"hash":"1953012d699dc24e","prefixes":{"":{"product":753}}}, // [Choozle, Inc.] + {"hash":"560479fb0478e76e","prefixes":{"":{"product":753}}}, // [Choozle, Inc.] + {"hash":"fb3c191358de9e97","prefixes":{"*":{"product":754}}}, // [Tapad] + {"hash":"faa63745af097785","prefixes":{"*":{"product":755}}}, // [ReachLocal, Inc] + {"hash":"c6a0024a8fdbd244","prefixes":{"":{"product":756}}}, // [OpenX Ad Exchange] + {"hash":"1b09e9588e619f63","prefixes":{"":{"product":756}}}, // [OpenX Ad Exchange] + {"hash":"c923ecff86d0026f","prefixes":{"":{"product":756}}}, // [OpenX Ad Exchange] + {"hash":"0b38e9ab66f134b5","prefixes":{"":{"product":756}}}, // [OpenX Ad Exchange] + {"hash":"b07b0a6a32b39c67","prefixes":{"rtb-":{"product":756}}}, // [OpenX Ad Exchange] + {"hash":"a66019bd1d8d6523","prefixes":{"":{"product":757}}}, // [OpenX] + {"hash":"3461a8e14dfa7234","prefixes":{"*":{"product":756}}}, // [OpenX Ad Exchange] + {"hash":"50f406d696ea09e2","prefixes":{"*":{"product":756}}}, // [OpenX Ad Exchange] + {"hash":"28f15a404c22c5ae","prefixes":{"":{"product":758}}}, // [Placester, Inc.] + {"hash":"2eabf2a9cc47f6db","prefixes":{"":{"product":758}}}, // [Placester, Inc.] + {"hash":"04e56c0e39ffb379","prefixes":{"":{"product":759}}}, // [Spiceworks, Inc] + {"hash":"a342cde98acae2a3","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"816943ad229d59bd","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"9aeccecf18437372","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"e24006f3d8614cdf","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"2678adb8d16c1bd0","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"690b9d06ee7b618c","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"ba64173aac6883fa","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"3f334c96b9489b6f","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"4acb6ede5efaa0ce","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"f848368a90033112","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"fc58526ac2c34887","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"bcec64a939aa6248","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"2237ba07a36c2440","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"9b1bd3e45cc8d37b","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"f9447e97a352a2e8","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"4aa3c10d0fa51575","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"edb1b5b0a2ecd2b9","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"3eee53fed713d5b2","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"5562c787acd84d8d","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"27d45cd108fe99b4","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"06da026d99fd30c4","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"43e91c0e2d266107","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"2b8ee71c143b611a","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"31773affdbc2f93f","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"24f5edbbeab30fc6","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"b1275f1680c5e050","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"1f0e98fd24844df9","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"207973ef90437496","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"5e573612b3181547","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"5237a5b231dbf245","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"b4872bd493703ef0","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"7268f40680faccfd","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"f18cc00e2cfb170d","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"2be692602def45f7","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"5d15e875488cccd1","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"7da3afc36d1c6b59","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"e0767b1a03a64ce6","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"202d0d2b1168aeb8","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"5e070df0dbe151e6","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"e4337ec6681cbc61","prefixes":{"":{"product":760}}}, // [WapStart] + {"hash":"944f2609259bac7c","prefixes":{"":{"product":761}}}, // [Ambercrow (Epayments)] + {"hash":"8bcd6f943c02d066","prefixes":{"":{"product":761}}}, // [Ambercrow (Epayments)] + {"hash":"f0529ee08a090045","prefixes":{"":{"product":761}}}, // [Ambercrow (Epayments)] + {"hash":"f44423b0de62f78e","prefixes":{"":{"product":761}}}, // [Ambercrow (Epayments)] + {"hash":"00fb6871b70e1fd9","prefixes":{"":{"product":761}}}, // [Ambercrow (Epayments)] + {"hash":"588e459942f9f953","prefixes":{"":{"product":762}}}, // [Audiencevalue Pte Ltd] + {"hash":"2b94a7bb996b3a9c","prefixes":{"":{"product":762}}}, // [Audiencevalue Pte Ltd] + {"hash":"5796b5e246266e0c","prefixes":{"":{"product":763}}}, // [UNITED. Inc.] + {"hash":"aa1ee475645d94b7","prefixes":{"":{"product":763}}}, // [UNITED. Inc.] + {"hash":"716f227b7327feba","prefixes":{"":{"product":763}}}, // [UNITED. Inc.] + {"hash":"44c3a88f2dcdee6f","prefixes":{"":{"product":764}}}, // [United Bypass Tech] + {"hash":"0f010d866f5763fa","prefixes":{"":{"product":765}}}, // [SAS Naoplay] + {"hash":"6a978d7b8efc594d","prefixes":{"":{"product":765}}}, // [SAS Naoplay] + {"hash":"d3e10ee4fc900d26","prefixes":{"":{"product":765}}}, // [SAS Naoplay] + {"hash":"e83a4038aff02b6e","prefixes":{"":{"product":765}}}, // [SAS Naoplay] + {"hash":"acce217e5e09149a","prefixes":{"":{"product":765}}}, // [SAS Naoplay] + {"hash":"3a93fbc111c8603f","prefixes":{"":{"product":766}}}, // [CuriosityStream] + {"hash":"065426aaa5552ffe","prefixes":{"":{"product":767}}}, // [Alliance Internet (ntree)] + {"hash":"9a843715746291f3","prefixes":{"":{"product":767}}}, // [Alliance Internet (ntree)] + {"hash":"2d4ed5b7f85f950a","prefixes":{"n":{"product":767}}}, // [Alliance Internet (ntree)] + {"hash":"86f628e66b67be4d","prefixes":{"":{"product":768}}}, // [Centraltag] + {"hash":"419e8a03b3f16a8c","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"b9ee79ef4e8c15f5","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"46e3b67ae4549155","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"3064ccb85e72d2df","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"1b58e47cefd5e220","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"93b3f973b09c5513","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"607d4f0311eec7af","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"311502ef7317df7d","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"cdf3897e12242607","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"a5a0d71d2f6b631c","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"10f5415b8d24f2b0","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"c126382fcba55688","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"a92d9d96a9338816","prefixes":{"":{"product":769}}}, // [eTargeting] + {"hash":"74549e43319467f8","prefixes":{"":{"product":770}}}, // [Walmart Inc] + {"hash":"3d8d433ae123d216","prefixes":{"":{"product":770}}}, // [Walmart Inc] + {"hash":"6baa1cd3500dc43f","prefixes":{"":{"product":771}}}, // [Walmart.com] + {"hash":"12bc009e74ca3655","prefixes":{"":{"product":770}}}, // [Walmart Inc] + {"hash":"abb8ec1b9995e7db","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"a489082dd8c6d1b0","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"b8eaffd4f3298628","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"fa888dd08124e8f6","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"d33163e42a34ac6b","prefixes":{"":{"product":773}}}, // [Extreme Reach Digital (ER Digital)] + {"hash":"26271fd98d79e29f","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"f958e80b82fbeb15","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"0efc1cccd29292d2","prefixes":{"*":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"ec1cfeb7c8ad8e74","prefixes":{"*":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"06ea295dea974069","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"166f6d9041d2cffe","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"d209952bca4546fc","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"8977e19e27e82a31","prefixes":{"":{"product":772}}}, // [Extreme Reach, Inc.] + {"hash":"f276cc84c3030bf7","prefixes":{"":{"product":774}}}, // [Netflix] + {"hash":"cf97c9d0a75da1fb","prefixes":{"":{"product":774}}}, // [Netflix] + {"hash":"acf43321c04d641b","prefixes":{"":{"product":774}}}, // [Netflix] + {"hash":"bb55681ae13b2fa8","prefixes":{"":{"product":774}}}, // [Netflix] + {"hash":"2d5400efd94a66d0","prefixes":{"":{"product":774}}}, // [Netflix] + {"hash":"c4a2daa282a0af48","prefixes":{"":{"product":774}}}, // [Netflix] + {"hash":"670a8e12f579ee63","prefixes":{"":{"product":775}}}, // [Netflix, Inc.] + {"hash":"15b7e69ddffa3a8c","prefixes":{"":{"product":775}}}, // [Netflix, Inc.] + {"hash":"7b7add7c969d93fe","prefixes":{"*":{"product":776}}}, // [Scarab Personalized Ads] + {"hash":"4e1907aa0eb0ff71","prefixes":{"":{"product":776}}}, // [Scarab Personalized Ads] + {"hash":"a3315978597aa707","prefixes":{"":{"product":777}}}, // [CrowdMob Inc.] + {"hash":"452715cede41650c","prefixes":{"":{"product":778}}}, // [Gruvi Ltd.] + {"hash":"36ce2eb140f75f32","prefixes":{"":{"product":777}}}, // [CrowdMob Inc.] + {"hash":"ae6b13daf4bc714d","prefixes":{"":{"product":106}}}, // [PocketMath] + {"hash":"13696777dabedeae","prefixes":{"":{"product":779}}}, // [Macromill, Inc.] + {"hash":"9971928c88e3fa4e","prefixes":{"*":{"product":780}}}, // [Nielsen (Cross Platform Brand Effect [IAG/TVBE])] + {"hash":"65df6515d4615cea","prefixes":{"f":{"product":781},"vast-":{"product":781},"ivid-":{"product":781}}}, // [GetIntent] [GetIntent] [GetIntent] + {"hash":"b54db54df2c407f8","prefixes":{"":{"product":781}}}, // [GetIntent] + {"hash":"2258062bb1ab01dd","prefixes":{"":{"product":781}}}, // [GetIntent] + {"hash":"630338f7109ea305","prefixes":{"*":{"product":781}}}, // [GetIntent] + {"hash":"b3c93c6142991123","prefixes":{"":{"product":781}}}, // [GetIntent] + {"hash":"6ef8d315575de4c7","prefixes":{"":{"product":781}}}, // [GetIntent] + {"hash":"b7769b25a2035147","prefixes":{"vid-":{"product":781}}}, // [GetIntent] + {"hash":"2e2859be7da1ee82","prefixes":{"":{"product":782}}}, // [Belboon] + {"hash":"ff591094a6a13480","prefixes":{"*":{"product":783}}}, // [Ozone Media Solutions Pvt Ltd] + {"hash":"3bd09c8fad6ac0cd","prefixes":{"*":{"product":783}}}, // [Ozone Media Solutions Pvt Ltd] + {"hash":"41385cd812900dd3","prefixes":{"*":{"product":783}}}, // [Ozone Media Solutions Pvt Ltd] + {"hash":"78bdf43617c9f3ed","prefixes":{"":{"product":784}}}, // [Hindustan Times Mobile Solutions Limited] + {"hash":"f669a1035330e753","prefixes":{"*":{"product":783}}}, // [Ozone Media Solutions Pvt Ltd] + {"hash":"74c5a373ee6172a1","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"3762c6694e48ada4","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"885e91388dd960b6","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"f6a553969be0331b","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"48d6fc29b318593f","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"1e571da94b4242ba","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"e6770fd6222ae990","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"20b17ee9acc1afb9","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"c483854c67d0c950","prefixes":{"":{"product":785}}}, // [SoftLayer Technologies, Inc.] + {"hash":"5214f14e27a71d7f","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"61c576ffa8275ef3","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"c6af9ddc14757f86","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"86e9128d66cc0207","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"c71d0bd2e6f83f82","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"97be67a3b0baedf9","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"9622fe3e417564fb","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"a63ebe94fca61ed9","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"bbc1dd12d3cdb0b4","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"962da7f5c63ff169","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"94bb51394747c625","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"3ff8867b3d239d21","prefixes":{"":{"product":786}}}, // [GoldSpot Media] + {"hash":"a970a6e82a98c461","prefixes":{"":{"product":787}}}, // [Zeeto Media] + {"hash":"4c975affcaea9100","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"dfd86d0838962249","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"91f2b2338b35b7b2","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"3bec5ca280ea1b83","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"3d88758bd6e0003a","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"5c4e7b396a2ff6d6","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"a95488bd107fec32","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"aa8e84bd80448410","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"63a0cca4218135c9","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"91a1733ac5874a1c","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"5c8abc657915fd35","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"917cc91081fe5b07","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"a53545381bdd0cbf","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"4bc186645ccbf442","prefixes":{"":{"product":788}}}, // [DYNADMIC SAS] + {"hash":"3d6ddf04f9124237","prefixes":{"":{"product":789}}}, // [Yoc Mobile Advertising GmbH] + {"hash":"ec5fe7560209f819","prefixes":{"":{"product":359}}}, // [Quantcast Inc.] + {"hash":"5fe3e321ae6e4bf6","prefixes":{"":{"product":359}}}, // [Quantcast Inc.] + {"hash":"8be056c242dd9d72","prefixes":{"":{"product":359}}}, // [Quantcast Inc.] + {"hash":"f8ee5abf7ea1abc7","prefixes":{"":{"product":790}}}, // [Velti] + {"hash":"59f840ce07769698","prefixes":{"":{"product":790}}}, // [Velti] + {"hash":"e013843f461a8d4b","prefixes":{"":{"product":790}}}, // [Velti] + {"hash":"bfe3cf089cc18922","prefixes":{"":{"product":790}}}, // [Velti] + {"hash":"9bd20c9549f20865","prefixes":{"":{"product":791}}}, // [Lockon] + {"hash":"f24f331de69a707a","prefixes":{"":{"product":791}}}, // [Lockon] + {"hash":"566f866251bd714e","prefixes":{"":{"product":791}}}, // [Lockon] + {"hash":"4baa350d3cc68f76","prefixes":{"":{"product":792}}}, // [Recruit Co., Ltd. Communications] + {"hash":"9bc564a146df07e7","prefixes":{"":{"product":792}}}, // [Recruit Co., Ltd. Communications] + {"hash":"35c56cfd7e378fad","prefixes":{"":{"product":793}}}, // [RECRUIT Communications] + {"hash":"6b9aad5cb94eb206","prefixes":{"":{"product":793}}}, // [RECRUIT Communications] + {"hash":"671a4bc2e5c9113e","prefixes":{"":{"product":794}}}, // [Hatena Co., Ltd] + {"hash":"6d3fdce985f31bd4","prefixes":{"":{"product":795}}}, // [Trafmag] + {"hash":"1a311d346529c16b","prefixes":{"":{"product":795}}}, // [Trafmag] + {"hash":"cd26b0ae13c2440e","prefixes":{"":{"product":796}}}, // [Pagewoo] + {"hash":"06e9b4b216458951","prefixes":{"":{"product":797}}}, // [Adnet Media] + {"hash":"cc0e43344ef5e735","prefixes":{"":{"product":797}}}, // [Adnet Media] + {"hash":"2bff36578242b6b3","prefixes":{"":{"product":797}}}, // [Adnet Media] + {"hash":"6bcb758317103435","prefixes":{"":{"product":797}}}, // [Adnet Media] + {"hash":"5819339a3e48afa9","prefixes":{"":{"product":797}}}, // [Adnet Media] + {"hash":"4249805768b5eaf7","prefixes":{"":{"product":798}}}, // [Ligatus GmbH] + {"hash":"c5d5cf4beb6462cf","prefixes":{"":{"product":798}}}, // [Ligatus GmbH] + {"hash":"41b628c1a22b441e","prefixes":{"":{"product":798}}}, // [Ligatus GmbH] + {"hash":"88d751d3810273e3","prefixes":{"":{"product":798}}}, // [Ligatus GmbH] + {"hash":"41ec9f7d9d627e03","prefixes":{"":{"product":798}}}, // [Ligatus GmbH] + {"hash":"02eb3f21dd6df789","prefixes":{"":{"product":798}}}, // [Ligatus GmbH] + {"hash":"6b48a245a94ab12a","prefixes":{"":{"product":798}}}, // [Ligatus GmbH] + {"hash":"41d07cfbdb75024a","prefixes":{"":{"product":799}}}, // [Webtrekk GmbH] + {"hash":"be359c4fcc3f3a06","prefixes":{"":{"product":799}}}, // [Webtrekk GmbH] + {"hash":"6f767739019dd808","prefixes":{"":{"product":799}}}, // [Webtrekk GmbH] + {"hash":"f253ef05e042b018","prefixes":{"":{"product":800}}}, // [Kantar World Panel] + {"hash":"d01896485cd39018","prefixes":{"":{"product":800}}}, // [Kantar World Panel] + {"hash":"0e0818ed2e61ee95","prefixes":{"":{"product":801}}}, // [PubSquared LLC] + {"hash":"b05f21964e55a827","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"d333d9afb4898681","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"1639db8eb5956a46","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"5549a4c097f9b83d","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"1dbbc11fea26048b","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"098ed47c32246e74","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"48556c67606eaf5a","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"e9846bc3bb03a920","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"49353a0b9f2b5bb5","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"96bcc9c54b64eb5b","prefixes":{"":{"product":802}}}, // [Baidu.com Times Technology (Beijing) Co., Ltd] + {"hash":"f6e1c581da74f06c","prefixes":{"":{"product":803}}}, // [travel audience GmbH] + {"hash":"a00eb1259ded5bb1","prefixes":{"":{"product":803}}}, // [travel audience GmbH] + {"hash":"8026726dce357c58","prefixes":{"":{"product":804}}}, // [Krux Digital, Inc.] + {"hash":"95e17daf76b8a492","prefixes":{"":{"product":804}}}, // [Krux Digital, Inc.] + {"hash":"e2aedb8e35ba9ad0","prefixes":{"":{"product":804}}}, // [Krux Digital, Inc.] + {"hash":"7e954379b20a3955","prefixes":{"":{"product":804}}}, // [Krux Digital, Inc.] + {"hash":"bb2cd97362773074","prefixes":{"":{"product":804}}}, // [Krux Digital, Inc.] + {"hash":"5f545b8ac29b5d2d","prefixes":{"":{"product":805}}}, // [The Weather Channel] + {"hash":"188340d4038f111f","prefixes":{"":{"product":805}}}, // [The Weather Channel] + {"hash":"88ce1340354cf56f","prefixes":{"":{"product":805}}}, // [The Weather Channel] + {"hash":"6aa9e1031d5ce5ea","prefixes":{"":{"product":806}}}, // [Social Quantum] + {"hash":"924b0bbf98095055","prefixes":{"":{"product":806}}}, // [Social Quantum] + {"hash":"a8316ec9f577d677","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"4987a53ffae0e799","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"afafb80716e16e29","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"d0487d64f040dbfa","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"2c543734a4cbc5dd","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"067045f689cb5363","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"7ff291f0586a3dca","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"0a19a7d8484e10da","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"4614ce2d8cea29b5","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"c3164e283cfacd29","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"a1df629f546b9e31","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"6cbf35f72f18a347","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"1cb0768d21f55dd6","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"085272e0502f18c3","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"64b888da84877b17","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"c579af2db70b5cc0","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"306de8baaf32da18","prefixes":{"":{"product":807}}}, // [AdVentori SAS] + {"hash":"1e9f7eb550327800","prefixes":{"":{"product":808}}}, // [MindTake Research GmbH] + {"hash":"1c922be621564b9b","prefixes":{"":{"product":808}}}, // [MindTake Research GmbH] + {"hash":"b3f33aa4d02b6fb2","prefixes":{"":{"product":809}}}, // [Leger Marketing] + {"hash":"eead3245cdc680da","prefixes":{"":{"product":809}}}, // [Leger Marketing] + {"hash":"1239f4aeb30a0c6e","prefixes":{"":{"product":810}}}, // [BlisMedia Limited] + {"hash":"544a45aa905f9c6e","prefixes":{"":{"product":810}}}, // [BlisMedia Limited] + {"hash":"c5e4c8510e3cac80","prefixes":{"":{"product":811}}}, // [Double6] + {"hash":"d0fa41ada692b1b9","prefixes":{"":{"product":812}}}, // [TRADEADS INTERACTIVE] + {"hash":"3db0a72bcd75db25","prefixes":{"":{"product":813}}}, // [Epigrams, Inc.] + {"hash":"f42d4e28510cfcb3","prefixes":{"":{"product":813}}}, // [Epigrams, Inc.] + {"hash":"22d2d672aad6f400","prefixes":{"":{"product":814}}}, // [Activecore Inc.] + {"hash":"1ab5c06bec002a04","prefixes":{"":{"product":814}}}, // [Activecore Inc.] + {"hash":"c34010f1f0a2a2bd","prefixes":{"":{"product":814}}}, // [Activecore Inc.] + {"hash":"b274e3909d6409ff","prefixes":{"":{"product":815}}}, // [Activecore,Inc.] + {"hash":"1bdc7aff17b34632","prefixes":{"":{"product":815}}}, // [Activecore,Inc.] + {"hash":"a5f385ad8794bbf7","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"7fbf5ab845c319c3","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"636714c3598df906","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"9e6785892cd34bdd","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"047573e0075b371a","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"30468687bd7540d7","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"d38c33250529e4cf","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"311b59743017ebda","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"fceae2ba76fa56d8","prefixes":{"":{"product":816}}}, // [ADCASH] + {"hash":"e3e53a304101f0b8","prefixes":{"*":{"product":817}}}, // [Videoplaza] + {"hash":"66ae7f0627345a0e","prefixes":{"":{"product":818}}}, // [Targetix LLC] + {"hash":"f2825950bbc13084","prefixes":{"":{"product":818}}}, // [Targetix LLC] + {"hash":"5844bebbb6ebc2c8","prefixes":{"":{"product":818}}}, // [Targetix LLC] + {"hash":"af1d9e1f4d4a29ee","prefixes":{"":{"product":818}}}, // [Targetix LLC] + {"hash":"eabbec12e479563f","prefixes":{"":{"product":818}}}, // [Targetix LLC] + {"hash":"063e5c59c898b6b2","prefixes":{"":{"product":818}}}, // [Targetix LLC] + {"hash":"768e497f05eb9210","prefixes":{"":{"product":819}}}, // [Adriver LLC] + {"hash":"a3e9e1ffdc9ef6f3","prefixes":{"":{"product":820}}}, // [Beso] + {"hash":"dc3b4a3bb1a98472","prefixes":{"*":{"product":821}}}, // [Voopter.com] + {"hash":"608c4fc0e1249087","prefixes":{"":{"product":822}}}, // [Hostbasket] + {"hash":"ec50bd1f8e55703a","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"75f08f4a618e4c29","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"5e28845a397448ed","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"43baf1a2f5c604eb","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"9113c91f7c97eda8","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"6e46faf33af4223b","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"b8629ee4f4882cf4","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"c580bf68b5705ecc","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"30c1badbfab295cc","prefixes":{"":{"product":823}}}, // [Rollad] + {"hash":"07206ed226f7d186","prefixes":{"":{"product":824}}}, // [hdtMedia] + {"hash":"af031421c35c12bb","prefixes":{"":{"product":824}}}, // [hdtMedia] + {"hash":"c1e9215522e84feb","prefixes":{"":{"product":824}}}, // [hdtMedia] + {"hash":"f99b1e1004ffe36c","prefixes":{"":{"product":824}}}, // [hdtMedia] + {"hash":"cc5aed10326009cd","prefixes":{"":{"product":824}}}, // [hdtMedia] + {"hash":"a59bac0d7936d725","prefixes":{"":{"product":824}}}, // [hdtMedia] + {"hash":"85c92ca45eb6137a","prefixes":{"":{"product":824}}}, // [hdtMedia] + {"hash":"ecfa86f147eea591","prefixes":{"":{"product":824}}}, // [hdtMedia] + {"hash":"89c418c61cc46dbf","prefixes":{"":{"product":825}}}, // [DXP Media] + {"hash":"36712fa880f9f4fd","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"77af178b53829cee","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"19e3098ce56eae94","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"605d50a0c132e0b5","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"9cb69b24762928be","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"7ad4195e3856bc8b","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"a1f419f69f83a3cb","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"a8184454cf33a5e3","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"8c22c5755c763066","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"862c4454fe07929d","prefixes":{"":{"product":826}}}, // [ebuilders BV] + {"hash":"ac0afa5e86463dcf","prefixes":{"":{"product":827}}}, // [COADVERTISE] + {"hash":"049d375ddb03cca6","prefixes":{"":{"product":827}}}, // [COADVERTISE] + {"hash":"2ce968c3c01dac2b","prefixes":{"":{"product":828}}}, // [Blizzard] + {"hash":"6e4734c6df5289ef","prefixes":{"":{"product":828}}}, // [Blizzard] + {"hash":"9fe6f25a55c854ab","prefixes":{"":{"product":829}}}, // [Adgibbon] + {"hash":"008bfe35bcbd016d","prefixes":{"":{"product":829}}}, // [Adgibbon] + {"hash":"084705198bf3893b","prefixes":{"":{"product":829}}}, // [Adgibbon] + {"hash":"a1b65ade9cc861cb","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"42f9c0b081bf4152","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"ea90626af94135dd","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"ae4cfcc8964ff838","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"29ed865d96c2134b","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"f0ad420cd06e916d","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"02fa3036dfdd1f55","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"2e1689e37ca87292","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"e0f93c05dad3c3c6","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"3a2e2804dc9fbb61","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"deed6c9248e53552","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"c4597285474e8048","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"e127ffe380012cba","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"2f548a00d118d32e","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"015756b75eb89403","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"35d2e95d1522b0c5","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"a43e409504b254d3","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"5a4147bb67b5b23c","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"9866a49aaacb720e","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"f101934b33880c8d","prefixes":{"":{"product":830}}}, // [Wider Planet, Inc.] + {"hash":"5fc0aea809357158","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"ed7b8006e50dc195","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"fe112ea448efa84b","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"807a0ad2ef7d844d","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"24daf2e5c1e30aeb","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"9091656f8979f5f0","prefixes":{"":{"product":107}}}, // [Kpsule] + {"hash":"bb5ef61e19944a06","prefixes":{"":{"product":831}}}, // [ResponsiveAds, Inc] + {"hash":"ef4fefbe73da4a59","prefixes":{"":{"product":831}}}, // [ResponsiveAds, Inc] + {"hash":"e78ef2faad563e6b","prefixes":{"":{"product":831}}}, // [ResponsiveAds, Inc] + {"hash":"20e99abfa9e5de7c","prefixes":{"":{"product":831}}}, // [ResponsiveAds, Inc] + {"hash":"0dedc650dbf5d391","prefixes":{"":{"product":831}}}, // [ResponsiveAds, Inc] + {"hash":"5bdaec9fd3ff4dac","prefixes":{"":{"product":832}}}, // [Duepuntozero Research SRL] + {"hash":"318bfe954294ea40","prefixes":{"":{"product":833}}}, // [AdMovate, Inc.] + {"hash":"e24259ffed4d244f","prefixes":{"":{"product":833}}}, // [AdMovate, Inc.] + {"hash":"c39b0621d7a8b616","prefixes":{"":{"product":834}}}, // [go.pl] + {"hash":"29c359aefdd48c40","prefixes":{"":{"product":834}}}, // [go.pl] + {"hash":"6ecf3c2aeb5aec2b","prefixes":{"":{"product":834}}}, // [go.pl] + {"hash":"eea1a3478557a7a6","prefixes":{"":{"product":834}}}, // [go.pl] + {"hash":"0b0e365de9c89436","prefixes":{"":{"product":834}}}, // [go.pl] + {"hash":"875d9d8b11a6dff7","prefixes":{"":{"product":834}}}, // [go.pl] + {"hash":"5edb8f1a6337d5d0","prefixes":{"":{"product":834}}}, // [go.pl] + {"hash":"435808ca4dd1dce5","prefixes":{"":{"product":834}}}, // [go.pl] + {"hash":"fd43d007391147de","prefixes":{"":{"product":710}}}, // [MASSMOTIONMEDIA SARL] + {"hash":"633236a94b694dd7","prefixes":{"":{"product":710}}}, // [MASSMOTIONMEDIA SARL] + {"hash":"a076e91e809efc28","prefixes":{"*":{"product":835}}}, // [AppLovin Corporation] + {"hash":"14c26da0caac0796","prefixes":{"":{"product":836}}}, // [twentysix ltd] + {"hash":"1f8d7bcebd64d2c9","prefixes":{"":{"product":15}}}, // [Tamome] + {"hash":"41f4c0bf12c8f029","prefixes":{"":{"product":336}}}, // [EuroAds Group A/S] + {"hash":"e674b14a061bb7d7","prefixes":{"":{"product":336}}}, // [EuroAds Group A/S] + {"hash":"f485e2bb528d3147","prefixes":{"":{"product":336}}}, // [EuroAds Group A/S] + {"hash":"82c09e315af7d70c","prefixes":{"":{"product":336}}}, // [EuroAds Group A/S] + {"hash":"fc2b985669148068","prefixes":{"":{"product":336}}}, // [EuroAds Group A/S] + {"hash":"cf9a9377bc72c2e4","prefixes":{"":{"product":336}}}, // [EuroAds Group A/S] + {"hash":"d550149a2d5cfdde","prefixes":{"":{"product":336}}}, // [EuroAds Group A/S] + {"hash":"d51c2c24cd771a6a","prefixes":{"":{"product":837}}}, // [Epsilon International SA] + {"hash":"d34cf98578200c93","prefixes":{"":{"product":838}}}, // [Zebestof] + {"hash":"44c2302577bd8e84","prefixes":{"":{"product":838}}}, // [Zebestof] + {"hash":"52b835c5657206c1","prefixes":{"":{"product":838}}}, // [Zebestof] + {"hash":"caa0c94cf57e2295","prefixes":{"":{"product":838}}}, // [Zebestof] + {"hash":"02e54ec6b0aca548","prefixes":{"":{"product":839}}}, // [SOL UTD Benelux BV] + {"hash":"1cf1a7f5c323d6e0","prefixes":{"":{"product":840}}}, // [M,P,NEWMEDIA, GmbH] + {"hash":"079a647886a5afa5","prefixes":{"":{"product":840}}}, // [M,P,NEWMEDIA, GmbH] + {"hash":"ec320d28ebdfac8c","prefixes":{"":{"product":840}}}, // [M,P,NEWMEDIA, GmbH] + {"hash":"e59449da7e642b45","prefixes":{"":{"product":840}}}, // [M,P,NEWMEDIA, GmbH] + {"hash":"1d61751db94dfc15","prefixes":{"":{"product":840}}}, // [M,P,NEWMEDIA, GmbH] + {"hash":"43f7746544162324","prefixes":{"":{"product":840}}}, // [M,P,NEWMEDIA, GmbH] + {"hash":"4f80c8700c459a79","prefixes":{"":{"product":840}}}, // [M,P,NEWMEDIA, GmbH] + {"hash":"23f62ba113f0fa4f","prefixes":{"":{"product":841}}}, // [MediaCrossing Inc.] + {"hash":"62fc0fc3102b918b","prefixes":{"":{"product":842}}}, // [MBR Targeting Gmbh] + {"hash":"0066f0743ade259b","prefixes":{"":{"product":842}}}, // [MBR Targeting Gmbh] + {"hash":"4288d021c49b7e1b","prefixes":{"":{"product":842}}}, // [MBR Targeting Gmbh] + {"hash":"6620a58a1f4cd480","prefixes":{"":{"product":843}}}, // [VivaKi] + {"hash":"47db57aade94670f","prefixes":{"":{"product":843}}}, // [VivaKi] + {"hash":"2924b77c1d7eab3b","prefixes":{"":{"product":843}}}, // [VivaKi] + {"hash":"d2143dbf1699f4fa","prefixes":{"":{"product":844}}}, // [Affiliate Window] + {"hash":"13c2a94c46886705","prefixes":{"":{"product":844}}}, // [Affiliate Window] + {"hash":"c0d30b023dc9139f","prefixes":{"*":{"product":845}}}, // [TubeMogul Inc. (AdWords/YouTube)] + {"hash":"108d788939e4b7a4","prefixes":{"":{"product":825}}}, // [DXP Media] + {"hash":"6fdc121917e19eb9","prefixes":{"":{"product":825}}}, // [DXP Media] + {"hash":"5262a88cfa363303","prefixes":{"":{"product":825}}}, // [DXP Media] + {"hash":"b597af924fe5ff97","prefixes":{"":{"product":825}}}, // [DXP Media] + {"hash":"dca60f44a70d5d38","prefixes":{"":{"product":825}}}, // [DXP Media] + {"hash":"c44308d5c2e3bb8a","prefixes":{"":{"product":825}}}, // [DXP Media] + {"hash":"90f5b971c961816f","prefixes":{"":{"product":825}}}, // [DXP Media] + {"hash":"5d64c1152c075739","prefixes":{"":{"product":846}}}, // [Way2traffic Polska S.A.] + {"hash":"01b13121d2ce9699","prefixes":{"":{"product":846}}}, // [Way2traffic Polska S.A.] + {"hash":"f48791a2746e2a93","prefixes":{"":{"product":847}}}, // [TNS Sifo AB] + {"hash":"935b8079b74f344c","prefixes":{"":{"product":847}}}, // [TNS Sifo AB] + {"hash":"24e5b8b342d1b1fe","prefixes":{"":{"product":848}}}, // [3xchange/Hunkal] + {"hash":"ad8c8bc165611bf2","prefixes":{"":{"product":848}}}, // [3xchange/Hunkal] + {"hash":"b1b5bdd82d143e90","prefixes":{"":{"product":849}}}, // [Emerse Sverige AB] + {"hash":"aeddaa071ba3b313","prefixes":{"":{"product":849}}}, // [Emerse Sverige AB] + {"hash":"474802ea42555e39","prefixes":{"":{"product":849}}}, // [Emerse Sverige AB] + {"hash":"b28e42ea18df1c00","prefixes":{"":{"product":849}}}, // [Emerse Sverige AB] + {"hash":"826635800b12eb0c","prefixes":{"":{"product":849}}}, // [Emerse Sverige AB] + {"hash":"22c10728f555913e","prefixes":{"":{"product":849}}}, // [Emerse Sverige AB] + {"hash":"0b7f364ef0beed45","prefixes":{"":{"product":849}}}, // [Emerse Sverige AB] + {"hash":"8d31e79105c5380a","prefixes":{"":{"product":850}}}, // [Sokno Media] + {"hash":"037264ef7d8383f6","prefixes":{"":{"product":850}}}, // [Sokno Media] + {"hash":"ff6372e6621d88ba","prefixes":{"":{"product":851}}}, // [Blackheart, a division of Hot Topic, Inc.] + {"hash":"37742852c2a4ccc8","prefixes":{"":{"product":852}}}, // [Torrid] + {"hash":"a86786ce90b23e3f","prefixes":{"":{"product":853}}}, // [Hot Topic, Inc.] + {"hash":"93e1c22a4427a32f","prefixes":{"":{"product":854}}}, // [WebHue LLC] + {"hash":"fca0a42aad108345","prefixes":{"static":{"product":855},"img":{"product":855},"log":{"product":855}}}, // [Content to Emotion] [Content to Emotion] [Content to Emotion] + {"hash":"15948cae619c2e58","prefixes":{"":{"product":855}}}, // [Content to Emotion] + {"hash":"db2a81c15837ddbd","prefixes":{"":{"product":856}}}, // [Adman Interactive SL] + {"hash":"accf1565984e2899","prefixes":{"":{"product":856}}}, // [Adman Interactive SL] + {"hash":"45351e5c4862b2dd","prefixes":{"":{"product":856}}}, // [Adman Interactive SL] + {"hash":"ba3196c50e6c1be4","prefixes":{"":{"product":856}}}, // [Adman Interactive SL] + {"hash":"eab724fbba4f83d6","prefixes":{"":{"product":856}}}, // [Adman Interactive SL] + {"hash":"0edc8e7b8a8fe3ee","prefixes":{"":{"product":857}}}, // [AudienceFUEL, Inc.] + {"hash":"dfa423315ab48813","prefixes":{"":{"product":858}}}, // [TapCommerce LLC] + {"hash":"777b0f4301aa1643","prefixes":{"":{"product":858}}}, // [TapCommerce LLC] + {"hash":"294cba0ae232447c","prefixes":{"":{"product":859}}}, // [AdTheorent, Inc.] + {"hash":"ba7ad5da6db014c7","prefixes":{"":{"product":860}}}, // [AdTheorent, Inc] + {"hash":"09bfc8a7f1c7896f","prefixes":{"":{"product":859}}}, // [AdTheorent, Inc.] + {"hash":"362136e847a35f7e","prefixes":{"":{"product":859}}}, // [AdTheorent, Inc.] + {"hash":"5492d77bb75e6380","prefixes":{"":{"product":861}}}, // [Barometric] + {"hash":"94bb816c4a375220","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"f10e1a418576d219","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"245fc951b0a9ce59","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"babdceabca3ae2b8","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"c6474114ede3195a","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"26faef64bd072723","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"0a19b1f547a3a935","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"b4ef56642916cc60","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"c1fc9d245fa85b02","prefixes":{"":{"product":862}}}, // [CrossInstall, Inc] + {"hash":"0e3ddb4868b0e99f","prefixes":{"":{"product":863}}}, // [Theorem Inc.] + {"hash":"976d4ea6507dfd2e","prefixes":{"":{"product":863}}}, // [Theorem Inc.] + {"hash":"70a3fe87d3bbbae9","prefixes":{"":{"product":863}}}, // [Theorem Inc.] + {"hash":"1cf1ec002a07d6a4","prefixes":{"":{"product":864}}}, // [KeyVersion] + {"hash":"b2c480f3bc7b03b6","prefixes":{"":{"product":864}}}, // [KeyVersion] + {"hash":"c94dbc0a3ffc6eda","prefixes":{"":{"product":865}}}, // [Ancestry] + {"hash":"04fb46af993df556","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"b9adf25b55c4d0f3","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"1c1211c84dbee054","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"575fafb094c71d93","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"f2b020b3d3861db2","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"609d13c5cd3771c8","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"aeae313934102cfe","prefixes":{"":{"product":867}}}, // [Shanghai Lijing Advertising Co., Ltd] + {"hash":"f8721bcf4d9f6196","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"09e5f7c9f30f4fd6","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"a1ce57d387427206","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"bb4b6377666172c4","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"d6a6a6cd062bc76e","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"0313ce8a9851d837","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"6862450d2314df40","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"0a34c9f6f0cf9556","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"8e311fce80eccafd","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"296fdad7e8336d5a","prefixes":{"":{"product":866}}}, // [Shanghai Lijing Advertising Co., Ltd.] + {"hash":"27c0e177312a3c0f","prefixes":{"":{"product":108}}}, // [Immedium, Inc.] + {"hash":"fc3c7114081863bd","prefixes":{"":{"product":108}}}, // [Immedium, Inc.] + {"hash":"811582f5df157ff9","prefixes":{"":{"product":868}}}, // [KissNoFrog.com] + {"hash":"284d8a4f9b174bab","prefixes":{"":{"product":869}}}, // [DigiEQ] + {"hash":"aa0e7b7a1fe279dc","prefixes":{"":{"product":869}}}, // [DigiEQ] + {"hash":"1f551f934400f81e","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"1c3b94cabbbc5b8c","prefixes":{"":{"product":86}}}, // [MediaMath Inc.] + {"hash":"505b194aeebd6baf","prefixes":{"":{"product":870}}}, // [Mobile Professinals BV] + {"hash":"8f2e12e9677e4d41","prefixes":{"":{"product":870}}}, // [Mobile Professinals BV] + {"hash":"e2a3fe5e482432e8","prefixes":{"":{"product":870}}}, // [Mobile Professinals BV] + {"hash":"7d03cd1b92c167a8","prefixes":{"":{"product":870}}}, // [Mobile Professinals BV] + {"hash":"532402cd3ab13402","prefixes":{"":{"product":870}}}, // [Mobile Professinals BV] + {"hash":"48e577ba3e61c748","prefixes":{"":{"product":870}}}, // [Mobile Professinals BV] + {"hash":"1480a6580cb81dde","prefixes":{"":{"product":870}}}, // [Mobile Professinals BV] + {"hash":"b1ec46dd2c4c824e","prefixes":{"*":{"product":109}}}, // [Fractional Media, LLC] + {"hash":"6c6240697771befd","prefixes":{"*":{"product":109}}}, // [Fractional Media, LLC] + {"hash":"f2e16038c0d83b85","prefixes":{"":{"product":871}}}, // [Decisive, Inc.] + {"hash":"654da4ed85d77fb9","prefixes":{"":{"product":871}}}, // [Decisive, Inc.] + {"hash":"c9e2ea0486216534","prefixes":{"":{"product":871}}}, // [Decisive, Inc.] + {"hash":"546a2acb9b2363f2","prefixes":{"*":{"product":872}}}, // [Microsoft Security Essentials] + {"hash":"9e4f5b2f200fbb25","prefixes":{"":{"product":873}}}, // [Tumi, Inc. US] + {"hash":"4378c60894195c4f","prefixes":{"":{"product":874}}}, // [Tumi, Inc. UK] + {"hash":"61e82c5e74c5ce7c","prefixes":{"":{"product":875}}}, // [Tumi, Inc. DE] + {"hash":"712f010eb9792fa4","prefixes":{"":{"product":876}}}, // [Nanigans, Inc] + {"hash":"589eceaef79619dd","prefixes":{"":{"product":876}}}, // [Nanigans, Inc] + {"hash":"1d66f17c949dbaee","prefixes":{"":{"product":876}}}, // [Nanigans, Inc] + {"hash":"6dbd7b9480163af0","prefixes":{"":{"product":876}}}, // [Nanigans, Inc] + {"hash":"41d56e0870364212","prefixes":{"":{"product":139}}}, // [Brandscreen Inc.] + {"hash":"2d701495cf72af5b","prefixes":{"*":{"product":877}}}, // [AdSniper LLC] + {"hash":"0bd14e8d5a6b2f2d","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"9b504cc586da72e5","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"a89d4cb4490b4286","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"dce925353cbc1dbe","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"ff8d26679ce7dafc","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"11db2fec76a76647","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"00a09e8788ee6f2b","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"709d79bfe214aa48","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"7a37fa5ff1a37799","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"42a24e1ab1c2d947","prefixes":{"":{"product":877}}}, // [AdSniper LLC] + {"hash":"63f4e4d315bc085b","prefixes":{"":{"product":878}}}, // [Fabric Worldwide Inc] + {"hash":"b9c11e67d7eb2963","prefixes":{"":{"product":878}}}, // [Fabric Worldwide Inc] + {"hash":"e3882e6bc0c2e382","prefixes":{"":{"product":879}}}, // [Vorwerk Deutschland Stiftung & Co. KG] + {"hash":"944d86d40b9ae089","prefixes":{"":{"product":879}}}, // [Vorwerk Deutschland Stiftung & Co. KG] + {"hash":"ebd456010571b5ae","prefixes":{"":{"product":879}}}, // [Vorwerk Deutschland Stiftung & Co. KG] + {"hash":"4456fa769be90ba2","prefixes":{"":{"product":65}}}, // [Active Agent] + {"hash":"84d65f5782dd26d6","prefixes":{"":{"product":880}}}, // [Chico Distribution Services, LLC] + {"hash":"a6185f46e2849f58","prefixes":{"":{"product":881}}}, // [12Mnkys GmbH] + {"hash":"bf874935c6972629","prefixes":{"":{"product":881}}}, // [12Mnkys GmbH] + {"hash":"094b301a712414f2","prefixes":{"":{"product":881}}}, // [12Mnkys GmbH] + {"hash":"56ee8d67408b2316","prefixes":{"":{"product":881}}}, // [12Mnkys GmbH] + {"hash":"d62d9c20c47ec863","prefixes":{"":{"product":881}}}, // [12Mnkys GmbH] + {"hash":"bb716a8269a2e79c","prefixes":{"":{"product":882}}}, // [Spacyz, Inc.] + {"hash":"5a6ff8a620bb8de1","prefixes":{"":{"product":882}}}, // [Spacyz, Inc.] + {"hash":"61cf0a8426083e54","prefixes":{"":{"product":882}}}, // [Spacyz, Inc.] + {"hash":"4cf4aa4b0e37fdfd","prefixes":{"":{"product":719}}}, // [E-Plus Mobilfunk GmbH & Co. KG] + {"hash":"745acee8f5973f56","prefixes":{"":{"product":719}}}, // [E-Plus Mobilfunk GmbH & Co. KG] + {"hash":"9a834a1cc05cb878","prefixes":{"":{"product":883}}}, // [MBuy, Inc.] + {"hash":"73cd161c10ebcf69","prefixes":{"":{"product":884}}}, // [FullSpeed Inc.] + {"hash":"75c72b5cc4c40c16","prefixes":{"":{"product":884}}}, // [FullSpeed Inc.] + {"hash":"33c5c398130ddfec","prefixes":{"":{"product":884}}}, // [FullSpeed Inc.] + {"hash":"1c0653651245a014","prefixes":{"":{"product":884}}}, // [FullSpeed Inc.] + {"hash":"271d057b15f67166","prefixes":{"":{"product":884}}}, // [FullSpeed Inc.] + {"hash":"e9723a229b8b9eeb","prefixes":{"":{"product":884}}}, // [FullSpeed Inc.] + {"hash":"8e3c99c5cc10c217","prefixes":{"":{"product":355}}}, // [New Allyes Information Technology (winmax)] + {"hash":"07141bfb96faa93c","prefixes":{"":{"product":885}}}, // [Madhouse Co. Limited (OptiMad)] + {"hash":"1d7294abbaa26e26","prefixes":{"":{"product":885}}}, // [Madhouse Co. Limited (OptiMad)] + {"hash":"6872272e097d2cd9","prefixes":{"":{"product":885}}}, // [Madhouse Co. Limited (OptiMad)] + {"hash":"66ec8aa560cf2231","prefixes":{"":{"product":885}}}, // [Madhouse Co. Limited (OptiMad)] + {"hash":"17cd9dcba702b7e6","prefixes":{"":{"product":885}}}, // [Madhouse Co. Limited (OptiMad)] + {"hash":"2347e9e88f6ead88","prefixes":{"":{"product":886}}}, // [Nano Interactive / Audiencemanager] + {"hash":"7f19740ff86dc642","prefixes":{"":{"product":886}}}, // [Nano Interactive / Audiencemanager] + {"hash":"18f553670e23734b","prefixes":{"":{"product":886}}}, // [Nano Interactive / Audiencemanager] + {"hash":"ca862d199926ad1c","prefixes":{"":{"product":886}}}, // [Nano Interactive / Audiencemanager] + {"hash":"2238776218564026","prefixes":{"":{"product":886}}}, // [Nano Interactive / Audiencemanager] + {"hash":"d7e222c8d7ba68d8","prefixes":{"*":{"product":887}}}, // [Youtube, LLC] + {"hash":"edff9009064b3fa4","prefixes":{"*":{"product":887}}}, // [Youtube, LLC] + {"hash":"0d60795f6997311e","prefixes":{"":{"product":888}}}, // [Omnibus co. Ltd] + {"hash":"f32008e13be0f96e","prefixes":{"":{"product":888}}}, // [Omnibus co. Ltd] + {"hash":"dd5533aa2d49cc10","prefixes":{"":{"product":889}}}, // [Omnibus co. Ltd.] + {"hash":"3bcd500b684d5142","prefixes":{"":{"product":888}}}, // [Omnibus co. Ltd] + {"hash":"14eef63a2889c8da","prefixes":{"":{"product":888}}}, // [Omnibus co. Ltd] + {"hash":"53f31e427551538d","prefixes":{"":{"product":888}}}, // [Omnibus co. Ltd] + {"hash":"5296819de63f3444","prefixes":{"":{"product":888}}}, // [Omnibus co. Ltd] + {"hash":"d8389fea989da8d7","prefixes":{"":{"product":41}}}, // [Airpush, Inc.] + {"hash":"e251b17a002b679b","prefixes":{"":{"product":890}}}, // [Education Management Corporation] + {"hash":"ec35188a522b7db9","prefixes":{"*":{"product":891}}}, // [Screen6 (s6.io)] + {"hash":"8ee1cf907bbca914","prefixes":{"":{"product":892}}}, // [LINK Marketing Services AG] + {"hash":"18a5decf96e8c80d","prefixes":{"":{"product":893}}}, // [TiqIQ] + {"hash":"8927e5364d1bf1d1","prefixes":{"":{"product":893}}}, // [TiqIQ] + {"hash":"86292c524ac79685","prefixes":{"":{"product":893}}}, // [TiqIQ] + {"hash":"52562df3f7208c8f","prefixes":{"":{"product":893}}}, // [TiqIQ] + {"hash":"a7d6fe32ab078e86","prefixes":{"":{"product":893}}}, // [TiqIQ] + {"hash":"497857c899894a0f","prefixes":{"":{"product":893}}}, // [TiqIQ] + {"hash":"0d8c5ad133d4c83e","prefixes":{"":{"product":894}}}, // [Ibibo Group Private Limited] + {"hash":"a8c10e41d2d4b31a","prefixes":{"":{"product":894}}}, // [Ibibo Group Private Limited] + {"hash":"9699d29520d1b9b8","prefixes":{"":{"product":894}}}, // [Ibibo Group Private Limited] + {"hash":"103308c15ca4c459","prefixes":{"":{"product":889}}}, // [Omnibus co. Ltd.] + {"hash":"d7a1b3fc5e1252ce","prefixes":{"":{"product":889}}}, // [Omnibus co. Ltd.] + {"hash":"ee40bf7c9bae2361","prefixes":{"":{"product":895}}}, // [justAd TV LTD] + {"hash":"8755d187f8ffb5e9","prefixes":{"":{"product":895}}}, // [justAd TV LTD] + {"hash":"1bdf54cf79d18915","prefixes":{"":{"product":895}}}, // [justAd TV LTD] + {"hash":"ed61950d43d99717","prefixes":{"":{"product":895}}}, // [justAd TV LTD] + {"hash":"aff928fc58637cb3","prefixes":{"":{"product":896}}}, // [Appier Inc.] + {"hash":"797f76218537ad5d","prefixes":{"*":{"product":896}}}, // [Appier Inc.] + {"hash":"6bdcdb03ff6173af","prefixes":{"*":{"product":896}}}, // [Appier Inc.] + {"hash":"6a3dec8b5a467987","prefixes":{"*":{"product":896}}}, // [Appier Inc.] + {"hash":"a3d5f58bbde56b12","prefixes":{"":{"product":896}}}, // [Appier Inc.] + {"hash":"7d0adb9089ff21ca","prefixes":{"":{"product":896}}}, // [Appier Inc.] + {"hash":"c241f36263ea7c84","prefixes":{"":{"product":897}}}, // [Kate Spade Saturday] + {"hash":"79dd595e79611766","prefixes":{"":{"product":898}}}, // [Teufel GmbH] + {"hash":"df829b8e840c11e1","prefixes":{"":{"product":898}}}, // [Teufel GmbH] + {"hash":"c3004b48539587e5","prefixes":{"":{"product":898}}}, // [Teufel GmbH] + {"hash":"08eb365a3723ee17","prefixes":{"":{"product":898}}}, // [Teufel GmbH] + {"hash":"71bc88c0bd0dc170","prefixes":{"":{"product":898}}}, // [Teufel GmbH] + {"hash":"fb208e41a565430a","prefixes":{"":{"product":899}}}, // [Aitarget LLC] + {"hash":"6121d39232612f56","prefixes":{"":{"product":899}}}, // [Aitarget LLC] + {"hash":"0ef54a01a72fdbaf","prefixes":{"*":{"product":676}}}, // [Rackspace, US Inc.] + {"hash":"532febae5cf9194f","prefixes":{"":{"product":900}}}, // [Engage Lab Ltd.] + {"hash":"fbfff8f12503c208","prefixes":{"":{"product":901}}}, // [Signal Digital, Inc dba Signal] + {"hash":"14afd1fc2da14d71","prefixes":{"":{"product":902}}}, // [Motrixi Media Group LLC] + {"hash":"aa674bd59dee4716","prefixes":{"":{"product":902}}}, // [Motrixi Media Group LLC] + {"hash":"7fd4fa8a06589cab","prefixes":{"":{"product":902}}}, // [Motrixi Media Group LLC] + {"hash":"56f94b2620357eb5","prefixes":{"":{"product":902}}}, // [Motrixi Media Group LLC] + {"hash":"32ddc8b6874f030b","prefixes":{"":{"product":903}}}, // [WHITE HOUSE | BLACK MARKET, Chico Brands, Inc.] + {"hash":"fea26de6b70aafb9","prefixes":{"":{"product":904}}}, // [Liftoff Mobile, Inc.] + {"hash":"4687be4922e9d22e","prefixes":{"":{"product":905}}}, // [etracker GmbH] + {"hash":"b41f235329fba677","prefixes":{"*":{"product":906}}}, // [MezzoMedia] + {"hash":"3362cc82ba37c826","prefixes":{"":{"product":906}}}, // [MezzoMedia] + {"hash":"7c5cbde65c1f0a61","prefixes":{"":{"product":907}}}, // [Eyeota Limited] + {"hash":"d85ebb0a3c0bdef4","prefixes":{"":{"product":908}}}, // [Beijing PageChoice Network Technology co., Ltd.] + {"hash":"52d134e034c55e1d","prefixes":{"":{"product":908}}}, // [Beijing PageChoice Network Technology co., Ltd.] + {"hash":"9d9805e510965156","prefixes":{"":{"product":908}}}, // [Beijing PageChoice Network Technology co., Ltd.] + {"hash":"5ed6426649a5142e","prefixes":{"":{"product":908}}}, // [Beijing PageChoice Network Technology co., Ltd.] + {"hash":"e629bb2b2df81562","prefixes":{"":{"product":908}}}, // [Beijing PageChoice Network Technology co., Ltd.] + {"hash":"37a1384fb3fc6090","prefixes":{"":{"product":270}}}, // [Intelliad] + {"hash":"9d931008b4067d12","prefixes":{"":{"product":909}}}, // [Padopolis, Inc.] + {"hash":"2e0eb051f10f4fe7","prefixes":{"":{"product":909}}}, // [Padopolis, Inc.] + {"hash":"e4e066ee012dd820","prefixes":{"":{"product":910}}}, // [Republic Project, Inc.] + {"hash":"6bda23da60f6517c","prefixes":{"":{"product":910}}}, // [Republic Project, Inc.] + {"hash":"8af361ef2f58932e","prefixes":{"":{"product":910}}}, // [Republic Project, Inc.] + {"hash":"9cf685ac4bd3c1a0","prefixes":{"":{"product":910}}}, // [Republic Project, Inc.] + {"hash":"ceb2ae18496c4062","prefixes":{"":{"product":910}}}, // [Republic Project, Inc.] + {"hash":"a1c74be312c1e501","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"e2305a44231103a0","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"e9ea3f2053ff0ac5","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"18f65ffdee8f8402","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"dc902603963cc6c8","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"6addef0844f148c5","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"c2889776656d63b3","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"1a1ac1eeae181fe7","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"fa03396d4a303d45","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"f632f69c34840122","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"082e62ce70aa66fc","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"775c7d89c606cd3c","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"74b282bb3c686219","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"a8e1956c97315aaf","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"26dc9f78ec72cc0c","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"c4ef7b6eeeda12ce","prefixes":{"":{"product":911}}}, // [C8 Network] + {"hash":"e78c2b0997dfa249","prefixes":{"":{"product":912}}}, // [Data Artist Inc.] + {"hash":"41045058078214c7","prefixes":{"":{"product":912}}}, // [Data Artist Inc.] + {"hash":"c3b23588f42fedff","prefixes":{"":{"product":912}}}, // [Data Artist Inc.] + {"hash":"5e94c742d87aa747","prefixes":{"":{"product":912}}}, // [Data Artist Inc.] + {"hash":"52632067f5495750","prefixes":{"":{"product":913}}}, // [AirFrance] + {"hash":"0b27c948dba1fda0","prefixes":{"":{"product":914}}}, // [YDigital Media] + {"hash":"7e30f931dbb7180a","prefixes":{"":{"product":915}}}, // [Zentrick] + {"hash":"b3e2285b6fc03e5c","prefixes":{"":{"product":915}}}, // [Zentrick] + {"hash":"e2f247795ad2ad87","prefixes":{"":{"product":915}}}, // [Zentrick] + {"hash":"1c9e4d1810e659fa","prefixes":{"":{"product":915}}}, // [Zentrick] + {"hash":"57511b10e2ed4785","prefixes":{"":{"product":915}}}, // [Zentrick] + {"hash":"c012faa898c62e0b","prefixes":{"":{"product":916}}}, // [FreeBit Co. Ltd.] + {"hash":"41b7f33c961162f8","prefixes":{"":{"product":916}}}, // [FreeBit Co. Ltd.] + {"hash":"ffac4f6c5fa1aa82","prefixes":{"":{"product":916}}}, // [FreeBit Co. Ltd.] + {"hash":"a23bae62ab25e2c1","prefixes":{"":{"product":916}}}, // [FreeBit Co. Ltd.] + {"hash":"ae08459757b76c12","prefixes":{"":{"product":916}}}, // [FreeBit Co. Ltd.] + {"hash":"83efb7fcb7770789","prefixes":{"":{"product":917}}}, // [Dennoo Inc.] + {"hash":"042456e26a44a268","prefixes":{"":{"product":917}}}, // [Dennoo Inc.] + {"hash":"cb0b16dd50ba9d7d","prefixes":{"":{"product":917}}}, // [Dennoo Inc.] + {"hash":"541a895df01d2b2c","prefixes":{"":{"product":917}}}, // [Dennoo Inc.] + {"hash":"8f01887bb7405ab5","prefixes":{"":{"product":918}}}, // [Adbrain] + {"hash":"33734a86766d25a9","prefixes":{"*":{"product":919}}}, // [MSI-ACI Europe BV] + {"hash":"f66543f279b3e1e7","prefixes":{"":{"product":920}}}, // [A.Mob] + {"hash":"74ee8c0f2fb31813","prefixes":{"":{"product":920}}}, // [A.Mob] + {"hash":"584db20b69521f40","prefixes":{"":{"product":921}}}, // [Toys R Us] + {"hash":"6a001e8bc99fede6","prefixes":{"":{"product":922}}}, // [Geniee,Inc] + {"hash":"b8ff7bd604535ea9","prefixes":{"":{"product":922}}}, // [Geniee,Inc] + {"hash":"3e005c1dfdc23488","prefixes":{"":{"product":923}}}, // [Adsecure] + {"hash":"ddef7bb2f5930121","prefixes":{"":{"product":923}}}, // [Adsecure] + {"hash":"424399e643b329b8","prefixes":{"":{"product":923}}}, // [Adsecure] + {"hash":"23df92e04ec66406","prefixes":{"":{"product":923}}}, // [Adsecure] + {"hash":"06f0caf4a4fe6ed1","prefixes":{"":{"product":923}}}, // [Adsecure] + {"hash":"9697a0818cbd79bd","prefixes":{"":{"product":923}}}, // [Adsecure] + {"hash":"ec7f61f2511ae20e","prefixes":{"":{"product":923}}}, // [Adsecure] + {"hash":"fb4e7ecb8304ade4","prefixes":{"":{"product":924}}}, // [Kimia Solutions SL] + {"hash":"84f5d0f934bd60f1","prefixes":{"":{"product":924}}}, // [Kimia Solutions SL] + {"hash":"b3fcc22fcd382f3b","prefixes":{"":{"product":924}}}, // [Kimia Solutions SL] + {"hash":"4eca8c3218b372ae","prefixes":{"":{"product":925}}}, // [Sojern] + {"hash":"9636bfb4feaef839","prefixes":{"":{"product":925}}}, // [Sojern] + {"hash":"a525b67906a4cb94","prefixes":{"":{"product":926}}}, // [Where 2 Get It, Inc.] + {"hash":"6c959985d7181026","prefixes":{"":{"product":926}}}, // [Where 2 Get It, Inc.] + {"hash":"7843db8d760e28cb","prefixes":{"":{"product":927}}}, // [Tomoko Cloud] + {"hash":"eb7b756fc1f88aa1","prefixes":{"":{"product":927}}}, // [Tomoko Cloud] + {"hash":"ed4d672687c27603","prefixes":{"":{"product":927}}}, // [Tomoko Cloud] + {"hash":"b156b34e4d693e49","prefixes":{"":{"product":927}}}, // [Tomoko Cloud] + {"hash":"16e146e87e7d0383","prefixes":{"":{"product":928}}}, // [RevenueMantra] + {"hash":"1457bcbc098b2864","prefixes":{"":{"product":928}}}, // [RevenueMantra] + {"hash":"450eb4f9273a5014","prefixes":{"":{"product":929}}}, // [Automobile Ltd.] + {"hash":"436e4eaa95b23fcb","prefixes":{"":{"product":929}}}, // [Automobile Ltd.] + {"hash":"a47ee9f00ca4f855","prefixes":{"":{"product":929}}}, // [Automobile Ltd.] + {"hash":"889ab5643c83668a","prefixes":{"":{"product":929}}}, // [Automobile Ltd.] + {"hash":"4ea96f1e0388da92","prefixes":{"":{"product":930}}}, // [Big Mobile Group Pty Ltd] + {"hash":"ae895a18cc8c416e","prefixes":{"":{"product":930}}}, // [Big Mobile Group Pty Ltd] + {"hash":"bbb46a7f5062448e","prefixes":{"":{"product":931}}}, // [ADMIZED AG] + {"hash":"0b4e26a40cc2e647","prefixes":{"":{"product":932}}}, // [Sparks47 s.r.l.] + {"hash":"745030cd7ac80402","prefixes":{"":{"product":933}}}, // [Between Digital dba Intency DSP] + {"hash":"55db959f7c533183","prefixes":{"":{"product":933}}}, // [Between Digital dba Intency DSP] + {"hash":"15b9a82ccbf2d7ff","prefixes":{"":{"product":933}}}, // [Between Digital dba Intency DSP] + {"hash":"c63156716e201b52","prefixes":{"":{"product":933}}}, // [Between Digital dba Intency DSP] + {"hash":"801d43da2c97866b","prefixes":{"":{"product":933}}}, // [Between Digital dba Intency DSP] + {"hash":"d9f810d72012d4c4","prefixes":{"":{"product":934}}}, // [eprofessional GmbH] + {"hash":"23c665bb68cc30cc","prefixes":{"":{"product":934}}}, // [eprofessional GmbH] + {"hash":"8439b0c8f73daf02","prefixes":{"":{"product":934}}}, // [eprofessional GmbH] + {"hash":"6a17378968b598f2","prefixes":{"":{"product":934}}}, // [eprofessional GmbH] + {"hash":"4e157cd578931a97","prefixes":{"":{"product":935}}}, // [Channel Factory, LLC] + {"hash":"9cdf7d74bee2159f","prefixes":{"":{"product":935}}}, // [Channel Factory, LLC] + {"hash":"58c684b764b4dcab","prefixes":{"":{"product":935}}}, // [Channel Factory, LLC] + {"hash":"85218b0017b8f452","prefixes":{"":{"product":935}}}, // [Channel Factory, LLC] + {"hash":"9f790c0c14fa8f4d","prefixes":{"":{"product":935}}}, // [Channel Factory, LLC] + {"hash":"e7518acf5c44d0a4","prefixes":{"":{"product":935}}}, // [Channel Factory, LLC] + {"hash":"ec763282f8f41299","prefixes":{"":{"product":935}}}, // [Channel Factory, LLC] + {"hash":"41a0870b895e73b4","prefixes":{"":{"product":936}}}, // [ConvertStar Incorporated] + {"hash":"f0134a17d368f3d8","prefixes":{"":{"product":936}}}, // [ConvertStar Incorporated] + {"hash":"58f6c00f44fb69a4","prefixes":{"":{"product":936}}}, // [ConvertStar Incorporated] + {"hash":"ff266e088e3010a8","prefixes":{"":{"product":937}}}, // [VisualDNA (Imagini)] + {"hash":"9748c01f8dcd17ee","prefixes":{"":{"product":937}}}, // [VisualDNA (Imagini)] + {"hash":"8e588a0818c4fcfe","prefixes":{"":{"product":937}}}, // [VisualDNA (Imagini)] + {"hash":"b499a754cc7d2c9b","prefixes":{"":{"product":937}}}, // [VisualDNA (Imagini)] + {"hash":"12d363fa8ee6c4d3","prefixes":{"":{"product":938}}}, // [Avocet] + {"hash":"a8bc7b6f66702489","prefixes":{"":{"product":938}}}, // [Avocet] + {"hash":"5166d9515e755f19","prefixes":{"":{"product":939}}}, // [Auction.com, LLC] + {"hash":"3adf8560ada830b8","prefixes":{"":{"product":939}}}, // [Auction.com, LLC] + {"hash":"f24f87c799e92ae5","prefixes":{"":{"product":939}}}, // [Auction.com, LLC] + {"hash":"a60dedada0fd44b6","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"5d0f226850d2e68c","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"e4c703070b535b17","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"bc640a9c42502b35","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"defb06aab760992e","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"9bb069524856b34b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ef253518584f013f","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"e100ba963ad8ecf2","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"05b625ee62d8685f","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"5a88fb5abffcefc9","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a31c0b568cc8ade8","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"3498894fbeac8341","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"1b82605c41d8b709","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"8138ad98ed0394e9","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"751c9ebb221bb5f9","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"8e3793804d9cdab5","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a6ecf1a95c24b6a3","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"d27fe0e9834e648b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"2a0c1b5a904b36c1","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"f0d6c35febee8008","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"3c01a29e3c9c7264","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"4a74fdfd9b816fe8","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"aac8184f18b04958","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"368da19a90db3ad7","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"116d46403c6e95b7","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"808bb76ca34e4d1b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"b19f61f6e35aaa6a","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"9afc01573e38d021","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"11d7bce194c54912","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a28be439cad08ebb","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"968792ffe85f5d2d","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"d3bc9343531e6c0c","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a11d6b37b0382222","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"4bf65fa3bba0c55b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"4e1d992a76cf0b41","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"b09978a8e01fdbca","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"7394b2453f854b55","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a76d4645267ff0be","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"7e60860df0c8945b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ed9d4b0db5598264","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"466d1d16f3a935fb","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"21ac8963bf026e1a","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"e04f72461a1fdf7d","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"549860a6933e906e","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ddc7313241f77312","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"d89759a9687ad368","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"343ad98c7f4d71ed","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"2cb5047919d88ce8","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"e9739ef3b0403f17","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"2b8ce30f483d90da","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"1d1295f1cdac7503","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"57fd3c09e35ca17e","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"050dd41acc3ac3be","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"1b853fb5c08c8727","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"1c3db6034ad5c3e7","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"4b87160668f51e71","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"d0875bc7510fbaa1","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a46257724ca67bc0","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"c93d397e8d1ed4fe","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"2cc9ae96e6b56fc3","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"051e72fcceee74d6","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"c24b55da2a7095df","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"88981afd076ecf48","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"1ac94ef8a4d9ad5b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a57f0b4ed1a21743","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ff1f4076329cb91c","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"1ea1f1044008a0b2","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"5a9b4bfa6d03c94b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"16bcde8365ddf2a8","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"7d3732d8588ffedb","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"fb6a6a06e4463001","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"e357e4b19a11617f","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ee8aa73feac66773","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"8aa426bd808ed75a","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"e5eed224f946f11b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"3b812696a50c7061","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a04facc89859d775","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"37413c9d9331953b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"d5713942d78606a2","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"52e5d6a64f47f9e8","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"bb49554e8d0ea27b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"9943d790e614f45a","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"b318da3bafca323e","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"8a6b3193ebf16daf","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"9a4e890c561b1f49","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"38f8c78460d0c171","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ceb269cfcbfb3737","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"6fc86b42fa2194c7","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"948868ae3f88e79e","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"f78a9daa44503acf","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"f065b37e2901ab44","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"03f33a24e5022801","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"db26cef88e3879aa","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"496f2ff2ad72eb56","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a33121c16f5e4d20","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"1b5947476f3297ae","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ebaf8ad16d676e4c","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"5ec7e2368a41e7d1","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"f69cbc017c2bee73","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"e2095571b8a4a9ea","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"8e8d9589ff612ccb","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"4acd7b14907d2eab","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"00b864b32ca6962f","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"0e7fd376997e0ce6","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"8947ab8c7604a712","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"12ae4c62954fb4d7","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"5c70a05c3b20c460","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"7920d4521fc31742","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"14d71824a114fae5","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"5c58f35ad11ea36d","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"c68ff69ac1086025","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"bfb4d21e0325cd27","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"1c5328ce70a7d781","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"3a27002f3a51c391","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"15c536b247ddda9b","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"b8dfd3383dc85e16","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"d834384f74b37311","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"33ad4a447e65fe4c","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"5f92334372103621","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"3b36a0673402ddea","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"b8e5c9de8614511a","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"d6f7d0951a76b20f","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"dc731362e0a22c79","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"c37ce29a12797b87","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"b7f8903cecd3c417","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"e9aea7737083ea26","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ca849c611df2249c","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"eba969c354d1a2b6","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"828d340d34a88e86","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"18a6bfdbc9b1999c","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"ea6de81ee5ad1594","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"46f992eaa836ef3e","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"a6c162781ee3a889","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"926d3b6bfe6e1943","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"7e15147781f2b0b4","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"221d9d61291eac74","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"d1c16d1fc4dc5998","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"25596ab30bae8f45","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"6f03c0e16553edcf","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"af7ab58246b4c2a5","prefixes":{"":{"product":940}}}, // [White Ops, Inc.] + {"hash":"82a102dc6f580854","prefixes":{"":{"product":773}}}, // [Extreme Reach Digital (ER Digital)] + {"hash":"35f665efa0e2a55c","prefixes":{"":{"product":773}}}, // [Extreme Reach Digital (ER Digital)] + {"hash":"3a48f18bf340e74a","prefixes":{"":{"product":773}}}, // [Extreme Reach Digital (ER Digital)] + {"hash":"ee365433c62a5c89","prefixes":{"":{"product":773}}}, // [Extreme Reach Digital (ER Digital)] + {"hash":"abe6370bd088dcf4","prefixes":{"":{"product":941}}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] + {"hash":"92dba1cdcba82d8a","prefixes":{"":{"product":941}}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] + {"hash":"ee0ccb57505565f7","prefixes":{"":{"product":941}}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] + {"hash":"f25f5e4909b9792c","prefixes":{"":{"product":941}}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] + {"hash":"8a9e6f7beeb084c5","prefixes":{"":{"product":941}}}, // [Hangzhou Qiguan Network Technology Co., Ltd.] + {"hash":"94542c85cf399cbd","prefixes":{"":{"product":323}}}, // [FinanceGenerator] + {"hash":"46ea052a92233562","prefixes":{"":{"product":942}}}, // [Admetrics] + {"hash":"ea5d2271464ca35f","prefixes":{"":{"product":942}}}, // [Admetrics] + {"hash":"286ad0ac5bca5d03","prefixes":{"":{"product":943}}}, // [Admetrics GmbH] + {"hash":"ff1ffc67b7e3f33a","prefixes":{"":{"product":944}}}, // [Amobee Inc. d/b/a Gradient X] + {"hash":"72cbc5ba1ec93b34","prefixes":{"":{"product":944}}}, // [Amobee Inc. d/b/a Gradient X] + {"hash":"a6fc31f82c22f31e","prefixes":{"":{"product":25}}}, // [Action Exchange, Inc.] + {"hash":"1ba64e5bb4889fc8","prefixes":{"":{"product":25}}}, // [Action Exchange, Inc.] + {"hash":"1d75cb11df01626e","prefixes":{"":{"product":25}}}, // [Action Exchange, Inc.] + {"hash":"91e1e972e2691a51","prefixes":{"":{"product":25}}}, // [Action Exchange, Inc.] + {"hash":"23c68afb7d193b79","prefixes":{"":{"product":25}}}, // [Action Exchange, Inc.] + {"hash":"63d64a70ef1c102e","prefixes":{"":{"product":792}}}, // [Recruit Co., Ltd. Communications] + {"hash":"5e5fd982d3646780","prefixes":{"":{"product":793}}}, // [RECRUIT Communications] + {"hash":"405787e06ebc6f4c","prefixes":{"dc":{"product":945}}}, // [Datalicious Pty Ltd] + {"hash":"cddbf4fe7458433c","prefixes":{"":{"product":945}}}, // [Datalicious Pty Ltd] + {"hash":"1c6b7ff16564ebc9","prefixes":{"":{"product":945}}}, // [Datalicious Pty Ltd] + {"hash":"8b00b076af37f543","prefixes":{"":{"product":945}}}, // [Datalicious Pty Ltd] + {"hash":"688a0b3764e1741b","prefixes":{"":{"product":946}}}, // [Intent Media] + {"hash":"04bba28fa6468b07","prefixes":{"":{"product":947}}}, // [AdNear Pte Ltd.] + {"hash":"c10761fe93bddd5e","prefixes":{"":{"product":948}}}, // [Zhejiang MediaAdx Network Technology Co., Ltd] + {"hash":"3b0fa4efa209ebfa","prefixes":{"":{"product":948}}}, // [Zhejiang MediaAdx Network Technology Co., Ltd] + {"hash":"e1b43b2549b03858","prefixes":{"":{"product":949}}}, // [Harris Interactive] + {"hash":"6ade6d73b73295b2","prefixes":{"":{"product":949}}}, // [Harris Interactive] + {"hash":"63efa9155999d096","prefixes":{"":{"product":950}}}, // [BuzzCity Pte Ltd] + {"hash":"564ef19eef7254cf","prefixes":{"":{"product":950}}}, // [BuzzCity Pte Ltd] + {"hash":"6bbdf3ce8eec6f52","prefixes":{"":{"product":950}}}, // [BuzzCity Pte Ltd] + {"hash":"436cc9ab781b357d","prefixes":{"":{"product":950}}}, // [BuzzCity Pte Ltd] + {"hash":"e8cc8c7d43c80259","prefixes":{"":{"product":951}}}, // [Sputnyx] + {"hash":"2150e4c3d0f193b2","prefixes":{"":{"product":951}}}, // [Sputnyx] + {"hash":"54d8f3466d5879bc","prefixes":{"":{"product":951}}}, // [Sputnyx] + {"hash":"b930b780e12735dc","prefixes":{"":{"product":951}}}, // [Sputnyx] + {"hash":"5817597124959a0f","prefixes":{"":{"product":952}}}, // [CyberZ, Inc] + {"hash":"52a82bf3bc55f753","prefixes":{"":{"product":953}}}, // [Spark Networks USA, LLC] + {"hash":"de87290af2710dce","prefixes":{"*":{"product":954}}}, // [Ezakus] + {"hash":"9fa7220a9c513b82","prefixes":{"":{"product":955}}}, // [V4x SAS] + {"hash":"cdea2314328db64c","prefixes":{"":{"product":955}}}, // [V4x SAS] + {"hash":"9ab122baee7ffef0","prefixes":{"":{"product":955}}}, // [V4x SAS] + {"hash":"5b7763887f62f0e2","prefixes":{"":{"product":956}}}, // [Tapjoy Inc.] + {"hash":"c476f40a640ceb50","prefixes":{"":{"product":12}}}, // [Madeleine Mode GmbH] + {"hash":"b03d8c48bc51a903","prefixes":{"":{"product":12}}}, // [Madeleine Mode GmbH] + {"hash":"6ccfc9d681f1cce4","prefixes":{"":{"product":12}}}, // [Madeleine Mode GmbH] + {"hash":"519e706c784712cd","prefixes":{"":{"product":12}}}, // [Madeleine Mode GmbH] + {"hash":"4b99f5579a32d2ac","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"7bb686f653faf750","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"59bac04b09eb1850","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"c7c593cac7252275","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"9a2642b29d01ad5a","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"19e7f2b697047d0f","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"9ccde0a8ec3f9703","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"a81ee6b474d31f53","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"3b743b2fda71dd5f","prefixes":{"":{"product":957}}}, // [EXEBID] + {"hash":"6378b3090ceafcf5","prefixes":{"*":{"product":562}}}, // [Bannerflow AB] + {"hash":"09de5d9c8a08a419","prefixes":{"":{"product":958}}}, // [Exposebox Ltd] + {"hash":"d88faf97f4f01be0","prefixes":{"":{"product":958}}}, // [Exposebox Ltd] + {"hash":"1b7a8075387597c8","prefixes":{"":{"product":958}}}, // [Exposebox Ltd] + {"hash":"42127ec758c4891f","prefixes":{"":{"product":959}}}, // [MotoMiner] + {"hash":"f5fe6be8ffc65b45","prefixes":{"":{"product":960}}}, // [BuySellAds.com Inc.] + {"hash":"4b9190a98d317014","prefixes":{"":{"product":960}}}, // [BuySellAds.com Inc.] + {"hash":"3b55d9e306cf9e3c","prefixes":{"":{"product":960}}}, // [BuySellAds.com Inc.] + {"hash":"a9da15f40498a6f3","prefixes":{"":{"product":961}}}, // [Viking River Cruises] + {"hash":"e2a8d45a4c55dae6","prefixes":{"":{"product":962}}}, // [Sittercity Incorporated] + {"hash":"5dd667d71a34b766","prefixes":{"":{"product":963}}}, // [Facetz] + {"hash":"00776ec1c54574db","prefixes":{"":{"product":964}}}, // [YOOSE Pte. Ltd.] + {"hash":"f8c3b8f01c62da3c","prefixes":{"":{"product":965}}}, // [AdYapper, Inc.] + {"hash":"6c1804db5b6679a9","prefixes":{"":{"product":965}}}, // [AdYapper, Inc.] + {"hash":"a361de3d2fcba609","prefixes":{"":{"product":965}}}, // [AdYapper, Inc.] + {"hash":"1f805f58db63a12d","prefixes":{"":{"product":965}}}, // [AdYapper, Inc.] + {"hash":"7090e79d9ec26768","prefixes":{"":{"product":965}}}, // [AdYapper, Inc.] + {"hash":"727b3d1100b7d374","prefixes":{"":{"product":965}}}, // [AdYapper, Inc.] + {"hash":"17b396141b96c236","prefixes":{"":{"product":799}}}, // [Webtrekk GmbH] + {"hash":"b0149857f43862d9","prefixes":{"":{"product":144}}}, // [Spark Flow S.A.] + {"hash":"f9c00c55ede792e2","prefixes":{"":{"product":144}}}, // [Spark Flow S.A.] + {"hash":"8eb24e3340b4c707","prefixes":{"":{"product":966}}}, // [Clickky LLP DBA Clickky] + {"hash":"2d27319a70fb6262","prefixes":{"":{"product":966}}}, // [Clickky LLP DBA Clickky] + {"hash":"b140173de591dc64","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"7e2351ba05fefcce","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"88a66ce20c2df5e5","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"705049ea1378adbd","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"eadabb19d68f0e02","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"684343b8f00b5425","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"fedb0d981d151071","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"eb26cb8958b84143","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"b4f42222ff48d611","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"3ef4c3e15c3889aa","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"346ac90e489a0d27","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"f0685c9d889a4dcb","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"d2ae279176821009","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"98746e3eb9075cc5","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"f4e7d8ca3f73252e","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"145b69a5570cf0e6","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"b96e6d8a02aabe0f","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"5f7def46ec1776af","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"075bdf0eaa8a2d8a","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"374ddadb8b59c656","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"e75f11f13cf01278","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"fa9f043b6f9f0225","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"51046da25aac136a","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"e3d9117eacb00b04","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"2291e11d2ca518c2","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"208af3ccccac3a8a","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"8a7e7a7c9510a539","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"d62dab4e5ba19318","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"9faead99b5b9e15b","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"21ad4e7e6897e7be","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"5f6e332f4f7ad081","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"28c87295fd99d9b2","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"ec4d1f8447f0f153","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"bcdb0c5a6199a121","prefixes":{"":{"product":967}}}, // [eRate Online Measurement Solutions Ltd.] + {"hash":"dc1424820d5085bc","prefixes":{"":{"product":967}}}, // [eRate Online Measurement Solutions Ltd.] + {"hash":"d110aae732b1c80d","prefixes":{"":{"product":967}}}, // [eRate Online Measurement Solutions Ltd.] + {"hash":"d461527c3648da49","prefixes":{"":{"product":968}}}, // [Baumann Ber Rivnay] + {"hash":"da8fb21c56d3c224","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"abb93e258191198a","prefixes":{"":{"product":13}}}, // [TripAdvisor LLC] + {"hash":"67ce1b0bfa972cc4","prefixes":{"":{"product":969}}}, // [TUI Connect GmbH] + {"hash":"63c1158e18a5ea27","prefixes":{"":{"product":970}}}, // [INFOnline GmbH] + {"hash":"46a40e7a328ffee7","prefixes":{"":{"product":971}}}, // [Adizio] + {"hash":"c5ca269d09b7c25f","prefixes":{"*":{"product":972}}}, // [Joystick Interactive] + {"hash":"93d3775dcb79f65b","prefixes":{"":{"product":973}}}, // [EngageClick Inc] + {"hash":"0af466da8ca75d0b","prefixes":{"":{"product":973}}}, // [EngageClick Inc] + {"hash":"111ffa6b19238149","prefixes":{"":{"product":974}}}, // [GeeeN, Inc.] + {"hash":"46313b1a2b164e11","prefixes":{"":{"product":974}}}, // [GeeeN, Inc.] + {"hash":"edeaec47d1406cde","prefixes":{"":{"product":974}}}, // [GeeeN, Inc.] + {"hash":"e4fa03e71e81c717","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"b05d395ad43a6851","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"babf6252b4c60056","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"db86fc0d97ddf866","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"ea61f7fa94bc2f36","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"495d58c312a856b7","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"ce854368c8ba8c24","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"7c1ebbe2aa48388d","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"0ac9c5956a237913","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"5e22bf81f1721d99","prefixes":{"":{"product":975}}}, // [Arbigo Inc.] + {"hash":"ad1ab56f3a151112","prefixes":{"":{"product":976}}}, // [FlxOne BV] + {"hash":"6c833f73351e1f63","prefixes":{"":{"product":976}}}, // [FlxOne BV] + {"hash":"82f8d328de710cff","prefixes":{"":{"product":976}}}, // [FlxOne BV] + {"hash":"95c33537789a441f","prefixes":{"":{"product":976}}}, // [FlxOne BV] + {"hash":"712c817d06c0d8c6","prefixes":{"":{"product":976}}}, // [FlxOne BV] + {"hash":"63446ae2652818ab","prefixes":{"":{"product":10}}}, // [eBay] + {"hash":"45b598cd3bc42672","prefixes":{"":{"product":977}}}, // [ExtendTV, Inc.] + {"hash":"372fde25faa1c878","prefixes":{"":{"product":977}}}, // [ExtendTV, Inc.] + {"hash":"db1da0ef7de3e1e6","prefixes":{"":{"product":978}}}, // [momentM, Inc] + {"hash":"5710705dc9e3a971","prefixes":{"":{"product":978}}}, // [momentM, Inc] + {"hash":"4ee3d75e96c8e3aa","prefixes":{"":{"product":978}}}, // [momentM, Inc] + {"hash":"b329682f26e4fcc6","prefixes":{"":{"product":978}}}, // [momentM, Inc] + {"hash":"71215c2931d79d35","prefixes":{"":{"product":978}}}, // [momentM, Inc] + {"hash":"667a4fe4b59238b9","prefixes":{"":{"product":978}}}, // [momentM, Inc] + {"hash":"a7fbeec4fb43f9d9","prefixes":{"*":{"product":979}}}, // [OnCard Marketing dba RevTrax] + {"hash":"48a825a8c5ef9edd","prefixes":{"*":{"product":980}}}, // [Youtube - API] + {"hash":"19459fb447ce4de4","prefixes":{"*":{"product":981}}}, // [Thirdpresence Ltd] + {"hash":"f079f0a998c56593","prefixes":{"":{"product":981}}}, // [Thirdpresence Ltd] + {"hash":"108dd21ceba53008","prefixes":{"":{"product":982}}}, // [Visual IQ, Inc.] + {"hash":"7b0dec2d8c1ec967","prefixes":{"":{"product":982}}}, // [Visual IQ, Inc.] + {"hash":"b5c3f2f3c1c37850","prefixes":{"":{"product":982}}}, // [Visual IQ, Inc.] + {"hash":"ba9b4eca15987843","prefixes":{"":{"product":982}}}, // [Visual IQ, Inc.] + {"hash":"4e521b3e57d76700","prefixes":{"":{"product":982}}}, // [Visual IQ, Inc.] + {"hash":"29ab2715e00761b0","prefixes":{"":{"product":982}}}, // [Visual IQ, Inc.] + {"hash":"688071d177180274","prefixes":{"":{"product":982}}}, // [Visual IQ, Inc.] + {"hash":"4e682c7f7f2ebda9","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"4b6451297bd74247","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"95d75ced1262ecda","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"ac94d70ffcad5c63","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"578591a9eb9652f2","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"5528666c72c7c71c","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"3deb1cd39233765c","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"4e05303df9f07532","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"99936e7e455c77b7","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"f0e5986dc65416fd","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"5095285a4ab05444","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"670fdb8ea86ee233","prefixes":{"":{"product":983}}}, // [Appreciate] + {"hash":"ad2de628bc6fd483","prefixes":{"":{"product":984},"lodeo-":{"product":985}}}, // [CyberAgent d/b/a GameLogic] [Lodeo] + {"hash":"66e26f9703f2dd67","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"80451e302607b653","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"bb7f7e8ab7b99724","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"51dbfc01b5f86601","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"2a8926ec16272a9b","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"4e2334aabf3b7af7","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"1bbabf05900a894f","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"a45a66788779b9bc","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"c4d101005526e2ef","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"7f43400e381d40ff","prefixes":{"":{"product":984}}}, // [CyberAgent d/b/a GameLogic] + {"hash":"e323697c02e1a841","prefixes":{"":{"product":986}}}, // [CyberAgent d/b/a Dynalyst] + {"hash":"11c505e6e21eefdb","prefixes":{"":{"product":986}}}, // [CyberAgent d/b/a Dynalyst] + {"hash":"afc246433e1284d8","prefixes":{"":{"product":986}}}, // [CyberAgent d/b/a Dynalyst] + {"hash":"c7322cd1ad0df953","prefixes":{"":{"product":986}}}, // [CyberAgent d/b/a Dynalyst] + {"hash":"a090f61c3ee5e028","prefixes":{"":{"product":986}}}, // [CyberAgent d/b/a Dynalyst] + {"hash":"846ea0c461560421","prefixes":{"":{"product":985}}}, // [Lodeo] + {"hash":"b433db4fe5f6ed8d","prefixes":{"":{"product":987}}}, // [LTD "RTB-MEDIA"] + {"hash":"e5b814009c134495","prefixes":{"":{"product":987}}}, // [LTD "RTB-MEDIA"] + {"hash":"ce6b2c64ce23d14b","prefixes":{"":{"product":988}}}, // [Maverick., inc.] + {"hash":"32ef8d9c49285e19","prefixes":{"":{"product":988}}}, // [Maverick., inc.] + {"hash":"ac99e3b588f1d521","prefixes":{"":{"product":989}}}, // [FuelX] + {"hash":"da9e2cf59b85610c","prefixes":{"":{"product":989}}}, // [FuelX] + {"hash":"d86ff177f1095173","prefixes":{"":{"product":989}}}, // [FuelX] + {"hash":"7853aedd1a267ffb","prefixes":{"":{"product":989}}}, // [FuelX] + {"hash":"5f505217689c7174","prefixes":{"":{"product":989}}}, // [FuelX] + {"hash":"27be294cbea039f9","prefixes":{"":{"product":989}}}, // [FuelX] + {"hash":"bae6c7f50fff772d","prefixes":{"":{"product":989}}}, // [FuelX] + {"hash":"63f51428712678ec","prefixes":{"":{"product":324}}}, // [Telstra Corporation] + {"hash":"f1d920cc5a16b3a9","prefixes":{"":{"product":986}}}, // [CyberAgent d/b/a Dynalyst] + {"hash":"12a7d7495eb7a91f","prefixes":{"":{"product":986}}}, // [CyberAgent d/b/a Dynalyst] + {"hash":"6ca28320c4765235","prefixes":{"":{"product":986}}}, // [CyberAgent d/b/a Dynalyst] + {"hash":"aeb171f5cad96320","prefixes":{"":{"product":990}}}, // [Tutor.com] + {"hash":"5de1c09366f03919","prefixes":{"":{"product":164}}}, // [wayStorm Co., Ltd.] + {"hash":"9977349a2c52c1b6","prefixes":{"":{"product":164}}}, // [wayStorm Co., Ltd.] + {"hash":"3304d732f88c9ebe","prefixes":{"":{"product":164}}}, // [wayStorm Co., Ltd.] + {"hash":"8b1b0e966a643448","prefixes":{"":{"product":164}}}, // [wayStorm Co., Ltd.] + {"hash":"852a445ec03b1224","prefixes":{"":{"product":991}}}, // [Rebelmouse] + {"hash":"ebef84a02e75b037","prefixes":{"*":{"product":991}}}, // [Rebelmouse] + {"hash":"6ff0ad7b4d4ee2ef","prefixes":{"":{"product":992}}}, // [Adviator DSP] + {"hash":"c28458e1bac3bb75","prefixes":{"":{"product":992}}}, // [Adviator DSP] + {"hash":"041403702e0ae9f0","prefixes":{"":{"product":992}}}, // [Adviator DSP] + {"hash":"31890d74ba2e0eca","prefixes":{"":{"product":525}}}, // [abilicom GmbH] + {"hash":"16f2a07aea3c767b","prefixes":{"":{"product":993}}}, // [Acens Technologies, S.L.] + {"hash":"4d628548402f2933","prefixes":{"":{"product":994}}}, // [Yashi, Inc] + {"hash":"6f97eb7dc578c1d3","prefixes":{"":{"product":677}}}, // [Taptica] + {"hash":"843cc26d64f855e3","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"7f81eef44c27fceb","prefixes":{"":{"product":495}}}, // [Miaozhen Systems] + {"hash":"f36b5e4d896bf9fc","prefixes":{"":{"product":995}}}, // [Flaminem Inc] + {"hash":"ec10935d7141abb2","prefixes":{"":{"product":996}}}, // [Mediahead AG] + {"hash":"d074e5017286d25a","prefixes":{"":{"product":996}}}, // [Mediahead AG] + {"hash":"e7aa39274c05edf9","prefixes":{"":{"product":996}}}, // [Mediahead AG] + {"hash":"06d3dae2f13a3dcb","prefixes":{"":{"product":996}}}, // [Mediahead AG] + {"hash":"1cc1098f16d364e0","prefixes":{"":{"product":996}}}, // [Mediahead AG] + {"hash":"15ee5953d4c74d48","prefixes":{"":{"product":996}}}, // [Mediahead AG] + {"hash":"b434b094029a84a8","prefixes":{"":{"product":997}}}, // [Admixer] + {"hash":"766b3c24fb448820","prefixes":{"":{"product":998}}}, // [Communication Services Tele2 GmbH] + {"hash":"35e369c14d37cb1c","prefixes":{"*":{"product":999}}}, // [99click] + {"hash":"c5b7e1faef274865","prefixes":{"":{"product":1000}}}, // [Ströer Mobile Media GmbH] + {"hash":"dc01aead4f784e4d","prefixes":{"":{"product":1000}}}, // [Ströer Mobile Media GmbH] + {"hash":"39525c5c19a8a044","prefixes":{"":{"product":1000}}}, // [Ströer Mobile Media GmbH] + {"hash":"397c77c97b7945f8","prefixes":{"":{"product":1000}}}, // [Ströer Mobile Media GmbH] + {"hash":"3097a79ab689b320","prefixes":{"":{"product":1000}}}, // [Ströer Mobile Media GmbH] + {"hash":"c27b66085df0c0da","prefixes":{"":{"product":1001}}}, // [LLC "Life Style"] + {"hash":"3a7913ff0ce0cc21","prefixes":{"":{"product":1001}}}, // [LLC "Life Style"] + {"hash":"9d1661dae6ff9304","prefixes":{"":{"product":1001}}}, // [LLC "Life Style"] + {"hash":"cdd266d878d3e9d5","prefixes":{"":{"product":1001}}}, // [LLC "Life Style"] + {"hash":"d3c08bf02865217e","prefixes":{"":{"product":1002}}}, // [2Y Media SAS (adserverpub)] + {"hash":"bbfe70399cd3fcb3","prefixes":{"":{"product":1002}}}, // [2Y Media SAS (adserverpub)] + {"hash":"a17b33aa94234849","prefixes":{"":{"product":1002}}}, // [2Y Media SAS (adserverpub)] + {"hash":"8e073ea44074ec93","prefixes":{"":{"product":1002}}}, // [2Y Media SAS (adserverpub)] + {"hash":"12afa442e09a07c9","prefixes":{"":{"product":1002}}}, // [2Y Media SAS (adserverpub)] + {"hash":"7fa5ca05a3a0b474","prefixes":{"":{"product":1003}}}, // [Platform IQ] + {"hash":"f6f0f65947909d69","prefixes":{"":{"product":1003}}}, // [Platform IQ] + {"hash":"77b586602e5dec3f","prefixes":{"":{"product":1003}}}, // [Platform IQ] + {"hash":"bba327f348a4693e","prefixes":{"":{"product":1003}}}, // [Platform IQ] + {"hash":"a544a3b25c4d5113","prefixes":{"":{"product":1004}}}, // [Silveredge Inc] + {"hash":"037cb4c9598870ee","prefixes":{"":{"product":1004}}}, // [Silveredge Inc] + {"hash":"2a6ed7c1b6a78509","prefixes":{"":{"product":1004}}}, // [Silveredge Inc] + {"hash":"06d23751a7963710","prefixes":{"":{"product":1005}}}, // [SMADEX] + {"hash":"0a84ed2865a74b8a","prefixes":{"":{"product":1005}}}, // [SMADEX] + {"hash":"e97a828dc740d645","prefixes":{"":{"product":1005}}}, // [SMADEX] + {"hash":"aa964a10a9ccda5b","prefixes":{"":{"product":1005}}}, // [SMADEX] + {"hash":"d7675b344c153db3","prefixes":{"":{"product":1005}}}, // [SMADEX] + {"hash":"76eaf06fcd95e8e8","prefixes":{"":{"product":1006}}}, // [Suzu Muchi] + {"hash":"fcf26d7eec95d72d","prefixes":{"":{"product":342}}}, // [LOKA Research inc.] + {"hash":"3b238a232cbc26bd","prefixes":{"":{"product":342}}}, // [LOKA Research inc.] + {"hash":"a544c76e7ec01862","prefixes":{"":{"product":342}}}, // [LOKA Research inc.] + {"hash":"7922cb2f11972c85","prefixes":{"":{"product":342}}}, // [LOKA Research inc.] + {"hash":"613551ec99bec944","prefixes":{"":{"product":1007}}}, // [PlannTo Technologies Private Limited] + {"hash":"bb07eb5de3d95038","prefixes":{"":{"product":1007}}}, // [PlannTo Technologies Private Limited] + {"hash":"729165b2ebbc6996","prefixes":{"":{"product":1007}}}, // [PlannTo Technologies Private Limited] + {"hash":"187629571e0338cf","prefixes":{"":{"product":1007}}}, // [PlannTo Technologies Private Limited] + {"hash":"bdd2e20221d7e507","prefixes":{"":{"product":1008}}}, // [Viator, Inc] + {"hash":"1caf0cb7a05c5941","prefixes":{"":{"product":474}}}, // [Xrost] + {"hash":"4c6513300966da08","prefixes":{"":{"product":1009}}}, // [NODDINGTON TECHNOLOGIES LIMITED] + {"hash":"2c1d6a203788af2b","prefixes":{"":{"product":1010}}}, // [Plan Blue Ltd] + {"hash":"3c0da2d4356e18a5","prefixes":{"":{"product":1010}}}, // [Plan Blue Ltd] + {"hash":"2568d0917d238964","prefixes":{"":{"product":1011}}}, // [Addictive Mobility] + {"hash":"f79c822ab2cca8ee","prefixes":{"":{"product":1011}}}, // [Addictive Mobility] + {"hash":"c610b5ae3df923cd","prefixes":{"":{"product":1011}}}, // [Addictive Mobility] + {"hash":"a614ab43bf2d906b","prefixes":{"":{"product":1011}}}, // [Addictive Mobility] + {"hash":"747eeca86822f19c","prefixes":{"":{"product":1012}}}, // [A6 Corporation] + {"hash":"cbdfdaeff8b7d933","prefixes":{"":{"product":1013}}}, // [Mobusi Mobile Advertising] + {"hash":"5b2d082c7811bead","prefixes":{"":{"product":1014}}}, // [Wishabi] + {"hash":"c811726b56670631","prefixes":{"":{"product":1014}}}, // [Wishabi] + {"hash":"09d3b27d380d43a0","prefixes":{"":{"product":1014}}}, // [Wishabi] + {"hash":"eecab68f555d1f9f","prefixes":{"":{"product":1015}}}, // [onAd GmbH] + {"hash":"d05b20f38e3bb984","prefixes":{"":{"product":1016}}}, // [Media Decision GmbH] + {"hash":"4a9b8dddd4c7567c","prefixes":{"":{"product":1016}}}, // [Media Decision GmbH] + {"hash":"5121142c1b8676fd","prefixes":{"":{"product":1016}}}, // [Media Decision GmbH] + {"hash":"e083b93d5a87c743","prefixes":{"":{"product":1017}}}, // [Unitymedia NRW] + {"hash":"c665046a783daa9d","prefixes":{"":{"product":1018}}}, // [Nowspots INC, dba Perfect Audience] + {"hash":"663d5fdbc3a52729","prefixes":{"":{"product":1018}}}, // [Nowspots INC, dba Perfect Audience] + {"hash":"991e96310871571b","prefixes":{"":{"product":1018}}}, // [Nowspots INC, dba Perfect Audience] + {"hash":"0503d775704af4a2","prefixes":{"":{"product":1018}}}, // [Nowspots INC, dba Perfect Audience] + {"hash":"2c55a4ef41531200","prefixes":{"":{"product":1019}}}, // [Avis] + {"hash":"62817a7d196cbdcf","prefixes":{"":{"product":1020}}}, // [Comcast] + {"hash":"2e4bb1f4d07f3180","prefixes":{"":{"product":1021}}}, // [Jack Spade] + {"hash":"e7d369c24971784a","prefixes":{"":{"product":1022}}}, // [Dollar General] + {"hash":"a8c9b93aae571ed5","prefixes":{"":{"product":1023}}}, // [Care.com] + {"hash":"713775b7bc737116","prefixes":{"":{"product":1024}}}, // [Clickagy, LLC] + {"hash":"8b6b45c1c86cd84b","prefixes":{"":{"product":1024}}}, // [Clickagy, LLC] + {"hash":"34376185507c9ae0","prefixes":{"":{"product":1025}}}, // [GlobalWebIndex] + {"hash":"8b17254892b590e3","prefixes":{"":{"product":1026}}}, // [King.com] + {"hash":"8516f044f19bb3ae","prefixes":{"":{"product":1026}}}, // [King.com] + {"hash":"d811179454fad251","prefixes":{"":{"product":1026}}}, // [King.com] + {"hash":"946611fa2f4b067a","prefixes":{"":{"product":1027}}}, // [Proquire LLC - Accenture] + {"hash":"cfa0e9025fcd5712","prefixes":{"":{"product":1028}}}, // [Dynamic Yield] + {"hash":"b01c0239af74e72e","prefixes":{"":{"product":1028}}}, // [Dynamic Yield] + {"hash":"e480d03a980d3bfc","prefixes":{"":{"product":1028}}}, // [Dynamic Yield] + {"hash":"f22fe7e17c58b562","prefixes":{"":{"product":1028}}}, // [Dynamic Yield] + {"hash":"f8da77c2fdcad106","prefixes":{"":{"product":1028}}}, // [Dynamic Yield] + {"hash":"cea3bf4afe576984","prefixes":{"":{"product":1029}}}, // [Charter business] + {"hash":"f8029c009186163f","prefixes":{"":{"product":1030}}}, // [The ADEX] + {"hash":"61b09f5d40722e69","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"aa63151485efa109","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"ed37e98f45f10404","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"f9fda879239cf155","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"2dfe9124b3439d80","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"93a9e29ac238b380","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"5e080b13877f5de7","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"eb22d59d4594e6d3","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"a28e072f804db6a0","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"945f948eacea62c9","prefixes":{"":{"product":1031}}}, // [OneDigitalAd Technologies] + {"hash":"b85074a47da33b83","prefixes":{"":{"product":1032}}}, // [ADJUST] + {"hash":"9f369deb4f045364","prefixes":{"":{"product":1032}}}, // [ADJUST] + {"hash":"2d6ceeb3eef38c27","prefixes":{"":{"product":1032}}}, // [ADJUST] + {"hash":"65240dfe37410021","prefixes":{"":{"product":1032}}}, // [ADJUST] + {"hash":"8d3dfcbf705f1191","prefixes":{"":{"product":1032}}}, // [ADJUST] + {"hash":"a2d35afc9c960448","prefixes":{"":{"product":1033}}}, // [ADmantX, SPA] + {"hash":"3d3bc7f00b0ef8d3","prefixes":{"":{"product":1034}}}, // [Sogou.com] + {"hash":"9eb0737a130a6f08","prefixes":{"":{"product":1035}}}, // [xAd, Inc.] + {"hash":"a9ff22250a3c29a1","prefixes":{"":{"product":1036}}}, // [VideoBlocks] + {"hash":"61f27a00f3821730","prefixes":{"":{"product":1037}}}, // [GraphicStocks] + {"hash":"f0792974945e7184","prefixes":{"":{"product":1038}}}, // [Microsoft Advertising] + {"hash":"81f27cb33d47894b","prefixes":{"":{"product":1039}}}, // [Rontar LTD] + {"hash":"e8ae13fee2796336","prefixes":{"":{"product":1039}}}, // [Rontar LTD] + {"hash":"d979ed3048a9dc3d","prefixes":{"":{"product":1039}}}, // [Rontar LTD] + {"hash":"187c7e948b323b3a","prefixes":{"dsp":{"product":1039}}}, // [Rontar LTD] + {"hash":"1a5cfb3f08b31682","prefixes":{"":{"product":1040}}}, // [Torrential, Inc.] + {"hash":"99bb29582234b47b","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"14a0b2d821feb403","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"4bfb576f457b3ad4","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"d2e32e6a4c73344a","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"3c8aa320c0f5fb9d","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"8c3cae9472fcd43a","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"18d673cc147eaba5","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"fb4f3dd591cba22f","prefixes":{"bid":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"49aef0b488ab5024","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"2b7a17458f44228d","prefixes":{"":{"product":1041}}}, // [AMoAd, Inc.] + {"hash":"7458cceedc9f812a","prefixes":{"":{"product":1042}}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] + {"hash":"dd47f178c7a055b8","prefixes":{"":{"product":1042}}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] + {"hash":"db975b73da86c8ce","prefixes":{"":{"product":1042}}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] + {"hash":"6216946891299e23","prefixes":{"":{"product":1042}}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] + {"hash":"52ee7c22862a74b6","prefixes":{"":{"product":1042}}}, // [Tukmob Information Technology(Shanghai)Co. Ltd] + {"hash":"3cc39e801c92ea89","prefixes":{"":{"product":1043}}}, // [Jampp/Devego S.A.] + {"hash":"31fb8bdf6d2b85d2","prefixes":{"":{"product":1044}}}, // [Placed] + {"hash":"a6ff57032773fe41","prefixes":{"*":{"product":1045}}}, // [Digitas Health] + {"hash":"caeb7a071a866e17","prefixes":{"*":{"product":1045}}}, // [Digitas Health] + {"hash":"6e946f5fac72b0b4","prefixes":{"":{"product":1046}}}, // [Answer Media, LLC] + {"hash":"31c3d7ef1499fd26","prefixes":{"":{"product":1046}}}, // [Answer Media, LLC] + {"hash":"d681f06391c7f861","prefixes":{"":{"product":1047}}}, // [1000mercis] + {"hash":"1549387212aaa6a3","prefixes":{"":{"product":1048}}}, // [Upstart Network, Inc.] + {"hash":"e48f63d27d0c24c7","prefixes":{"":{"product":1049}}}, // [Forensiq, LLC] + {"hash":"7579b1341b228f93","prefixes":{"":{"product":1049}}}, // [Forensiq, LLC] + {"hash":"f2e570ecd2540399","prefixes":{"":{"product":1050}}}, // [LoopMe Ltd] + {"hash":"7d4e3b90001a044e","prefixes":{"":{"product":1050}}}, // [LoopMe Ltd] + {"hash":"528167a286d8ccf8","prefixes":{"":{"product":1051}}}, // [Bannerlink (Liquidus)] + {"hash":"0b2d528c0dce5bba","prefixes":{"":{"product":1051}}}, // [Bannerlink (Liquidus)] + {"hash":"758cc459053712cf","prefixes":{"":{"product":1051}}}, // [Bannerlink (Liquidus)] + {"hash":"57951d4b34add1a9","prefixes":{"":{"product":1051}}}, // [Bannerlink (Liquidus)] + {"hash":"3d93ddd2b1dc8083","prefixes":{"":{"product":1051}}}, // [Bannerlink (Liquidus)] + {"hash":"88f0b992c4b99c65","prefixes":{"":{"product":1052}}}, // [Dell Inc.] + {"hash":"f8820430b42f89f8","prefixes":{"":{"product":1053}}}, // [My Perfect Resume] + {"hash":"6f018db6d7a12f5e","prefixes":{"":{"product":1054}}}, // [Ajillion Max Ltd] + {"hash":"b591ab0baccecd52","prefixes":{"":{"product":1054}}}, // [Ajillion Max Ltd] + {"hash":"35d86d0121dbd41a","prefixes":{"":{"product":1055}}}, // [Swelen France SA.] + {"hash":"25b09dd0e8d2977a","prefixes":{"":{"product":1055}}}, // [Swelen France SA.] + {"hash":"f5cb853eba61193a","prefixes":{"":{"product":1056}}}, // [Sleeptrain] + {"hash":"c620109c65610aeb","prefixes":{"":{"product":1057}}}, // [Sleepcountry] + {"hash":"9c6554ebebdbfa08","prefixes":{"":{"product":1058}}}, // [Hoover] + {"hash":"f34ddb2b65330794","prefixes":{"":{"product":1059}}}, // [Bidtellect] + {"hash":"5d9eb2a8dc1a1687","prefixes":{"":{"product":1059}}}, // [Bidtellect] + {"hash":"e2207dc37f1b89e8","prefixes":{"":{"product":1059}}}, // [Bidtellect] + {"hash":"682ec1b47db930b9","prefixes":{"":{"product":1059}}}, // [Bidtellect] + {"hash":"638a2c2dc53d2ab7","prefixes":{"":{"product":1060}}}, // [Mocoplex Inc.] + {"hash":"ee330ed89897a55c","prefixes":{"":{"product":1060}}}, // [Mocoplex Inc.] + {"hash":"60ab035f28a35e30","prefixes":{"":{"product":1060}}}, // [Mocoplex Inc.] + {"hash":"5929f2c289d9e073","prefixes":{"":{"product":778}}}, // [Gruvi Ltd.] + {"hash":"c16c02248018a374","prefixes":{"":{"product":778}}}, // [Gruvi Ltd.] + {"hash":"e6eb2d1c03d2be83","prefixes":{"":{"product":778}}}, // [Gruvi Ltd.] + {"hash":"95fa4396cadc336e","prefixes":{"":{"product":778}}}, // [Gruvi Ltd.] + {"hash":"2604a7d96f168c84","prefixes":{"":{"product":1061}}}, // [Brightsparc Technologies Pty Ltd trading as Native] + {"hash":"294fdebfd41771a8","prefixes":{"":{"product":1061}}}, // [Brightsparc Technologies Pty Ltd trading as Native] + {"hash":"c6beb451fe4c58c2","prefixes":{"":{"product":1061}}}, // [Brightsparc Technologies Pty Ltd trading as Native] + {"hash":"b0f5f51210e6f2af","prefixes":{"":{"product":1061}}}, // [Brightsparc Technologies Pty Ltd trading as Native] + {"hash":"ab3da2eb1a35cba6","prefixes":{"":{"product":1062}}}, // [Verengo Solar] + {"hash":"c3bf5a10743772ec","prefixes":{"*":{"product":1063}}}, // [Mobile Professionals BV] + {"hash":"d22325b6d6b04918","prefixes":{"*":{"product":1064}}}, // [APNIC Pty Ltd] + {"hash":"27966131215e508a","prefixes":{"*":{"product":1064}}}, // [APNIC Pty Ltd] + {"hash":"80022b9d8ddaffde","prefixes":{"*":{"product":1064}}}, // [APNIC Pty Ltd] + {"hash":"9352d77ba286c03d","prefixes":{"":{"product":1065}}}, // [Dun and Bradstreet Corporation] + {"hash":"74b7ef01f42d93ee","prefixes":{"":{"product":1066}}}, // [UpToLike] + {"hash":"f2a78bb802dc4abd","prefixes":{"":{"product":1066}}}, // [UpToLike] + {"hash":"f885798eaaace647","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"3bc813f113908f06","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"e1a6852c86da418c","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"0343b65d010db4b2","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"a9b74e1bcb339dd0","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"e8d325d7be0f046b","prefixes":{"*":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"f7ccb1753b9ae2ed","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"4a96db729cb9dd47","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"afde8c2ebcc380c8","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"05840eb3ff4432a8","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"3e5f0f83b0e34391","prefixes":{"":{"product":1067}}}, // [Mobile360 Sdn Bhd] + {"hash":"f79fe1474c475ade","prefixes":{"":{"product":1068}}}, // [Elite Fixtures] + {"hash":"b8b63e29130d798a","prefixes":{"":{"product":1069}}}, // [Advanse Ads] + {"hash":"662442a875d5ae62","prefixes":{"":{"product":1069}}}, // [Advanse Ads] + {"hash":"d8487e4c88a21c7f","prefixes":{"":{"product":1069}}}, // [Advanse Ads] + {"hash":"37694729baf29aa5","prefixes":{"":{"product":1070}}}, // [Insurance Step] + {"hash":"0153c16587a0c97c","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"c7bcca7e8dc820fe","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"2176b8c755bbab42","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"1979b33f68eb0ded","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"32a9ea6936225587","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"16fec67040bf3ff7","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"ed89b325a7c3ac6d","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"ae3bc4690ef89e8d","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"ed9a2a4f49ad4647","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"398d36669a77aca6","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"5bf9c0f450f9eb14","prefixes":{"":{"product":1071}}}, // [Causal Impact] + {"hash":"30e7de1e277c7698","prefixes":{"":{"product":145}}}, // [Aarki, Inc.] + {"hash":"888d9446d621d82f","prefixes":{"":{"product":145}}}, // [Aarki, Inc.] + {"hash":"3061a4cd653ea2dc","prefixes":{"*":{"product":1072}}}, // [Pixnet] + {"hash":"06daa5b82ebfa413","prefixes":{"":{"product":1073}}}, // [Zapp360] + {"hash":"6f0f9a90c798715d","prefixes":{"":{"product":1074}}}, // [Kijiji] + {"hash":"0259cba89f8f9676","prefixes":{"":{"product":1075}}}, // [Spotad LTD.] + {"hash":"0ca2babbaeeca3bf","prefixes":{"":{"product":1075}}}, // [Spotad LTD.] + {"hash":"e9a9d4fd1b468042","prefixes":{"":{"product":1075}}}, // [Spotad LTD.] + {"hash":"6a79ac93cddc0cc5","prefixes":{"":{"product":1075}}}, // [Spotad LTD.] + {"hash":"685b17838f2d9b82","prefixes":{"":{"product":1076}}}, // [Go Daddy] + {"hash":"aa66f01be33256d0","prefixes":{"":{"product":1077}}}, // [Bilendi] + {"hash":"3b8dfd79ab921d9b","prefixes":{"":{"product":1078}}}, // [Hitokuse] + {"hash":"1992e397e0dff18d","prefixes":{"":{"product":1078}}}, // [Hitokuse] + {"hash":"03eb230cd9f67c73","prefixes":{"":{"product":1078}}}, // [Hitokuse] + {"hash":"0a75c26f5c580b3a","prefixes":{"*":{"product":1079}}}, // [MGID Inc.] + {"hash":"4466d73d5d5dce19","prefixes":{"*":{"product":1079}}}, // [MGID Inc.] + {"hash":"10bfb2327400e2ea","prefixes":{"":{"product":1079}}}, // [MGID Inc.] + {"hash":"121b77f4b4412c85","prefixes":{"":{"product":1079}}}, // [MGID Inc.] + {"hash":"e46ec8aa62f3f432","prefixes":{"":{"product":1080}}}, // [AreaOne] + {"hash":"9849db4d4a694f16","prefixes":{"t":{"product":1081},"s":{"product":1081},"sna":{"product":1081}}}, // [DynAd] [DynAd] [DynAd] + {"hash":"5fc0aef37d5fffca","prefixes":{"":{"product":1082}}}, // [Loop Pay] + {"hash":"4cc75be32553222a","prefixes":{"":{"product":1083}}}, // [Remerge GmbH] + {"hash":"b1a90d2aaa32932c","prefixes":{"":{"product":1083}}}, // [Remerge GmbH] + {"hash":"3bea81bc90640751","prefixes":{"":{"product":1084}}}, // [Audience Trading Platform LTD] + {"hash":"d193540bb7540e5c","prefixes":{"":{"product":1085}}}, // [Whisla] + {"hash":"abd1c2a37733128b","prefixes":{"":{"product":1085}}}, // [Whisla] + {"hash":"9546fe15199cbce1","prefixes":{"":{"product":1085}}}, // [Whisla] + {"hash":"f43c40d1949f2c52","prefixes":{"":{"product":1086}}}, // [Bridgevine] + {"hash":"28577f7c17c55b0f","prefixes":{"":{"product":1087}}}, // [ADgraph DMP] + {"hash":"fa0c51dc55faddc6","prefixes":{"":{"product":1088}}}, // [Gravity4 Inc.] + {"hash":"6dea119c4c95aaa8","prefixes":{"":{"product":1088}}}, // [Gravity4 Inc.] + {"hash":"79b4752426dedd83","prefixes":{"":{"product":1089}}}, // [Hipmunk] + {"hash":"ccba9f3966eabe6b","prefixes":{"*":{"product":1090}}}, // [VideoHub DSP] + {"hash":"a6baef75f126450e","prefixes":{"":{"product":1091}}}, // [DuMedia] + {"hash":"cf45efd3feda1ba3","prefixes":{"":{"product":1091}}}, // [DuMedia] + {"hash":"efebf8c1ea6bab87","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"a866ff2bfe3133e7","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"49b89555382dac9e","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"e2d2b1537271ff61","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"5804e7cf2cdbbda0","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"db0ef2b4eff25274","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"d960535fd30704f8","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"258915df4406fd06","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"b34a1e3fd51aadb9","prefixes":{"":{"product":1092}}}, // [F@N Communications, Inc.] + {"hash":"c0ad7ec6e1d46299","prefixes":{"":{"product":1093}}}, // [VIVALU GmbH] + {"hash":"65c049a517720b5c","prefixes":{"":{"product":1093}}}, // [VIVALU GmbH] + {"hash":"b60b2426028baba5","prefixes":{"":{"product":1094}}}, // [Advertising Technologies LTD] + {"hash":"6d2df95e2bf52046","prefixes":{"":{"product":1094}}}, // [Advertising Technologies LTD] + {"hash":"b4ad6ea6ca336f55","prefixes":{"":{"product":1094}}}, // [Advertising Technologies LTD] + {"hash":"b750311cbd5c4013","prefixes":{"":{"product":1095}}}, // [Authenticated Digital Inc] + {"hash":"aa634a3d8862b3be","prefixes":{"":{"product":1095}}}, // [Authenticated Digital Inc] + {"hash":"361eefdfa29ada10","prefixes":{"":{"product":1096}}}, // [PaeDae, Inc., DBA The Mobile Majority] + {"hash":"8a5c2c8d21fc6dd4","prefixes":{"":{"product":1096}}}, // [PaeDae, Inc., DBA The Mobile Majority] + {"hash":"13b3d2e0ed78e518","prefixes":{"":{"product":1097}}}, // [METROPCS] + {"hash":"c9b29fe277a47b8d","prefixes":{"":{"product":1098}}}, // [PapayaMobile Inc.] + {"hash":"60f32eaa9335e4b9","prefixes":{"":{"product":1098}}}, // [PapayaMobile Inc.] + {"hash":"c8f49035cb273e42","prefixes":{"":{"product":1098}}}, // [PapayaMobile Inc.] + {"hash":"92d9e360f16ecdc9","prefixes":{"":{"product":1098}}}, // [PapayaMobile Inc.] + {"hash":"2d3b5566afb5350b","prefixes":{"":{"product":1098}}}, // [PapayaMobile Inc.] + {"hash":"0ca3b2088de2514c","prefixes":{"":{"product":1098}}}, // [PapayaMobile Inc.] + {"hash":"67da96d5ae99faa0","prefixes":{"":{"product":1098}}}, // [PapayaMobile Inc.] + {"hash":"347e898491d027ee","prefixes":{"":{"product":1099}}}, // [Raumfeld] + {"hash":"ae3f27ad4a4f2f7d","prefixes":{"":{"product":1099}}}, // [Raumfeld] + {"hash":"cd0c8a5dd793ee52","prefixes":{"":{"product":1099}}}, // [Raumfeld] + {"hash":"451f4083f03028e0","prefixes":{"":{"product":1099}}}, // [Raumfeld] + {"hash":"e1b048e4fa37eef2","prefixes":{"":{"product":1099}}}, // [Raumfeld] + {"hash":"837ae92d9cf6b933","prefixes":{"":{"product":1099}}}, // [Raumfeld] + {"hash":"6ae82aac87db94d3","prefixes":{"":{"product":1099}}}, // [Raumfeld] + {"hash":"84ddfa5a2e93cb69","prefixes":{"":{"product":1100}}}, // [GREE Ads DSP] + {"hash":"a7201f5a80824242","prefixes":{"":{"product":1100}}}, // [GREE Ads DSP] + {"hash":"2ec22ca05310362a","prefixes":{"":{"product":1101}}}, // [Tender Industries AB] + {"hash":"d159abbaeda22e7c","prefixes":{"":{"product":1102}}}, // [BySide] + {"hash":"426783d6fe8d039e","prefixes":{"":{"product":1103}}}, // [Sentrant Security Inc.] + {"hash":"d270ad50bb53fb01","prefixes":{"":{"product":1103}}}, // [Sentrant Security Inc.] + {"hash":"49c693058534be83","prefixes":{"":{"product":1104}}}, // [Locon Solutions Pvt. Ltd.] + {"hash":"a2b9f627da8737de","prefixes":{"":{"product":1105}}}, // [Interrogare GmbH] + {"hash":"13b73169540642e5","prefixes":{"":{"product":1105}}}, // [Interrogare GmbH] + {"hash":"afaa106d11846fa4","prefixes":{"*":{"product":1106}}}, // [ChannelAdvisor] + {"hash":"b8b4eadeb5d3a123","prefixes":{"":{"product":1107}}}, // [VideoAmp] + {"hash":"43546e9f6ff5bf2a","prefixes":{"":{"product":1107}}}, // [VideoAmp] + {"hash":"61a9edb9af3769cf","prefixes":{"":{"product":1107}}}, // [VideoAmp] + {"hash":"f76b3fe7048e93e2","prefixes":{"":{"product":1107}}}, // [VideoAmp] + {"hash":"57263424c0e88a92","prefixes":{"track":{"product":1108}}}, // [The Bridge] + {"hash":"516e9fc5ede4af0d","prefixes":{"":{"product":1108}}}, // [The Bridge] + {"hash":"6bc0303a4262cb67","prefixes":{"":{"product":1108}}}, // [The Bridge] + {"hash":"30a32fe2a89dccd8","prefixes":{"":{"product":1109}}}, // [Pharmaca Integrative Pharmacy] + {"hash":"7881da604383a640","prefixes":{"":{"product":673}}}, // [Videology DSP] + {"hash":"dec02c663bb06094","prefixes":{"":{"product":1110}}}, // [Scrutineer] + {"hash":"6e2098af577c671d","prefixes":{"":{"product":1111}}}, // [TF1 - FR] + {"hash":"60774075776e2612","prefixes":{"":{"product":1112}}}, // [Core Digital] + {"hash":"79a775eea6a902e6","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"c0576db5cf4ba20b","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"d90fda985e3fb6bf","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"ee49dcf9326e853f","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"5b3c6abcd44de720","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"f97a320480541a27","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"57fc36302e0bde3c","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"736a8a8a0cef7bdf","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"36263b4a0d607a71","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"226480bf58f4fd1c","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"58379bd623597d70","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"f68e3a544a7397be","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"0f2880d71794e516","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"4ef52741fc1ade73","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"97d6a93635d72af0","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"ab75a46dc8bf2c1e","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"efeb8f966db03b87","prefixes":{"":{"product":110}}}, // [Bonzai Digital Pvt. Ltd] + {"hash":"2a4d804c0fb397c1","prefixes":{"":{"product":1113}}}, // [Adventive, Inc.] + {"hash":"29d370caa59b4ecc","prefixes":{"":{"product":1113}}}, // [Adventive, Inc.] + {"hash":"a06632ec1be82319","prefixes":{"":{"product":1114}}}, // [Turner Sports Interactive, Inc.] + {"hash":"bf10e40bae047225","prefixes":{"":{"product":1114}}}, // [Turner Sports Interactive, Inc.] + {"hash":"67dbd4ebfca8ed7c","prefixes":{"":{"product":1114}}}, // [Turner Sports Interactive, Inc.] + {"hash":"3ccffb010a6151f8","prefixes":{"":{"product":1115}}}, // [SnappyTV] + {"hash":"7d838005a39552d8","prefixes":{"":{"product":1116}}}, // [Target Media Partners] + {"hash":"003b7537bd9a7cfe","prefixes":{"":{"product":1116}}}, // [Target Media Partners] + {"hash":"26b33a79482ab7ab","prefixes":{"":{"product":1117}}}, // [KAIZEN platform Inc.] + {"hash":"15cdc1963890a6a2","prefixes":{"":{"product":1117}}}, // [KAIZEN platform Inc.] + {"hash":"1f36b920b7ac4ebb","prefixes":{"":{"product":1118}}}, // [Bidstalk Pte Ltd] + {"hash":"a59afaa1821362bd","prefixes":{"":{"product":1119}}}, // [ESPN] + {"hash":"e0edcd5fafde3e7a","prefixes":{"":{"product":1119}}}, // [ESPN] + {"hash":"7bd48d6255130db4","prefixes":{"":{"product":1120}}}, // [Online Media Group] + {"hash":"88c41fbe0db9a483","prefixes":{"":{"product":1120}}}, // [Online Media Group] + {"hash":"fc1080dbf4fae3f9","prefixes":{"":{"product":1120}}}, // [Online Media Group] + {"hash":"37fc205007f7a040","prefixes":{"":{"product":1120}}}, // [Online Media Group] + {"hash":"60c02a2ea01a737e","prefixes":{"":{"product":1121}}}, // [Luxury Link] + {"hash":"24108d30b52b9587","prefixes":{"":{"product":1122}}}, // [ESKIMI] + {"hash":"9de510a2d7f81a6e","prefixes":{"":{"product":1122}}}, // [ESKIMI] + {"hash":"d0e18c7e72b51bf3","prefixes":{"":{"product":1122}}}, // [ESKIMI] + {"hash":"6dbe16f5894e20af","prefixes":{"":{"product":1123}}}, // [AdsYolo Media] + {"hash":"5292ef4ba83623a8","prefixes":{"":{"product":1124}}}, // [Arrivalist] + {"hash":"b94285ae50bcb48e","prefixes":{"":{"product":1124}}}, // [Arrivalist] + {"hash":"cd7b432729ab016a","prefixes":{"":{"product":1124}}}, // [Arrivalist] + {"hash":"ae0442a300ebce47","prefixes":{"":{"product":1124}}}, // [Arrivalist] + {"hash":"3c8cc56d5b514ec5","prefixes":{"":{"product":1124}}}, // [Arrivalist] + {"hash":"794b2a51546b4f7f","prefixes":{"":{"product":1125}}}, // [MobileWebAdz Ltd] + {"hash":"223eacf97254c888","prefixes":{"":{"product":1125}}}, // [MobileWebAdz Ltd] + {"hash":"a3c5eeef285517f3","prefixes":{"":{"product":1125}}}, // [MobileWebAdz Ltd] + {"hash":"b222f9498fcdb24c","prefixes":{"":{"product":1126}}}, // [Demand Side Science, Inc.] + {"hash":"87af9974b30c5872","prefixes":{"":{"product":1126}}}, // [Demand Side Science, Inc.] + {"hash":"db4cd3e487418898","prefixes":{"":{"product":1126}}}, // [Demand Side Science, Inc.] + {"hash":"3fca6191597f44f2","prefixes":{"":{"product":1127}}}, // [SOCIETE FRANCAISE DU RADIOTELEPHONE] + {"hash":"6873487b11c1952e","prefixes":{"":{"product":1127}}}, // [SOCIETE FRANCAISE DU RADIOTELEPHONE] + {"hash":"d4b4ed3e1d07b00e","prefixes":{"":{"product":1127}}}, // [SOCIETE FRANCAISE DU RADIOTELEPHONE] + {"hash":"e647f502df0429f5","prefixes":{"":{"product":1128}}}, // [Mobitrans FZ LLC] + {"hash":"34f8fc43e14bad03","prefixes":{"":{"product":1128}}}, // [Mobitrans FZ LLC] + {"hash":"5f43507f5a877784","prefixes":{"":{"product":1128}}}, // [Mobitrans FZ LLC] + {"hash":"1466e6f77352194a","prefixes":{"":{"product":1128}}}, // [Mobitrans FZ LLC] + {"hash":"e097497d606d17ed","prefixes":{"":{"product":1128}}}, // [Mobitrans FZ LLC] + {"hash":"05c9b2eb92c8b5ba","prefixes":{"":{"product":1128}}}, // [Mobitrans FZ LLC] + {"hash":"ce3bfbe09ddeb858","prefixes":{"":{"product":1128}}}, // [Mobitrans FZ LLC] + {"hash":"3aa96b751d2c0d1c","prefixes":{"":{"product":1128}}}, // [Mobitrans FZ LLC] + {"hash":"289e565c9c686515","prefixes":{"":{"product":1129}}}, // [CoCo Reef] + {"hash":"0b6740b395a65857","prefixes":{"":{"product":1130}}}, // [Kumma DP LTD] + {"hash":"c00365ed4d1c464c","prefixes":{"":{"product":1131}}}, // [Cablato Limited] + {"hash":"9ee2a07ebdef206f","prefixes":{"":{"product":1131}}}, // [Cablato Limited] + {"hash":"e270375f27c5f31c","prefixes":{"":{"product":1131}}}, // [Cablato Limited] + {"hash":"0ebf573ebb6b30e1","prefixes":{"":{"product":1131}}}, // [Cablato Limited] + {"hash":"e28b4c253b1f854f","prefixes":{"":{"product":1132}}}, // [EURO DISNEY SCA] + {"hash":"3c8900c851ba9b28","prefixes":{"":{"product":1133}}}, // [Norstat] + {"hash":"39518e51adbe0cd7","prefixes":{"":{"product":1133}}}, // [Norstat] + {"hash":"2c5efd1ea532f0eb","prefixes":{"":{"product":1134}}}, // [John Varvatos] + {"hash":"5679368e6b2bb34c","prefixes":{"":{"product":1135}}}, // [Spritz Technology, Inc.] + {"hash":"60be7d8a6a76b62f","prefixes":{"":{"product":1135}}}, // [Spritz Technology, Inc.] + {"hash":"cefbe9240a377d95","prefixes":{"":{"product":1136}}}, // [Wix.com] + {"hash":"a56824227bb496f7","prefixes":{"*":{"product":1137}}}, // [TapTap Networks] + {"hash":"41f2ae7d2d6939ae","prefixes":{"":{"product":1137}}}, // [TapTap Networks] + {"hash":"51fd8b1d5b983b31","prefixes":{"":{"product":1138}}}, // [Permodo] + {"hash":"12c3d246c6d4e6b1","prefixes":{"":{"product":1138}}}, // [Permodo] + {"hash":"69da2bfa4e414e2c","prefixes":{"":{"product":1138}}}, // [Permodo] + {"hash":"67bb1e3460d8e2eb","prefixes":{"":{"product":1138}}}, // [Permodo] + {"hash":"38912558a11ff73e","prefixes":{"":{"product":1138}}}, // [Permodo] + {"hash":"eb4d36cf5e4909c1","prefixes":{"":{"product":1139}}}, // [Gaiam, Inc.] + {"hash":"16578371f24a5e90","prefixes":{"":{"product":1140}}}, // [Plusing interactive co.,Ltd] + {"hash":"ecdb68bd2622aa9e","prefixes":{"":{"product":42}}}, // [Clinch.co] + {"hash":"8adccb3847cc8b0f","prefixes":{"":{"product":42}}}, // [Clinch.co] + {"hash":"2b5433780334b542","prefixes":{"":{"product":42}}}, // [Clinch.co] + {"hash":"c853c92986d0cc90","prefixes":{"":{"product":42}}}, // [Clinch.co] + {"hash":"388da33e7f0adca1","prefixes":{"":{"product":42}}}, // [Clinch.co] + {"hash":"03d7a65aa1bc87bc","prefixes":{"":{"product":1141}}}, // [Paypersale] + {"hash":"176eb71c0938f4a5","prefixes":{"":{"product":111}}}, // [Epic Combo Malta Ltd.] + {"hash":"4366ec3ae9f41f7c","prefixes":{"":{"product":111}}}, // [Epic Combo Malta Ltd.] + {"hash":"2e9617ef727ff0b5","prefixes":{"":{"product":1142}}}, // [OCP Collective Corp. (d/b/a AdCade)] + {"hash":"11d6d13e3eb48a86","prefixes":{"":{"product":1142}}}, // [OCP Collective Corp. (d/b/a AdCade)] + {"hash":"20e14ef573248fec","prefixes":{"":{"product":1142}}}, // [OCP Collective Corp. (d/b/a AdCade)] + {"hash":"234345fe3c0da842","prefixes":{"":{"product":1142}}}, // [OCP Collective Corp. (d/b/a AdCade)] + {"hash":"30fb60c9c450221d","prefixes":{"":{"product":1142}}}, // [OCP Collective Corp. (d/b/a AdCade)] + {"hash":"69ddc7852224e18f","prefixes":{"":{"product":1143}}}, // [ESV Digital] + {"hash":"240174bad001108d","prefixes":{"":{"product":1144}}}, // [RevJet LLC.] + {"hash":"5250a7a3f2e4f73a","prefixes":{"":{"product":1144}}}, // [RevJet LLC.] + {"hash":"590feb6b793ba20a","prefixes":{"":{"product":1144}}}, // [RevJet LLC.] + {"hash":"23db13cb66f769e3","prefixes":{"":{"product":1145}}}, // [GET IT Mobile, Inc] + {"hash":"cd7cd6c16f714024","prefixes":{"":{"product":1145}}}, // [GET IT Mobile, Inc] + {"hash":"00ea35d76275dbeb","prefixes":{"":{"product":1145}}}, // [GET IT Mobile, Inc] + {"hash":"5239cdae00ff02ea","prefixes":{"":{"product":1146}}}, // [IBM] + {"hash":"e2c539bef8d2970c","prefixes":{"":{"product":1147}}}, // [Lucid Holdings, LLC] + {"hash":"7078be8707b5ec93","prefixes":{"":{"product":1147}}}, // [Lucid Holdings, LLC] + {"hash":"d65fe9ed9f23afbd","prefixes":{"":{"product":1148}}}, // [Stratio Big Data Inc.] + {"hash":"9ccfc7f2ecfbaeec","prefixes":{"":{"product":1148}}}, // [Stratio Big Data Inc.] + {"hash":"54e80254bbf4e20b","prefixes":{"":{"product":1149}}}, // [Tripping.com] + {"hash":"4aaf263b4bae6cd1","prefixes":{"":{"product":1150}}}, // [Vidible] + {"hash":"b782d6c10170e732","prefixes":{"":{"product":1150}}}, // [Vidible] + {"hash":"ceef29c52329c405","prefixes":{"":{"product":1150}}}, // [Vidible] + {"hash":"f14df6ca2e300bb9","prefixes":{"":{"product":1150}}}, // [Vidible] + {"hash":"514c355026c95637","prefixes":{"":{"product":1150}}}, // [Vidible] + {"hash":"b2529b7ebc0cbb27","prefixes":{"":{"product":1150}}}, // [Vidible] + {"hash":"14ebf9b0cbed945c","prefixes":{"":{"product":1150}}}, // [Vidible] + {"hash":"048147eea378ee03","prefixes":{"":{"product":1150}}}, // [Vidible] + {"hash":"7b61440491bd2a65","prefixes":{"":{"product":1151}}}, // [MiMTiD Corp] + {"hash":"036455e7801e853e","prefixes":{"":{"product":1151}}}, // [MiMTiD Corp] + {"hash":"e631f0342d59176b","prefixes":{"":{"product":1152}}}, // [Moloco, Inc.] + {"hash":"87938522c669c682","prefixes":{"":{"product":1152}}}, // [Moloco, Inc.] + {"hash":"a102c87f9921beb6","prefixes":{"":{"product":1153}}}, // [MEC SP. Z O.O] + {"hash":"f9133e4a069a0f4f","prefixes":{"":{"product":1153}}}, // [MEC SP. Z O.O] + {"hash":"6c7737c0ecbf0d91","prefixes":{"":{"product":1153}}}, // [MEC SP. Z O.O] + {"hash":"a82a0c18f6dfb959","prefixes":{"":{"product":1153}}}, // [MEC SP. Z O.O] + {"hash":"e1bea08ea38dc600","prefixes":{"":{"product":1154}}}, // [Abudantia LLC] + {"hash":"d60e1ada8dfa48fa","prefixes":{"":{"product":1154}}}, // [Abudantia LLC] + {"hash":"b57f7b99f9deda72","prefixes":{"":{"product":1154}}}, // [Abudantia LLC] + {"hash":"63f93976910e4aff","prefixes":{"":{"product":1154}}}, // [Abudantia LLC] + {"hash":"93a75f3f5174e472","prefixes":{"":{"product":1154}}}, // [Abudantia LLC] + {"hash":"ba1500537064494c","prefixes":{"":{"product":1155}}}, // [Realzeit] + {"hash":"ce1c3e183853dec6","prefixes":{"":{"product":1156}}}, // [Shanghai DigitalMatrix Information Technology Co.] + {"hash":"697384881bfb9a85","prefixes":{"":{"product":1156}}}, // [Shanghai DigitalMatrix Information Technology Co.] + {"hash":"3b5e1e5d71bb095f","prefixes":{"":{"product":1156}}}, // [Shanghai DigitalMatrix Information Technology Co.] + {"hash":"a55289ec43a1fd1c","prefixes":{"":{"product":1156}}}, // [Shanghai DigitalMatrix Information Technology Co.] + {"hash":"0ea122741b5a296d","prefixes":{"":{"product":1156}}}, // [Shanghai DigitalMatrix Information Technology Co.] + {"hash":"139c147db4a66d45","prefixes":{"":{"product":1156}}}, // [Shanghai DigitalMatrix Information Technology Co.] + {"hash":"91a98ad80489e2aa","prefixes":{"":{"product":1156}}}, // [Shanghai DigitalMatrix Information Technology Co.] + {"hash":"276f5a9d0ed20d4a","prefixes":{"":{"product":1156}}}, // [Shanghai DigitalMatrix Information Technology Co.] + {"hash":"8408cf6c93d2f24f","prefixes":{"":{"product":1157}}}, // [Alkemics] + {"hash":"658222dab2aa692d","prefixes":{"":{"product":1157}}}, // [Alkemics] + {"hash":"8835156415ef5c70","prefixes":{"":{"product":1157}}}, // [Alkemics] + {"hash":"7d76d0b7c2c49e1c","prefixes":{"":{"product":1158}}}, // [Sodel Software Solutions Pvt. Ltd.] + {"hash":"26b315a19bf762cb","prefixes":{"":{"product":1159}}}, // [Clearstream.TV, Inc] + {"hash":"80ce05889af3bb0e","prefixes":{"":{"product":1159}}}, // [Clearstream.TV, Inc] + {"hash":"2d9798f06f3cd0f8","prefixes":{"":{"product":1159}}}, // [Clearstream.TV, Inc] + {"hash":"4724e51f601e7bfa","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"e0f93294ef7c3cb7","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"ed1e0f5237be934f","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"4a0011b21f8d95f9","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"1af9efa73c3b3b29","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"49eea06f0c13da7f","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"584459c8cfe5595f","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"3b72d8e306588e71","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"f8d6e6d127d9c894","prefixes":{"":{"product":1161}}}, // [Adluxe] + {"hash":"93d864f5e43a3230","prefixes":{"":{"product":1161}}}, // [Adluxe] + {"hash":"793d5c9c5c35b0d9","prefixes":{"":{"product":1161}}}, // [Adluxe] + {"hash":"7b9c5864364d5f5e","prefixes":{"*":{"product":1162}}}, // [NinthDecimal] + {"hash":"7c86152a561c4b4b","prefixes":{"*":{"product":1162}}}, // [NinthDecimal] + {"hash":"ec998efdf99b0c51","prefixes":{"*":{"product":1163}}}, // [RICH MEDIA STUDIO] + {"hash":"e83c01ec741a261e","prefixes":{"":{"product":1164}}}, // [TenMax Co., Ltd.] + {"hash":"e333d70ac6d1acda","prefixes":{"":{"product":1165}}}, // [twiago GmbH] + {"hash":"bb81d5fd8a052b7f","prefixes":{"":{"product":1166}}}, // [Ad Dynamo International (Pty) Ltd] + {"hash":"55b2516509252adb","prefixes":{"":{"product":1166}}}, // [Ad Dynamo International (Pty) Ltd] + {"hash":"59e4e15b4fe6dccd","prefixes":{"":{"product":1166}}}, // [Ad Dynamo International (Pty) Ltd] + {"hash":"f5005d7a14ac0a47","prefixes":{"":{"product":1166}}}, // [Ad Dynamo International (Pty) Ltd] + {"hash":"ecf3dd9926cbe25e","prefixes":{"":{"product":1166}}}, // [Ad Dynamo International (Pty) Ltd] + {"hash":"2ccdffccbbf20e3f","prefixes":{"":{"product":1167}}}, // [Swarm Enterprises Inc] + {"hash":"bf0dd6fdbc53dfee","prefixes":{"":{"product":112}}}, // [Quixey] + {"hash":"ac6a75f10f9f0391","prefixes":{"":{"product":1168}}}, // [Media Forum] + {"hash":"2b5f5e731d08116b","prefixes":{"":{"product":1168}}}, // [Media Forum] + {"hash":"8bf2fe7f00b3c760","prefixes":{"":{"product":1169}}}, // [Beeswax.io] + {"hash":"480bd3bc762861ae","prefixes":{"":{"product":1170}}}, // [Varick Media Management] + {"hash":"d9faa36410c39273","prefixes":{"":{"product":1170}}}, // [Varick Media Management] + {"hash":"6114cbf7f45fd639","prefixes":{"":{"product":1171}}}, // [JD] + {"hash":"739568f083f383e2","prefixes":{"":{"product":1171}}}, // [JD] + {"hash":"1fc63d36c1d330fa","prefixes":{"":{"product":1171}}}, // [JD] + {"hash":"bdb07c050de0bbd2","prefixes":{"":{"product":1171}}}, // [JD] + {"hash":"6b6a891a61b05435","prefixes":{"":{"product":1172}}}, // [Lotlinx Inc.] + {"hash":"b7831e02a1168f7b","prefixes":{"":{"product":1172}}}, // [Lotlinx Inc.] + {"hash":"78f18ca9103e6031","prefixes":{"":{"product":1173}}}, // [Lotlinx Inc] + {"hash":"9a1f751c74a01145","prefixes":{"":{"product":40}}}, // [F# Inc.] + {"hash":"09ddf4e3268e6597","prefixes":{"":{"product":1174}}}, // [Ingenio, LLC] + {"hash":"c5dd0849ac2febc6","prefixes":{"":{"product":1175}}}, // [MVMT Watches] + {"hash":"3b471f8f08410883","prefixes":{"":{"product":1176}}}, // [C1X Inc] + {"hash":"1bb74ab211a41c9c","prefixes":{"":{"product":1177}}}, // [Vitro Agency] + {"hash":"96c0acfcd81b2dd4","prefixes":{"":{"product":1178}}}, // [Kabbage] + {"hash":"0e5b93a6bd6f704b","prefixes":{"":{"product":1179}}}, // [Redbranch, Inc. (dba Fraudlogix)] + {"hash":"a810be7236bf948f","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"ffe13e4342fd9a35","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"91d97869f45df6e0","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"c9f1716ec9d29c4b","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"4a9148e717cb46a9","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"9b57b296c2edd01c","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"1db72ea880eb28dc","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"61889d0078dd40a7","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"fd8f5971bf862430","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"d0da5b57fc3b9611","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"00449172b70d520c","prefixes":{"":{"product":1180}}}, // [AutoWeb, Inc.] + {"hash":"7b3027f5a6cb4f20","prefixes":{"":{"product":1181}}}, // [Aimee Soft Ltd.] + {"hash":"c29f58ba8b9fccc8","prefixes":{"":{"product":1181}}}, // [Aimee Soft Ltd.] + {"hash":"e8cb0d7824f08de4","prefixes":{"":{"product":1181}}}, // [Aimee Soft Ltd.] + {"hash":"97efe77626897346","prefixes":{"":{"product":1181}}}, // [Aimee Soft Ltd.] + {"hash":"78afe44858076c81","prefixes":{"":{"product":1181}}}, // [Aimee Soft Ltd.] + {"hash":"f5162869e573dcd7","prefixes":{"":{"product":1182}}}, // [SAS Azameo] + {"hash":"3d35f5eb2971f5fe","prefixes":{"":{"product":17}}}, // [iJento] + {"hash":"b98ddad6854c87b1","prefixes":{"":{"product":1183}}}, // [Keyade] + {"hash":"65aab53cd46ccc7f","prefixes":{"":{"product":1184}}}, // [Digital To Store (DTS)] + {"hash":"e1e5e1a4c7857257","prefixes":{"":{"product":1184}}}, // [Digital To Store (DTS)] + {"hash":"c9f6517091430433","prefixes":{"":{"product":1184}}}, // [Digital To Store (DTS)] + {"hash":"76d9ee307388af98","prefixes":{"":{"product":1184}}}, // [Digital To Store (DTS)] + {"hash":"8fdb48d3b5466119","prefixes":{"":{"product":1184}}}, // [Digital To Store (DTS)] + {"hash":"bd53e2fe0c663545","prefixes":{"":{"product":1185}}}, // [Media iQ Digital] + {"hash":"58b54a485703f6ba","prefixes":{"":{"product":1185}}}, // [Media iQ Digital] + {"hash":"35616fb4ea0feb94","prefixes":{"":{"product":1185}}}, // [Media iQ Digital] + {"hash":"0a1563142096ed40","prefixes":{"":{"product":1186}}}, // [Ingenious Technologies] + {"hash":"427d170b486c74e6","prefixes":{"":{"product":1186}}}, // [Ingenious Technologies] + {"hash":"79480a2b5de32430","prefixes":{"":{"product":1186}}}, // [Ingenious Technologies] + {"hash":"aaf44b046fb5c10d","prefixes":{"":{"product":1187}}}, // [Sonicmoov co.,ltd] + {"hash":"e5d30ca87b591280","prefixes":{"":{"product":1187}}}, // [Sonicmoov co.,ltd] + {"hash":"9ec9f8d7f0fc8c4a","prefixes":{"":{"product":1187}}}, // [Sonicmoov co.,ltd] + {"hash":"dd0bf16db1e39f59","prefixes":{"":{"product":1187}}}, // [Sonicmoov co.,ltd] + {"hash":"b32a4da2bc7363a0","prefixes":{"":{"product":1188}}}, // [Bonadza LLC] + {"hash":"66a81343ae0223df","prefixes":{"":{"product":1188}}}, // [Bonadza LLC] + {"hash":"ae76287d25b463d2","prefixes":{"":{"product":1188}}}, // [Bonadza LLC] + {"hash":"7429b926177d09a6","prefixes":{"":{"product":1188}}}, // [Bonadza LLC] + {"hash":"170cc20a9af99287","prefixes":{"":{"product":1189}}}, // [Axis Shift Limited] + {"hash":"6e94333e674450ec","prefixes":{"":{"product":1190}}}, // [TreSensa, Inc.] + {"hash":"8fac335d75d968fd","prefixes":{"":{"product":1190}}}, // [TreSensa, Inc.] + {"hash":"eec936c95af6b31f","prefixes":{"":{"product":1190}}}, // [TreSensa, Inc.] + {"hash":"3c744b206f15c46d","prefixes":{"":{"product":1190}}}, // [TreSensa, Inc.] + {"hash":"049a71c722403987","prefixes":{"":{"product":1191}}}, // [Media Logic Group LLC dba AerisWeather LLC] + {"hash":"efa6686cf4b6b73c","prefixes":{"":{"product":57}}}, // [DeinDeal AG] + {"hash":"847219545b169784","prefixes":{"":{"product":1192}}}, // [Fugumobile] + {"hash":"790ab0ec39724b71","prefixes":{"":{"product":1193}}}, // [Localstars Ltd] + {"hash":"0133a556a80fd808","prefixes":{"":{"product":1194}}}, // [Fluidads] + {"hash":"fc8901303bada188","prefixes":{"":{"product":1194}}}, // [Fluidads] + {"hash":"ba549c39b1f43523","prefixes":{"*":{"product":1195}}}, // [Wayfair LLC] + {"hash":"5d0b84261f7b6d5c","prefixes":{"*":{"product":1195}}}, // [Wayfair LLC] + {"hash":"d48dde21b5917906","prefixes":{"*":{"product":1195}}}, // [Wayfair LLC] + {"hash":"337dabe98702ae52","prefixes":{"*":{"product":1195}}}, // [Wayfair LLC] + {"hash":"efad84d0a4dd4e46","prefixes":{"*":{"product":1195}}}, // [Wayfair LLC] + {"hash":"019c3ee24427b355","prefixes":{"*":{"product":1195}}}, // [Wayfair LLC] + {"hash":"ec79c848ccf2d648","prefixes":{"*":{"product":1195}}}, // [Wayfair LLC] + {"hash":"ccf7fd5647b92698","prefixes":{"*":{"product":1195}}}, // [Wayfair LLC] + {"hash":"3b03a24eea2a6345","prefixes":{"":{"product":1196}}}, // [VCCORP CORPORATION] + {"hash":"f7f53c88139460cf","prefixes":{"":{"product":1197}}}, // [IGAWorks] + {"hash":"93b9518285f54887","prefixes":{"":{"product":1198}}}, // [Cellcom Ltd] + {"hash":"f59e6427783c3da0","prefixes":{"*":{"product":1199}}}, // [Upsolver] + {"hash":"adb55d98bb4aef6e","prefixes":{"":{"product":1199}}}, // [Upsolver] + {"hash":"7efba976eeea2d41","prefixes":{"":{"product":1199}}}, // [Upsolver] + {"hash":"cdde375c1240991d","prefixes":{"":{"product":935}}}, // [Channel Factory, LLC] + {"hash":"79c4dc57905857aa","prefixes":{"":{"product":1200}}}, // [Phluidmedia, Inc.] + {"hash":"4948ea141d1a72e1","prefixes":{"":{"product":1200}}}, // [Phluidmedia, Inc.] + {"hash":"e4aed5bf5c295c3a","prefixes":{"":{"product":1201}}}, // [Roy Morgan Research Ltd] + {"hash":"547734f17effda77","prefixes":{"":{"product":1202}}}, // [TapSense, Inc.] + {"hash":"96289aeef06d0a9f","prefixes":{"":{"product":1203}}}, // [Southwest Airlines] + {"hash":"de497c6fbed4588d","prefixes":{"":{"product":794}}}, // [Hatena Co., Ltd] + {"hash":"91412d534d321cb4","prefixes":{"":{"product":1204}}}, // [Market Points, Inc.] + {"hash":"97aab20dd873dd2c","prefixes":{"":{"product":1205}}}, // [UberMedia Inc.] + {"hash":"73cadeea8e1bd9e8","prefixes":{"":{"product":1205}}}, // [UberMedia Inc.] + {"hash":"0e1bd2938a06a2e6","prefixes":{"":{"product":1206}}}, // [Kadam SIA] + {"hash":"aa9658253aa81e62","prefixes":{"":{"product":1206}}}, // [Kadam SIA] + {"hash":"75f1eab33abfa4f9","prefixes":{"":{"product":1206}}}, // [Kadam SIA] + {"hash":"59f64b09b8e430ae","prefixes":{"":{"product":1206}}}, // [Kadam SIA] + {"hash":"61947a042af2e3e8","prefixes":{"":{"product":1207}}}, // [Alveo Platform (VMM own bidder)] + {"hash":"62c095e8dfbb0458","prefixes":{"":{"product":1208}}}, // [Adbalancer EDV-DienstleistungsgesellschaftgmbH] + {"hash":"5cc57103c26df76f","prefixes":{"":{"product":1208}}}, // [Adbalancer EDV-DienstleistungsgesellschaftgmbH] + {"hash":"53685a43fc711ea4","prefixes":{"":{"product":1209}}}, // [JWPlayer] + {"hash":"53bdf03af7a44e2c","prefixes":{"":{"product":1209}}}, // [JWPlayer] + {"hash":"eaff21a1a9591dab","prefixes":{"":{"product":1209}}}, // [JWPlayer] + {"hash":"1d5626ba7394e6a1","prefixes":{"":{"product":1209}}}, // [JWPlayer] + {"hash":"2e106bd1d1d064c6","prefixes":{"":{"product":1209}}}, // [JWPlayer] + {"hash":"9f7691306b0805ea","prefixes":{"":{"product":1209}}}, // [JWPlayer] + {"hash":"909df8f91bd33085","prefixes":{"*":{"product":1210}}}, // [Adiquity Technologies Pvt Ltd] + {"hash":"d1eca614d3cc8883","prefixes":{"":{"product":1211}}}, // [Smart Digital GmbH] + {"hash":"53d8de6b018c1793","prefixes":{"":{"product":1212}}}, // [Auditorius LLC] + {"hash":"fd9a420ade305e17","prefixes":{"":{"product":1213}}}, // [Treepodia] + {"hash":"c9fd06f055d4af40","prefixes":{"":{"product":1213}}}, // [Treepodia] + {"hash":"6eb24d470a6f1018","prefixes":{"":{"product":1213}}}, // [Treepodia] + {"hash":"e35bb07367786d4c","prefixes":{"":{"product":1214}}}, // [Adamatic] + {"hash":"3943df18ad20915d","prefixes":{"":{"product":1214}}}, // [Adamatic] + {"hash":"341d248dc25e4ea7","prefixes":{"":{"product":1214}}}, // [Adamatic] + {"hash":"ad1f1681bab5cc6b","prefixes":{"":{"product":1215}}}, // [SAS Web2ROI] + {"hash":"532b5bfa711f405e","prefixes":{"":{"product":1216}}}, // [Cardlytics] + {"hash":"799c283a6f1c8ece","prefixes":{"":{"product":1217}}}, // [MyFonts Inc.] + {"hash":"4448bee927dde7e6","prefixes":{"*":{"product":1218}}}, // [Bluecore, Inc.] + {"hash":"a0bd7b7a099833b6","prefixes":{"":{"product":1218}}}, // [Bluecore, Inc.] + {"hash":"fa3e77058c021551","prefixes":{"*":{"product":1218}}}, // [Bluecore, Inc.] + {"hash":"4761d233715098ce","prefixes":{"":{"product":1219}}}, // [EverQuote, Inc.] + {"hash":"35694b7df8059b22","prefixes":{"":{"product":1219}}}, // [EverQuote, Inc.] + {"hash":"0740880077130a2d","prefixes":{"":{"product":1220}}}, // [Optimize LCC D.B.A Genius Monkey] + {"hash":"bfe2c1b41278b804","prefixes":{"":{"product":1220}}}, // [Optimize LCC D.B.A Genius Monkey] + {"hash":"6dd3f6dcd500314e","prefixes":{"":{"product":1220}}}, // [Optimize LCC D.B.A Genius Monkey] + {"hash":"2f3f7cacd3fe0763","prefixes":{"*":{"product":1221}}}, // [EverString Technology Ltd] + {"hash":"bca80e9becb74dcb","prefixes":{"*":{"product":1221}}}, // [EverString Technology Ltd] + {"hash":"46edf29e9ba4ff96","prefixes":{"":{"product":1222}}}, // [Axonix Limited] + {"hash":"98961093c86583f5","prefixes":{"":{"product":1223}}}, // [Ubimo Ltd.] + {"hash":"1110ef291b2bd2e8","prefixes":{"":{"product":1224}}}, // [gskinner.com,Inc] + {"hash":"e36b044e9f38bad6","prefixes":{"":{"product":1225}}}, // [PlaceIQ, Inc.] + {"hash":"fe5b89fe7dbbd534","prefixes":{"":{"product":1226}}}, // [DataBerries] + {"hash":"ddf083a57d484592","prefixes":{"":{"product":1227}}}, // [Otto (GmbH & Co KG)] + {"hash":"7975f7864f4fa759","prefixes":{"":{"product":1228}}}, // [Beijing ADirects Technology Corporation Limited] + {"hash":"657d40c4d7322985","prefixes":{"":{"product":1229}}}, // [MagicGroup Asia Pte. Ltd.] + {"hash":"f5559eee02dd33ad","prefixes":{"":{"product":1229}}}, // [MagicGroup Asia Pte. Ltd.] + {"hash":"e81aa2815c378230","prefixes":{"":{"product":1229}}}, // [MagicGroup Asia Pte. Ltd.] + {"hash":"49269f9273c66c00","prefixes":{"":{"product":1230}}}, // [Local Content, Inc.] + {"hash":"210fa7d8376be47a","prefixes":{"":{"product":1230}}}, // [Local Content, Inc.] + {"hash":"e4ba8ed4e3abc143","prefixes":{"":{"product":1231}}}, // [Flavia] + {"hash":"269e0d49e8a941b8","prefixes":{"":{"product":43}}}, // [Transout Inc.] + {"hash":"b9cd0135bbe2cf40","prefixes":{"":{"product":43}}}, // [Transout Inc.] + {"hash":"e4b01f2271c4171a","prefixes":{"":{"product":44}}}, // [ADZIP] + {"hash":"e5b4daaa6c4ae231","prefixes":{"":{"product":44}}}, // [ADZIP] + {"hash":"cfb3a28bf24f27aa","prefixes":{"":{"product":44}}}, // [ADZIP] + {"hash":"db9ed30c0cbece36","prefixes":{"":{"product":44}}}, // [ADZIP] + {"hash":"dd0d7f22eaea16dd","prefixes":{"":{"product":44}}}, // [ADZIP] + {"hash":"4f3fc6e6fa75b1e8","prefixes":{"":{"product":44}}}, // [ADZIP] + {"hash":"17a3e1cbc6818d71","prefixes":{"*":{"product":1232}}}, // [Webtype] + {"hash":"39697008117e49f0","prefixes":{"":{"product":1233}}}, // [Aditor] + {"hash":"50bd67909486f9f5","prefixes":{"":{"product":1233}}}, // [Aditor] + {"hash":"d58ef2e47a1b5b3b","prefixes":{"":{"product":1234}}}, // [YCmedia] + {"hash":"fc8e2c447edda6c9","prefixes":{"":{"product":1235}}}, // [Addwish Aps] + {"hash":"d5ac956c9f185ddc","prefixes":{"":{"product":1236}}}, // [AIDO TECHNOLOGY INC.] + {"hash":"24015d44b9900d67","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"460c0600751cbd60","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"77050f5bbccffdb1","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"cdf17fb674f06ff0","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"8fb430c8ece16b19","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"c9d79e6f1f2694cd","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"80c75e37374a2ef0","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"5f9ca3a706ea1a32","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"2839cda166e75a72","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"762d13ee90c23c96","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"c0ea3a6ac5a15ff4","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"70c0b65944772c14","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"06d5db93059745e1","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"ea0e52e08087883d","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"dd48f2110336efda","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"bac37ceef6479f6b","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"a16d986f3ad09790","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"44addae7b850b89f","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"ef08ba4268518e6e","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"1f2560020ac4a5dd","prefixes":{"":{"product":1237}}}, // [Herolens Group LLC] + {"hash":"67339d8f9c205d19","prefixes":{"":{"product":1238}}}, // [Fuisz Media, Inc.] + {"hash":"b3ebdb16675afb79","prefixes":{"":{"product":1238}}}, // [Fuisz Media, Inc.] + {"hash":"33e8b1a10d6cf9bb","prefixes":{"":{"product":1238}}}, // [Fuisz Media, Inc.] + {"hash":"33c97b2942fbfb43","prefixes":{"":{"product":1239}}}, // [Bucksense, Inc.] + {"hash":"f7be89ec6f521ed4","prefixes":{"":{"product":664}}}, // [Adiant] + {"hash":"8ad32c44e7f009d9","prefixes":{"":{"product":1240}}}, // [Taylor Nelson Sofres Ukraine LLC] + {"hash":"9913ba5c46add495","prefixes":{"*":{"product":1241}}}, // [MotionLead] + {"hash":"ed341f6ed9722885","prefixes":{"":{"product":1242}}}, // [CJSC Recomendatsii tovarov i uslug] + {"hash":"b84298a2bc76c877","prefixes":{"":{"product":1242}}}, // [CJSC Recomendatsii tovarov i uslug] + {"hash":"d7bd450b5571f32e","prefixes":{"":{"product":1242}}}, // [CJSC Recomendatsii tovarov i uslug] + {"hash":"788739abab822f20","prefixes":{"":{"product":1242}}}, // [CJSC Recomendatsii tovarov i uslug] + {"hash":"5119113b90253a71","prefixes":{"":{"product":1242}}}, // [CJSC Recomendatsii tovarov i uslug] + {"hash":"8391f54ef2dd8235","prefixes":{"":{"product":1243}}}, // [Adobe Edge] + {"hash":"ecbec0da60be727e","prefixes":{"":{"product":1243}}}, // [Adobe Edge] + {"hash":"45df05b3b1fd2039","prefixes":{"":{"product":1243}}}, // [Adobe Edge] + {"hash":"7f8b63980ada138f","prefixes":{"":{"product":1244}}}, // [Bizible] + {"hash":"fcff0eb6c45bdf72","prefixes":{"":{"product":1245}}}, // [Adludio Limited] + {"hash":"58ae271664061630","prefixes":{"":{"product":1245}}}, // [Adludio Limited] + {"hash":"19795a3ce2a7e620","prefixes":{"":{"product":1245}}}, // [Adludio Limited] + {"hash":"aa3822d449c78db7","prefixes":{"":{"product":1245}}}, // [Adludio Limited] + {"hash":"b1269cde121b05a1","prefixes":{"":{"product":1245}}}, // [Adludio Limited] + {"hash":"1581c4afe0d48fa2","prefixes":{"":{"product":1246}}}, // [Ally Financial] + {"hash":"2e171be7640cf4cd","prefixes":{"":{"product":1247}}}, // [AIAd Ltd.] + {"hash":"ae7e973c225c0a01","prefixes":{"":{"product":1248}}}, // [Petplan] + {"hash":"78db497564cd4bf9","prefixes":{"bis":{"product":1249},"":{"product":1249},"openrtb":{"product":1249}}}, // [Vidazoo Ltd.] [Vidazoo Ltd.] [Vidazoo Ltd.] + {"hash":"c0fe1ea55404bcbb","prefixes":{"bis":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"f7fbe362db50024a","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"55abab5eb4ff10bd","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"e542a9dea283be31","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"a49f5bf572a22bae","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"7e02810393b6fe89","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"89c0cbd03222de92","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"31a98e1d5bba9ae8","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"15010236a7d3b5a4","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"9f927848d0417d12","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"7a2f5d3665e9a31a","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"02fb27611632d82c","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"0b95d00c8ef095ae","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"2d42c2d0a5ab84ae","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"5e405c19a402590e","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"d037ae4b54d7e7ec","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"fa3f8d822f471c8e","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"1fd9bf19c5906c01","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"f989a4e87da3e6d9","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"26937f461d535e88","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"755c357326a37b3f","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"aac0686686b2d53c","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"3ce944fa0cf0291a","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"7302f12cc4ace16b","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"52816f3d4542b887","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"59bf7df499fa52b5","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"ebabd6c7d62c634c","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"53ac53e2f538f2e2","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"3ed73abadb9c898a","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"9574295460ce8d22","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"d591fdb6045eb90f","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"8b39993907ef0e90","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"fff9c91a1d02e69d","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"d086a625516bdfc4","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"dedb3c41790f0b92","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"d9d28815b7351be9","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"0eceb2a8a6aa5014","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"c3cd28d4911901ef","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"906300510974cf1a","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"6bbcc411e84396ce","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"18f5beb4c7518ce0","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"445bbe1950cddd7b","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"2b7cc0f2bdc9e984","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"2e1ee42fbe53923e","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"0d75de8359eb1e90","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"5439763db9182a6c","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"924a269d7fa953ef","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"56592bc98da7add7","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"e350307b97e81944","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"62d2d232adb7f5ce","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"9dbd1f71df681f87","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"60aa872cca3ea408","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"574290c159276582","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"520b03b31ba99ffe","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"8927dc4db3ffd9a0","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"cea75e5e1bcb3e75","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"00fc1e5ba5cf11e1","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"241443df62a11bdb","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"958852e67d999446","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"d7dde4fdd1b42ac2","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"b9a6386a13d1f59c","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"1b00c21eb7dca0ba","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"3b5a0569a91c1f27","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"07b3c413dcf0891e","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"4b15aef39cd29476","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"25743e7a87ec9352","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"2fe16a518eee6e23","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"a154647a02beff09","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"73a06806ded66bc0","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"a82c6297f56276f8","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"514b9e5dcf88791d","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"80e10fe664c9676b","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"788de2248f446546","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"e1e58f0b49a0e5c7","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"c56afa9dadaace8c","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"4084c6a05941d212","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"fafe943a3ce4b828","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"00cfc10d1f469dfc","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"cdb86fef4fddecdd","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"97b3473c7082a4c8","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"99973a84d3bda612","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"ba67aff057d8dc5d","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"a3c0da72dbefc878","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"5925a6291dd2ab40","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"34129d9457495ffd","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"e2505c6002184e33","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"9bcac0e9fd77ddc6","prefixes":{"":{"product":1249}}}, // [Vidazoo Ltd.] + {"hash":"737c1868e55d5b37","prefixes":{"":{"product":1250}}}, // [RTBstar LLC] + {"hash":"302ec6d6755746d6","prefixes":{"*":{"product":1251}}}, // [Chalk Media Holdings] + {"hash":"202f611d1eab4195","prefixes":{"*":{"product":1251}}}, // [Chalk Media Holdings] + {"hash":"a6ab491761f03a1c","prefixes":{"":{"product":1252}}}, // [Oxford Biochronometrics] + {"hash":"3f8968d2bc62134f","prefixes":{"":{"product":1252}}}, // [Oxford Biochronometrics] + {"hash":"fbe6ab3bc9d21558","prefixes":{"":{"product":1252}}}, // [Oxford Biochronometrics] + {"hash":"769529a9ad14a9b6","prefixes":{"":{"product":1253}}}, // [SGN Games, Inc.] + {"hash":"f1a29a10c1ad80be","prefixes":{"*":{"product":1254}}}, // [Crutchfield New Media, LLC] + {"hash":"930e98a126b97c02","prefixes":{"":{"product":113}}}, // [YOOX NET-A-PORTER GROUP SPA] + {"hash":"915e8620d61e782c","prefixes":{"":{"product":113}}}, // [YOOX NET-A-PORTER GROUP SPA] + {"hash":"0492037eab3835e9","prefixes":{"":{"product":113}}}, // [YOOX NET-A-PORTER GROUP SPA] + {"hash":"5eee79daa9337871","prefixes":{"":{"product":1255}}}, // [Laserlike Inc] + {"hash":"3262121ae456ec62","prefixes":{"":{"product":1256}}}, // [Adtile Technologies Inc.] + {"hash":"eefeb24c8c0df59f","prefixes":{"":{"product":1256}}}, // [Adtile Technologies Inc.] + {"hash":"2f77e3292a158b3c","prefixes":{"":{"product":1256}}}, // [Adtile Technologies Inc.] + {"hash":"ed04984091cd6edc","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"63c7ce8ea1112262","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"842d35d55c520fe2","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"3fb91c004caeeba4","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"4673bf1b8fb41012","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"60c5047efc444089","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"9daf201eb861bd5a","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"8fb045042ba8dab7","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"b5c5f11aa533f8f8","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"e69760fd18a7e847","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"afdb50efeb58adb3","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"54ade0c2fc33d554","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"980a9356a0a0604b","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"0d064f41dd06b071","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"4e9cc6d069d0700f","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"abc81c5ff7313f61","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"4d8e54db683834b5","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"958c891ea7a22760","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"02b0aa7dbed4dc70","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"cd655ca6f3e7c5b4","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"117f6d5c385c0fbf","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"974b3e895eb889a1","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"2315e038cabe7cf3","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"256e6db135713c95","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"44b4921e1dd83428","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"2ececb465fbab022","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"b3a40f1c056c981d","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"c4f8f640f24e3592","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"0a6e85210f1d99c3","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"d12573269a748046","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"deabc924d996a1ca","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"cfae73045c7b29a6","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"c97db9d5ee0b71c7","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"ca6bb3442fa58646","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"f38fbdc9759c0ef4","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"6db47cd08bed3122","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"86c0634020121d1e","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"98c1045cd61f979d","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"7bdc1a49cf0ccc1d","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"cdceb4fead43eafb","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"b8a7ac4ace0ce6e9","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"081ccef35be56b36","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"e29de726004b6e06","prefixes":{"":{"product":1257}}}, // [Adgravity] + {"hash":"3044eb15f0757788","prefixes":{"":{"product":1258}}}, // [Protected Media LTD] + {"hash":"675f561d3f31df38","prefixes":{"":{"product":1258}}}, // [Protected Media LTD] + {"hash":"506fa56f8b748a67","prefixes":{"":{"product":1259}}}, // [Media Detect GmbH] + {"hash":"d8795d8df3c0b355","prefixes":{"":{"product":1260}}}, // [Centro CDN] + {"hash":"92690e6cf2068609","prefixes":{"":{"product":1261}}}, // [DeltaX] + {"hash":"bcb296520399e855","prefixes":{"":{"product":1261}}}, // [DeltaX] + {"hash":"eeba4d486f2a193f","prefixes":{"":{"product":1261}}}, // [DeltaX] + {"hash":"36ba79aa125ace1d","prefixes":{"":{"product":1261}}}, // [DeltaX] + {"hash":"755e0fb3a6d853e7","prefixes":{"":{"product":1261}}}, // [DeltaX] + {"hash":"926c7d8c47cdc6a4","prefixes":{"":{"product":1261}}}, // [DeltaX] + {"hash":"112f8b5a749d05e9","prefixes":{"":{"product":1261}}}, // [DeltaX] + {"hash":"9a9421d022c231d0","prefixes":{"":{"product":1262}}}, // [jQuery] + {"hash":"7ed84366898b6e95","prefixes":{"":{"product":1263}}}, // [SoMo Audience Corp.] + {"hash":"7b7b8f4fea812106","prefixes":{"":{"product":1263}}}, // [SoMo Audience Corp.] + {"hash":"cd27a627925b605e","prefixes":{"":{"product":1263}}}, // [SoMo Audience Corp.] + {"hash":"8de4d0c2e5a785d3","prefixes":{"":{"product":1263}}}, // [SoMo Audience Corp.] + {"hash":"dd090795aa41d39d","prefixes":{"":{"product":1263}}}, // [SoMo Audience Corp.] + {"hash":"a792d2d9ef8b7d44","prefixes":{"":{"product":1264}}}, // [Distribute Ltd] + {"hash":"a41d8bffbb4dde29","prefixes":{"c":{"product":1264}}}, // [Distribute Ltd] + {"hash":"cb03110aa3eafbc3","prefixes":{"":{"product":1264}}}, // [Distribute Ltd] + {"hash":"f8d5c1c3c0137b95","prefixes":{"":{"product":1264}}}, // [Distribute Ltd] + {"hash":"fdbd7675a0a4e4d2","prefixes":{"":{"product":1264}}}, // [Distribute Ltd] + {"hash":"c152daf3639bfd61","prefixes":{"":{"product":1265}}}, // [Poppin] + {"hash":"77afc197a9ed3c55","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"f000d51ba83c9b81","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"e6b0bd562a8a6c44","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"b67bb576e01ff3b8","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"b74cae0d407627c6","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"d02ca334243aeff6","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"27c4aebe8feb1d61","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"cffac58b1fe6a120","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"060b85b3672a94b9","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"993dbbd9a4428116","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"5d08c05fd8f3e6b6","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"733a8e48aa3496c3","prefixes":{"":{"product":1266}}}, // [EUROZEST MEDIA LIMITED/Avid Ad Server] + {"hash":"6dfff15240319d9a","prefixes":{"":{"product":1267}}}, // [Art of Click Pte. Ltd] + {"hash":"3771e4f81d75a524","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"b2d089ed26249e9c","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"6cb0920968e42462","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"779587babf9a845a","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"21fd838e467abf9e","prefixes":{"":{"product":45}}}, // [Adways SAS] + {"hash":"f35db5d8e41be046","prefixes":{"":{"product":1268}}}, // [Quantasy LLC] + {"hash":"fb881e61783803db","prefixes":{"":{"product":1269}}}, // [Wavenet Technology Co., Ltd.] + {"hash":"4a5a7154d849b6df","prefixes":{"":{"product":1270}}}, // [ENVISIONX LTD] + {"hash":"4c0fc55e8fe8be51","prefixes":{"":{"product":1271}}}, // [Adhood] + {"hash":"62b3383734c496fb","prefixes":{"":{"product":1271}}}, // [Adhood] + {"hash":"27e40c1642b59b0e","prefixes":{"":{"product":1271}}}, // [Adhood] + {"hash":"b7bcec8f502b24cf","prefixes":{"":{"product":1271}}}, // [Adhood] + {"hash":"b8885905bf9e213f","prefixes":{"":{"product":1271}}}, // [Adhood] + {"hash":"8238d8729bb3748c","prefixes":{"":{"product":1272}}}, // [Telogical Systems, LLC] + {"hash":"92dbc0e618f7816a","prefixes":{"":{"product":1272}}}, // [Telogical Systems, LLC] + {"hash":"d3be8ee2ff24c8dd","prefixes":{"":{"product":1272}}}, // [Telogical Systems, LLC] + {"hash":"0767b016186f9908","prefixes":{"":{"product":1273}}}, // [twyn group IT solutions & marketing services G] + {"hash":"1bf7b44093f4c0b5","prefixes":{"":{"product":1274}}}, // [Marchex Sales, LLC] + {"hash":"b60099c5a3ff5579","prefixes":{"":{"product":1275}}}, // [SmartyAds LLP] + {"hash":"e1e5cbc69ff27126","prefixes":{"":{"product":1275}}}, // [SmartyAds LLP] + {"hash":"01e34dcf6356f25b","prefixes":{"":{"product":1275}}}, // [SmartyAds LLP] + {"hash":"0587980840cbad28","prefixes":{"":{"product":1275}}}, // [SmartyAds LLP] + {"hash":"c13574923293d7c7","prefixes":{"":{"product":1276}}}, // [Reach150 Social, Inc.] + {"hash":"e87358da2b6ad070","prefixes":{"":{"product":1277}}}, // [Leadmill ApS] + {"hash":"2a24bd7dfe371075","prefixes":{"":{"product":1277}}}, // [Leadmill ApS] + {"hash":"3f6b6ba536add909","prefixes":{"":{"product":1278}}}, // [TapHeaven, Inc.] + {"hash":"050f2328d34461a7","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"a66968f05d34f468","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"2c309ae9bbbe9384","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"fec9fa0c1f3ffe6e","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"49989970caa95c3d","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"32eb6e39519c6d65","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"5ecf91c30b7a0a6a","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"6394e1f93c660614","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"3c6f41d29f5df8e7","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"cda46d94d7efda5c","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"164782d64f732408","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"af8c779439f17da0","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"0394176d4d575a93","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"c9a808631e01ad1a","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"1478be442db9d539","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"171f00354c5c6b43","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"521c6ad185dff69d","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"a7c6cb92086909f4","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"efb2ad70cb64944c","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"35e50ce841196503","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"683154a56472f3e2","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"6664d0e4be1a2011","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"efefa03759c8eb89","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"20477544dc10c916","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"922d2397edef5dbd","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"a9591e706ed0da18","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"c073c895bf061908","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"8a7804498832f633","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"45c2a9f766453c46","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"2a167f8c5d41c9de","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"0992810376313bba","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"e4588caa0c232726","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"73fbee089a438309","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"48743ee797fa8c98","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"22561af78d8aeebe","prefixes":{"":{"product":1279}}}, // [spring GmbH & Co. KG] + {"hash":"ee6f381082694c53","prefixes":{"":{"product":1280}}}, // [Roq.ad GmbH] + {"hash":"606e92e46118e68c","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"5dd554237e590e7a","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"47c55cdc2352963b","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"698477bb42f577d9","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"4d35613bf3c5608c","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"8bbcbb68e24aba75","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"b5d289baee77f3bb","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"1eab99893ccb6812","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"2d38a36747a2e4c8","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"2aaee44de4212440","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"720594080864ccd0","prefixes":{"":{"product":1281}}}, // [AdKernel LLC] + {"hash":"8cf8db96c95ad703","prefixes":{"":{"product":1282}}}, // [Uprise Technologies] + {"hash":"bdc43b963f1225e2","prefixes":{"":{"product":1283}}}, // [Sled, Inc.] + {"hash":"bd39b2a3b45ba87e","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"987e96fd53ac3377","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"ae49bd17dd4ead54","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"8d132f34a86f51b3","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"4210564496763546","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"c395c961fb4a60ae","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"2f37a86d54dbe4f9","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"04730e3fb984e716","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"8e040ece0573e008","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"a2aafd8d622f674d","prefixes":{"":{"product":1284}}}, // [Pengtai Interactive Advertising Co.Ltd] + {"hash":"4da566f98e41e44f","prefixes":{"":{"product":1285}}}, // [Adello Group AG] + {"hash":"469779ba0b2d7340","prefixes":{"":{"product":1286}}}, // [BitGravity] + {"hash":"bfa6d675fa0c17c3","prefixes":{"":{"product":1287}}}, // [44 Interactive] + {"hash":"ac3b5f670d7fd934","prefixes":{"*":{"product":1288}}}, // [KeyCDN] + {"hash":"1058dbbc2bdc3042","prefixes":{"*":{"product":1288}}}, // [KeyCDN] + {"hash":"d36cc93ea8fd1701","prefixes":{"":{"product":1289}}}, // [Shopalyst Technologies Pvt Ltd] + {"hash":"95ad856091f4bc84","prefixes":{"":{"product":1289}}}, // [Shopalyst Technologies Pvt Ltd] + {"hash":"848239e92ef8cdab","prefixes":{"":{"product":1290}}}, // [Total Access Communication Public Company Limited] + {"hash":"0607ff309fec62de","prefixes":{"":{"product":1291}}}, // [TACTIC Real-time marketing AS] + {"hash":"abcc33d7fb9de6d9","prefixes":{"":{"product":1292}}}, // [Mobilewalla, Inc] + {"hash":"c7bbc2be78119074","prefixes":{"":{"product":1293}}}, // [Nuviad Ltd] + {"hash":"63da29d5b072ec2a","prefixes":{"*":{"product":1294}}}, // [AmberData LLC] + {"hash":"6c2f34d23d272f4b","prefixes":{"":{"product":1295}}}, // [Aedgency] + {"hash":"2da837b490e2d01c","prefixes":{"":{"product":1296}}}, // [MobPartner, Inc.] + {"hash":"900c7f6444e6ccdd","prefixes":{"":{"product":1297}}}, // [AdTriba GmbH] + {"hash":"9110b2853fbfc1f4","prefixes":{"":{"product":1297}}}, // [AdTriba GmbH] + {"hash":"0a9e29016a6f697e","prefixes":{"":{"product":1298}}}, // [DISH Network L.L.C.] + {"hash":"8c7746484c824908","prefixes":{"":{"product":1299}}}, // [Monotype Imaging Inc.] + {"hash":"11826b3490b70597","prefixes":{"":{"product":1299}}}, // [Monotype Imaging Inc.] + {"hash":"55b9261efb54de8c","prefixes":{"":{"product":1299}}}, // [Monotype Imaging Inc.] + {"hash":"57f3bae0dc69937f","prefixes":{"":{"product":1299}}}, // [Monotype Imaging Inc.] + {"hash":"b5158a795c80a5a2","prefixes":{"":{"product":1300}}}, // [MEDIAN Ltd.] + {"hash":"e79098d30fde1a28","prefixes":{"":{"product":1301}}}, // [ClickForce Inc.] + {"hash":"36739070f3c0ff16","prefixes":{"":{"product":1301}}}, // [ClickForce Inc.] + {"hash":"40febfb412ba6ddb","prefixes":{"":{"product":1302}}}, // [Zemanta Inc.] + {"hash":"80e8d865a27bacac","prefixes":{"":{"product":237}}}, // [Hostelworld.com Limited] + {"hash":"2a3acfae7d4bdd9b","prefixes":{"":{"product":861}}}, // [Barometric] + {"hash":"a48dde8fab3a9ca1","prefixes":{"*":{"product":1303}}}, // [jsdelivr.com] + {"hash":"82b544dbc2fdd536","prefixes":{"*":{"product":1304}}}, // [Adssets AB] + {"hash":"46106972e3ebaddb","prefixes":{"":{"product":1305}}}, // [Sellpoints Inc.] + {"hash":"bbc8c43681364c14","prefixes":{"":{"product":46}}}, // [Opera Mediaworks Inc.] + {"hash":"a3ff6df6612f21df","prefixes":{"":{"product":46}}}, // [Opera Mediaworks Inc.] + {"hash":"759162b5823db802","prefixes":{"":{"product":1306}}}, // [HockeyCurve Growth Solutions Pvt Ltd] + {"hash":"b3f9f03fb2c77a95","prefixes":{"":{"product":1307}}}, // [HockeyCurve Growth Solutions Pyt Ltd] + {"hash":"0dcbeed5fe35278e","prefixes":{"":{"product":1307}}}, // [HockeyCurve Growth Solutions Pyt Ltd] + {"hash":"e6898006d78eca08","prefixes":{"":{"product":1308}}}, // [Umeng Plus Beijing Technology Limited Company] + {"hash":"c2ecb9f34950ae64","prefixes":{"":{"product":1308}}}, // [Umeng Plus Beijing Technology Limited Company] + {"hash":"e530de1f6ff8ed8c","prefixes":{"":{"product":1308}}}, // [Umeng Plus Beijing Technology Limited Company] + {"hash":"ef9efb2dfd9b223f","prefixes":{"":{"product":1308}}}, // [Umeng Plus Beijing Technology Limited Company] + {"hash":"8dad79c9e968f7ca","prefixes":{"":{"product":1309}}}, // [Survata, Inc.] + {"hash":"7a5110db80c0493f","prefixes":{"":{"product":1309}}}, // [Survata, Inc.] + {"hash":"e8cb0ee7c430e881","prefixes":{"":{"product":1310}}}, // [JustWatch GmbH] + {"hash":"c85404f2f558dbd2","prefixes":{"":{"product":1310}}}, // [JustWatch GmbH] + {"hash":"b44b63d6e81094e4","prefixes":{"":{"product":1310}}}, // [JustWatch GmbH] + {"hash":"e1614140e3cef4fd","prefixes":{"":{"product":1310}}}, // [JustWatch GmbH] + {"hash":"2cfd0204e2673c60","prefixes":{"":{"product":1311}}}, // [Interactive Tracker Next] + {"hash":"1c5f8f88ce98b360","prefixes":{"":{"product":1312}}}, // [Unisport A/S] + {"hash":"fbac71ab5dbe5025","prefixes":{"":{"product":1312}}}, // [Unisport A/S] + {"hash":"4e08c60f61b1ac87","prefixes":{"":{"product":1312}}}, // [Unisport A/S] + {"hash":"3f4bfa67b5290ada","prefixes":{"":{"product":1312}}}, // [Unisport A/S] + {"hash":"a9b4af4145bf90b2","prefixes":{"":{"product":1312}}}, // [Unisport A/S] + {"hash":"d0d20b9b53b41e58","prefixes":{"":{"product":1312}}}, // [Unisport A/S] + {"hash":"ff37598c00132a2a","prefixes":{"":{"product":1312}}}, // [Unisport A/S] + {"hash":"daae3b285b4abeee","prefixes":{"":{"product":1312}}}, // [Unisport A/S] + {"hash":"20cdb1f9a8066fe2","prefixes":{"":{"product":1313}}}, // [OPTDCOM TEKNOLOJİ YATIRIM ANONİM ŞİRKETİ] + {"hash":"a53281df69681314","prefixes":{"":{"product":1313}}}, // [OPTDCOM TEKNOLOJİ YATIRIM ANONİM ŞİRKETİ] + {"hash":"f25344f6f85b0f61","prefixes":{"":{"product":1313}}}, // [OPTDCOM TEKNOLOJİ YATIRIM ANONİM ŞİRKETİ] + {"hash":"b544d72a6726ee1d","prefixes":{"":{"product":1314}}}, // [Softcube Inc.] + {"hash":"d924c399f5813397","prefixes":{"":{"product":1315}}}, // [YOptima Media Solutions Pvt. Ltd] + {"hash":"3405e57b3b784a16","prefixes":{"":{"product":1316}}}, // [ZMAGS INC] + {"hash":"8a5251fd38cf1d3b","prefixes":{"":{"product":1316}}}, // [ZMAGS INC] + {"hash":"039987610c50338a","prefixes":{"":{"product":1316}}}, // [ZMAGS INC] + {"hash":"0f7d3c301f70dfd0","prefixes":{"":{"product":1316}}}, // [ZMAGS INC] + {"hash":"77f81cf76bb9bd07","prefixes":{"":{"product":1316}}}, // [ZMAGS INC] + {"hash":"ec6a93785f45f030","prefixes":{"":{"product":1316}}}, // [ZMAGS INC] + {"hash":"3fcf5b9a36bb776b","prefixes":{"":{"product":1316}}}, // [ZMAGS INC] + {"hash":"b81945950b163ad1","prefixes":{"":{"product":1307}}}, // [HockeyCurve Growth Solutions Pyt Ltd] + {"hash":"36c7ea48fdc42362","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"a3bfc331d20ba3e8","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"64a6cb69505f47a8","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"e7d85f73b7514aa9","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"0263af098607c6d6","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"5c911e8fdb9b3507","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"665d12aaa10c1858","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"206d02e9bc6dc66f","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"681210fb48272cc7","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"f42b80ceeefbc2e4","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"9b951656519bc7f0","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"f615949643fdcebb","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"814934c13235b868","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"d451d289ed990ba0","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"b2de6bb8607f747e","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"fa9dba37389c8a9e","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"fe776ee0016175b8","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"1a561a0db871620b","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"84440122e5725865","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"f98baedc7354d85b","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"cedef135a07c2d1b","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"b7196f696d55352b","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"90c8f9118f9c2653","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"7ec460729f94a446","prefixes":{"":{"product":1317}}}, // [Realtag] + {"hash":"c84baed94e3d3a6c","prefixes":{"":{"product":1318}}}, // [Bitsngo.net] + {"hash":"88c72a0edf15af94","prefixes":{"":{"product":1318}}}, // [Bitsngo.net] + {"hash":"8ffa9e7ae82fdaf3","prefixes":{"":{"product":1318}}}, // [Bitsngo.net] + {"hash":"b2b5e138d12de438","prefixes":{"":{"product":1318}}}, // [Bitsngo.net] + {"hash":"1249b88e4ce20439","prefixes":{"":{"product":1318}}}, // [Bitsngo.net] + {"hash":"9555623f08217ce6","prefixes":{"":{"product":1318}}}, // [Bitsngo.net] + {"hash":"0358d164ffcdc51b","prefixes":{"":{"product":1318}}}, // [Bitsngo.net] + {"hash":"d27b1458c8f91a54","prefixes":{"":{"product":1319}}}, // [AdCanvas] + {"hash":"5fd4bc3f3699146f","prefixes":{"":{"product":1319}}}, // [AdCanvas] + {"hash":"62f35089b9876ae5","prefixes":{"":{"product":1319}}}, // [AdCanvas] + {"hash":"92d701b4810ee717","prefixes":{"":{"product":1319}}}, // [AdCanvas] + {"hash":"3f927191adac8a66","prefixes":{"":{"product":1319}}}, // [AdCanvas] + {"hash":"051c9e70f0515e04","prefixes":{"":{"product":1320}}}, // [Happyfication, Inc.] + {"hash":"13580fc82945639c","prefixes":{"":{"product":1320}}}, // [Happyfication, Inc.] + {"hash":"914acd1211445b65","prefixes":{"":{"product":1320}}}, // [Happyfication, Inc.] + {"hash":"046e11a0cbc2124d","prefixes":{"":{"product":1320}}}, // [Happyfication, Inc.] + {"hash":"df5483bdd241c0b5","prefixes":{"":{"product":258}}}, // [HQ GmbH] + {"hash":"8ee2f36b1789e47d","prefixes":{"":{"product":1321},"*":{"product":1322}}}, // [Cinarra Systems Japan株式会社] [Cinarra Systems Japan] + {"hash":"92d76828c2bc5e86","prefixes":{"":{"product":959}}}, // [MotoMiner] + {"hash":"014c99288a655e93","prefixes":{"":{"product":1323}}}, // [CUBED Attribution] + {"hash":"34d44a28a42c30fc","prefixes":{"":{"product":1323}}}, // [CUBED Attribution] + {"hash":"6525e4a55892b035","prefixes":{"":{"product":1324}}}, // [StackAdapt Inc.] + {"hash":"9108433b6be75366","prefixes":{"":{"product":1324}}}, // [StackAdapt Inc.] + {"hash":"ca5914cdc3f95c5a","prefixes":{"":{"product":1324}}}, // [StackAdapt Inc.] + {"hash":"e25d7e6b7298b655","prefixes":{"":{"product":1325}}}, // [Statiq Ltd] + {"hash":"63a988f83e509eca","prefixes":{"":{"product":47}}}, // [DistroScale Inc.] + {"hash":"e6c8188134efb49f","prefixes":{"":{"product":47}}}, // [DistroScale Inc.] + {"hash":"eab29bab9fd4b969","prefixes":{"":{"product":47}}}, // [DistroScale Inc.] + {"hash":"c24f6dcb16cfd4e4","prefixes":{"":{"product":47}}}, // [DistroScale Inc.] + {"hash":"e6a95c6bb8d6efd4","prefixes":{"":{"product":1326}}}, // [Tagular Analytics, LLC] + {"hash":"9c2dcc6473dee219","prefixes":{"":{"product":1326}}}, // [Tagular Analytics, LLC] + {"hash":"986af10dd127ff07","prefixes":{"":{"product":1326}}}, // [Tagular Analytics, LLC] + {"hash":"f74107ff21d433e9","prefixes":{"":{"product":1326}}}, // [Tagular Analytics, LLC] + {"hash":"ce5c1378f1f941f6","prefixes":{"":{"product":1327}}}, // [ComboTag Technologies Ltd.] + {"hash":"879f914a15df34e0","prefixes":{"":{"product":1328}}}, // [Juice Mobile] + {"hash":"17e1300dbcc39afb","prefixes":{"":{"product":1328}}}, // [Juice Mobile] + {"hash":"d22e60c27d37cebc","prefixes":{"":{"product":1328}}}, // [Juice Mobile] + {"hash":"80d91748976f3124","prefixes":{"":{"product":1328}}}, // [Juice Mobile] + {"hash":"74478806654499c0","prefixes":{"":{"product":1328}}}, // [Juice Mobile] + {"hash":"1b02f5c5bfc0039d","prefixes":{"":{"product":1328}}}, // [Juice Mobile] + {"hash":"ee644eb69f62cdec","prefixes":{"":{"product":1329}}}, // [Bebe] + {"hash":"8fb13ead8b0754fb","prefixes":{"":{"product":1330}}}, // [Augur] + {"hash":"cfbc4ef83efc3949","prefixes":{"*":{"product":1331}}}, // [Seracast Digital LLC] + {"hash":"a8285782e9e5e7e8","prefixes":{"*":{"product":1331}}}, // [Seracast Digital LLC] + {"hash":"3a9b5e5bcd74ba27","prefixes":{"":{"product":1332}}}, // [AerServ LLC] + {"hash":"dd4e82ee18499196","prefixes":{"":{"product":1332}}}, // [AerServ LLC] + {"hash":"2163ccdaf0c7ac04","prefixes":{"*":{"product":1321}}}, // [Cinarra Systems Japan株式会社] + {"hash":"e16e6171a3e50ef3","prefixes":{"":{"product":1333}}}, // [PT. Tokopedia] + {"hash":"74b1225f4470815b","prefixes":{"":{"product":1334}}}, // [Terapeak, Inc.] + {"hash":"7eb7af632c622616","prefixes":{"*":{"product":1335}}}, // [OpenStreetMap France] + {"hash":"63c3a3e6536f0b7f","prefixes":{"*":{"product":1336}}}, // [Front Porch, Inc] + {"hash":"2c6aaec84cacd716","prefixes":{"*":{"product":1336}}}, // [Front Porch, Inc] + {"hash":"6f469a516e0ea586","prefixes":{"":{"product":1337}}}, // [Vertamedia] + {"hash":"d06485ea48298b7e","prefixes":{"":{"product":1338}}}, // [Codewise (VoluumDSP)] + {"hash":"7a43b35e42bcef39","prefixes":{"":{"product":1339}}}, // [Virool Inc.] + {"hash":"4ff7c90f1d23d419","prefixes":{"":{"product":1340}}}, // [SpringServe LLC] + {"hash":"7f06352affc07566","prefixes":{"":{"product":1340}}}, // [SpringServe LLC] + {"hash":"96ea998589a3f7ab","prefixes":{"":{"product":1340}}}, // [SpringServe LLC] + {"hash":"32ec7c3af8d6ee95","prefixes":{"":{"product":1340}}}, // [SpringServe LLC] + {"hash":"294e9804817557ed","prefixes":{"":{"product":1340}}}, // [SpringServe LLC] + {"hash":"e9c681d4af45ad2c","prefixes":{"":{"product":1341}}}, // [Intimate Merger] + {"hash":"68cc07724e870731","prefixes":{"":{"product":1341}}}, // [Intimate Merger] + {"hash":"60f3c72e07bc4431","prefixes":{"":{"product":1342}}}, // [Telecoming Connectivity S.A.] + {"hash":"22081b94e890803c","prefixes":{"":{"product":1343}}}, // [Webssup] + {"hash":"00e22da76abf1e12","prefixes":{"":{"product":1343}}}, // [Webssup] + {"hash":"498495a391901f22","prefixes":{"":{"product":1344}}}, // [INCUBIQ Solutions Ltd] + {"hash":"606c712ed1507693","prefixes":{"":{"product":1344}}}, // [INCUBIQ Solutions Ltd] + {"hash":"a9af6bbd9a022211","prefixes":{"*":{"product":1336}}}, // [Front Porch, Inc] + {"hash":"5ad9370da8c17992","prefixes":{"":{"product":1345}}}, // [ADSpend] + {"hash":"fdd6cbfd0c4ad857","prefixes":{"":{"product":1346}}}, // [AdTradr Corporation] + {"hash":"81b9923f85553957","prefixes":{"":{"product":1346}}}, // [AdTradr Corporation] + {"hash":"a145ad19f618929c","prefixes":{"":{"product":1346}}}, // [AdTradr Corporation] + {"hash":"b1930e60745f4b68","prefixes":{"":{"product":1346}}}, // [AdTradr Corporation] + {"hash":"57f97d99aaf44ad1","prefixes":{"":{"product":1346}}}, // [AdTradr Corporation] + {"hash":"99143cae9fe8dcb7","prefixes":{"":{"product":1347}}}, // [Quint Growth Inc.] + {"hash":"eb5cead7881dc3da","prefixes":{"":{"product":1348}}}, // [ZAPR] + {"hash":"2fe0bb9349d1959b","prefixes":{"":{"product":1348}}}, // [ZAPR] + {"hash":"b959bccbc132d322","prefixes":{"":{"product":20}}}, // [On Device Research Ltd.] + {"hash":"8a8c554522b206ad","prefixes":{"":{"product":1349}}}, // [1trn LLC] + {"hash":"45dfdbe509539a3b","prefixes":{"":{"product":1349}}}, // [1trn LLC] + {"hash":"6265ceee0906cac8","prefixes":{"":{"product":1350}}}, // [Matiro] + {"hash":"279177f20ce276af","prefixes":{"":{"product":1350}}}, // [Matiro] + {"hash":"b90e8e5f5f114049","prefixes":{"":{"product":1350}}}, // [Matiro] + {"hash":"68519953a094dc0c","prefixes":{"":{"product":1350}}}, // [Matiro] + {"hash":"2c9356fdc6a3d512","prefixes":{"":{"product":1350}}}, // [Matiro] + {"hash":"486312e316f3d301","prefixes":{"":{"product":1350}}}, // [Matiro] + {"hash":"0dea0840245a2dd2","prefixes":{"*":{"product":1351}}}, // [Inspired Mobile Ltd.] + {"hash":"e1e444909b156b50","prefixes":{"*":{"product":1351}}}, // [Inspired Mobile Ltd.] + {"hash":"ba50b6623ecf06b7","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"bb2433b4d9ceaf93","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"752e7bd32ca03e2b","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"fecd6600c1d54571","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"e2f5a8707a39a63f","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"6e7cd3eb7639d09a","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"e0afc2f779238a59","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"51c4028642a5ca4c","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"1640e1eeb06df964","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"6c8c2f363cf647b5","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"d9f31e1a90e8fb75","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"845fb9c6387bef97","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"0459341df70cc072","prefixes":{"":{"product":1352}}}, // [Selectmedia International LTD.] + {"hash":"be68419b316ae4c5","prefixes":{"":{"product":1353}}}, // [Internet Billboard, a. s] + {"hash":"ba9be76fe6cba556","prefixes":{"":{"product":1353}}}, // [Internet Billboard, a. s] + {"hash":"ec3aa5f11c754de6","prefixes":{"":{"product":1353}}}, // [Internet Billboard, a. s] + {"hash":"5023d333c14740b4","prefixes":{"":{"product":1353}}}, // [Internet Billboard, a. s] + {"hash":"eab4b496bc09743b","prefixes":{"":{"product":1354}}}, // [Volvelle] + {"hash":"2afdaf8d1bad3cb9","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"c2367faa0f667908","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"ed58e98b46833296","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"e0249363e0c7a5fa","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"3f6dfa5fad816543","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"0daca079a0ddd2c5","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"eb2192ea666d6dfe","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"6a1269cf9dab66ee","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"39079f1183b1a260","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"9455a8e3fa7e9a01","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"bebc1faa9d73b7b0","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"c5069619b84c740e","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"8effee3cd53dcfe8","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"8251bfe6accaab04","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"f1019f4619cbfe25","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"825efa7e6eea91b6","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"cdeee95936c4dbc6","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"812ac20217e10e22","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"a20318efcc942b0c","prefixes":{"":{"product":1355}}}, // [StreamRail Ltd.] + {"hash":"7ba3379a648cbc77","prefixes":{"":{"product":1356}}}, // [E-Contenta] + {"hash":"faca10de01f9747f","prefixes":{"":{"product":1356}}}, // [E-Contenta] + {"hash":"c4bada2290c2b41f","prefixes":{"":{"product":1356}}}, // [E-Contenta] + {"hash":"7e8290508a81ecfd","prefixes":{"":{"product":1356}}}, // [E-Contenta] + {"hash":"415fbdfa7cf95914","prefixes":{"":{"product":1356}}}, // [E-Contenta] + {"hash":"97322010e9179343","prefixes":{"":{"product":1356}}}, // [E-Contenta] + {"hash":"07549a667fcd0003","prefixes":{"":{"product":1356}}}, // [E-Contenta] + {"hash":"f5359f09115e5a08","prefixes":{"":{"product":1357}}}, // [Bath and Body Works] + {"hash":"93931937deeacde2","prefixes":{"":{"product":1160}}}, // [KuaiziTech] + {"hash":"885efbcb155efd68","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"8515f5e803de693b","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"7990fea9b4cdbdf9","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"a6632e804a3dc853","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"ae4745b0fd104fbb","prefixes":{"":{"product":691}}}, // [Aniview LTD.] + {"hash":"956dc47eeadd36f7","prefixes":{"":{"product":1358}}}, // [WooTag Pte Ltd] + {"hash":"091b478316a2c133","prefixes":{"":{"product":1358}}}, // [WooTag Pte Ltd] + {"hash":"dc8fa04b592b8307","prefixes":{"":{"product":1358}}}, // [WooTag Pte Ltd] + {"hash":"fe0e326ac7dc8d7c","prefixes":{"":{"product":1358}}}, // [WooTag Pte Ltd] + {"hash":"410a107d660ab1cd","prefixes":{"":{"product":1359}}}, // [Cint AB] + {"hash":"b294fa0e55fcd2f4","prefixes":{"":{"product":1359}}}, // [Cint AB] + {"hash":"5519fa5da5e57ad1","prefixes":{"":{"product":1360}}}, // [Stein Mart] + {"hash":"7277d1fc5bc3c416","prefixes":{"":{"product":1361}}}, // [Sift Media, Inc.] + {"hash":"4b89510469e15939","prefixes":{"":{"product":1362}}}, // [StartApp Inc.] + {"hash":"d3b5600174198250","prefixes":{"":{"product":1362}}}, // [StartApp Inc.] + {"hash":"5597070a34018ed8","prefixes":{"":{"product":1362}}}, // [StartApp Inc.] + {"hash":"17f290970639d124","prefixes":{"":{"product":1363}}}, // [Pxene - DSP] + {"hash":"263ee2028bc08b39","prefixes":{"":{"product":1363}}}, // [Pxene - DSP] + {"hash":"da0f3a897355dd67","prefixes":{"":{"product":1363}}}, // [Pxene - DSP] + {"hash":"9ca84ba93389421b","prefixes":{"":{"product":1363}}}, // [Pxene - DSP] + {"hash":"4d5f045a10baa819","prefixes":{"":{"product":1364}}}, // [Integral Marketing] + {"hash":"50bc395b6101613b","prefixes":{"":{"product":1364}}}, // [Integral Marketing] + {"hash":"3affd533937e3000","prefixes":{"":{"product":48}}}, // [Enzymic Consultancy] + {"hash":"971d313a32301476","prefixes":{"":{"product":48}}}, // [Enzymic Consultancy] + {"hash":"c5351f32e51be2fe","prefixes":{"":{"product":48}}}, // [Enzymic Consultancy] + {"hash":"4d91e6834270bed5","prefixes":{"":{"product":48}}}, // [Enzymic Consultancy] + {"hash":"5d662bce285c4f62","prefixes":{"":{"product":48}}}, // [Enzymic Consultancy] + {"hash":"acf96648fc83494b","prefixes":{"":{"product":48}}}, // [Enzymic Consultancy] + {"hash":"ea62a03194f9fd95","prefixes":{"":{"product":1365}}}, // [Expedia, Inc.] + {"hash":"d3ff1633dded1329","prefixes":{"":{"product":1366}}}, // [DeepIntent, Inc] + {"hash":"2b5d6ad4574423ab","prefixes":{"":{"product":1366}}}, // [DeepIntent, Inc] + {"hash":"dd8c5b641bb2871c","prefixes":{"":{"product":1366}}}, // [DeepIntent, Inc] + {"hash":"4ba3d981f5073f12","prefixes":{"":{"product":1366}}}, // [DeepIntent, Inc] + {"hash":"92f845f3cbfc83ca","prefixes":{"":{"product":181}}}, // [Rakuten Attribution] + {"hash":"ff93f30e29dd79ce","prefixes":{"":{"product":1367}}}, // [OmniVirt] + {"hash":"1219256c57d362c2","prefixes":{"":{"product":1367}}}, // [OmniVirt] + {"hash":"588a30ada75d3716","prefixes":{"":{"product":1367}}}, // [OmniVirt] + {"hash":"f99b3a1ad217ec24","prefixes":{"":{"product":1367}}}, // [OmniVirt] + {"hash":"cffe29c52b1b73c5","prefixes":{"":{"product":1367}}}, // [OmniVirt] + {"hash":"f03d942c23c65a90","prefixes":{"":{"product":1367}}}, // [OmniVirt] + {"hash":"272d4c035a088de6","prefixes":{"":{"product":1367}}}, // [OmniVirt] + {"hash":"724568d0c3fc08a4","prefixes":{"":{"product":1368}}}, // ["Index20" LLC] + {"hash":"be108323d132726d","prefixes":{"":{"product":1369}}}, // [Conversion Logic, Inc.] + {"hash":"9fc3b91614502933","prefixes":{"":{"product":1370}}}, // [Collegehumor] + {"hash":"bb96895ad0b3fb46","prefixes":{"":{"product":1370}}}, // [Collegehumor] + {"hash":"830074aed1a53d22","prefixes":{"":{"product":1370}}}, // [Collegehumor] + {"hash":"db32b2c0136553c9","prefixes":{"":{"product":775}}}, // [Netflix, Inc.] + {"hash":"47df197461b802d5","prefixes":{"":{"product":775}}}, // [Netflix, Inc.] + {"hash":"acbf0ad8badc6915","prefixes":{"":{"product":775}}}, // [Netflix, Inc.] + {"hash":"c0b6c8d5b1736dc3","prefixes":{"":{"product":775}}}, // [Netflix, Inc.] + {"hash":"05dd2a6c687d34c0","prefixes":{"":{"product":1371}}}, // [Silence Media Limited] + {"hash":"cea7d6ed17648fbf","prefixes":{"":{"product":1371}}}, // [Silence Media Limited] + {"hash":"ad7149f316532231","prefixes":{"":{"product":1372}}}, // [Zuuvi Aps] + {"hash":"693751faddbb5801","prefixes":{"":{"product":1372}}}, // [Zuuvi Aps] + {"hash":"8da87b729d579d09","prefixes":{"":{"product":1372}}}, // [Zuuvi Aps] + {"hash":"ab5cc3951d346c77","prefixes":{"":{"product":1372}}}, // [Zuuvi Aps] + {"hash":"f41a4b1ac6a3bfb2","prefixes":{"":{"product":1373}}}, // [SoftBank Corp.] + {"hash":"1d0977195dc18825","prefixes":{"":{"product":1374}}}, // [ConnectOM, Inc.] + {"hash":"912365bfc81f780a","prefixes":{"":{"product":1374}}}, // [ConnectOM, Inc.] + {"hash":"7434a888611cfaf2","prefixes":{"":{"product":1374}}}, // [ConnectOM, Inc.] + {"hash":"1d73404a4d6b3cda","prefixes":{"":{"product":49}}}, // [Fluct Inc.] + {"hash":"938fa93131d63045","prefixes":{"":{"product":49}}}, // [Fluct Inc.] + {"hash":"67f35499d1bf5216","prefixes":{"":{"product":1375}}}, // [Skillup Video Technologies Corporation] + {"hash":"c8e2c0c42c388ceb","prefixes":{"":{"product":1375}}}, // [Skillup Video Technologies Corporation] + {"hash":"0800074e9a8aa9e0","prefixes":{"":{"product":1375}}}, // [Skillup Video Technologies Corporation] + {"hash":"f6853fba1d5c8366","prefixes":{"":{"product":1376}}}, // [Adara Impact Analytics] + {"hash":"7d5aa0875b5b03ee","prefixes":{"":{"product":1376}}}, // [Adara Impact Analytics] + {"hash":"e1a55205d635d049","prefixes":{"":{"product":1376}}}, // [Adara Impact Analytics] + {"hash":"b5ea51bb0aec174c","prefixes":{"":{"product":1376}}}, // [Adara Impact Analytics] + {"hash":"e4140b78afda4fb4","prefixes":{"":{"product":1376}}}, // [Adara Impact Analytics] + {"hash":"a4c74032025e0589","prefixes":{"":{"product":1377}}}, // [TabMo SAS] + {"hash":"d9638d78485cff41","prefixes":{"":{"product":1377}}}, // [TabMo SAS] + {"hash":"6ea3e003ed64d138","prefixes":{"":{"product":58}}}, // [Sixt Leasing SE] + {"hash":"22ae0beb131c4b8c","prefixes":{"":{"product":1378}}}, // [Casale Media] + {"hash":"29d1aaca6b9e6edd","prefixes":{"":{"product":1378}}}, // [Casale Media] + {"hash":"7d1380d196e0f347","prefixes":{"":{"product":1379}}}, // [StickyADStv] + {"hash":"147ac7b767355a32","prefixes":{"":{"product":1380}}}, // [Teads Technology SAS] + {"hash":"4b60266ba860a4c5","prefixes":{"":{"product":1381}}}, // [Anomaly Communications, LLC] + {"hash":"296893a9570ca935","prefixes":{"*":{"product":1382}}}, // [ClickTicker, LTD] + {"hash":"094df71f27ebd6f1","prefixes":{"":{"product":1383}}}, // [Tremour] + {"hash":"a1119d262b86de34","prefixes":{"":{"product":1384}}}, // [Roket Media LTD] + {"hash":"b1ed20b79d8a43cb","prefixes":{"":{"product":152}}}, // [e-Planning] + {"hash":"5bd05c801bb32096","prefixes":{"":{"product":1385}}}, // [Video Jam (MauDau LTD)] + {"hash":"dacf613410c17f5e","prefixes":{"":{"product":1385}}}, // [Video Jam (MauDau LTD)] + {"hash":"561f104ba38d6e64","prefixes":{"":{"product":1385}}}, // [Video Jam (MauDau LTD)] + {"hash":"a43509d6a08d1777","prefixes":{"":{"product":1386}}}, // [MINIMOB (CY) LTD] + {"hash":"b66514ac6530a3fd","prefixes":{"":{"product":1387}}}, // [Snitcher B.V.] + {"hash":"79b3660bde132ba1","prefixes":{"":{"product":1387}}}, // [Snitcher B.V.] + {"hash":"aaf7781b800509ba","prefixes":{"":{"product":1387}}}, // [Snitcher B.V.] + {"hash":"8fec6c672f5b204a","prefixes":{"":{"product":1387}}}, // [Snitcher B.V.] + {"hash":"825e7df4dfb9bae0","prefixes":{"":{"product":1387}}}, // [Snitcher B.V.] + {"hash":"379899626d07c3b5","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"664ce6265f73ec6a","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"ab33f6650feb44c2","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"557404c787c545cb","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"0c881f692e8ec0de","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"36c7264c1d06cc29","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"2f7b75786675821b","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"714fd218f12fce7a","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"d140a5f747a666f1","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"cbcfd9d592f2d19d","prefixes":{"":{"product":1388}}}, // [Analights] + {"hash":"a214f6ac537cafa2","prefixes":{"":{"product":1389}}}, // [Romir Panel Ltd.] + {"hash":"84fe21a8c3ba3818","prefixes":{"":{"product":1390}}}, // [Dievision] + {"hash":"c709bff0d2284ecc","prefixes":{"":{"product":1391}}}, // [Ignite Technologies] + {"hash":"3510f10ff5914868","prefixes":{"*":{"product":1392}}}, // [Navegg] + {"hash":"ea594377060cafc6","prefixes":{"":{"product":1393}}}, // [Adalyser] + {"hash":"631a68a191554bae","prefixes":{"":{"product":1393}}}, // [Adalyser] + {"hash":"8d9638d9355607ba","prefixes":{"":{"product":1393}}}, // [Adalyser] + {"hash":"d6e288fc5cf14c83","prefixes":{"":{"product":1393}}}, // [Adalyser] + {"hash":"90ec5cb29381e1ca","prefixes":{"":{"product":1393}}}, // [Adalyser] + {"hash":"3debdfa22ee00230","prefixes":{"":{"product":1393}}}, // [Adalyser] + {"hash":"45852dff86c0a7e0","prefixes":{"":{"product":1394}}}, // [Nordic Factory International Inc.] + {"hash":"063e04585364c0e5","prefixes":{"":{"product":719}}}, // [E-Plus Mobilfunk GmbH & Co. KG] + {"hash":"aa4bfba1bb15cb76","prefixes":{"":{"product":719}}}, // [E-Plus Mobilfunk GmbH & Co. KG] + {"hash":"3f43b05864c28c77","prefixes":{"":{"product":1395}}}, // [Addition Plus Ltd] + {"hash":"9e1057ca77b6610c","prefixes":{"":{"product":1395}}}, // [Addition Plus Ltd] + {"hash":"a6278ebdf3ca7bce","prefixes":{"":{"product":1396}}}, // [Karmatech Mediaworks Pvt Ltd] + {"hash":"954044969f047c6c","prefixes":{"":{"product":1396}}}, // [Karmatech Mediaworks Pvt Ltd] + {"hash":"ca32fcf3a3101ca0","prefixes":{"":{"product":1397}}}, // [Mediatropy Pte Ltd] + {"hash":"54a24d41044c0730","prefixes":{"":{"product":26}}}, // [FSN ASIA PRIVATE LIMITED] + {"hash":"b3302c4e4fc23cb8","prefixes":{"":{"product":856}}}, // [Adman Interactive SL] + {"hash":"328eed54ff330e78","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"98ed7d7c1ae050b7","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"226e8c98a3e2e091","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"9e777471181caa70","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"9df613858a859407","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"37af06459214925d","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"0fdc04aa3cd4e402","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"f33f14a479b17a13","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"535ad6947981986b","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"0db16a6e891b1ff5","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"11b3c502d4eb1fa3","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"001bd6970eef2bbe","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"93505db1f9bf4a47","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"a1c9ac37054fc828","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"f4f375e269f34a60","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"e61b3b137beecfbc","prefixes":{"":{"product":1398}}}, // [Wayve Limited] + {"hash":"07bf22fe56be8d20","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"619e27568b55894c","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"374b60567f3c7f8b","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"af11cb377f269b3b","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"6bd694da478fa87b","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"75c4aedcbc945c91","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"71e8a51762fe81b6","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"5412f8017660207b","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"ba29363e3e139066","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"05de1e7e9f3271c3","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"5d73b4b9b8705846","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"711d9c4ce084aa7d","prefixes":{"*":{"product":1399}}}, // [Kreditech Holding SSL GmbH] + {"hash":"1ed6fde4c65ef6f7","prefixes":{"*":{"product":269}}}, // [DePauli AG] + {"hash":"4a6b1490dc059d0b","prefixes":{"*":{"product":269}}}, // [DePauli AG] + {"hash":"cab81dbe20c0957f","prefixes":{"*":{"product":269}}}, // [DePauli AG] + {"hash":"af1f3ce39e3b4862","prefixes":{"*":{"product":269}}}, // [DePauli AG] + {"hash":"19e97b139ae15342","prefixes":{"*":{"product":269}}}, // [DePauli AG] + {"hash":"fb46fee1ceef4719","prefixes":{"*":{"product":269}}}, // [DePauli AG] + {"hash":"798bda76da15abeb","prefixes":{"*":{"product":21}}}, // [SuperVista AG] + {"hash":"38bf7a598ad0c498","prefixes":{"*":{"product":21}}}, // [SuperVista AG] + {"hash":"48651b09d312b814","prefixes":{"*":{"product":21}}}, // [SuperVista AG] + {"hash":"bb2482d6022a94ba","prefixes":{"*":{"product":21}}}, // [SuperVista AG] + {"hash":"d58f8af9ff4e691e","prefixes":{"":{"product":1400}}}, // [Yengo] + {"hash":"8e9bee1bb54d54ee","prefixes":{"":{"product":1400}}}, // [Yengo] + {"hash":"06689ed650e2b8c3","prefixes":{"":{"product":1401}}}, // [DANtrack] + {"hash":"96c97b30a03579ac","prefixes":{"":{"product":1401}}}, // [DANtrack] + {"hash":"407face2b2c27397","prefixes":{"":{"product":1402}}}, // [Integral Markt- und Meinungsforschungsges.m.b.H.] + {"hash":"184f80ef53a0f178","prefixes":{"":{"product":1402}}}, // [Integral Markt- und Meinungsforschungsges.m.b.H.] + {"hash":"076a460590b496bf","prefixes":{"":{"product":1403}}}, // [Millemedia GmbH] + {"hash":"9febeed138f5f2ce","prefixes":{"":{"product":1404}}}, // [Hiro-Media] + {"hash":"dd35fc0a5f5fa07b","prefixes":{"":{"product":1404}}}, // [Hiro-Media] + {"hash":"b22687a0d4bd341f","prefixes":{"":{"product":1404}}}, // [Hiro-Media] + {"hash":"1625640192348305","prefixes":{"*":{"product":1405}}}, // [Simplaex Gmbh] + {"hash":"444356542461e7d2","prefixes":{"":{"product":1405}}}, // [Simplaex Gmbh] + {"hash":"b9edac442e488ddf","prefixes":{"":{"product":1405}}}, // [Simplaex Gmbh] + {"hash":"afed9502218fc1cd","prefixes":{"":{"product":1406}}}, // [Telekom Deutschland GmbH] + {"hash":"286d994374ce8a57","prefixes":{"":{"product":1407}}}, // [Infernotions Technologies Limited] + {"hash":"385225c96e123b3f","prefixes":{"":{"product":1408}}}, // [Goldfish Media LLC] + {"hash":"19c11b9ad47802f9","prefixes":{"":{"product":1365}}}, // [Expedia, Inc.] + {"hash":"d710ea66b904cd82","prefixes":{"":{"product":1409}}}, // [Smartology Limited] + {"hash":"24bbec831a84a114","prefixes":{"":{"product":1409}}}, // [Smartology Limited] + {"hash":"9c3279986e25304c","prefixes":{"":{"product":1409}}}, // [Smartology Limited] + {"hash":"8ce94299572dc9b8","prefixes":{"":{"product":1409}}}, // [Smartology Limited] + {"hash":"e51f784e66ceb1cb","prefixes":{"":{"product":1410}}}, // [GroupM] + {"hash":"72bd4ec98dea633b","prefixes":{"":{"product":1410}}}, // [GroupM] + {"hash":"0836c753510a4fd6","prefixes":{"*":{"product":1411}}}, // [Haystagg Inc.] + {"hash":"4a13b1d65c372006","prefixes":{"*":{"product":1411}}}, // [Haystagg Inc.] + {"hash":"14ec018232a87385","prefixes":{"":{"product":1412}}}, // [Digital Turbine Media, Inc] + {"hash":"fdabfb7535e313a7","prefixes":{"":{"product":1412}}}, // [Digital Turbine Media, Inc] + {"hash":"cddef42a99b1f0e2","prefixes":{"":{"product":1413}}}, // [MDSP INC] + {"hash":"1bcc84762c0f2df8","prefixes":{"":{"product":1414}}}, // [Quadas (YiDong Data Inc.)] + {"hash":"6c46a69428837566","prefixes":{"":{"product":1415}}}, // [Recruit Career Co., Ltd.] + {"hash":"17319e35b3b89b68","prefixes":{"":{"product":1416}}}, // [R2Net Inc.] + {"hash":"0825a9da5dd17373","prefixes":{"":{"product":1416}}}, // [R2Net Inc.] + {"hash":"aaa00ce722ea0d35","prefixes":{"":{"product":1416}}}, // [R2Net Inc.] + {"hash":"40b683cc8c061f6f","prefixes":{"":{"product":1417}}}, // [Madberry OY] + {"hash":"ea76804ad8af9b5c","prefixes":{"":{"product":1417}}}, // [Madberry OY] + {"hash":"985cce26aeab90d8","prefixes":{"":{"product":1417}}}, // [Madberry OY] + {"hash":"d67d98cbb9553a81","prefixes":{"":{"product":1417}}}, // [Madberry OY] + {"hash":"ee1c2ae2ffdaa754","prefixes":{"":{"product":1417}}}, // [Madberry OY] + {"hash":"8c162379b63896e6","prefixes":{"":{"product":1418}}}, // [QVC] + {"hash":"4c23d567b98e413f","prefixes":{"*":{"product":1419}}}, // [Micro Cube Digital Limited] + {"hash":"9e74e9e50b7e6116","prefixes":{"":{"product":1420}}}, // [egg.de GmbH] + {"hash":"8242efdacea6ca14","prefixes":{"":{"product":1421}}}, // [Headway Mexico] + {"hash":"82b90117a5beb869","prefixes":{"":{"product":1422}}}, // [RTBiQ Inc.] + {"hash":"7d4bc30e9d495d00","prefixes":{"":{"product":1194}}}, // [Fluidads] + {"hash":"44305a81af328854","prefixes":{"":{"product":1194}}}, // [Fluidads] + {"hash":"f5d44df23b5b7f0a","prefixes":{"":{"product":1423}}}, // [SCIBIDS TECHNOLOGY S.A.S.] + {"hash":"e86f56889ef213b9","prefixes":{"*":{"product":1424}}}, // [Kyocera Communication Systems] + {"hash":"e4f3abfdd94fbb95","prefixes":{"":{"product":1425}}}, // [Cortex Media Group] + {"hash":"7525243b1d665de2","prefixes":{"":{"product":1426}}}, // [appTV] + {"hash":"f27453a19aa2370e","prefixes":{"":{"product":1426}}}, // [appTV] + {"hash":"8156979d25af9817","prefixes":{"":{"product":1427}}}, // [ProgSol, Programmatic Solution, s.r.o.] + {"hash":"888e465d14dbfeb5","prefixes":{"":{"product":1427}}}, // [ProgSol, Programmatic Solution, s.r.o.] + {"hash":"6bcdb8e4cf28b730","prefixes":{"":{"product":1427}}}, // [ProgSol, Programmatic Solution, s.r.o.] + {"hash":"cc229228113699d8","prefixes":{"":{"product":1428}}}, // [Rezonence] + {"hash":"fd7695df1dfe9f20","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"fbee76565472bc95","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"e53efe28417732fd","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"4f76dc8807a1b25a","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"b7cd9e7666d175e1","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"ba124e354a70dcb2","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"8c9e63d24d4fbe6b","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"ea6157fcb7c5c718","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"6063abef39fb89be","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"8a3b3647f6fae243","prefixes":{"":{"product":1429}}}, // [LKQD Platform] + {"hash":"25b652ead7abb3b0","prefixes":{"":{"product":1059}}}, // [Bidtellect] + {"hash":"ff03eb40846eaa12","prefixes":{"":{"product":1059}}}, // [Bidtellect] + {"hash":"649ada5375fff936","prefixes":{"":{"product":1059}}}, // [Bidtellect] + {"hash":"79b86a55d8f149ef","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"a6e4ebed7d417fec","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"e7a27d73288188b6","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"24028ce468194c75","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"d7f89ed8a6431a69","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"079f75aa60b736a9","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"55ab38530ebcb2e7","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"244a99d8deb4343c","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"02afdba2d61c6d76","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"50fcadafe2e2734b","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"0a5d998860b7968b","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"ff1ef9a23c2bdf51","prefixes":{"":{"product":1430}}}, // [target value GmbH] + {"hash":"b7be02479c9268a9","prefixes":{"":{"product":1431}}}, // [Firecracker] + {"hash":"2841b43a0e0ddb37","prefixes":{"":{"product":1431}}}, // [Firecracker] + {"hash":"ea77ccf69493306c","prefixes":{"":{"product":1431}}}, // [Firecracker] + {"hash":"786060a6ba793b32","prefixes":{"":{"product":1431}}}, // [Firecracker] + {"hash":"af58243a5817c930","prefixes":{"":{"product":1431}}}, // [Firecracker] + {"hash":"b90ffee242259e26","prefixes":{"":{"product":1432}}}, // [MADGIC] + {"hash":"037e84a783130203","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"724c85c96cc72a57","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"df235ed188c82f5b","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"df21bf354a2570e1","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"52a9b224dd3e1b02","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"ef749fa483d558a9","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"a6b20df6c2ce3063","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"7e7e73dfe402883c","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"1cafe416eb8d746c","prefixes":{"":{"product":1433}}}, // [Digiseg] + {"hash":"45c0ef64ecfb6525","prefixes":{"":{"product":1434}}}, // [Bulbit Inc.] + {"hash":"d3710c2900e90f84","prefixes":{"":{"product":1434}}}, // [Bulbit Inc.] + {"hash":"fed8a97b9e855887","prefixes":{"":{"product":1435}}}, // [Lost My Name] + {"hash":"5c18434d5d721456","prefixes":{"":{"product":1436}}}, // [People Media] + {"hash":"fd4a671d34b4c76c","prefixes":{"":{"product":1436}}}, // [People Media] + {"hash":"181b385a8ea5a687","prefixes":{"":{"product":1437}}}, // [Platform.io] + {"hash":"00393673ca5c1d8a","prefixes":{"":{"product":1437}}}, // [Platform.io] + {"hash":"6479d1c5a241da2f","prefixes":{"":{"product":1438}}}, // [Bidspeaker] + {"hash":"f30e44d5503f39c4","prefixes":{"":{"product":1439}}}, // [YouGov PLC] + {"hash":"4f07a6c89151a2eb","prefixes":{"":{"product":1440}}}, // [SAP SE] + {"hash":"519e46f12c141c48","prefixes":{"":{"product":1440}}}, // [SAP SE] + {"hash":"413cdfaa8215b8f5","prefixes":{"":{"product":1440}}}, // [SAP SE] + {"hash":"dd8e150b503eb12f","prefixes":{"":{"product":1441}}}, // [Adchex] + {"hash":"58e19248782ce688","prefixes":{"":{"product":1442}}}, // [Smart Bid Limited] + {"hash":"8ccc1dd45e305817","prefixes":{"":{"product":1443}}}, // [UAd Exchange] + {"hash":"9543cb4330fa1d6e","prefixes":{"":{"product":1444}}}, // [UAd Exchange - IBV] + {"hash":"fc1a7604d6ced7c3","prefixes":{"":{"product":1445}}}, // [esc mediagroup GmbH] + {"hash":"90c354743de0b811","prefixes":{"":{"product":1446}}}, // [defacto smart reach GmbH] + {"hash":"0d8f41278d702251","prefixes":{"":{"product":1447}}}, // [Research and Analysis of Media in Sweden AB] + {"hash":"607c4919b3c8d5cd","prefixes":{"":{"product":1447}}}, // [Research and Analysis of Media in Sweden AB] + {"hash":"d0d7647dc2ea9dd9","prefixes":{"":{"product":1448}}}, // [OnAudience.com] + {"hash":"3c0c99d90a16804f","prefixes":{"":{"product":1449}}}, // [OneTag] + {"hash":"a50e8a4129f160c8","prefixes":{"":{"product":1449}}}, // [OneTag] + {"hash":"62105b8204658950","prefixes":{"":{"product":1449}}}, // [OneTag] + {"hash":"8069afc9a94b92b9","prefixes":{"*":{"product":1349}}}, // [1trn LLC] + {"hash":"c9cc947471099a0e","prefixes":{"":{"product":59}}}, // [Air Berlin] + {"hash":"516352d734faa684","prefixes":{"":{"product":1450}}}, // [Fiverr International Ltd.] + {"hash":"562a3da1a81ae4ec","prefixes":{"":{"product":1450}}}, // [Fiverr International Ltd.] + {"hash":"1ba262fef7c9b94b","prefixes":{"":{"product":1451}}}, // [Adikteev] + {"hash":"cd63e73725fa2b8d","prefixes":{"":{"product":1451}}}, // [Adikteev] + {"hash":"f24819f2edd93c69","prefixes":{"":{"product":1451}}}, // [Adikteev] + {"hash":"e3b1538bced5ec72","prefixes":{"":{"product":1451}}}, // [Adikteev] + {"hash":"341df4e708fbbc07","prefixes":{"":{"product":1452}}}, // [CenterPoint Media] + {"hash":"1f984187723f0399","prefixes":{"":{"product":1452}}}, // [CenterPoint Media] + {"hash":"6ed35b33de5147be","prefixes":{"":{"product":1452}}}, // [CenterPoint Media] + {"hash":"d3f04891cabd56f8","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"8429b5e7c7905de9","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"eadb7778a29b9617","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"15595532c38662ea","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"ce365566d66a72ee","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"60ef24e820a6a881","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"176be2434ca2f68b","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"3f72f2b4ec7f816e","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"bcc519027914bd79","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"86628b5f01332c66","prefixes":{"":{"product":1453}}}, // [Digital Trace] + {"hash":"1c34c62a587e6c1e","prefixes":{"":{"product":1454}}}, // [Pure Cobalt] + {"hash":"a30f9c646772cf0f","prefixes":{"":{"product":60}}}, // [Aegon ESPAÑA, S.A. de Seguros y Reaseguros, Uniper] + {"hash":"b23d960b2ea25b51","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"030115e352ac8e99","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"77da2d98a4da970f","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"aa0555e19cf5d6cc","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"badc121e99935aa5","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"115df78aa77ac9c4","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"04713a193ed96cc0","prefixes":{"s":{"product":1455}}}, // [Cedato] + {"hash":"fd6fd1f29de71285","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"c5de6239a6efe818","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"4c0be33a1c14428d","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"09eaafed5beae0e1","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"ff987bad618809e7","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"2b50033d06870385","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"8b399204d27d07c0","prefixes":{"s":{"product":1455}}}, // [Cedato] + {"hash":"5f78d380fa9514b1","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"c4912c3d8f04b948","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"361fa6df6bb3216e","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"b18ab9dfb513f02b","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"de511a44653105ea","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"f990d873e1622766","prefixes":{"":{"product":1455}}}, // [Cedato] + {"hash":"f53618fe52956dbf","prefixes":{"s":{"product":1455}}}, // [Cedato] + {"hash":"96ed69252978cab9","prefixes":{"cdn":{"product":1455}}}, // [Cedato] + {"hash":"f38b31b7e204355c","prefixes":{"":{"product":943}}}, // [Admetrics GmbH] + {"hash":"895a9c8a3b52c95f","prefixes":{"":{"product":943}}}, // [Admetrics GmbH] + {"hash":"62ba002fe91498c3","prefixes":{"":{"product":943}}}, // [Admetrics GmbH] + {"hash":"30fe524e700bd89f","prefixes":{"":{"product":943}}}, // [Admetrics GmbH] + {"hash":"6ce86e7a47d6dfbc","prefixes":{"":{"product":1456}}}, // [Jonsden Properties Limited] + {"hash":"8bed3db3f252b657","prefixes":{"":{"product":1457}}}, // [Realytics] + {"hash":"c2dcbb4796436e89","prefixes":{"":{"product":1458}}}, // [Twinpine] + {"hash":"97a8b5033c272f3c","prefixes":{"":{"product":1458}}}, // [Twinpine] + {"hash":"3d39eae9e2370c3e","prefixes":{"":{"product":1459}}}, // [Mopedo AB] + {"hash":"38a9bfae76c9b2f1","prefixes":{"*":{"product":1460}}}, // [Netsales] + {"hash":"9b1148932500d0b5","prefixes":{"":{"product":1461}}}, // [ViewersLogic LTD] + {"hash":"901b2f7c0272451b","prefixes":{"":{"product":1461}}}, // [ViewersLogic LTD] + {"hash":"7d45cf386f5c3f27","prefixes":{"":{"product":1462}}}, // [ADMAN] + {"hash":"e68e2f7630af032b","prefixes":{"":{"product":1462}}}, // [ADMAN] + {"hash":"79dc640f62208944","prefixes":{"":{"product":1462}}}, // [ADMAN] + {"hash":"4d6e312d6aba1117","prefixes":{"":{"product":1462}}}, // [ADMAN] + {"hash":"29e85c5af35e3751","prefixes":{"":{"product":1462}}}, // [ADMAN] + {"hash":"e5f2b6b92cc6daa3","prefixes":{"":{"product":1463}}}, // [Hyper] + {"hash":"f867e04c6dde165d","prefixes":{"":{"product":1464}}}, // [Hurra Communications] + {"hash":"8353a34ee35ff30c","prefixes":{"":{"product":1464}}}, // [Hurra Communications] + {"hash":"cc7f69fa55a7518f","prefixes":{"":{"product":1465}}}, // [Groundhog TW] + {"hash":"95ba2d10b1b0675c","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"e8f62a52edbdf4bd","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"321a7e08d4b4fd55","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"c6f272ed0a206999","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"bd34d2733467c1af","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"1b49f6113458dc63","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"58b83e3b3a97943e","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"814bcd9589533387","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"9465fc7c156593f4","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"7c1c219e78b2f270","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"9d376bb5982319b0","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"12ec9a0b2888a4a9","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"f3d7dfcb60972b0c","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"460cac48ec149c6d","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"2614d3ffa05e19a5","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"1f9b770c08378ced","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"054864ff1578657a","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"a5505306000e20f2","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"12cae8bbd22d8673","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"d7724f8a49b96fa6","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"3013078a26f31e2f","prefixes":{"":{"product":1466}}}, // [ImediaMax] + {"hash":"80193dea6eb67372","prefixes":{"":{"product":1467}}}, // [Flixbus] + {"hash":"02c439b9cc7e33b5","prefixes":{"":{"product":1468}}}, // [AudienceTV] + {"hash":"fcb4adbb2bc559e7","prefixes":{"":{"product":1469}}}, // [b34106183_test] + {"hash":"b08446a676a287b4","prefixes":{"":{"product":1469}}}, // [b34106183_test] + {"hash":"12105a6cc17c32e7","prefixes":{"":{"product":1470}}}, // [Netscore] + {"hash":"036d15c3ad4a0735","prefixes":{"":{"product":1470}}}, // [Netscore] + {"hash":"c0c40e6f63813be1","prefixes":{"":{"product":1471}}}, // [Chu Technology Limited] + {"hash":"4c434a52325c0b6e","prefixes":{"":{"product":1472}}}, // [SmartyAds LLC] + {"hash":"825a99c46b61053a","prefixes":{"":{"product":1473}}}, // [dbupdate1] + {"hash":"31ab5366866e8622","prefixes":{"":{"product":1473}}}, // [dbupdate1] + {"hash":"32a21f3a47cb8ddc","prefixes":{"":{"product":128}}}, // [ConvertMedia Ltd.] + {"hash":"44a7af6d373bd713","prefixes":{"":{"product":985}}}, // [Lodeo] + {"hash":"36d8680483227de8","prefixes":{"":{"product":985}}}, // [Lodeo] + {"hash":"9709c6b69d2e3e90","prefixes":{"":{"product":985}}}, // [Lodeo] + {"hash":"5efefb78ec9b146f","prefixes":{"":{"product":1474}}}, // [The Big Willow Inc.] + {"hash":"3e5b3c0010f7172f","prefixes":{"":{"product":1475}}}, // [LiveIntent Inc] + {"hash":"07bf09263d039040","prefixes":{"":{"product":1476}}}, // [OpenLedger ApS] + {"hash":"db879c71876741d0","prefixes":{"":{"product":1477}}}, // [Whichit] + {"hash":"4a6caded0a4565d9","prefixes":{"":{"product":1477}}}, // [Whichit] + {"hash":"05e0b02a9af28e89","prefixes":{"":{"product":1477}}}, // [Whichit] + {"hash":"3aca7ac4649f240c","prefixes":{"":{"product":1477}}}, // [Whichit] + {"hash":"81abe9587250fab1","prefixes":{"":{"product":1477}}}, // [Whichit] + {"hash":"a3b6bac891e57819","prefixes":{"":{"product":1478}}}, // [ParkDIA] + {"hash":"ddc3507bc6f79de4","prefixes":{"":{"product":1479}}}, // [Atedra Inc.] + {"hash":"881c2002027de2ec","prefixes":{"":{"product":1479}}}, // [Atedra Inc.] + {"hash":"28084881f4e51f6c","prefixes":{"":{"product":1480}}}, // [Digital Forest OÜ] + {"hash":"d563745e0fed92f4","prefixes":{"":{"product":1481}}}, // [Isobar Werbeagentur GmbH] + {"hash":"d6d13ce525771ba4","prefixes":{"":{"product":1482}}}, // [Valuepotion Pte. Ltd.] + {"hash":"446766a73edf76a2","prefixes":{"":{"product":1483}}}, // [Vuble Inc] + {"hash":"0753aa310a2a75aa","prefixes":{"":{"product":1484}}}, // [adlocal.net] + {"hash":"772284fa4bfb8e5a","prefixes":{"":{"product":1484}}}, // [adlocal.net] + {"hash":"0dd444b76d1727ab","prefixes":{"":{"product":1484}}}, // [adlocal.net] + {"hash":"1db1fc299e2dc73a","prefixes":{"":{"product":1484}}}, // [adlocal.net] + {"hash":"169447ab5f0b2b32","prefixes":{"":{"product":1485}}}, // [Freckle IoT] + {"hash":"143b45f2e52d871c","prefixes":{"":{"product":1486}}}, // [Personalization LLC] + {"hash":"bdc55693a2c62858","prefixes":{"":{"product":1487}}}, // [ThoughtLeadr Inc.] + {"hash":"e69058fe073a8d07","prefixes":{"":{"product":1487}}}, // [ThoughtLeadr Inc.] + {"hash":"ded94145085d34ed","prefixes":{"":{"product":1487}}}, // [ThoughtLeadr Inc.] + {"hash":"ef66f2e1bb6a3605","prefixes":{"":{"product":1487}}}, // [ThoughtLeadr Inc.] + {"hash":"5f62ecd0eec6b82a","prefixes":{"":{"product":1487}}}, // [ThoughtLeadr Inc.] + {"hash":"45281cea83398309","prefixes":{"":{"product":1488},"web":{"product":1488},"app":{"product":1488},"cdn":{"product":1488},"api":{"product":1488},"p":{"product":1488},"mobpages":{"product":1488}}}, // [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] + {"hash":"7ea0c7c8484fb7b4","prefixes":{"cdn":{"product":1488}}}, // [Noqoush Mobile Media Group FZ-LLC] + {"hash":"6839ebcbc8dce175","prefixes":{"":{"product":1488},"p":{"product":1488}}}, // [Noqoush Mobile Media Group FZ-LLC] [Noqoush Mobile Media Group FZ-LLC] + {"hash":"c338016e1aa2a5f7","prefixes":{"":{"product":1489}}}, // [G-Core Labs] + {"hash":"3bb879b5ab17cbeb","prefixes":{"":{"product":1490}}}, // [Haensel AMS GmbH] + {"hash":"26627407df51f73d","prefixes":{"t":{"product":1490},"d":{"product":1490},"s":{"product":1490}}}, // [Haensel AMS GmbH] [Haensel AMS GmbH] [Haensel AMS GmbH] + {"hash":"44e07e9341e20fd0","prefixes":{"":{"product":1491}}}, // [LemonPI] + {"hash":"71d7abda3a6093b3","prefixes":{"":{"product":1491}}}, // [LemonPI] + {"hash":"f57717e6df039cd1","prefixes":{"":{"product":1491}}}, // [LemonPI] + {"hash":"0ae0087cfa7b79c3","prefixes":{"":{"product":1491}}}, // [LemonPI] + {"hash":"59afc0d091a22f58","prefixes":{"":{"product":1492}}}, // [4Info, Inc.] + {"hash":"fd299e0138ad90c1","prefixes":{"":{"product":1492}}}, // [4Info, Inc.] + {"hash":"1e408d2e9033a6e2","prefixes":{"":{"product":1492}}} // [4Info, Inc.] ]);
diff --git a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js index dfdec25..3492278 100644 --- a/third_party/WebKit/Source/devtools/front_end/ui/Icon.js +++ b/third_party/WebKit/Source/devtools/front_end/ui/Icon.js
@@ -140,6 +140,7 @@ 'smallicon-triangle-up': {position: 'b1', spritesheet: 'smallicons', isMask: true}, 'smallicon-user-command': {position: 'c1', spritesheet: 'smallicons'}, 'smallicon-warning': {position: 'd1', spritesheet: 'smallicons'}, + 'smallicon-network-product': {position: 'e1', spritesheet: 'smallicons'}, 'mediumicon-clear-storage': {position: 'a4', spritesheet: 'mediumicons', isMask: true}, 'mediumicon-cookie': {position: 'b4', spritesheet: 'mediumicons', isMask: true},
diff --git a/third_party/WebKit/Source/devtools/scripts/convert-3pas-product-registry.js b/third_party/WebKit/Source/devtools/scripts/convert-3pas-product-registry.js index a187664..79d63d3c 100644 --- a/third_party/WebKit/Source/devtools/scripts/convert-3pas-product-registry.js +++ b/third_party/WebKit/Source/devtools/scripts/convert-3pas-product-registry.js
@@ -1,3 +1,6 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. const fs = require('fs'); /* @@ -19,6 +22,13 @@ const b64pad = '='; /* base-64 pad character. "=" for strict RFC compliance */ const chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ +const typeClassifications = new Map([ + ['cdn_provider', 'CDN'], ['cdn_commercial_owner', 'CDN'], ['cdn_creative_agency', 'CDN'], ['ad_blocking', 'Ad'], + ['ad_exchange', 'Ad'], ['ad_server_ad_network', 'Ad'], ['ad_server_advertiser', 'Ad'], ['demand_side_platform', 'Ad'], + ['vast_provider', 'Ad'], ['data_management_platform', 'Tracking'], ['research_analytics', 'Tracking'], + ['research_verification', 'Tracking'], ['research_brand_lift', 'Tracking'] +]); + var data = fs.readFileSync('3pas.csv', 'utf8'); var headerLine = data.split('\n', 1)[0]; data = data.substr(headerLine.length); @@ -114,6 +124,7 @@ } var outputProducts = []; +var outputTypes = []; var outputObj = new Map(); for (var [baseDomain, subdomains] of map) { for (var prefixes of subdomains.values()) { @@ -154,7 +165,7 @@ outputPart = {hash: hex_sha1(fullSubdomain).substr(0, 16), prefixes: {}}; outputObj.set(fullSubdomain, outputPart); } - outputPart.prefixes[lineObj.prefix] = registerOutputProduct(lineObj.name_legal_product); + outputPart.prefixes[lineObj.prefix] = registerOutputProduct(lineObj.name_legal_product, lineObj.type_vendor); } } } @@ -166,6 +177,12 @@ '// clang-format off\n' + '/* eslint-disable */\n' + 'ProductRegistry.register(['); +if (outputTypes.length) { + var data = JSON.stringify(outputTypes).replace(/","/g, '",\n "'); + console.log(' ' + data.substring(1, data.length - 1)); +} +console.log('],'); +console.log('['); var data = JSON.stringify(outputProducts).replace(/","/g, '",\n "'); console.log(' ' + data.substring(1, data.length - 1)); console.log('],'); @@ -175,8 +192,14 @@ var obj = outputObjArray[i]; var lineEnding = (i === outputObjArray.length - 1) ? '' : ','; var comments = []; - for (var prefix in obj.prefixes) - comments.push('[' + outputProducts[obj.prefixes[prefix]] + ']'); + for (var prefix in obj.prefixes) { + var typeName = outputTypes[obj.prefixes[prefix].type]; + if (!typeName) + typeName = ''; + else + typeName = ':' + typeName; + comments.push('[' + outputProducts[obj.prefixes[prefix].product] + typeName + ']'); + } console.log(' ' + JSON.stringify(obj) + lineEnding + ' // ' + comments.join(' ')); } console.log(']);'); @@ -189,11 +212,27 @@ // Linear but meh. -function registerOutputProduct(name) { +function registerOutputProduct(name, type) { var index = outputProducts.indexOf(name); + var typeIndex = registerOutputType(type); + var outObj = {product: index}; if (index === -1) { outputProducts.push(name); - return outputProducts.length - 1; + outObj.product = outputProducts.length - 1; + } + if (typeIndex !== -1) + outObj.type = typeIndex; + return outObj; +} + +function registerOutputType(type) { + var name = typeClassifications.get(type); + if (!name) + return -1; + var index = outputTypes.indexOf(name); + if (index === -1) { + outputTypes.push(name); + return outputTypes.length - 1; } return index; }
diff --git a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp index 44ccdb3c..bbeb7d5 100644 --- a/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp +++ b/third_party/WebKit/Source/modules/canvas2d/BaseRenderingContext2D.cpp
@@ -1122,7 +1122,7 @@ } if (image_source->IsCanvasElement()) { HTMLCanvasElement* canvas = static_cast<HTMLCanvasElement*>(image_source); - if (canvas->IsAnimated2D()) { + if (canvas->IsAnimated2d()) { *reason = kDisableDeferralReasonDrawImageOfAnimated2dCanvas; return true; }
diff --git a/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp b/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp index 42a7867..9d9c5f2 100644 --- a/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp +++ b/third_party/WebKit/Source/modules/indexeddb/IDBTransaction.cpp
@@ -195,7 +195,7 @@ IDBObjectStore* IDBTransaction::objectStore(const String& name, ExceptionState& exception_state) { - if (IsFinished()) { + if (IsFinished() || IsFinishing()) { exception_state.ThrowDOMException( kInvalidStateError, IDBDatabase::kTransactionFinishedErrorMessage); return nullptr;
diff --git a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp index 46f1f6f..1dcaab2e 100644 --- a/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp +++ b/third_party/WebKit/Source/modules/media_controls/MediaControlsImpl.cpp
@@ -529,7 +529,9 @@ bool MediaControlsImpl::ShouldHideMediaControls(unsigned behavior_flags) const { // Never hide for a media element without visual representation. if (!MediaElement().IsHTMLVideoElement() || !MediaElement().HasVideo() || - MediaElement().IsPlayingRemotely()) { + MediaElement().IsPlayingRemotely() || + toHTMLVideoElement(MediaElement()).GetMediaRemotingStatus() == + HTMLVideoElement::MediaRemotingStatus::kStarted) { return false; }
diff --git a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp index 4a8e117a..bfbd9ea 100644 --- a/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp +++ b/third_party/WebKit/Source/modules/webgl/WebGLRenderingContextBase.cpp
@@ -4982,7 +4982,7 @@ GLint xoffset, GLint yoffset, const IntRect& source_sub_rectangle) { - if (!canvas->Is3D()) { + if (!canvas->Is3d()) { ImageBuffer* buffer = canvas->Buffer(); if (buffer && !buffer->CopyToPlatformTexture(
diff --git a/third_party/WebKit/Source/platform/ThemeTypes.h b/third_party/WebKit/Source/platform/ThemeTypes.h index 73060c81..65d01139 100644 --- a/third_party/WebKit/Source/platform/ThemeTypes.h +++ b/third_party/WebKit/Source/platform/ThemeTypes.h
@@ -78,6 +78,7 @@ kMediaSubtitlesIconPart, kMediaOverflowMenuButtonPart, kMediaDownloadIconPart, + kMediaRemotingCastIconPart, kMenulistPart, kMenulistButtonPart, kMenulistTextPart,
diff --git a/third_party/WebKit/Source/platform/geometry/TransformState.cpp b/third_party/WebKit/Source/platform/geometry/TransformState.cpp index a80b4af..1bdc814 100644 --- a/third_party/WebKit/Source/platform/geometry/TransformState.cpp +++ b/third_party/WebKit/Source/platform/geometry/TransformState.cpp
@@ -49,12 +49,13 @@ } void TransformState::TranslateTransform(const LayoutSize& offset) { - if (direction_ == kApplyTransformDirection) - accumulated_transform_->TranslateRight(offset.Width().ToDouble(), - offset.Height().ToDouble()); - else + if (direction_ == kApplyTransformDirection) { + accumulated_transform_->PostTranslate(offset.Width().ToDouble(), + offset.Height().ToDouble()); + } else { accumulated_transform_->Translate(offset.Width().ToDouble(), offset.Height().ToDouble()); + } } void TransformState::TranslateMappedCoordinates(const LayoutSize& offset) {
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.cpp b/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.cpp index e4bbc8d5..e0b6a388 100644 --- a/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.cpp +++ b/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.cpp
@@ -5,6 +5,7 @@ #include "platform/graphics/gpu/SharedGpuContext.h" #include "gpu/command_buffer/client/gles2_interface.h" +#include "gpu/command_buffer/common/capabilities.h" #include "platform/CrossThreadFunctional.h" #include "platform/WaitableEvent.h" #include "platform/WebTaskRunner.h" @@ -127,4 +128,12 @@ ->GetGraphicsResetStatusKHR() == GL_NO_ERROR; } +bool SharedGpuContext::AllowSoftwareToAcceleratedCanvasUpgrade() { + if (!IsValid()) + return kNoSharedContext; + SharedGpuContext* this_ptr = GetInstanceForCurrentThread(); + return this_ptr->context_provider_->GetCapabilities() + .software_to_accelerated_canvas_upgrade; +} + } // blink
diff --git a/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.h b/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.h index cbd4fe9..d773626a 100644 --- a/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.h +++ b/third_party/WebKit/Source/platform/graphics/gpu/SharedGpuContext.h
@@ -34,6 +34,9 @@ Gl(); // May re-create context if context was lost static GrContext* Gr(); // May re-create context if context was lost static bool IsValid(); // May re-create context if context was lost + // May re-create context if context was lost + static bool AllowSoftwareToAcceleratedCanvasUpgrade(); + static bool IsValidWithoutRestoring(); typedef std::function<std::unique_ptr<WebGraphicsContext3DProvider>()> ContextProviderFactory;
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp index a215a535..75f9fdd2 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.cpp
@@ -23,12 +23,11 @@ list->AppendClipPathItem(clip_path_, true); } -int BeginClipPathDisplayItem::NumberOfSlowPaths() const { +void BeginClipPathDisplayItem::AnalyzeForGpuRasterization( + SkPictureGpuAnalyzer& analyzer) const { // Temporarily disabled (pref regressions due to GPU veto stickiness: // http://crbug.com/603969). // analyzer.analyzeClipPath(m_clipPath, SkRegion::kIntersect_Op, true); - // TODO(enne): fixup this code to return an int. - return 0; } void EndClipPathDisplayItem::Replay(GraphicsContext& context) const {
diff --git a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h index 3952c87..42434ae 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h +++ b/third_party/WebKit/Source/platform/graphics/paint/ClipPathDisplayItem.h
@@ -24,7 +24,7 @@ void AppendToWebDisplayItemList(const IntRect&, WebDisplayItemList*) const override; - int NumberOfSlowPaths() const override; + void AnalyzeForGpuRasterization(SkPictureGpuAnalyzer&) const override; private: #ifndef NDEBUG
diff --git a/third_party/WebKit/Source/platform/graphics/paint/CompositingRecorder.cpp b/third_party/WebKit/Source/platform/graphics/paint/CompositingRecorder.cpp index 7eac3491..cdcd8fd 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/CompositingRecorder.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/CompositingRecorder.cpp
@@ -29,8 +29,58 @@ CompositingRecorder::~CompositingRecorder() { if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) return; - graphics_context_.GetPaintController().EndItem<EndCompositingDisplayItem>( - client_); + // If the end of the current display list is of the form + // [BeginCompositingDisplayItem] [DrawingDisplayItem], then fold the + // BeginCompositingDisplayItem into a new DrawingDisplayItem that replaces + // them both. This allows Skia to optimize for the case when the + // BeginCompositingDisplayItem represents a simple opacity/color that can be + // merged into the opacity/color of the drawing. See crbug.com/628831 for more + // details. + PaintController& paint_controller = graphics_context_.GetPaintController(); + const DisplayItem* last_display_item = paint_controller.LastDisplayItem(0); + const DisplayItem* second_to_last_display_item = + paint_controller.LastDisplayItem(1); + // TODO(chrishtr): remove the call to LastDisplayItemIsSubsequenceEnd when + // https://codereview.chromium.org/2768143002 lands. + if (!RuntimeEnabledFeatures::slimmingPaintV2Enabled() && last_display_item && + second_to_last_display_item && last_display_item->DrawsContent() && + second_to_last_display_item->GetType() == + DisplayItem::kBeginCompositing && + !paint_controller.LastDisplayItemIsSubsequenceEnd()) { + FloatRect cull_rect( + ((DrawingDisplayItem*)last_display_item)->GetPaintRecord()->cullRect()); + const DisplayItemClient& display_item_client = last_display_item->Client(); + DisplayItem::Type display_item_type = last_display_item->GetType(); + + // Re-record the last two DisplayItems into a new drawing. The new item + // cannot be cached, because it is a mutation of the DisplayItem the client + // thought it was painting. + paint_controller.BeginSkippingCache(); + { +#if DCHECK_IS_ON() + // In the recorder's scope we remove the last two display items which + // are combined into a new drawing. + DisableListModificationCheck disabler; +#endif + DrawingRecorder new_recorder(graphics_context_, display_item_client, + display_item_type, cull_rect); + DCHECK(!DrawingRecorder::UseCachedDrawingIfPossible( + graphics_context_, display_item_client, display_item_type)); + + second_to_last_display_item->Replay(graphics_context_); + last_display_item->Replay(graphics_context_); + EndCompositingDisplayItem(client_).Replay(graphics_context_); + + // Remove the DrawingDisplayItem. + paint_controller.RemoveLastDisplayItem(); + // Remove the BeginCompositingDisplayItem. + paint_controller.RemoveLastDisplayItem(); + } + paint_controller.EndSkippingCache(); + } else { + graphics_context_.GetPaintController().EndItem<EndCompositingDisplayItem>( + client_); + } } } // namespace blink
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h index d5f344e6..a9afcd1 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h +++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItem.h
@@ -17,6 +17,8 @@ #include "platform/wtf/text/WTFString.h" #endif +class SkPictureGpuAnalyzer; + namespace blink { class GraphicsContext; @@ -335,7 +337,7 @@ virtual bool DrawsContent() const { return false; } // Override to implement specific analysis strategies. - virtual int NumberOfSlowPaths() const { return 0; } + virtual void AnalyzeForGpuRasterization(SkPictureGpuAnalyzer&) const {} #ifndef NDEBUG static WTF::String TypeAsDebugString(DisplayItem::Type);
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp index 4fe7853..474a7d12 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/DisplayItemList.cpp
@@ -7,6 +7,7 @@ #include "platform/graphics/LoggingCanvas.h" #include "platform/graphics/paint/DrawingDisplayItem.h" #include "platform/graphics/paint/PaintChunk.h" +#include "third_party/skia/include/core/SkPictureAnalyzer.h" #ifndef NDEBUG #include "platform/wtf/text/WTFString.h"
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp index 7c14717..2a17f3d 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.cpp
@@ -10,6 +10,7 @@ #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkData.h" +#include "third_party/skia/include/core/SkPictureAnalyzer.h" namespace blink { @@ -29,8 +30,14 @@ return record_.get(); } -int DrawingDisplayItem::NumberOfSlowPaths() const { - return record_ ? record_->numSlowPaths() : 0; +void DrawingDisplayItem::AnalyzeForGpuRasterization( + SkPictureGpuAnalyzer& analyzer) const { + // TODO(enne): Need an SkPictureGpuAnalyzer on PictureRecord. + // This is a bit overkill to ToSkPicture a record just to get + // numSlowPaths. + if (!record_) + return; + analyzer.analyzePicture(ToSkPicture(record_).get()); } #ifndef NDEBUG
diff --git a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h index 3dedbcbd..e5f989f8 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h +++ b/third_party/WebKit/Source/platform/graphics/paint/DrawingDisplayItem.h
@@ -41,7 +41,7 @@ return known_to_be_opaque_; } - int NumberOfSlowPaths() const override; + void AnalyzeForGpuRasterization(SkPictureGpuAnalyzer&) const override; private: #ifndef NDEBUG
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp index d3b7553..e26681df 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/PaintController.cpp
@@ -16,8 +16,6 @@ #include <stdio.h> #endif -static constexpr int kMaxNumberOfSlowPathsBeforeVeto = 5; - namespace blink { void PaintController::SetTracksRasterInvalidations(bool value) { @@ -537,7 +535,7 @@ !new_display_item_list_.IsEmpty()) GenerateChunkRasterInvalidationRects(new_paint_chunks_.LastChunk()); - int num_slow_paths = 0; + SkPictureGpuAnalyzer gpu_analyzer; current_cache_generation_ = DisplayItemClient::CacheGenerationOrInvalidationReason::Next(); @@ -556,8 +554,8 @@ Vector<const DisplayItemClient*> skipped_cache_clients; for (const auto& item : new_display_item_list_) { // No reason to continue the analysis once we have a veto. - if (num_slow_paths <= kMaxNumberOfSlowPathsBeforeVeto) - num_slow_paths += item.NumberOfSlowPaths(); + if (gpu_analyzer.suitableForGpuRasterization()) + item.AnalyzeForGpuRasterization(gpu_analyzer); // TODO(wkorman): Only compute and append visual rect for drawings. new_display_item_list_.AppendVisualRect( @@ -595,7 +593,7 @@ new_display_item_list_.ShrinkToFit(); current_paint_artifact_ = PaintArtifact( std::move(new_display_item_list_), new_paint_chunks_.ReleasePaintChunks(), - num_slow_paths <= kMaxNumberOfSlowPathsBeforeVeto); + gpu_analyzer.suitableForGpuRasterization()); ResetCurrentListIndices(); out_of_order_item_indices_.Clear(); out_of_order_chunk_indices_.Clear();
diff --git a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp index 038f0bd..f5f45cc8 100644 --- a/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp +++ b/third_party/WebKit/Source/platform/graphics/paint/PaintControllerTest.cpp
@@ -2273,6 +2273,40 @@ DisplayItemClient::EndShouldKeepAliveAllClients(); #endif } + + void TestFoldCompositingDrawingInSubsequence() { + FakeDisplayItemClient container("container"); + FakeDisplayItemClient content("content"); + GraphicsContext context(GetPaintController()); + + { + SubsequenceRecorder subsequence(context, container); + CompositingRecorder compositing(context, content, SkBlendMode::kSrc, 0.5); + DrawRect(context, content, kBackgroundDrawingType, + FloatRect(100, 100, 300, 300)); + } + GetPaintController().CommitNewDisplayItems(); + EXPECT_EQ( + 1u, + GetPaintController().GetPaintArtifact().GetDisplayItemList().size()); + + { + EXPECT_FALSE(SubsequenceRecorder::UseCachedSubsequenceIfPossible( + context, container)); + SubsequenceRecorder subsequence(context, container); + CompositingRecorder compositing(context, content, SkBlendMode::kSrc, 0.5); + DrawRect(context, content, kBackgroundDrawingType, + FloatRect(100, 100, 300, 300)); + } + GetPaintController().CommitNewDisplayItems(); + EXPECT_EQ( + 1u, + GetPaintController().GetPaintArtifact().GetDisplayItemList().size()); + +#if CHECK_DISPLAY_ITEM_CLIENT_ALIVENESS + DisplayItemClient::EndShouldKeepAliveAllClients(); +#endif + } }; TEST_F(PaintControllerUnderInvalidationTest, ChangeDrawing) { @@ -2329,6 +2363,11 @@ TestInvalidationInSubsequence(); } +TEST_F(PaintControllerUnderInvalidationTest, + FoldCompositingDrawingInSubsequence) { + TestFoldCompositingDrawingInSubsequence(); +} + #endif // defined(GTEST_HAS_DEATH_TEST) && !OS(ANDROID) } // namespace blink
diff --git a/third_party/WebKit/Source/platform/mac/LocalCurrentGraphicsContext.mm b/third_party/WebKit/Source/platform/mac/LocalCurrentGraphicsContext.mm index e3eada5..6379d6a 100644 --- a/third_party/WebKit/Source/platform/mac/LocalCurrentGraphicsContext.mm +++ b/third_party/WebKit/Source/platform/mac/LocalCurrentGraphicsContext.mm
@@ -24,7 +24,6 @@ #include "platform/graphics/paint/PaintCanvas.h" #include "platform/mac/ThemeMac.h" #include "platform_canvas.h" -#include "third_party/skia/include/core/SkRegion.h" namespace blink {
diff --git a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.cpp b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.cpp index d8c1da9..d6eeed0 100644 --- a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.cpp +++ b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.cpp
@@ -72,19 +72,6 @@ // to be held responsible. Basically, don't be a jerk, and remember that // anything free comes with no guarantee. -// A clarification about the storage of matrix elements -// -// This class uses a 2 dimensional array internally to store the elements of the -// matrix. The first index into the array refers to the column that the element -// lies in; the second index refers to the row. -// -// In other words, this is the layout of the matrix: -// -// | matrix_[0][0] matrix_[1][0] matrix_[2][0] matrix_[3][0] | -// | matrix_[0][1] matrix_[1][1] matrix_[2][1] matrix_[3][1] | -// | matrix_[0][2] matrix_[1][2] matrix_[2][2] matrix_[3][2] | -// | matrix_[0][3] matrix_[1][3] matrix_[2][3] matrix_[3][3] | - typedef double Vector4[4]; typedef double Vector3[3]; @@ -1203,8 +1190,8 @@ return *this; } -TransformationMatrix& TransformationMatrix::TranslateRight(double tx, - double ty) { +TransformationMatrix& TransformationMatrix::PostTranslate(double tx, + double ty) { if (tx != 0) { matrix_[0][0] += matrix_[0][3] * tx; matrix_[1][0] += matrix_[1][3] * tx; @@ -1222,10 +1209,10 @@ return *this; } -TransformationMatrix& TransformationMatrix::TranslateRight3d(double tx, - double ty, - double tz) { - TranslateRight(tx, ty); +TransformationMatrix& TransformationMatrix::PostTranslate3d(double tx, + double ty, + double tz) { + PostTranslate(tx, ty); if (tz != 0) { matrix_[0][2] += matrix_[0][3] * tz; matrix_[1][2] += matrix_[1][3] * tz; @@ -1262,7 +1249,7 @@ TransformationMatrix& TransformationMatrix::ApplyTransformOrigin(double x, double y, double z) { - TranslateRight3d(x, y, z); + PostTranslate3d(x, y, z); Translate3d(-x, -y, -z); return *this; } @@ -1278,32 +1265,37 @@ } // Calculates *this = *this * mat. -// Note: A * B means that the transforms represented by A happen first, and -// then the transforms represented by B. That is, the matrix A * B corresponds -// to a CSS transform list <transform-function-A> <transform-function-B>. -// Some branches of this function may make use of the fact that -// transpose(A * B) == transpose(B) * transpose(A); remember that -// matrix_[a][b] is matrix element row b, col a. -// FIXME: As of 2016-05-04, the ARM64 branch is NOT triggered by tests on the CQ -// bots, see crbug.com/477892 and crbug.com/584508. +// Note: As we are using the column vector convention, i.e. T * P, +// (lhs * rhs) * P = lhs * (rhs * P) +// That means from the perspective of the transformed object, the combined +// transform is equal to applying the rhs(mat) first, then lhs(*this) second. +// For example: +// TransformationMatrix lhs; lhs.Rotate(90.f); +// TransformationMatrix rhs; rhs.Translate(12.f, 34.f); +// TransformationMatrix prod = lhs; +// prod.Multiply(rhs); +// lhs.MapPoint(rhs.MapPoint(p)) == prod.MapPoint(p) +// Also 'prod' corresponds to CSS transform:rotateZ(90deg)translate(12px,34px). +// TODO(crbug.com/584508): As of 2017-04-11, the ARM64 CQ bots skip +// blink_platform_unittests, therefore the ARM64 branch is not tested by CQ. TransformationMatrix& TransformationMatrix::Multiply( const TransformationMatrix& mat) { #if CPU(ARM64) - double* right_matrix = &(matrix_[0][0]); - const double* left_matrix = &(mat.matrix_[0][0]); + double* left_matrix = &(matrix_[0][0]); + const double* right_matrix = &(mat.matrix_[0][0]); asm volatile( + // Load this->matrix_ to v24 - v31. // Load mat.matrix_ to v16 - v23. - // Load this.matrix_ to v24 - v31. - // Result: this = mat * this - // | v0, v1 | | v16, v17 | | v24, v25 | - // | v2, v3 | = | v18, v19 | * | v26, v27 | - // | v4, v5 | | v20, v21 | | v28, v29 | - // | v6, v7 | | v22, v23 | | v30, v31 | - "mov x9, %[right_matrix] \t\n" - "ld1 {v16.2d - v19.2d}, [%[left_matrix]], 64 \t\n" - "ld1 {v20.2d - v23.2d}, [%[left_matrix]] \t\n" - "ld1 {v24.2d - v27.2d}, [%[right_matrix]], 64 \t\n" - "ld1 {v28.2d - v31.2d}, [%[right_matrix]] \t\n" + // Result: *this = *this * mat + // | v0 v2 v4 v6 | | v24 v26 v28 v30 | | v16 v18 v20 v22 | + // | | = | | * | | + // | v1 v3 v5 v7 | | v25 v27 v29 v31 | | v17 v19 v21 v23 | + // | | | | | | + "mov x9, %[left_matrix] \t\n" + "ld1 {v16.2d - v19.2d}, [%[right_matrix]], 64 \t\n" + "ld1 {v20.2d - v23.2d}, [%[right_matrix]] \t\n" + "ld1 {v24.2d - v27.2d}, [%[left_matrix]], 64 \t\n" + "ld1 {v28.2d - v31.2d}, [%[left_matrix]] \t\n" "fmul v0.2d, v24.2d, v16.d[0] \t\n" "fmul v1.2d, v25.2d, v16.d[0] \t\n" @@ -1343,95 +1335,95 @@ "st1 {v0.2d - v3.2d}, [x9], 64 \t\n" "st1 {v4.2d - v7.2d}, [x9] \t\n" - : [left_matrix] "+r"(left_matrix), [right_matrix] "+r"(right_matrix) + : [right_matrix] "+r"(right_matrix), [left_matrix] "+r"(left_matrix) : : "memory", "x9", "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31", "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7"); #elif HAVE(MIPS_MSA_INTRINSICS) - v2f64 v_left_m0, v_left_m1, v_left_m2, v_left_m3, v_left_m4, v_left_m5, - v_left_m6, v_left_m7; v2f64 v_right_m0, v_right_m1, v_right_m2, v_right_m3, v_right_m4, v_right_m5, v_right_m6, v_right_m7; + v2f64 v_left_m0, v_left_m1, v_left_m2, v_left_m3, v_left_m4, v_left_m5, + v_left_m6, v_left_m7; v2f64 v_tmp_m0, v_tmp_m1, v_tmp_m2, v_tmp_m3; - v_right_m0 = LD_DP(&(matrix_[0][0])); - v_right_m1 = LD_DP(&(matrix_[0][2])); - v_right_m2 = LD_DP(&(matrix_[1][0])); - v_right_m3 = LD_DP(&(matrix_[1][2])); - v_right_m4 = LD_DP(&(matrix_[2][0])); - v_right_m5 = LD_DP(&(matrix_[2][2])); - v_right_m6 = LD_DP(&(matrix_[3][0])); - v_right_m7 = LD_DP(&(matrix_[3][2])); + v_left_m0 = LD_DP(&(matrix_[0][0])); + v_left_m1 = LD_DP(&(matrix_[0][2])); + v_left_m2 = LD_DP(&(matrix_[1][0])); + v_left_m3 = LD_DP(&(matrix_[1][2])); + v_left_m4 = LD_DP(&(matrix_[2][0])); + v_left_m5 = LD_DP(&(matrix_[2][2])); + v_left_m6 = LD_DP(&(matrix_[3][0])); + v_left_m7 = LD_DP(&(matrix_[3][2])); - v_left_m0 = LD_DP(&(mat.matrix_[0][0])); - v_left_m2 = LD_DP(&(mat.matrix_[0][2])); - v_left_m4 = LD_DP(&(mat.matrix_[1][0])); - v_left_m6 = LD_DP(&(mat.matrix_[1][2])); + v_right_m0 = LD_DP(&(mat.matrix_[0][0])); + v_right_m2 = LD_DP(&(mat.matrix_[0][2])); + v_right_m4 = LD_DP(&(mat.matrix_[1][0])); + v_right_m6 = LD_DP(&(mat.matrix_[1][2])); - v_left_m1 = (v2f64)__msa_splati_d((v2i64)v_left_m0, 1); - v_left_m0 = (v2f64)__msa_splati_d((v2i64)v_left_m0, 0); - v_left_m3 = (v2f64)__msa_splati_d((v2i64)v_left_m2, 1); - v_left_m2 = (v2f64)__msa_splati_d((v2i64)v_left_m2, 0); - v_left_m5 = (v2f64)__msa_splati_d((v2i64)v_left_m4, 1); - v_left_m4 = (v2f64)__msa_splati_d((v2i64)v_left_m4, 0); - v_left_m7 = (v2f64)__msa_splati_d((v2i64)v_left_m6, 1); - v_left_m6 = (v2f64)__msa_splati_d((v2i64)v_left_m6, 0); + v_right_m1 = (v2f64)__msa_splati_d((v2i64)v_right_m0, 1); + v_right_m0 = (v2f64)__msa_splati_d((v2i64)v_right_m0, 0); + v_right_m3 = (v2f64)__msa_splati_d((v2i64)v_right_m2, 1); + v_right_m2 = (v2f64)__msa_splati_d((v2i64)v_right_m2, 0); + v_right_m5 = (v2f64)__msa_splati_d((v2i64)v_right_m4, 1); + v_right_m4 = (v2f64)__msa_splati_d((v2i64)v_right_m4, 0); + v_right_m7 = (v2f64)__msa_splati_d((v2i64)v_right_m6, 1); + v_right_m6 = (v2f64)__msa_splati_d((v2i64)v_right_m6, 0); - v_tmp_m0 = v_left_m0 * v_right_m0; - v_tmp_m1 = v_left_m0 * v_right_m1; - v_tmp_m0 += v_left_m1 * v_right_m2; - v_tmp_m1 += v_left_m1 * v_right_m3; - v_tmp_m0 += v_left_m2 * v_right_m4; - v_tmp_m1 += v_left_m2 * v_right_m5; - v_tmp_m0 += v_left_m3 * v_right_m6; - v_tmp_m1 += v_left_m3 * v_right_m7; + v_tmp_m0 = v_right_m0 * v_left_m0; + v_tmp_m1 = v_right_m0 * v_left_m1; + v_tmp_m0 += v_right_m1 * v_left_m2; + v_tmp_m1 += v_right_m1 * v_left_m3; + v_tmp_m0 += v_right_m2 * v_left_m4; + v_tmp_m1 += v_right_m2 * v_left_m5; + v_tmp_m0 += v_right_m3 * v_left_m6; + v_tmp_m1 += v_right_m3 * v_left_m7; - v_tmp_m2 = v_left_m4 * v_right_m0; - v_tmp_m3 = v_left_m4 * v_right_m1; - v_tmp_m2 += v_left_m5 * v_right_m2; - v_tmp_m3 += v_left_m5 * v_right_m3; - v_tmp_m2 += v_left_m6 * v_right_m4; - v_tmp_m3 += v_left_m6 * v_right_m5; - v_tmp_m2 += v_left_m7 * v_right_m6; - v_tmp_m3 += v_left_m7 * v_right_m7; + v_tmp_m2 = v_right_m4 * v_left_m0; + v_tmp_m3 = v_right_m4 * v_left_m1; + v_tmp_m2 += v_right_m5 * v_left_m2; + v_tmp_m3 += v_right_m5 * v_left_m3; + v_tmp_m2 += v_right_m6 * v_left_m4; + v_tmp_m3 += v_right_m6 * v_left_m5; + v_tmp_m2 += v_right_m7 * v_left_m6; + v_tmp_m3 += v_right_m7 * v_left_m7; - v_left_m0 = LD_DP(&(mat.matrix_[2][0])); - v_left_m2 = LD_DP(&(mat.matrix_[2][2])); - v_left_m4 = LD_DP(&(mat.matrix_[3][0])); - v_left_m6 = LD_DP(&(mat.matrix_[3][2])); + v_right_m0 = LD_DP(&(mat.matrix_[2][0])); + v_right_m2 = LD_DP(&(mat.matrix_[2][2])); + v_right_m4 = LD_DP(&(mat.matrix_[3][0])); + v_right_m6 = LD_DP(&(mat.matrix_[3][2])); ST_DP(v_tmp_m0, &(matrix_[0][0])); ST_DP(v_tmp_m1, &(matrix_[0][2])); ST_DP(v_tmp_m2, &(matrix_[1][0])); ST_DP(v_tmp_m3, &(matrix_[1][2])); - v_left_m1 = (v2f64)__msa_splati_d((v2i64)v_left_m0, 1); - v_left_m0 = (v2f64)__msa_splati_d((v2i64)v_left_m0, 0); - v_left_m3 = (v2f64)__msa_splati_d((v2i64)v_left_m2, 1); - v_left_m2 = (v2f64)__msa_splati_d((v2i64)v_left_m2, 0); - v_left_m5 = (v2f64)__msa_splati_d((v2i64)v_left_m4, 1); - v_left_m4 = (v2f64)__msa_splati_d((v2i64)v_left_m4, 0); - v_left_m7 = (v2f64)__msa_splati_d((v2i64)v_left_m6, 1); - v_left_m6 = (v2f64)__msa_splati_d((v2i64)v_left_m6, 0); + v_right_m1 = (v2f64)__msa_splati_d((v2i64)v_right_m0, 1); + v_right_m0 = (v2f64)__msa_splati_d((v2i64)v_right_m0, 0); + v_right_m3 = (v2f64)__msa_splati_d((v2i64)v_right_m2, 1); + v_right_m2 = (v2f64)__msa_splati_d((v2i64)v_right_m2, 0); + v_right_m5 = (v2f64)__msa_splati_d((v2i64)v_right_m4, 1); + v_right_m4 = (v2f64)__msa_splati_d((v2i64)v_right_m4, 0); + v_right_m7 = (v2f64)__msa_splati_d((v2i64)v_right_m6, 1); + v_right_m6 = (v2f64)__msa_splati_d((v2i64)v_right_m6, 0); - v_tmp_m0 = v_left_m0 * v_right_m0; - v_tmp_m1 = v_left_m0 * v_right_m1; - v_tmp_m0 += v_left_m1 * v_right_m2; - v_tmp_m1 += v_left_m1 * v_right_m3; - v_tmp_m0 += v_left_m2 * v_right_m4; - v_tmp_m1 += v_left_m2 * v_right_m5; - v_tmp_m0 += v_left_m3 * v_right_m6; - v_tmp_m1 += v_left_m3 * v_right_m7; + v_tmp_m0 = v_right_m0 * v_left_m0; + v_tmp_m1 = v_right_m0 * v_left_m1; + v_tmp_m0 += v_right_m1 * v_left_m2; + v_tmp_m1 += v_right_m1 * v_left_m3; + v_tmp_m0 += v_right_m2 * v_left_m4; + v_tmp_m1 += v_right_m2 * v_left_m5; + v_tmp_m0 += v_right_m3 * v_left_m6; + v_tmp_m1 += v_right_m3 * v_left_m7; - v_tmp_m2 = v_left_m4 * v_right_m0; - v_tmp_m3 = v_left_m4 * v_right_m1; - v_tmp_m2 += v_left_m5 * v_right_m2; - v_tmp_m3 += v_left_m5 * v_right_m3; - v_tmp_m2 += v_left_m6 * v_right_m4; - v_tmp_m3 += v_left_m6 * v_right_m5; - v_tmp_m2 += v_left_m7 * v_right_m6; - v_tmp_m3 += v_left_m7 * v_right_m7; + v_tmp_m2 = v_right_m4 * v_left_m0; + v_tmp_m3 = v_right_m4 * v_left_m1; + v_tmp_m2 += v_right_m5 * v_left_m2; + v_tmp_m3 += v_right_m5 * v_left_m3; + v_tmp_m2 += v_right_m6 * v_left_m4; + v_tmp_m3 += v_right_m6 * v_left_m5; + v_tmp_m2 += v_right_m7 * v_left_m6; + v_tmp_m3 += v_right_m7 * v_left_m7; ST_DP(v_tmp_m0, &(matrix_[2][0])); ST_DP(v_tmp_m1, &(matrix_[2][2])); @@ -1445,7 +1437,7 @@ __m128d matrix_block_e = _mm_load_pd(&(matrix_[2][0])); __m128d matrix_block_g = _mm_load_pd(&(matrix_[3][0])); - // First row. + // First column. __m128d other_matrix_first_param = _mm_set1_pd(mat.matrix_[0][0]); __m128d other_matrix_second_param = _mm_set1_pd(mat.matrix_[0][1]); __m128d other_matrix_third_param = _mm_set1_pd(mat.matrix_[0][2]); @@ -1478,7 +1470,7 @@ accumulator = _mm_add_pd(accumulator, temp3); _mm_store_pd(&matrix_[0][2], accumulator); - // Second row. + // Second column. other_matrix_first_param = _mm_set1_pd(mat.matrix_[1][0]); other_matrix_second_param = _mm_set1_pd(mat.matrix_[1][1]); other_matrix_third_param = _mm_set1_pd(mat.matrix_[1][2]); @@ -1506,7 +1498,7 @@ accumulator = _mm_add_pd(accumulator, temp3); _mm_store_pd(&matrix_[1][2], accumulator); - // Third row. + // Third column. other_matrix_first_param = _mm_set1_pd(mat.matrix_[2][0]); other_matrix_second_param = _mm_set1_pd(mat.matrix_[2][1]); other_matrix_third_param = _mm_set1_pd(mat.matrix_[2][2]); @@ -1534,7 +1526,7 @@ accumulator = _mm_add_pd(accumulator, temp3); _mm_store_pd(&matrix_[2][2], accumulator); - // Fourth row. + // Fourth column. other_matrix_first_param = _mm_set1_pd(mat.matrix_[3][0]); other_matrix_second_param = _mm_set1_pd(mat.matrix_[3][1]); other_matrix_third_param = _mm_set1_pd(mat.matrix_[3][2]);
diff --git a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h index 54377d98..76e3e94 100644 --- a/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h +++ b/third_party/WebKit/Source/platform/transforms/TransformationMatrix.h
@@ -50,13 +50,26 @@ #define TRANSFORMATION_MATRIX_USE_X86_64_SSE2 #endif -// TransformationMatrix must not be allocated on Oilpan's heap since -// Oilpan doesn't (yet) have an ability to allocate the TransformationMatrix -// with 16-byte alignment. PartitionAlloc has the ability. class PLATFORM_EXPORT TransformationMatrix { + // TransformationMatrix must not be allocated on Oilpan's heap since + // Oilpan doesn't (yet) have an ability to allocate the TransformationMatrix + // with 16-byte alignment. PartitionAlloc has the ability. USING_FAST_MALLOC(TransformationMatrix); public: +// Throughout this class, we will be speaking in column vector convention. +// i.e. Applying a transform T to point P is T * P. +// The elements of the matrix and the vector looks like: +// | scale_x skew_y_x skew_z_x translate_x | | x | +// | skew_x_y scale_y skew_z_y translate_y | * | y | +// | skew_x_z skew_y_z scale_z translate_z | | z | +// | persp_x persp_y persp_z persp_w | | w | +// Internally the matrix is stored as a 2-dimensional array in col-major order. +// In other words, this is the layout of the matrix: +// | matrix_[0][0] matrix_[1][0] matrix_[2][0] matrix_[3][0] | +// | matrix_[0][1] matrix_[1][1] matrix_[2][1] matrix_[3][1] | +// | matrix_[0][2] matrix_[1][2] matrix_[2][2] matrix_[3][2] | +// | matrix_[0][3] matrix_[1][3] matrix_[2][3] matrix_[3][3] | #if defined(TRANSFORMATION_MATRIX_USE_X86_64_SSE2) typedef WTF_ALIGNED(double, Matrix4[4][4], 16); #else @@ -252,6 +265,11 @@ void TransformBox(FloatBox&) const; + // Important: These indices are spoken in col-major order. i.e.: + // | M11() M21() M31() M41() | + // | M12() M22() M32() M42() | + // | M13() M23() M33() M43() | + // | M14() M24() M34() M44() | double M11() const { return matrix_[0][0]; } void SetM11(double f) { matrix_[0][0] = f; } double M12() const { return matrix_[0][1]; } @@ -322,9 +340,12 @@ TransformationMatrix& Translate(double tx, double ty); TransformationMatrix& Translate3d(double tx, double ty, double tz); - // translation added with a post-multiply - TransformationMatrix& TranslateRight(double tx, double ty); - TransformationMatrix& TranslateRight3d(double tx, double ty, double tz); + // Append translation after existing operations. i.e. + // TransformationMatrix t2 = t1; + // t2.PostTranslate(x, y); + // t2.MapPoint(p) == t1.MapPoint(p) + FloatPoint(x, y) + TransformationMatrix& PostTranslate(double tx, double ty); + TransformationMatrix& PostTranslate3d(double tx, double ty, double tz); TransformationMatrix& Skew(double angle_x, double angle_y); TransformationMatrix& SkewX(double angle) { return Skew(angle, 0); }
diff --git a/third_party/WebKit/Source/platform/transforms/TransformationMatrixTest.cpp b/third_party/WebKit/Source/platform/transforms/TransformationMatrixTest.cpp index a896126..97ea309 100644 --- a/third_party/WebKit/Source/platform/transforms/TransformationMatrixTest.cpp +++ b/third_party/WebKit/Source/platform/transforms/TransformationMatrixTest.cpp
@@ -94,6 +94,162 @@ EXPECT_EQ(expected_atimes_b, a); } +TEST(TransformationMatrixTest, BasicOperations) { + // Just some arbitrary matrix that introduces no rounding, and is unlikely + // to commute with other operations. + TransformationMatrix m(2.f, 3.f, 5.f, 0.f, 7.f, 11.f, 13.f, 0.f, 17.f, 19.f, + 23.f, 0.f, 29.f, 31.f, 37.f, 1.f); + + FloatPoint3D p(41.f, 43.f, 47.f); + + EXPECT_EQ(FloatPoint3D(1211.f, 1520.f, 1882.f), m.MapPoint(p)); + + { + TransformationMatrix n; + n.Scale(2.f); + EXPECT_EQ(FloatPoint3D(82.f, 86.f, 47.f), n.MapPoint(p)); + + TransformationMatrix mn = m; + mn.Scale(2.f); + EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p))); + } + + { + TransformationMatrix n; + n.ScaleNonUniform(2.f, 3.f); + EXPECT_EQ(FloatPoint3D(82.f, 129.f, 47.f), n.MapPoint(p)); + + TransformationMatrix mn = m; + mn.ScaleNonUniform(2.f, 3.f); + EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p))); + } + + { + TransformationMatrix n; + n.Scale3d(2.f, 3.f, 4.f); + EXPECT_EQ(FloatPoint3D(82.f, 129.f, 188.f), n.MapPoint(p)); + + TransformationMatrix mn = m; + mn.Scale3d(2.f, 3.f, 4.f); + EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p))); + } + + { + TransformationMatrix n; + n.Rotate(90.f); + EXPECT_FLOAT_EQ(0.f, + (FloatPoint3D(-43.f, 41.f, 47.f) - n.MapPoint(p)).length()); + + TransformationMatrix mn = m; + mn.Rotate(90.f); + EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length()); + } + + { + TransformationMatrix n; + n.Rotate3d(10.f, 10.f, 10.f, 120.f); + EXPECT_FLOAT_EQ(0.f, + (FloatPoint3D(47.f, 41.f, 43.f) - n.MapPoint(p)).length()); + + TransformationMatrix mn = m; + mn.Rotate3d(10.f, 10.f, 10.f, 120.f); + EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length()); + } + + { + TransformationMatrix n; + n.Translate(5.f, 6.f); + EXPECT_EQ(FloatPoint3D(46.f, 49.f, 47.f), n.MapPoint(p)); + + TransformationMatrix mn = m; + mn.Translate(5.f, 6.f); + EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p))); + } + + { + TransformationMatrix n; + n.Translate3d(5.f, 6.f, 7.f); + EXPECT_EQ(FloatPoint3D(46.f, 49.f, 54.f), n.MapPoint(p)); + + TransformationMatrix mn = m; + mn.Translate3d(5.f, 6.f, 7.f); + EXPECT_EQ(mn.MapPoint(p), m.MapPoint(n.MapPoint(p))); + } + + { + TransformationMatrix nm = m; + nm.PostTranslate(5.f, 6.f); + EXPECT_EQ(nm.MapPoint(p), m.MapPoint(p) + FloatPoint3D(5.f, 6.f, 0.f)); + } + + { + TransformationMatrix nm = m; + nm.PostTranslate3d(5.f, 6.f, 7.f); + EXPECT_EQ(nm.MapPoint(p), m.MapPoint(p) + FloatPoint3D(5.f, 6.f, 7.f)); + } + + { + TransformationMatrix n; + n.Skew(45.f, -45.f); + EXPECT_FLOAT_EQ(0.f, + (FloatPoint3D(84.f, 2.f, 47.f) - n.MapPoint(p)).length()); + + TransformationMatrix mn = m; + mn.Skew(45.f, -45.f); + EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length()); + } + + { + TransformationMatrix n; + n.SkewX(45.f); + EXPECT_FLOAT_EQ(0.f, + (FloatPoint3D(84.f, 43.f, 47.f) - n.MapPoint(p)).length()); + + TransformationMatrix mn = m; + mn.SkewX(45.f); + EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length()); + } + + { + TransformationMatrix n; + n.SkewY(45.f); + EXPECT_FLOAT_EQ(0.f, + (FloatPoint3D(41.f, 84.f, 47.f) - n.MapPoint(p)).length()); + + TransformationMatrix mn = m; + mn.SkewY(45.f); + EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length()); + } + + { + TransformationMatrix n; + n.ApplyPerspective(94.f); + EXPECT_FLOAT_EQ(0.f, + (FloatPoint3D(82.f, 86.f, 94.f) - n.MapPoint(p)).length()); + + TransformationMatrix mn = m; + mn.ApplyPerspective(94.f); + EXPECT_FLOAT_EQ(0.f, (mn.MapPoint(p) - m.MapPoint(n.MapPoint(p))).length()); + } + + { + FloatPoint3D origin(5.f, 6.f, 7.f); + TransformationMatrix n = m; + n.ApplyTransformOrigin(origin); + EXPECT_EQ(m.MapPoint(p - origin) + origin, n.MapPoint(p)); + } + + { + TransformationMatrix n = m; + n.Zoom(2.f); + FloatPoint3D expectation = p; + expectation.Scale(0.5f, 0.5f, 0.5f); + expectation = m.MapPoint(expectation); + expectation.Scale(2.f, 2.f, 2.f); + EXPECT_EQ(expectation, n.MapPoint(p)); + } +} + TEST(TransformationMatrixTest, ToString) { TransformationMatrix zeros(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); EXPECT_EQ("[0,0,0,0,\n0,0,0,0,\n0,0,0,0,\n0,0,0,0] (degenerate)",
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git.py index 18452fb..49eee785 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git.py
@@ -66,57 +66,61 @@ self.checkout_root = self.find_checkout_root(self.cwd) def _init_executable_name(self): - # FIXME: This is a hack and should be removed. + """Sets the executable name on Windows. + + The Win port uses the depot_tools package, which contains a number + of development tools, including Python and git. Instead of using a + real git executable, depot_tools indirects via a batch file, called + "git.bat". This batch file is used because it allows depot_tools to + auto-update the real git executable, which is contained in a + subdirectory. + + FIXME: This is a hack and should be resolved in a different way if + possible. + """ try: self._executive.run_command(['git', 'help']) except OSError: try: self._executive.run_command(['git.bat', 'help']) - # The Win port uses the depot_tools package, which contains a number - # of development tools, including Python and git. Instead of using a - # real git executable, depot_tools indirects via a batch file, called - # "git.bat". This batch file allows depot_tools to auto-update the real - # git executable, which is contained in a subdirectory. _log.debug('Engaging git.bat Windows hack.') self.executable_name = 'git.bat' except OSError: _log.debug('Failed to engage git.bat Windows hack.') - def _run_git(self, - command_args, - cwd=None, - input=None, # pylint: disable=redefined-builtin - timeout_seconds=None, - decode_output=True, - return_exit_code=False): + def run(self, command_args, cwd=None, stdin=None, decode_output=True, return_exit_code=False): + """Invokes git with the given args.""" full_command_args = [self.executable_name] + command_args cwd = cwd or self.checkout_root return self._executive.run_command( full_command_args, cwd=cwd, - input=input, - timeout_seconds=timeout_seconds, + input=stdin, return_exit_code=return_exit_code, decode_output=decode_output) - # SCM always returns repository relative path, but sometimes we need - # absolute paths to pass to rm, etc. def absolute_path(self, repository_relative_path): + """Converts repository-relative paths to absolute paths.""" return self._filesystem.join(self.checkout_root, repository_relative_path) @classmethod def in_working_directory(cls, path, executive=None): try: executive = executive or Executive() - return executive.run_command([cls.executable_name, 'rev-parse', '--is-inside-work-tree'], - cwd=path, error_handler=Executive.ignore_error).rstrip() == 'true' + return executive.run_command( + [cls.executable_name, 'rev-parse', '--is-inside-work-tree'], + cwd=path, error_handler=Executive.ignore_error).rstrip() == 'true' except OSError: - # The Windows bots seem to through a WindowsError when git isn't installed. + # The Windows bots seem to throw a WindowsError when git isn't installed. + # TODO(qyearsley): This might be because the git executable name + # isn't initialized yet; maybe this would be fixed by using the + # _init_executable_name hack above. + _log.warn('Got OSError when running Git.in_working_directory.') return False def find_checkout_root(self, path): # "git rev-parse --show-cdup" would be another way to get to the root - checkout_root = self._run_git(['rev-parse', '--show-toplevel'], cwd=(path or './')).strip() + checkout_root = self.run(['rev-parse', '--show-toplevel'], cwd=(path or './')).strip() if not self._filesystem.isabs(checkout_root): # Sometimes git returns relative paths checkout_root = self._filesystem.join(path, checkout_root) return checkout_root @@ -132,10 +136,7 @@ [cls.executable_name, 'config', '--get-all', key], error_handler=Executive.ignore_error, cwd=cwd).rstrip('\n') def _discard_local_commits(self): - self._run_git(['reset', '--hard', self._remote_branch_ref()]) - - def _local_commits(self, ref='HEAD'): - return self._run_git(['log', '--pretty=oneline', ref + '...' + self._remote_branch_ref()]).splitlines() + self.run(['reset', '--hard', self._remote_branch_ref()]) def _rebase_in_progress(self): return self._filesystem.exists(self.absolute_path(self._filesystem.join('.git', 'rebase-apply'))) @@ -145,14 +146,14 @@ command = ['diff', 'HEAD', '--no-renames', '--name-only'] if pathspec: command.extend(['--', pathspec]) - return self._run_git(command) != '' + return self.run(command) != '' def _discard_working_directory_changes(self): - # Could run git clean here too, but that wouldn't match subversion - self._run_git(['reset', 'HEAD', '--hard']) - # Aborting rebase even though this does not match subversion + # TODO(qyearsley): Could run git clean here too; this wasn't done + # before in order to match svn, but this is no longer a concern. + self.run(['reset', 'HEAD', '--hard']) if self._rebase_in_progress(): - self._run_git(['rebase', '--abort']) + self.run(['rebase', '--abort']) def unstaged_changes(self): """Lists files with unstaged changes, including untracked files. @@ -163,7 +164,7 @@ """ # `git status -z` is a version of `git status -s`, that's recommended # for machine parsing. Lines are terminated with NUL rather than LF. - change_lines = self._run_git(['status', '-z', '--untracked-files=all']).rstrip('\x00') + change_lines = self.run(['status', '-z', '--untracked-files=all']).rstrip('\x00') if not change_lines: return {} # No changes. unstaged_changes = {} @@ -176,16 +177,16 @@ return unstaged_changes def add_list(self, paths, return_exit_code=False): - return self._run_git(['add'] + paths, return_exit_code=return_exit_code) + return self.run(['add'] + paths, return_exit_code=return_exit_code) def delete_list(self, paths): - return self._run_git(['rm', '-f'] + paths) + return self.run(['rm', '-f'] + paths) def move(self, origin, destination): - return self._run_git(['mv', '-f', origin, destination]) + return self.run(['mv', '-f', origin, destination]) def exists(self, path): - return_code = self._run_git(['show', 'HEAD:%s' % path], return_exit_code=True, decode_output=False) + return_code = self.run(['show', 'HEAD:%s' % path], return_exit_code=True, decode_output=False) return return_code != self.ERROR_FILE_IS_MISSING def _branch_from_ref(self, ref): @@ -193,7 +194,7 @@ def current_branch(self): """Returns the name of the current branch, or empty string if HEAD is detached.""" - ref = self._run_git(['rev-parse', '--symbolic-full-name', 'HEAD']).strip() + ref = self.run(['rev-parse', '--symbolic-full-name', 'HEAD']).strip() if ref == 'HEAD': # HEAD is detached; return an empty string. return '' @@ -204,7 +205,7 @@ branch_name = self.current_branch() if not branch_name: # HEAD is detached; use commit SHA instead. - return self._run_git(['rev-parse', 'HEAD']).strip() + return self.run(['rev-parse', 'HEAD']).strip() return branch_name def _upstream_branch(self): @@ -244,7 +245,7 @@ def _run_status_and_extract_filenames(self, status_command, status_regexp): filenames = [] # We run with cwd=self.checkout_root so that returned-paths are root-relative. - for line in self._run_git(status_command, cwd=self.checkout_root).splitlines(): + for line in self.run(status_command, cwd=self.checkout_root).splitlines(): match = re.search(status_regexp, line) if not match: continue @@ -263,6 +264,7 @@ @staticmethod def supports_local_commits(): + # TODO(qyearsley): Remove this. return True def display_name(self): @@ -271,7 +273,7 @@ def most_recent_log_matching(self, grep_str, path): # We use '--grep=' + foo rather than '--grep', foo because # git 1.7.0.4 (and earlier) didn't support the separate arg. - return self._run_git(['log', '-1', '--grep=' + grep_str, '--date=iso', self.find_checkout_root(path)]) + return self.run(['log', '-1', '--grep=' + grep_str, '--date=iso', self.find_checkout_root(path)]) def _commit_position_from_git_log(self, git_log): match = re.search(r"^\s*Cr-Commit-Position:.*@\{#(?P<commit_position>\d+)\}", git_log, re.MULTILINE) @@ -305,6 +307,7 @@ def create_patch(self, git_commit=None, changed_files=None): """Returns a byte array (str()) representing the patch file. + Patch files are effectively binary since they may contain files of multiple different encodings. """ @@ -325,7 +328,7 @@ command += [self._merge_base(git_commit), '--'] if changed_files: command += changed_files - return self._run_git(command, decode_output=False, cwd=self.checkout_root) + return self.run(command, decode_output=False, cwd=self.checkout_root) def _patch_order(self): # Put code changes at the top of the patch and layout tests @@ -342,24 +345,24 @@ return self._commit_position_from_git_log(git_log) def checkout_branch(self, name): - self._run_git(['checkout', '-q', name]) + self.run(['checkout', '-q', name]) def create_clean_branch(self, name): - self._run_git(['checkout', '-q', '-b', name, self._remote_branch_ref()]) + self.run(['checkout', '-q', '-b', name, self._remote_branch_ref()]) def blame(self, path): - return self._run_git(['blame', '--show-email', path]) + return self.run(['blame', '--show-email', path]) # Git-specific methods: def _branch_ref_exists(self, branch_ref): - return self._run_git(['show-ref', '--quiet', '--verify', branch_ref], return_exit_code=True) == 0 + return self.run(['show-ref', '--quiet', '--verify', branch_ref], return_exit_code=True) == 0 def delete_branch(self, branch_name): if self._branch_ref_exists('refs/heads/' + branch_name): - self._run_git(['branch', '-D', branch_name]) + self.run(['branch', '-D', branch_name]) def _remote_merge_base(self): - return self._run_git(['merge-base', self._remote_branch_ref(), 'HEAD']).strip() + return self.run(['merge-base', self._remote_branch_ref(), 'HEAD']).strip() def _remote_branch_ref(self): # Use references so that we can avoid collisions, e.g. we don't want to operate on refs/heads/trunk if it exists. @@ -370,45 +373,33 @@ def commit_locally_with_message(self, message): command = ['commit', '--all', '-F', '-'] - self._run_git(command, input=message) - - def pull(self, timeout_seconds=None): - self._run_git(['pull'], timeout_seconds=timeout_seconds) + self.run(command, stdin=message) def latest_git_commit(self): - return self._run_git(['log', '-1', '--format=%H']).strip() + return self.run(['log', '-1', '--format=%H']).strip() def git_commits_since(self, commit): - return self._run_git(['log', commit + '..master', '--format=%H', '--reverse']).split() + return self.run(['log', commit + '..master', '--format=%H', '--reverse']).split() def git_commit_detail(self, commit, format=None): # pylint: disable=redefined-builtin args = ['log', '-1', commit] if format: args.append('--format=' + format) - return self._run_git(args) + return self.run(args) def affected_files(self, commit): - output = self._run_git(['log', '-1', '--format=', '--name-only', commit]) + output = self.run(['log', '-1', '--format=', '--name-only', commit]) return output.strip().split('\n') def _branch_tracking_remote_master(self): - origin_info = self._run_git(['remote', 'show', 'origin', '-n']) + origin_info = self.run(['remote', 'show', 'origin', '-n']) match = re.search(r"^\s*(?P<branch_name>\S+)\s+merges with remote master$", origin_info, re.MULTILINE) if not match: raise ScriptError(message='Unable to find local branch tracking origin/master.') branch = str(match.group('branch_name')) - return self._branch_from_ref(self._run_git(['rev-parse', '--symbolic-full-name', branch]).strip()) - - def is_cleanly_tracking_remote_master(self): - if self.has_working_directory_changes(): - return False - if self.current_branch() != self._branch_tracking_remote_master(): - return False - if len(self._local_commits(self._branch_tracking_remote_master())) > 0: - return False - return True + return self._branch_from_ref(self.run(['rev-parse', '--symbolic-full-name', branch]).strip()) def ensure_cleanly_tracking_remote_master(self): self._discard_working_directory_changes() - self._run_git(['checkout', '-q', self._branch_tracking_remote_master()]) + self.run(['checkout', '-q', self._branch_tracking_remote_master()]) self._discard_local_commits()
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git_unittest.py index 07f4895..87ce53c 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git_unittest.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/common/checkout/git_unittest.py
@@ -198,8 +198,7 @@ def _assert_timestamp_of_revision(self, canned_git_output, expected): git = self.make_git() git.find_checkout_root = lambda path: '' - # Modifying protected method. pylint: disable=protected-access - git._run_git = lambda args: canned_git_output + git.run = lambda args: canned_git_output self.assertEqual(git.timestamp_of_revision('some-path', '12345'), expected) def test_timestamp_of_revision_utc(self): @@ -222,8 +221,7 @@ 'M d/modified-staged.txt', 'A d/added-staged.txt', ] - # pylint: disable=protected-access - git._run_git = lambda args: '\x00'.join(status_lines) + '\x00' + git.run = lambda args: '\x00'.join(status_lines) + '\x00' self.assertEqual( git.unstaged_changes(), {
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py index eeef1f69..7ef8fd40 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl.py
@@ -77,8 +77,6 @@ return 1 if builders_with_no_results and not options.fill_missing: - # TODO(qyearsley): Support trying to continue as long as there are - # some results from some builder; see http://crbug.com/673966. _log.error('The following builders have no results:') for builder in builders_with_no_results: _log.error(' %s', builder) @@ -86,13 +84,15 @@ _log.debug('Getting results for issue %d.', issue_number) builds_to_results = self._fetch_results(builds) - if builds_to_results is None: + if not options.fill_missing and len(builds_to_results) < len(builds): return 1 test_baseline_set = TestBaselineSet(tool) if args: for test in args: for build in builds: + if not builds_to_results.get(build): + continue test_baseline_set.add(test, build) else: test_baseline_set = self._make_test_baseline_set( @@ -160,10 +160,10 @@ results_url = buildbot.results_url(build.builder_name, build.build_number) layout_test_results = buildbot.fetch_results(build) if layout_test_results is None: - _log.error('Failed to fetch results for: %s', build) - _log.error('Results were expected to exist at:\n%s/results.html', results_url) - _log.error('If the job failed, you could retry by running:\ngit cl try -b %s', build.builder_name) - return None + _log.info('Failed to fetch results for %s', build) + _log.info('Results URL: %s/results.html', results_url) + _log.info('Retry job by running: git cl try -b %s', build.builder_name) + continue results[build] = layout_test_results return results
diff --git a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py index 7a205dd6d..0a4de89 100644 --- a/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py +++ b/third_party/WebKit/Tools/Scripts/webkitpy/tool/commands/rebaseline_cl_unittest.py
@@ -324,11 +324,24 @@ return_code = self.command.execute(self.command_options(), [], self.tool) self.assertEqual(return_code, 1) self.assertLog([ - 'ERROR: Failed to fetch results for: Build(builder_name=\'MOCK Try Win\', build_number=5000)\n', - 'ERROR: Results were expected to exist at:\n' - 'https://storage.googleapis.com/chromium-layout-test-archives/MOCK_Try_Win/5000/layout-test-results/results.html\n', - 'ERROR: If the job failed, you could retry by running:\n' - 'git cl try -b MOCK Try Win\n' + 'INFO: Failed to fetch results for Build(builder_name=\'MOCK Try Win\', build_number=5000)\n', + 'INFO: Results URL: https://storage.googleapis.com/chromium-layout-test-archives' + '/MOCK_Try_Win/5000/layout-test-results/results.html\n', + 'INFO: Retry job by running: git cl try -b MOCK Try Win\n' + ]) + + def test_continues_with_missing_results_when_filling_results(self): + self.tool.buildbot.set_results(Build('MOCK Try Win', 5000), None) + return_code = self.command.execute(self.command_options(fill_missing=True), ['fast/dom/prototype-taco.html'], self.tool) + self.assertEqual(return_code, 0) + self.assertLog([ + 'INFO: Failed to fetch results for Build(builder_name=\'MOCK Try Win\', build_number=5000)\n', + 'INFO: Results URL: https://storage.googleapis.com/chromium-layout-test-archives' + '/MOCK_Try_Win/5000/layout-test-results/results.html\n', + 'INFO: Retry job by running: git cl try -b MOCK Try Win\n', + 'INFO: For fast/dom/prototype-taco.html:\n', + 'INFO: Using Build(builder_name=\'MOCK Try Mac\', build_number=4000) to supply results for test-win-win7.\n', + 'INFO: Rebaselining fast/dom/prototype-taco.html\n' ]) def test_bails_when_there_are_unstaged_baselines(self):
diff --git a/third_party/WebKit/public/blink_image_resources.grd b/third_party/WebKit/public/blink_image_resources.grd index adcb7f4f..16f6574 100644 --- a/third_party/WebKit/public/blink_image_resources.grd +++ b/third_party/WebKit/public/blink_image_resources.grd
@@ -25,6 +25,8 @@ <structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_OVERLAY_CAST_BUTTON_OFF" file="blink/mediaplayer_overlay_cast_off.png" /> <structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_OVERLAY_CAST_BUTTON_ON" file="blink/mediaplayer_overlay_cast_on.png" /> <structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_OVERLAY_PLAY_BUTTON" file="blink/mediaplayer_overlay_play.png" /> + <structure type="chrome_scaled_image" name="IDR_MEDIA_REMOTING_CAST_ICON" + file="blink/mediaremoting_cast.png" /> <structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_TRACKSELECTION_CHECKMARK" file="blink/mediaplayer_trackselection_checkmark.png" /> <structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_CLOSEDCAPTIONS_ICON" file="blink/mediaplayer_closedcaptions_icon.png" /> <structure type="chrome_scaled_image" name="IDR_MEDIAPLAYER_OVERFLOW_MENU_ICON" file="blink/mediaplayer_overflow_menu.png" />
diff --git a/third_party/WebKit/public/default_100_percent/blink/mediaremoting_cast.png b/third_party/WebKit/public/default_100_percent/blink/mediaremoting_cast.png new file mode 100644 index 0000000..6188c7af --- /dev/null +++ b/third_party/WebKit/public/default_100_percent/blink/mediaremoting_cast.png Binary files differ
diff --git a/third_party/WebKit/public/default_200_percent/blink/mediaremoting_cast.png b/third_party/WebKit/public/default_200_percent/blink/mediaremoting_cast.png new file mode 100644 index 0000000..377c4e4 --- /dev/null +++ b/third_party/WebKit/public/default_200_percent/blink/mediaremoting_cast.png Binary files differ
diff --git a/third_party/WebKit/public/platform/WebLocalizedString.h b/third_party/WebKit/public/platform/WebLocalizedString.h index 25cbf453..9ae231a 100644 --- a/third_party/WebKit/public/platform/WebLocalizedString.h +++ b/third_party/WebKit/public/platform/WebLocalizedString.h
@@ -104,6 +104,8 @@ kFileButtonChooseMultipleFilesLabel, kFileButtonNoFileSelectedLabel, kInputElementAltText, + kMediaRemotingDisableText, + kMediaRemotingCastText, kMissingPluginText, kMultipleFileUploadText, kOtherColorLabel,
diff --git a/third_party/WebKit/public/platform/WebMediaPlayerClient.h b/third_party/WebKit/public/platform/WebMediaPlayerClient.h index 301fad67..35d5db5 100644 --- a/third_party/WebKit/public/platform/WebMediaPlayerClient.h +++ b/third_party/WebKit/public/platform/WebMediaPlayerClient.h
@@ -116,6 +116,10 @@ // Returns the selected video track id (or an empty id if there's none). virtual WebMediaPlayer::TrackId GetSelectedVideoTrackId() = 0; + // Informs that media starts/stops being rendered and played back remotely. + virtual void MediaRemotingStarted() {} + virtual void MediaRemotingStopped() {} + protected: ~WebMediaPlayerClient() {} };
diff --git a/third_party/closure_compiler/compiled_resources2.gyp b/third_party/closure_compiler/compiled_resources2.gyp index d65f1769..e38e522a 100644 --- a/third_party/closure_compiler/compiled_resources2.gyp +++ b/third_party/closure_compiler/compiled_resources2.gyp
@@ -33,7 +33,6 @@ '<(DEPTH)/chrome/browser/resources/offline_pages/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/settings/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/uber/compiled_resources2.gyp:*', - '<(DEPTH)/chrome/browser/resources/vr_shell/compiled_resources2.gyp:*', '<(DEPTH)/chrome/browser/resources/webapks/compiled_resources2.gyp:*', '<(DEPTH)/ui/file_manager/compiled_resources2.gyp:*', '<(DEPTH)/ui/webui/resources/cr_elements/compiled_resources2.gyp:*',
diff --git a/tools/grit/grit/testdata/substitute_tmpl.grd b/tools/grit/grit/testdata/substitute_tmpl.grd new file mode 100644 index 0000000..be7b6017 --- /dev/null +++ b/tools/grit/grit/testdata/substitute_tmpl.grd
@@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- --> +<grit + base_dir="." + source_lang_id="en" + tc_project="GoogleDesktopWindowsClient" + latest_public_release="0" + current_release="1" + enc_check="möl"> + <outputs> + <output filename="resource.h" type="rc_header" /> + <output filename="en_${name}_resources.rc" type="rc_all" lang="en" /> + <output filename="sv_${name}_resources.rc" type="rc_all" lang="sv" /> + </outputs> + <translations> + <file path="substitute.xmb" lang="sv" /> + </translations> + <release seq="1" allow_pseudo="false"> + <messages first_id="8192"> + <message name="IDS_COPYRIGHT_GOOGLE_LONG" sub_variable="true" desc="Gadget copyright notice. Needs to be updated every year."> + Copyright 2008 Google Inc. All Rights Reserved. + </message> + <message name="IDS_NEWS_PANEL_COPYRIGHT"> + Google Desktop News gadget +[IDS_COPYRIGHT_GOOGLE_LONG] +View news that is personalized based on the articles you read. + +For example, if you read lots of sports news, you'll see more sports articles. If you read technology news less often, you'll see fewer of those articles. + </message> + </messages> + </release> +</grit>
diff --git a/tools/grit/grit/tool/build.py b/tools/grit/grit/tool/build.py index cab2b37..d2c9ff16 100755 --- a/tools/grit/grit/tool/build.py +++ b/tools/grit/grit/tool/build.py
@@ -352,7 +352,7 @@ else: for output in self.res.GetOutputFiles(): output.output_filename = os.path.abspath(os.path.join( - self.output_directory, output.GetFilename())) + self.output_directory, output.GetOutputFilename())) # If there are whitelisted names, tag the tree once up front, this way # while looping through the actual output, it is just an attribute check. @@ -360,7 +360,7 @@ self.AddWhitelistTags(self.res, self.whitelist_names) for output in self.res.GetOutputFiles(): - self.VerboseOut('Creating %s...' % output.GetFilename()) + self.VerboseOut('Creating %s...' % output.GetOutputFilename()) # Microsoft's RC compiler can only deal with single-byte or double-byte # files (no UTF-8), so we make all RC files UTF-16 to support all @@ -451,7 +451,8 @@ # Compare the absolute path names, sorted. asserted = sorted([os.path.abspath(i) for i in assert_output_files]) actual = sorted([ - os.path.abspath(os.path.join(self.output_directory, i.GetFilename())) + os.path.abspath(os.path.join(self.output_directory, + i.GetOutputFilename())) for i in self.res.GetOutputFiles()]) if asserted != actual: @@ -519,7 +520,7 @@ # Get the first output file relative to the depdir. outputs = self.res.GetOutputFiles() output_file = os.path.join(self.output_directory, - outputs[0].GetFilename()) + outputs[0].GetOutputFilename()) output_file = os.path.relpath(output_file, depdir) # The path prefix to prepend to dependencies in the depfile.
diff --git a/tools/grit/grit/tool/build_unittest.py b/tools/grit/grit/tool/build_unittest.py index 952eff9..1750bfc 100755 --- a/tools/grit/grit/tool/build_unittest.py +++ b/tools/grit/grit/tool/build_unittest.py
@@ -114,6 +114,36 @@ '-a', os.path.abspath( os.path.join(output_dir, 'resource.h'))])) + def testAssertTemplateOutputs(self): + output_dir = tempfile.mkdtemp() + class DummyOpts(object): + def __init__(self): + self.input = util.PathFromRoot('grit/testdata/substitute_tmpl.grd') + self.verbose = False + self.extra_verbose = False + + # Incomplete output file list should fail. + builder_fail = build.RcBuilder() + self.failUnlessEqual(2, + builder_fail.Run(DummyOpts(), [ + '-o', output_dir, + '-E', 'name=foo', + '-a', os.path.abspath( + os.path.join(output_dir, 'en_foo_resources.rc'))])) + + # Complete output file list should succeed. + builder_ok = build.RcBuilder() + self.failUnlessEqual(0, + builder_ok.Run(DummyOpts(), [ + '-o', output_dir, + '-E', 'name=foo', + '-a', os.path.abspath( + os.path.join(output_dir, 'en_foo_resources.rc')), + '-a', os.path.abspath( + os.path.join(output_dir, 'sv_foo_resources.rc')), + '-a', os.path.abspath( + os.path.join(output_dir, 'resource.h'))])) + def _verifyWhitelistedOutput(self, filename, whitelisted_ids,
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index fa2bc62..a5bcff4c 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml
@@ -7518,6 +7518,9 @@ </histogram> <histogram name="Clipboard.ConstructedHasher" enum="BooleanSuccess"> + <obsolete> + Launched briefly in M-59 dev, then refactoring made obsolete. + </obsolete> <owner>mpearson@chromium.org</owner> <summary> Whether Android's Clipboard.java successfully constructed a hasher to hash @@ -41796,6 +41799,38 @@ </summary> </histogram> +<histogram base="true" + name="NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger" units="ms"> + <owner>jkrcal@chromium.org</owner> + <summary> + Android: The time since the last fetch, recorded upon the first soft fetch + trigger. The first soft trigger does not necessarily cause a fetch (if it + comes before the end of the respective scheduling interval). This metric is + recorded at most once (per lifetime of a Chrome instance) after each fetch. + This is used to understand how changing scheduling intervals will impact + traffic of background fetches. + </summary> +</histogram> + +<histogram base="true" + name="NewTabPage.ContentSuggestions.TimeUntilPersistentFetch" units="ms"> + <owner>jkrcal@chromium.org</owner> + <summary> + Android: The time since the last fetch, recorded upon a persistent fetch. + This is used to understand what are the real persistent fetching intervals + in the wild. + </summary> +</histogram> + +<histogram base="true" name="NewTabPage.ContentSuggestions.TimeUntilSoftFetch" + units="ms"> + <owner>jkrcal@chromium.org</owner> + <summary> + Android: The time since the last fetch, recorded upon a soft fetch. This is + used to understand what are the real soft fetching intervals in the wild. + </summary> +</histogram> + <histogram name="NewTabPage.ContentSuggestions.UIUpdateResult" enum="ContentSuggestionsUIUpdateResult"> <obsolete> @@ -48705,7 +48740,7 @@ </summary> </histogram> -<histogram name="PaymentRequest.CanMakePayment.Usage" enum="Boolean"> +<histogram name="PaymentRequest.CanMakePayment.Usage" enum="CanMakePaymentUsage"> <owner>sebsg@chromium.org</owner> <summary> Whether the merchant used the CanMakePayment method during a Payment @@ -48788,6 +48823,14 @@ </summary> </histogram> +<histogram name="PaymentRequest.CheckoutFunnel.SkippedShow" + enum="BooleanSkipped"> + <owner>sebsg@chromium.org</owner> + <summary> + When the Payment Request UI gets skipped to go directly to the payment app. + </summary> +</histogram> + <histogram name="PaymentRequest.NumberOfSelectionAdds"> <owner>sebsg@chromium.org</owner> <summary> @@ -85135,6 +85178,11 @@ <int value="23" label="Too many RenderPassDrawQuads."/> </enum> +<enum name="CanMakePaymentUsage" type="int"> + <int value="0" lable="Used"/> + <int value="1" lable="Not Used"/> +</enum> + <enum name="CanvasContextType" type="int"> <int value="0" label="2d"/> <int value="1" label="(obsolete) webkit-3d"/> @@ -101998,6 +102046,7 @@ <int value="880510010" label="enable-permissions-bubbles"/> <int value="884106779" label="supervised-user-safesites"/> <int value="887011602" label="enable-spelling-auto-correct"/> + <int value="902608487" label="AutofillUpstreamRequestCvcIfMissing:enabled"/> <int value="903267263" label="disable-offline-pages"/> <int value="908523940" label="PassiveEventListenersDueToFling:disabled"/> <int value="909439558" label="disable-device-discovery"/> @@ -102145,6 +102194,7 @@ <int value="1416592483" label="ash-enable-mirrored-screen"/> <int value="1418054870" label="SpecialLocale:enabled"/> <int value="1421620678" label="simple-clear-browsing-data-support-string"/> + <int value="1435018419" label="AutofillUpstreamRequestCvcIfMissing:disabled"/> <int value="1441897340" label="AndroidSpellCheckerNonLowEnd:enabled"/> <int value="1442798825" label="enable-quic"/> <int value="1442830837" label="MemoryAblation:disabled"/> @@ -125590,6 +125640,17 @@ <affected-histogram name="Setup.Install.LzmaUnPackStatus"/> </histogram_suffixes> +<histogram_suffixes name="UserClasses" separator="."> + <suffix name="RareNTPUser" label="Rare NTP user"/> + <suffix name="ActiveNTPUser" label="Active NTP user"/> + <suffix name="ActiveSuggestionsConsumer" label="Active suggestions consumer"/> + <affected-histogram + name="NewTabPage.ContentSuggestions.TimeUntilFirstSoftTrigger"/> + <affected-histogram + name="NewTabPage.ContentSuggestions.TimeUntilPersistentFetch"/> + <affected-histogram name="NewTabPage.ContentSuggestions.TimeUntilSoftFetch"/> +</histogram_suffixes> + <histogram_suffixes name="UserScriptRunLocation" separator="."> <suffix name="DocumentStart" label="Scripts with run_at: document_start."/> <suffix name="DocumentEnd" label="Scripts with run_at: document_end."/>
diff --git a/tools/metrics/ukm/ukm.xml b/tools/metrics/ukm/ukm.xml index e83eafe9..541f1f6 100644 --- a/tools/metrics/ukm/ukm.xml +++ b/tools/metrics/ukm/ukm.xml
@@ -20,6 +20,139 @@ </metric> </event> +<event name="Autofill.DeveloperEngagement"> + <owner>csashi@google.com</owner> + <summary> + Recorded when we parse a form to log whether developer has used autocomplete + markup or UPI-VPA hints. + </summary> + <metric name="DeveloperEngagement"/> +</event> + +<event name="Autofill.FormSubmitted"> + <owner>csashi@google.com</owner> + <summary> + Recorded when user submits a form. + </summary> + <metric name="AutofillFormSubmittedState"> + <summary> + Whether form's fields were all autofilled, some fields were autofilled, or + none of the field were autofilled. See |AutofillFormSubmittedState|. + </summary> + </metric> + <metric name="MillisecondsSinceFormLoaded"> + <summary> + Time since form parse. + </summary> + </metric> +</event> + +<event name="Autofill.InteractedWithForm"> + <owner>csashi@google.com</owner> + <summary> + Recorded when we parse a form to log form metadata and autofill settings + that apply to all subsequent events for this form. + </summary> + <metric name="IsForCreditCard"> + <summary> + True for credit card forms, false for address/profile forms. + </summary> + </metric> + <metric name="LocalRecordTypeCount"> + <summary> + Number of local credit cards or local autofill profiles. + </summary> + </metric> + <metric name="ServerRecordTypeCount"> + <summary> + Number of masked and full server credit cards or server autofill profiles. + </summary> + </metric> +</event> + +<event name="Autofill.SuggestionsShown"> + <owner>csashi@google.com</owner> + <metric name="MillisecondsSinceFormLoaded"> + <summary> + Time since form parse. + </summary> + </metric> +</event> + +<event name="Autofill.SelectedMaskedServerCard"> + <owner>csashi@google.com</owner> + <metric name="MillisecondsSinceFormLoaded"> + <summary> + Time since form parse. + </summary> + </metric> +</event> + +<event name="Autofill.SuggestionFilled"> + <owner>csashi@google.com</owner> + <summary> + Recorded when user selects a suggestion and we fill the form with that + suggestion. + </summary> + <metric name="MillisecondsSinceFormLoaded"> + <summary> + Time since form parse. + </summary> + </metric> + <metric name="RecordType"> + <summary> + Whether the suggestion was from a local card/autofill profile or from a + server card/autofill profile. + </summary> + </metric> +</event> + +<event name="Autofill.TextFieldDidChange"> + <owner>csashi@google.com</owner> + <summary> + Recorded when user edits a text field. The text field may have been + autofilled. + </summary> + <metric name="FieldTypeGroup"> + <summary> + Field's |FieldTypeGroup|. See |AutofillType.group()|. + </summary> + </metric> + <metric name="HeuristicType"> + <summary> + Field's |ServerFieldType| based on heuristics. See + |AutofillField.heuristic_type()|. + </summary> + </metric> + <metric name="HtmlFieldMode"> + <summary> + Whether the field's autocomplete hint specified 'billing' or 'shipping'. + See |AutofillField.html_mode()|. + </summary> + </metric> + <metric name="HtmlFieldType"> + <summary> + Field's autocomplete field type hint. See |AutofillField.html_type()|. + </summary> + </metric> + <metric name="IsAutofilled"> + <summary> + True whether field was autofilled. See |AutofillField.is_autofilled|. + </summary> + </metric> + <metric name="MillisecondsSinceFormLoaded"> + <summary> + Time since form parse. + </summary> + </metric> + <metric name="ServerType"> + <summary> + Field's |ServerFieldType| returned by server. See + |AutofillField.server_type()|. + </summary> + </metric> +</event> + <event name="PageLoad"> <owner>bmcquade@chromium.org</owner> <summary>
diff --git a/tools/perf/page_sets/tough_video_cases.py b/tools/perf/page_sets/tough_video_cases.py index 96b3d07..6ff629b15 100644 --- a/tools/perf/page_sets/tough_video_cases.py +++ b/tools/perf/page_sets/tough_video_cases.py
@@ -20,7 +20,6 @@ 'audio_video', 'audio_only', 'video_only', - 'looping_audio', # Other filter tags: 'is_50fps', 'is_4k', @@ -36,14 +35,6 @@ super(ToughVideoCasesPage, self).__init__( url=url, page_set=page_set, tags=tags) - def LoopMixedAudio(self, action_runner): - action_runner.PlayMedia(selector='#background_audio', - playing_event_timeout_in_seconds=60) - action_runner.LoopMedia(loop_count=50, selector='#mixed_audio') - - def LoopSingleAudio(self, action_runner): - action_runner.LoopMedia(loop_count=50, selector='#single_audio') - def PlayAction(self, action_runner): # Play the media until it has finished or it times out. action_runner.PlayMedia(playing_event_timeout_in_seconds=60, @@ -449,36 +440,6 @@ self.SeekBeforeAndAfterPlayhead(action_runner) -class Page28(ToughVideoCasesPage): - - # This page tests looping a single audio track. - def __init__(self, page_set): - super(Page28, self).__init__( - url='file://tough_video_cases/audio_playback.html?id=single_audio', - page_set=page_set, - tags=['pcm', 'looping_audio']) - - self.skip_basic_metrics = True - - def RunPageInteractions(self, action_runner): - self.LoopSingleAudio(action_runner) - - -class Page29(ToughVideoCasesPage): - - # This page tests looping an audio track and playing another in the - # background. - def __init__(self, page_set): - super(Page29, self).__init__( - url='file://tough_video_cases/audio_playback.html?id=mixed_audio', - page_set=page_set, - tags=['pcm', 'looping_audio']) - - self.skip_basic_metrics = True - - def RunPageInteractions(self, action_runner): - self.LoopMixedAudio(action_runner) - class Page30(ToughVideoCasesPage): def __init__(self, page_set): @@ -631,7 +592,8 @@ class ToughVideoCasesPageSet(story.StorySet): """ - Description: Video Stack Perf benchmark that report time_to_play. + Description: Video Stack Perf pages that report time_to_play and many other + media-specific and generic metrics. """ def __init__(self): super(ToughVideoCasesPageSet, self).__init__( @@ -671,8 +633,7 @@ class ToughVideoCasesExtraPageSet(story.StorySet): """ - Description: Video Stack Perf benchmarks that report seek time and audio - avg_loop_time. + Description: Video Stack Perf pages that only report seek time. """ def __init__(self): super(ToughVideoCasesExtraPageSet, self).__init__( @@ -687,8 +648,6 @@ self.AddStory(Page25(self)) self.AddStory(Page26(self)) self.AddStory(Page27(self)) - self.AddStory(Page28(self)) - self.AddStory(Page29(self)) self.AddStory(Page31(self)) self.AddStory(Page33(self)) self.AddStory(Page35(self))
diff --git a/tools/perf/page_sets/tough_video_cases/audio_playback.html b/tools/perf/page_sets/tough_video_cases/audio_playback.html deleted file mode 100644 index c5ccbc8..0000000 --- a/tools/perf/page_sets/tough_video_cases/audio_playback.html +++ /dev/null
@@ -1,23 +0,0 @@ -<!DOCTYPE html> -<html> - <body> - <script> - function addAudio(id, file) { - var audio = document.createElement('audio'); - audio.src = file; - audio.loop = true; - audio.id = id; - document.body.appendChild(audio); - } - - function getIDFromURL() { - var query = window.location.search; - return query.substring(query.lastIndexOf("id=") + 3); - } - - addAudio('background_audio', 'pink_noise_20s.wav'); - // Main audio file is identified by an ID passed in the page-set. - addAudio(getIDFromURL(), 'pink_noise_140ms.wav'); - </script> - </body> -</html> \ No newline at end of file
diff --git a/tools/perf/page_sets/tough_video_cases/pink_noise_140ms.wav.sha1 b/tools/perf/page_sets/tough_video_cases/pink_noise_140ms.wav.sha1 deleted file mode 100644 index 839f387..0000000 --- a/tools/perf/page_sets/tough_video_cases/pink_noise_140ms.wav.sha1 +++ /dev/null
@@ -1 +0,0 @@ -16c54ce40621b7d4410554206529759607c16d70 \ No newline at end of file
diff --git a/tools/perf/page_sets/tough_video_cases/pink_noise_20s.wav.sha1 b/tools/perf/page_sets/tough_video_cases/pink_noise_20s.wav.sha1 deleted file mode 100644 index 0f9a50b1..0000000 --- a/tools/perf/page_sets/tough_video_cases/pink_noise_20s.wav.sha1 +++ /dev/null
@@ -1 +0,0 @@ -2f6874496d8dd0a7e13e60542c7143a245fea8ef \ No newline at end of file
diff --git a/ui/android/BUILD.gn b/ui/android/BUILD.gn index 22deece..749a638 100644 --- a/ui/android/BUILD.gn +++ b/ui/android/BUILD.gn
@@ -234,14 +234,12 @@ junit_binary("ui_junit_tests") { java_files = [ - "junit/src/org/chromium/ui/base/ClipboardTest.java", "junit/src/org/chromium/ui/base/SelectFileDialogTest.java", "junit/src/org/chromium/ui/text/SpanApplierTest.java", ] deps = [ ":ui_java", "//base:base_java", - "//base:base_java_test_support", ] }
diff --git a/ui/android/java/src/org/chromium/ui/base/Clipboard.java b/ui/android/java/src/org/chromium/ui/base/Clipboard.java index abfdf2e..0f3eb25 100644 --- a/ui/android/java/src/org/chromium/ui/base/Clipboard.java +++ b/ui/android/java/src/org/chromium/ui/base/Clipboard.java
@@ -5,23 +5,23 @@ package org.chromium.ui.base; import android.content.ClipData; +import android.content.ClipDescription; import android.content.ClipboardManager; import android.content.Context; +import android.text.Html; +import android.text.Spanned; +import android.text.style.CharacterStyle; +import android.text.style.ParagraphStyle; +import android.text.style.UpdateAppearance; import org.chromium.base.ContextUtils; -import org.chromium.base.Log; import org.chromium.base.annotations.CalledByNative; import org.chromium.base.annotations.JNINamespace; import org.chromium.base.annotations.SuppressFBWarnings; -import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordUserAction; import org.chromium.ui.R; import org.chromium.ui.widget.Toast; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.Arrays; - /** * Simple proxy that provides C++ code with an access pathway to the Android clipboard. */ @@ -29,25 +29,12 @@ public class Clipboard implements ClipboardManager.OnPrimaryClipChangedListener { private static Clipboard sInstance; - private static final String TAG = "Clipboard"; - // Necessary for coercing clipboard contents to text if they require // access to network resources, etceteras (e.g., URI in clipboard) private final Context mContext; private final ClipboardManager mClipboardManager; - // A message hasher that's used to hash clipboard contents so we can tell - // when a clipboard changes without storing the full contents. - private MessageDigest mMd5Hasher; - // The hash of the current clipboard. - // TODO(mpearson): unsuppress this warning once saving and restoring - // the hash from prefs is added. - @SuppressFBWarnings("URF_UNREAD_FIELD") - private byte[] mClipboardMd5; - // The time when the clipboard was last updated. Set to 0 if unknown. - private long mClipboardChangeTime; - /** * Get the singleton Clipboard instance (creating it if needed). */ @@ -65,19 +52,6 @@ (ClipboardManager) ContextUtils.getApplicationContext().getSystemService( Context.CLIPBOARD_SERVICE); mClipboardManager.addPrimaryClipChangedListener(this); - try { - mMd5Hasher = MessageDigest.getInstance("MD5"); - mClipboardMd5 = weakMd5Hash(); - } catch (NoSuchAlgorithmException e) { - Log.e(TAG, - "Unable to construct MD5 MessageDigest: %s; assume " - + "clipboard last update time is start of epoch.", - e); - mMd5Hasher = null; - mClipboardMd5 = new byte[] {}; - } - RecordHistogram.recordBooleanHistogram("Clipboard.ConstructedHasher", mMd5Hasher != null); - mClipboardChangeTime = 0; } /** @@ -98,7 +72,7 @@ */ @SuppressWarnings("javadoc") @CalledByNative - private String getCoercedText() { + public String getCoercedText() { // getPrimaryClip() has been observed to throw unexpected exceptions for some devices (see // crbug.com/654802 and b/31501780) try { @@ -111,6 +85,18 @@ } } + // TODO(ctzsm): Remove this method after Android API is updated + private boolean hasStyleSpan(Spanned spanned) { + Class<?>[] styleClasses = { + CharacterStyle.class, ParagraphStyle.class, UpdateAppearance.class}; + for (Class<?> clazz : styleClasses) { + if (spanned.nextSpanTransition(-1, spanned.length(), clazz) < spanned.length()) { + return true; + } + } + return false; + } + /** * Gets the HTML text of top item on the primary clip on the Android clipboard. * @@ -118,29 +104,26 @@ * text or no entries on the primary clip. */ @CalledByNative - private String getHTMLText() { + public String getHTMLText() { // getPrimaryClip() has been observed to throw unexpected exceptions for some devices (see // crbug/654802 and b/31501780) try { - return mClipboardManager.getPrimaryClip().getItemAt(0).getHtmlText(); + ClipData clipData = mClipboardManager.getPrimaryClip(); + ClipDescription description = clipData.getDescription(); + if (description.hasMimeType(ClipDescription.MIMETYPE_TEXT_HTML)) { + return clipData.getItemAt(0).getHtmlText(); + } + + if (description.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) { + Spanned spanned = (Spanned) clipData.getItemAt(0).getText(); + if (hasStyleSpan(spanned)) { + return Html.toHtml(spanned); + } + } } catch (Exception e) { return null; } - } - - /** - * Gets the time the clipboard content last changed. - * - * This is calculated according to the device's clock. E.g., it continues - * increasing when the device is suspended. Likewise, it can be in the - * future if the user's clock updated after this information was recorded. - * - * @return a Java long recording the last changed time in milliseconds since - * epoch, or 0 if the time could not be determined. - */ - @CalledByNative - public long getClipboardContentChangeTimeInMillis() { - return mClipboardChangeTime; + return null; } /** @@ -179,7 +162,7 @@ setPrimaryClipNoException(ClipData.newPlainText(null, null)); } - private void setPrimaryClipNoException(ClipData clip) { + public void setPrimaryClipNoException(ClipData clip) { try { mClipboardManager.setPrimaryClip(clip); } catch (Exception ex) { @@ -190,35 +173,17 @@ } /** - * Updates mClipboardMd5 and mClipboardChangeTime when the clipboard updates. + * Tells the C++ Clipboard that the clipboard has changed. * * Implements OnPrimaryClipChangedListener to listen for clipboard updates. */ @Override public void onPrimaryClipChanged() { - if (mMd5Hasher == null) return; RecordUserAction.record("MobileClipboardChanged"); - mClipboardMd5 = weakMd5Hash(); - // Always update the clipboard change time even if the clipboard - // content hasn't changed. This is because if the user put something - // in the clipboard recently (even if it was not necessary because it - // was already there), that content should be considered recent. - mClipboardChangeTime = System.currentTimeMillis(); + long nativeClipboardAndroid = nativeInit(); + if (nativeClipboardAndroid != 0) nativeOnPrimaryClipChanged(nativeClipboardAndroid); } - /** - * Returns a weak hash of getCoercedText(). - * - * @return a Java byte[] with the weak hash. - */ - private byte[] weakMd5Hash() { - if (getCoercedText() == null) { - return new byte[] {}; - } - // Compute a hash consisting of the first 4 bytes of the MD5 hash of - // getCoercedText(). This value is used to detect clipboard content - // change. Keeping only 4 bytes is a privacy requirement to introduce - // collision and allow deniability of having copied a given string. - return Arrays.copyOfRange(mMd5Hasher.digest(getCoercedText().getBytes()), 0, 4); - } + private native long nativeInit(); + private native void nativeOnPrimaryClipChanged(long nativeClipboardAndroid); }
diff --git a/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java b/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java deleted file mode 100644 index a4bd9666..0000000 --- a/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java +++ /dev/null
@@ -1,50 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -package org.chromium.ui.base; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.annotation.Config; - -import org.chromium.base.ContextUtils; -import org.chromium.base.metrics.RecordHistogram; -import org.chromium.base.metrics.RecordUserAction; -import org.chromium.testing.local.LocalRobolectricTestRunner; - -/** - * Tests logic in the Clipboard class. - */ -@RunWith(LocalRobolectricTestRunner.class) -@Config(manifest = Config.NONE) -public class ClipboardTest { - @BeforeClass - public static void beforeClass() { - RecordHistogram.setDisabledForTests(true); - RecordUserAction.setDisabledForTests(true); - } - - @AfterClass - public static void afterClass() { - RecordHistogram.setDisabledForTests(false); - RecordUserAction.setDisabledForTests(false); - } - - @Test - public void testGetClipboardContentChangeTimeInMillis() { - ContextUtils.initApplicationContext(RuntimeEnvironment.application); - // Upon launch, the clipboard should be at start of epoch, i.e., ancient. - Clipboard clipboard = Clipboard.getInstance(); - assertEquals(0, clipboard.getClipboardContentChangeTimeInMillis()); - // After updating the clipboard, it should have a new time. - clipboard.onPrimaryClipChanged(); - assertTrue(clipboard.getClipboardContentChangeTimeInMillis() > 0); - } -}
diff --git a/ui/base/android/ui_base_jni_registrar.cc b/ui/base/android/ui_base_jni_registrar.cc index e86f51adc1..26343df 100644 --- a/ui/base/android/ui_base_jni_registrar.cc +++ b/ui/base/android/ui_base_jni_registrar.cc
@@ -7,13 +7,15 @@ #include "base/android/jni_android.h" #include "base/android/jni_registrar.h" #include "base/macros.h" +#include "ui/base/clipboard/clipboard_android.h" #include "ui/base/l10n/l10n_util_android.h" namespace ui { namespace android { static base::android::RegistrationMethod kUiRegisteredMethods[] = { - { "LocalizationUtils", l10n_util::RegisterLocalizationUtil }, + {"LocalizationUtils", l10n_util::RegisterLocalizationUtil}, + {"ClipboardAndroid", ui::RegisterClipboardAndroid}, }; bool RegisterJni(JNIEnv* env) {
diff --git a/ui/base/clipboard/clipboard.cc b/ui/base/clipboard/clipboard.cc index 6c98121..c8c0c38d 100644 --- a/ui/base/clipboard/clipboard.cc +++ b/ui/base/clipboard/clipboard.cc
@@ -86,10 +86,12 @@ clipboard_map->erase(it); } -base::Time Clipboard::GetClipboardLastModifiedTime() const { +base::Time Clipboard::GetLastModifiedTime() const { return base::Time(); } +void Clipboard::ClearLastModifiedTime() {} + void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) { // Ignore writes with empty parameters. for (const auto& param : params) {
diff --git a/ui/base/clipboard/clipboard.h b/ui/base/clipboard/clipboard.h index 5d27f2c..dd18ad5 100644 --- a/ui/base/clipboard/clipboard.h +++ b/ui/base/clipboard/clipboard.h
@@ -207,7 +207,10 @@ // Returns an estimate of the time the clipboard was last updated. If the // time is unknown, returns Time::Time(). - virtual base::Time GetClipboardLastModifiedTime() const; + virtual base::Time GetLastModifiedTime() const; + + // Resets the clipboard last modified time to Time::Time(). + virtual void ClearLastModifiedTime(); // Gets the FormatType corresponding to an arbitrary format string, // registering it with the system if needed. Due to Windows/Linux
diff --git a/ui/base/clipboard/clipboard_android.cc b/ui/base/clipboard/clipboard_android.cc index 8f35282a..1761d6c 100644 --- a/ui/base/clipboard/clipboard_android.cc +++ b/ui/base/clipboard/clipboard_android.cc
@@ -8,10 +8,12 @@ #include "base/android/context_utils.h" #include "base/android/jni_string.h" +#include "base/android/scoped_java_ref.h" #include "base/lazy_instance.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock.h" +#include "base/time/time.h" #include "jni/Clipboard_jni.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/size.h" @@ -37,6 +39,7 @@ namespace ui { namespace { + // Various formats we support. const char kURLFormat[] = "url"; const char kPlainTextFormat[] = "text"; @@ -50,25 +53,36 @@ public: ClipboardMap(); std::string Get(const std::string& format); - int64_t GetLastClipboardChangeTimeInMillis(); + uint64_t GetSequenceNumber() const; + base::Time GetLastModifiedTime() const; + void ClearLastModifiedTime(); bool HasFormat(const std::string& format); + void OnPrimaryClipboardChanged(); void Set(const std::string& format, const std::string& data); void CommitToAndroidClipboard(); void Clear(); private: + enum class MapState { + kOutOfDate, + kUpToDate, + kPreparingCommit, + }; + void UpdateFromAndroidClipboard(); std::map<std::string, std::string> map_; + MapState map_state_; base::Lock lock_; - int64_t last_clipboard_change_time_ms_; + uint64_t sequence_number_; + base::Time last_modified_time_; // Java class and methods for the Android ClipboardManager. ScopedJavaGlobalRef<jobject> clipboard_manager_; }; base::LazyInstance<ClipboardMap>::Leaky g_map = LAZY_INSTANCE_INITIALIZER; -ClipboardMap::ClipboardMap() { +ClipboardMap::ClipboardMap() : map_state_(MapState::kOutOfDate) { clipboard_manager_.Reset(Java_Clipboard_getInstance(AttachCurrentThread())); DCHECK(clipboard_manager_.obj()); } @@ -80,10 +94,16 @@ return it == map_.end() ? std::string() : it->second; } -int64_t ClipboardMap::GetLastClipboardChangeTimeInMillis() { - base::AutoLock lock(lock_); - UpdateFromAndroidClipboard(); - return last_clipboard_change_time_ms_; +uint64_t ClipboardMap::GetSequenceNumber() const { + return sequence_number_; +} + +base::Time ClipboardMap::GetLastModifiedTime() const { + return last_modified_time_; +} + +void ClipboardMap::ClearLastModifiedTime() { + last_modified_time_ = base::Time(); } bool ClipboardMap::HasFormat(const std::string& format) { @@ -92,9 +112,16 @@ return base::ContainsKey(map_, format); } +void ClipboardMap::OnPrimaryClipboardChanged() { + sequence_number_++; + last_modified_time_ = base::Time::Now(); + map_state_ = MapState::kOutOfDate; +} + void ClipboardMap::Set(const std::string& format, const std::string& data) { base::AutoLock lock(lock_); map_[format] = data; + map_state_ = MapState::kPreparingCommit; } void ClipboardMap::CommitToAndroidClipboard() { @@ -122,6 +149,9 @@ Java_Clipboard_clear(env, clipboard_manager_); NOTIMPLEMENTED(); } + map_state_ = MapState::kUpToDate; + sequence_number_++; + last_modified_time_ = base::Time::Now(); } void ClipboardMap::Clear() { @@ -129,6 +159,9 @@ base::AutoLock lock(lock_); map_.clear(); Java_Clipboard_clear(env, clipboard_manager_); + map_state_ = MapState::kUpToDate; + sequence_number_++; + last_modified_time_ = base::Time::Now(); } // Add a key:jstr pair to map, but only if jstr is not null, and also @@ -144,38 +177,24 @@ } } -// Return true if all the key-value pairs in map1 are also in map2. -bool MapIsSubset(const std::map<std::string, std::string>& map1, - const std::map<std::string, std::string>& map2) { - for (const auto& val : map1) { - auto iter = map2.find(val.first); - if (iter == map2.end() || iter->second != val.second) - return false; - } - return true; -} - void ClipboardMap::UpdateFromAndroidClipboard() { - // Fetch the current Android clipboard state. Replace our state with - // the Android state if the Android state has been changed. + DCHECK_NE(MapState::kPreparingCommit, map_state_); + if (map_state_ == MapState::kUpToDate) + return; + + // Fetch the current Android clipboard state. lock_.AssertAcquired(); JNIEnv* env = AttachCurrentThread(); - std::map<std::string, std::string> android_clipboard_state; - ScopedJavaLocalRef<jstring> jtext = Java_Clipboard_getCoercedText(env, clipboard_manager_); ScopedJavaLocalRef<jstring> jhtml = Java_Clipboard_getHTMLText(env, clipboard_manager_); - AddMapEntry(env, &android_clipboard_state, kPlainTextFormat, jtext); - AddMapEntry(env, &android_clipboard_state, kHTMLFormat, jhtml); - last_clipboard_change_time_ms_ = - Java_Clipboard_getClipboardContentChangeTimeInMillis(env, - clipboard_manager_); + AddMapEntry(env, &map_, kPlainTextFormat, jtext); + AddMapEntry(env, &map_, kHTMLFormat, jhtml); - if (!MapIsSubset(android_clipboard_state, map_)) - android_clipboard_state.swap(map_); + map_state_ = MapState::kUpToDate; } } // namespace @@ -277,6 +296,13 @@ } // ClipboardAndroid implementation. + +void ClipboardAndroid::OnPrimaryClipChanged( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj) { + g_map.Get().OnPrimaryClipboardChanged(); +} + ClipboardAndroid::ClipboardAndroid() { DCHECK(CalledOnValidThread()); } @@ -289,10 +315,7 @@ uint64_t ClipboardAndroid::GetSequenceNumber(ClipboardType /* type */) const { DCHECK(CalledOnValidThread()); - // TODO: implement this. For now this interface will advertise - // that the clipboard never changes. That's fine as long as we - // don't rely on this signal. - return 0; + return g_map.Get().GetSequenceNumber(); } bool ClipboardAndroid::IsFormatAvailable(const Clipboard::FormatType& format, @@ -414,10 +437,14 @@ *result = g_map.Get().Get(format.ToString()); } -base::Time ClipboardAndroid::GetClipboardLastModifiedTime() const { +base::Time ClipboardAndroid::GetLastModifiedTime() const { DCHECK(CalledOnValidThread()); - return base::Time::FromJavaTime( - g_map.Get().GetLastClipboardChangeTimeInMillis()); + return g_map.Get().GetLastModifiedTime(); +} + +void ClipboardAndroid::ClearLastModifiedTime() { + DCHECK(CalledOnValidThread()); + g_map.Get().ClearLastModifiedTime(); } // Main entry point used to write several values in the clipboard. @@ -485,4 +512,14 @@ g_map.Get().Set(format.ToString(), std::string(data_data, data_len)); } +bool RegisterClipboardAndroid(JNIEnv* env) { + return RegisterNativesImpl(env); +} + +// Returns a pointer to the current ClipboardAndroid object. +static jlong Init(JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj) { + return reinterpret_cast<intptr_t>(Clipboard::GetForCurrentThread()); +} + } // namespace ui
diff --git a/ui/base/clipboard/clipboard_android.h b/ui/base/clipboard/clipboard_android.h index a41550a2..8589e9c 100644 --- a/ui/base/clipboard/clipboard_android.h +++ b/ui/base/clipboard/clipboard_android.h
@@ -11,11 +11,19 @@ #include <stddef.h> #include <stdint.h> +#include "base/android/scoped_java_ref.h" #include "base/macros.h" +#include "base/time/time.h" namespace ui { class ClipboardAndroid : public Clipboard { + public: + // Called by Java when the Java Clipboard is notified that the clipboard has + // changed. + void OnPrimaryClipChanged(JNIEnv* env, + const base::android::JavaParamRef<jobject>& obj); + private: friend class Clipboard; @@ -45,7 +53,8 @@ base::string16* result) const override; void ReadBookmark(base::string16* title, std::string* url) const override; void ReadData(const FormatType& format, std::string* result) const override; - base::Time GetClipboardLastModifiedTime() const override; + base::Time GetLastModifiedTime() const override; + void ClearLastModifiedTime() override; void WriteObjects(ClipboardType type, const ObjectMap& objects) override; void WriteText(const char* text_data, size_t text_len) override; void WriteHTML(const char* markup_data, @@ -66,6 +75,9 @@ DISALLOW_COPY_AND_ASSIGN(ClipboardAndroid); }; +// Registers the ClipboardAndroid native method. +bool RegisterClipboardAndroid(JNIEnv* env); + } // namespace ui #endif // UI_BASE_CLIPBOARD_CLIPBOARD_ANDROID_H_
diff --git a/ui/base/test/test_clipboard.cc b/ui/base/test/test_clipboard.cc index 7bceec1..1fdb268d 100644 --- a/ui/base/test/test_clipboard.cc +++ b/ui/base/test/test_clipboard.cc
@@ -26,7 +26,7 @@ return clipboard; } -void TestClipboard::SetClipboardLastModifiedTime(const base::Time& time) { +void TestClipboard::SetLastModifiedTime(const base::Time& time) { last_modified_time_ = time; } @@ -130,10 +130,14 @@ *result = it->second; } -base::Time TestClipboard::GetClipboardLastModifiedTime() const { +base::Time TestClipboard::GetLastModifiedTime() const { return last_modified_time_; } +void TestClipboard::ClearLastModifiedTime() { + last_modified_time_ = base::Time(); +} + void TestClipboard::WriteObjects(ClipboardType type, const ObjectMap& objects) { Clear(type); default_store_type_ = type;
diff --git a/ui/base/test/test_clipboard.h b/ui/base/test/test_clipboard.h index 7bf88a24..a574adba 100644 --- a/ui/base/test/test_clipboard.h +++ b/ui/base/test/test_clipboard.h
@@ -26,8 +26,8 @@ // Clipboard::DestroyClipboardForCurrentThread() on the same thread. static Clipboard* CreateForCurrentThread(); - // Sets the time to be returned by GetClipboardLastModifiedTime(); - void SetClipboardLastModifiedTime(const base::Time& time); + // Sets the time to be returned by GetLastModifiedTime(); + void SetLastModifiedTime(const base::Time& time); // Clipboard overrides. void OnPreShutdown() override; @@ -52,7 +52,8 @@ base::string16* result) const override; void ReadBookmark(base::string16* title, std::string* url) const override; void ReadData(const FormatType& format, std::string* result) const override; - base::Time GetClipboardLastModifiedTime() const override; + base::Time GetLastModifiedTime() const override; + void ClearLastModifiedTime() override; void WriteObjects(ClipboardType type, const ObjectMap& objects) override; void WriteText(const char* text_data, size_t text_len) override; void WriteHTML(const char* markup_data,
diff --git a/ui/chromeos/resources/default_100_percent/print_notification/print_job_waiting.png b/ui/chromeos/resources/default_100_percent/print_notification/print_job_waiting.png deleted file mode 100644 index 324b684..0000000 --- a/ui/chromeos/resources/default_100_percent/print_notification/print_job_waiting.png +++ /dev/null Binary files differ
diff --git a/ui/chromeos/resources/default_200_percent/print_notification/print_job_waiting.png b/ui/chromeos/resources/default_200_percent/print_notification/print_job_waiting.png deleted file mode 100644 index 03debe6..0000000 --- a/ui/chromeos/resources/default_200_percent/print_notification/print_job_waiting.png +++ /dev/null Binary files differ
diff --git a/ui/chromeos/resources/ui_chromeos_resources.grd b/ui/chromeos/resources/ui_chromeos_resources.grd index 688c7922..3695229 100644 --- a/ui/chromeos/resources/ui_chromeos_resources.grd +++ b/ui/chromeos/resources/ui_chromeos_resources.grd
@@ -60,7 +60,6 @@ <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_PAUSE" file="print_notification/pause.png" /> <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_PLAY" file="print_notification/play.png" /> <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_HELP" file="print_notification/help.png" /> - <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_WAITING" file="print_notification/print_job_waiting.png" /> <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_PRINTING" file="print_notification/print_job_printing.png" /> <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_DONE" file="print_notification/print_job_done.png" /> <structure type="chrome_scaled_image" name="IDR_PRINT_NOTIFICATION_ERROR" file="print_notification/print_job_error.png" />
diff --git a/ui/display/manager/chromeos/display_configurator.h b/ui/display/manager/chromeos/display_configurator.h index d6cd968..8519bd11 100644 --- a/ui/display/manager/chromeos/display_configurator.h +++ b/ui/display/manager/chromeos/display_configurator.h
@@ -153,7 +153,7 @@ // The delay to perform configuration after RRNotify. See the comment for // |configure_timer_|. - static const int kConfigureDelayMs = 500; + static const int kConfigureDelayMs = 1000; // The delay to perform configuration after waking up from suspend when in // multi display mode. Should be bigger than |kConfigureDelayMs|. Generally
diff --git a/ui/display/manager/display_manager.cc b/ui/display/manager/display_manager.cc index 6b34dedc..0a7bd13 100644 --- a/ui/display/manager/display_manager.cc +++ b/ui/display/manager/display_manager.cc
@@ -377,27 +377,37 @@ display_property_changed = true; } else { display_modes_[display_id] = *iter; - if (info.bounds_in_native().size() != display_mode->size()) + if (info.bounds_in_native().size() != display_mode->size()) { + // If resolution changes, then we can break right here. No need to + // continue to fill |display_info_list|, since we won't be + // synchronously updating the displays here. resolution_changed = true; + break; + } if (info.device_scale_factor() != display_mode->device_scale_factor()) { info.set_device_scale_factor(display_mode->device_scale_factor()); display_property_changed = true; } } } - display_info_list.push_back(info); + display_info_list.emplace_back(info); } - if (display_property_changed) { + + if (display_property_changed && !resolution_changed) { + // We shouldn't synchronously update the displays here if the resolution + // changed. This should happen asynchronously when configuration is + // triggered. AddMirrorDisplayInfoIfAny(&display_info_list); UpdateDisplaysWith(display_info_list); } - if (resolution_changed && IsInUnifiedMode()) { + + if (resolution_changed && IsInUnifiedMode()) ReconfigureDisplays(); #if defined(OS_CHROMEOS) - } else if (resolution_changed && configure_displays_) { + else if (resolution_changed && configure_displays_) delegate_->display_configurator()->OnConfigurationChanged(); -#endif - } +#endif // defined(OS_CHROMEOS) + return resolution_changed || display_property_changed; }
diff --git a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm index ce75522..c77bdf4 100644 --- a/ui/views/controls/scrollbar/cocoa_scroll_bar.mm +++ b/ui/views/controls/scrollbar/cocoa_scroll_bar.mm
@@ -5,7 +5,6 @@ #import "ui/views/controls/scrollbar/cocoa_scroll_bar.h" #import "base/mac/sdk_forward_declarations.h" -#include "cc/paint/paint_shader.h" #include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/effects/SkGradientShader.h" #include "ui/compositor/layer.h"
diff --git a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js index c98c75a..3e9fc20 100644 --- a/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js +++ b/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js
@@ -48,6 +48,9 @@ /** @private {?IntersectionObserver} */ intersectionObserver_: null, + /** @private {?MutationObserver} */ + mutationObserver_: null, + /** @override */ ready: function() { // If the active history entry changes (i.e. user clicks back button), @@ -63,39 +66,68 @@ /** @override */ attached: function() { - if (this.showScrollBorders) { - var bodyContainer = this.$$('.body-container'); + if (!this.showScrollBorders) + return; - var bottomMarker = this.$.bodyBottomMarker; - var topMarker = this.$.bodyTopMarker; + this.mutationObserver_ = new MutationObserver(function() { + if (this.open) { + this.addIntersectionObserver_(); + } else { + this.removeIntersectionObserver_(); + } + }.bind(this)); - var callback = function(entries) { - assert(entries.length <= 2); - for (var i = 0; i < entries.length; i++) { - var target = entries[i].target; - assert(target == bottomMarker || target == topMarker); - - var classToToggle = - target == bottomMarker ? 'bottom-scrollable' : 'top-scrollable'; - - bodyContainer.classList.toggle( - classToToggle, entries[i].intersectionRatio == 0); - } - }; - - this.intersectionObserver_ = new IntersectionObserver( - callback, - /** @type {IntersectionObserverInit} */ ({ - root: bodyContainer, - threshold: 0, - })); - this.intersectionObserver_.observe(bottomMarker); - this.intersectionObserver_.observe(topMarker); - } + this.mutationObserver_.observe(this, { + attributes: true, + attributeFilter: ['open'], + }); }, /** @override */ detached: function() { + this.removeIntersectionObserver_(); + if (this.mutationObserver_) { + this.mutationObserver_.disconnect(); + this.mutationObserver_ = null; + } + }, + + /** @private */ + addIntersectionObserver_: function() { + if (this.intersectionObserver_) + return; + + var bodyContainer = this.$$('.body-container'); + + var bottomMarker = this.$.bodyBottomMarker; + var topMarker = this.$.bodyTopMarker; + + var callback = function(entries) { + assert(entries.length <= 2); + for (var i = 0; i < entries.length; i++) { + var target = entries[i].target; + assert(target == bottomMarker || target == topMarker); + + var classToToggle = + target == bottomMarker ? 'bottom-scrollable' : 'top-scrollable'; + + bodyContainer.classList.toggle( + classToToggle, entries[i].intersectionRatio == 0); + } + }; + + this.intersectionObserver_ = new IntersectionObserver( + callback, + /** @type {IntersectionObserverInit} */ ({ + root: bodyContainer, + threshold: 0, + })); + this.intersectionObserver_.observe(bottomMarker); + this.intersectionObserver_.observe(topMarker); + }, + + /** @private */ + removeIntersectionObserver_: function() { if (this.intersectionObserver_) { this.intersectionObserver_.disconnect(); this.intersectionObserver_ = null;