0

Added initial page annotator code.

This is the first class in the image annotation client library, which will
support the use of the image annotation service (see Google-internal doc
go/chrome-image-annotation-dd) for images on webpages.

The page annotator will be used to schedule and receive image annotations for
one web page. This initial CL only contains minimal observer logic; the
annotation logic (i.e. communication with the annotation service) will be added
when the service is complete.

Bug: crbug.com/916363
Change-Id: I74ff44ba707cab972866999cbe0c9542d509293d
Reviewed-on: https://chromium-review.googlesource.com/c/1383756
Reviewed-by: Avi Drissman <avi@chromium.org>
Reviewed-by: Sadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: Colin Blundell <blundell@chromium.org>
Reviewed-by: Charles . <charleszhao@chromium.org>
Commit-Queue: Michael Martis <martis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#621336}
This commit is contained in:
Michael Martis
2019-01-09 22:23:55 +00:00
committed by Commit Bot
parent 87fd1bc02d
commit 1a57af941a
13 changed files with 384 additions and 0 deletions

@ -128,6 +128,7 @@ test("components_unittests") {
"//components/omnibox/browser:unit_tests",
"//components/open_from_clipboard:unit_tests",
"//components/os_crypt:unit_tests",
"//components/page_image_annotation/core:unit_tests",
"//components/password_manager/core/browser:unit_tests",
"//components/password_manager/core/common:unit_tests",
"//components/payments/core:unit_tests",

@ -0,0 +1,5 @@
include_rules = [
# Page image annotation is a layered component; subdirectories must explicitly
# introduce the ability to use the content layer as appropriate.
"-components/page_image_annotation/content",
]

@ -0,0 +1,3 @@
amoylan@chromium.org
dmazzoni@chromium.org
martis@chromium.org

@ -0,0 +1,11 @@
# //components/page_image_annotation
Library for using the image annotation service on images on webpages.
The image annotation service performs general image processing / labeling tasks
in Chromium. This library enables use of the service with webpages; for example,
by tracking images on webpages and sending their pixel data to the service.
This is a layered component
(https://sites.google.com/a/chromium.org/dev/developers/design-documents/layered-components-design)
which allows it to be shared cleanly on iOS.

@ -0,0 +1,4 @@
include_rules = [
"+components/page_image_annotation/core",
"+content/public/common",
]

@ -0,0 +1,17 @@
# Copyright 2019 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.
static_library("renderer") {
sources = [
"content_page_annotator_driver.cc",
"content_page_annotator_driver.h",
]
deps = [
"//base",
"//components/page_image_annotation/core",
"//content/public/common",
"//content/public/renderer",
]
}

@ -0,0 +1,5 @@
include_rules = [
"+components/page_image_annotation/core",
"+content/public/renderer",
"+ui/base",
]

@ -0,0 +1,45 @@
// Copyright 2019 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/page_image_annotation/content/renderer/content_page_annotator_driver.h"
#include "content/public/renderer/render_frame.h"
namespace page_image_annotation {
ContentPageAnnotatorDriver::ContentPageAnnotatorDriver(
content::RenderFrame* const render_frame)
: RenderFrameObserver(render_frame),
RenderFrameObserverTracker<ContentPageAnnotatorDriver>(render_frame) {}
ContentPageAnnotatorDriver::~ContentPageAnnotatorDriver() {}
// static
ContentPageAnnotatorDriver* ContentPageAnnotatorDriver::GetOrCreate(
content::RenderFrame* const render_frame) {
ContentPageAnnotatorDriver* const existing = Get(render_frame);
if (existing)
return existing;
return new ContentPageAnnotatorDriver(render_frame);
}
PageAnnotator& ContentPageAnnotatorDriver::GetPageAnnotator() {
return page_annotator_;
}
void ContentPageAnnotatorDriver::DidCommitProvisionalLoad(
const bool /* is_same_document_navigation */,
const ui::PageTransition /* transition */) {
// TODO(crbug.com/915076): schedule repeated DOM traversals to track image
// addition / modification / removal.
}
void ContentPageAnnotatorDriver::OnDestruct() {
// TODO(crbug.com/915076): cancel DOM traversal.
delete this;
}
} // namespace page_image_annotation

@ -0,0 +1,44 @@
// Copyright 2019 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_PAGE_IMAGE_ANNOTATION_CONTENT_RENDERER_CONTENT_PAGE_ANNOTATOR_DRIVER_H_
#define COMPONENTS_PAGE_IMAGE_ANNOTATION_CONTENT_RENDERER_CONTENT_PAGE_ANNOTATOR_DRIVER_H_
#include "base/macros.h"
#include "components/page_image_annotation/core/page_annotator.h"
#include "content/public/renderer/render_frame_observer.h"
#include "content/public/renderer/render_frame_observer_tracker.h"
#include "ui/base/page_transition_types.h"
namespace page_image_annotation {
class ContentPageAnnotatorDriver
: public content::RenderFrameObserver,
public content::RenderFrameObserverTracker<ContentPageAnnotatorDriver> {
public:
~ContentPageAnnotatorDriver() override;
static ContentPageAnnotatorDriver* GetOrCreate(
content::RenderFrame* render_frame);
PageAnnotator& GetPageAnnotator();
private:
// We delete ourselves on frame destruction, so disallow construction on the
// stack.
ContentPageAnnotatorDriver(content::RenderFrame* render_frame);
// content::RenderFrameObserver:
void DidCommitProvisionalLoad(bool is_same_document_navigation,
ui::PageTransition transition) override;
void OnDestruct() override;
PageAnnotator page_annotator_;
DISALLOW_COPY_AND_ASSIGN(ContentPageAnnotatorDriver);
};
} // namespace page_image_annotation
#endif // COMPONENTS_PAGE_IMAGE_ANNOTATION_CONTENT_RENDERER_CONTENT_PAGE_ANNOTATOR_DRIVER_H_

@ -0,0 +1,29 @@
# Copyright 2019 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.
static_library("core") {
sources = [
"page_annotator.cc",
"page_annotator.h",
]
deps = [
"//base",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"page_annotator_unittest.cc",
]
deps = [
":core",
"//base",
"//base/test:test_support",
"//testing/gmock",
"//testing/gtest",
]
}

@ -0,0 +1,66 @@
// Copyright 2019 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/page_image_annotation/core/page_annotator.h"
namespace page_image_annotation {
PageAnnotator::Observer::~Observer() {}
PageAnnotator::Subscription::Subscription(
const Observer* const observer,
base::WeakPtr<PageAnnotator> page_annotator)
: observer_(observer), page_annotator_(page_annotator) {}
PageAnnotator::Subscription::Subscription(Subscription&& subscription) =
default;
PageAnnotator::Subscription::~Subscription() {
Cancel();
}
void PageAnnotator::Subscription::Cancel() {
if (page_annotator_)
page_annotator_->RemoveObserver(observer_);
}
PageAnnotator::PageAnnotator() : weak_ptr_factory_(this) {}
PageAnnotator::~PageAnnotator() {}
void PageAnnotator::ImageAdded(const uint64_t node_id,
const std::string& source_id) {
// TODO(crbug.com/916363): create a connection to the image annotation service
// for this image.
for (Observer& observer : observers_) {
observer.OnImageAdded(node_id);
}
}
void PageAnnotator::ImageModified(const uint64_t node_id,
const std::string& source_id) {
// TODO(crbug.com/916363): reset the service connection for this image.
for (Observer& observer : observers_) {
observer.OnImageModified(node_id);
}
}
void PageAnnotator::ImageRemoved(const uint64_t node_id) {
// TODO(crbug.com/916363): close the service connection for this image.
for (Observer& observer : observers_) {
observer.OnImageRemoved(node_id);
}
}
PageAnnotator::Subscription PageAnnotator::AddObserver(Observer* observer) {
observers_.AddObserver(observer);
return Subscription(observer, weak_ptr_factory_.GetWeakPtr());
}
void PageAnnotator::RemoveObserver(const Observer* observer) {
observers_.RemoveObserver(observer);
}
} // namespace page_image_annotation

@ -0,0 +1,90 @@
// Copyright 2019 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_PAGE_IMAGE_ANNOTATION_CORE_PAGE_ANNOTATOR_H_
#define COMPONENTS_PAGE_IMAGE_ANNOTATION_CORE_PAGE_ANNOTATOR_H_
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
namespace page_image_annotation {
// Notifies clients of page images that can be annotated and forwards annotation
// requests for these images to the image annotation service.
//
// TODO(crbug.com/916363): this class is not yet complete - add more logic (e.g.
// communication with the service).
class PageAnnotator {
public:
// Clients (i.e. classes that annotate page images) should implement this
// interface.
class Observer : public base::CheckedObserver {
public:
~Observer() override;
// These methods are called during page lifecycle to notify the observer
// about changes to page images.
// Called exactly once per image, at the point that the image appears on the
// page (or at the point that the observer subscribes to the page annotator,
// if the image already exists on page).
virtual void OnImageAdded(uint64_t node_id) = 0;
// Called at the point that an image source is updated.
virtual void OnImageModified(uint64_t node_id) = 0;
// Called at the point that an image disappears from the page.
virtual void OnImageRemoved(uint64_t node_id) = 0;
};
// A subscription instance must be held by each observer of the page
// annotator; an observer will receive updates from the page annotator until
// the Cancel method of the subscription is called (this occurs automatically
// on subscription destruction).
//
// Typically, both the page annotator and its observers are scoped to the
// lifetime of a render frame. Destruction of such objects can proceed in an
// unspecified order, so subscriptions are used to ensure the page annotator
// only communicates with an observers that are still alive.
class Subscription {
public:
Subscription(const Observer* observer,
base::WeakPtr<PageAnnotator> page_annotator);
Subscription(Subscription&& subscription);
~Subscription();
// Unsubscribe from updates from the page annotator.
void Cancel();
private:
const Observer* observer_;
base::WeakPtr<PageAnnotator> page_annotator_;
DISALLOW_COPY_AND_ASSIGN(Subscription);
};
PageAnnotator();
~PageAnnotator();
// Called by platform drivers.
void ImageAdded(uint64_t node_id, const std::string& source_id);
void ImageModified(uint64_t node_id, const std::string& source_id);
void ImageRemoved(uint64_t node_id);
Subscription AddObserver(Observer* observer) WARN_UNUSED_RESULT;
private:
void RemoveObserver(const Observer* observer);
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<PageAnnotator> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(PageAnnotator);
};
} // namespace page_image_annotation
#endif // COMPONENTS_PAGE_IMAGE_ANNOTATION_CORE_PAGE_ANNOTATOR_H_

@ -0,0 +1,64 @@
// Copyright 2019 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/page_image_annotation/core/page_annotator.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace page_image_annotation {
using testing::Eq;
// Tests that destroying subscriptions successfully prevents notifications.
TEST(PageAnnotatorTest, Subscriptions) {
class TestObserver : public PageAnnotator::Observer {
public:
TestObserver(PageAnnotator* const page_annotator)
: sub_(page_annotator->AddObserver(this)), last_id_(0ul) {}
void OnImageAdded(const uint64_t node_id) override { last_id_ = node_id; }
void OnImageModified(const uint64_t node_id) override {
last_id_ = node_id;
}
void OnImageRemoved(const uint64_t node_id) override { last_id_ = node_id; }
PageAnnotator::Subscription sub_;
uint64_t last_id_;
};
PageAnnotator page_annotator;
TestObserver o1(&page_annotator), o2(&page_annotator);
page_annotator.ImageAdded(1ul, "test.jpg");
EXPECT_THAT(o1.last_id_, Eq(1ul));
EXPECT_THAT(o2.last_id_, Eq(1ul));
page_annotator.ImageAdded(2ul, "example.png");
EXPECT_THAT(o1.last_id_, Eq(2ul));
EXPECT_THAT(o2.last_id_, Eq(2ul));
page_annotator.ImageModified(1ul, "demo.gif");
EXPECT_THAT(o1.last_id_, Eq(1ul));
EXPECT_THAT(o2.last_id_, Eq(1ul));
page_annotator.ImageRemoved(2ul);
EXPECT_THAT(o1.last_id_, Eq(2ul));
EXPECT_THAT(o2.last_id_, Eq(2ul));
o1.sub_.Cancel();
page_annotator.ImageAdded(3ul, "placeholder.bmp");
EXPECT_THAT(o1.last_id_, Eq(2ul));
EXPECT_THAT(o2.last_id_, Eq(3ul));
o2.sub_.Cancel();
page_annotator.ImageRemoved(1ul);
EXPECT_THAT(o1.last_id_, Eq(2ul));
EXPECT_THAT(o2.last_id_, Eq(3ul));
}
// TODO(crbug.com/916363): add more tests when behavior is added to the
// PageAnnotator class.
} // namespace page_image_annotation