Commit ec9945ae authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

weblayer: copies ObserverList from base

We can't use base in the public API, so this makes a copy of ObserverList.

BUG=none
TEST=none

Change-Id: Ia346c8a5c11204c9ee8e77a2a0c2ca861f25543f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1793782Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Commit-Queue: Scott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695258}
parent 2ccd0f32
......@@ -12,6 +12,7 @@ android_library("java") {
"org/chromium/weblayer/Navigation.java",
"org/chromium/weblayer/NavigationController.java",
"org/chromium/weblayer/NavigationObserver.java",
"org/chromium/weblayer/ObserverList.java",
"org/chromium/weblayer/Profile.java",
"org/chromium/weblayer/WebLayer.java",
]
......
......@@ -14,15 +14,12 @@ import org.chromium.weblayer_private.aidl.IBrowserController;
import org.chromium.weblayer_private.aidl.IBrowserControllerClient;
import org.chromium.weblayer_private.aidl.ObjectWrapper;
import java.util.concurrent.CopyOnWriteArrayList;
public final class BrowserController {
private static final String TAG = "WL_BrowserController";
private final IBrowserController mImpl;
private final NavigationController mNavigationController;
// TODO(sky): copy ObserverList from base and use it instead.
private final CopyOnWriteArrayList<BrowserObserver> mObservers;
private final ObserverList<BrowserObserver> mObservers;
public BrowserController(IBrowserController impl) {
mImpl = impl;
......@@ -33,7 +30,7 @@ public final class BrowserController {
throw new AndroidRuntimeException(e);
}
mObservers = new CopyOnWriteArrayList<BrowserObserver>();
mObservers = new ObserverList<BrowserObserver>();
mNavigationController = NavigationController.create(mImpl);
}
......@@ -56,11 +53,11 @@ public final class BrowserController {
}
public void addObserver(BrowserObserver observer) {
mObservers.add(observer);
mObservers.addObserver(observer);
}
public void removeObserver(BrowserObserver observer) {
mObservers.remove(observer);
mObservers.removeObserver(observer);
}
public void destroy() {
......
......@@ -15,16 +15,13 @@ import org.chromium.weblayer_private.aidl.INavigation;
import org.chromium.weblayer_private.aidl.INavigationController;
import org.chromium.weblayer_private.aidl.INavigationControllerClient;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Provides methods to control navigation, along with maintaining the current list of navigations.
*/
public final class NavigationController {
private static final String TAG = "WebLayer";
private INavigationController mNavigationController;
// TODO(sky): copy ObserverList from base and use it instead.
private final CopyOnWriteArrayList<NavigationObserver> mObservers;
private final ObserverList<NavigationObserver> mObservers;
static NavigationController create(IBrowserController browserController) {
NavigationController navigationController = new NavigationController();
......@@ -40,7 +37,7 @@ public final class NavigationController {
}
private NavigationController() {
mObservers = new CopyOnWriteArrayList<NavigationObserver>();
mObservers = new ObserverList<NavigationObserver>();
}
public void navigate(Uri uri) {
......@@ -116,11 +113,11 @@ public final class NavigationController {
}
public void addObserver(NavigationObserver observer) {
mObservers.add(observer);
mObservers.addObserver(observer);
}
public void removeObserver(NavigationObserver observer) {
mObservers.remove(observer);
mObservers.removeObserver(observer);
}
private final class NavigationControllerClientImpl extends INavigationControllerClient.Stub {
......
// Copyright 2013 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.weblayer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* A container for a list of observers.
* <p/>
* This container can be modified during iteration without invalidating the iterator.
* So, it safely handles the case of an observer removing itself or other observers from the list
* while observers are being notified.
* <p/>
* The implementation (and the interface) is heavily influenced by the C++ ObserverList.
* Notable differences:
* - The iterator implements NOTIFY_EXISTING_ONLY.
* - The range-based for loop is left to the clients to implement in terms of iterator().
* <p/>
* This class is not threadsafe. Observers MUST be added, removed and will be notified on the same
* thread this is created.
*
* @param <E> The type of observers that this list should hold.
*/
class ObserverList<E> implements Iterable<E> {
/**
* Extended iterator interface that provides rewind functionality.
*/
public interface RewindableIterator<E> extends Iterator<E> {
/**
* Rewind the iterator back to the beginning.
*
* If we need to iterate multiple times, we can avoid iterator object reallocation by using
* this method.
*/
public void rewind();
}
public final List<E> mObservers = new ArrayList<E>();
private int mIterationDepth;
private int mCount;
private boolean mNeedsCompact;
public ObserverList() {}
/**
* Add an observer to the list.
* <p/>
* An observer should not be added to the same list more than once. If an iteration is already
* in progress, this observer will be not be visible during that iteration.
*
* @return true if the observer list changed as a result of the call.
*/
public boolean addObserver(E obs) {
// Avoid adding null elements to the list as they may be removed on a compaction.
if (obs == null || mObservers.contains(obs)) {
return false;
}
// Structurally modifying the underlying list here. This means we
// cannot use the underlying list's iterator to iterate over the list.
boolean result = mObservers.add(obs);
assert result;
++mCount;
return true;
}
/**
* Remove an observer from the list if it is in the list.
*
* @return true if an element was removed as a result of this call.
*/
public boolean removeObserver(E obs) {
if (obs == null) {
return false;
}
int index = mObservers.indexOf(obs);
if (index == -1) {
return false;
}
if (mIterationDepth == 0) {
// No one is iterating over the list.
mObservers.remove(index);
} else {
mNeedsCompact = true;
mObservers.set(index, null);
}
--mCount;
assert mCount >= 0;
return true;
}
public boolean hasObserver(E obs) {
return mObservers.contains(obs);
}
public void clear() {
mCount = 0;
if (mIterationDepth == 0) {
mObservers.clear();
return;
}
int size = mObservers.size();
mNeedsCompact |= size != 0;
for (int i = 0; i < size; i++) {
mObservers.set(i, null);
}
}
@Override
public Iterator<E> iterator() {
return new ObserverListIterator();
}
/**
* It's the same as {@link ObserverList#iterator()} but the return type is
* {@link RewindableIterator}. Use this iterator type if you need to use
* {@link RewindableIterator#rewind()}.
*/
public RewindableIterator<E> rewindableIterator() {
return new ObserverListIterator();
}
/**
* Returns the number of observers currently registered in the ObserverList.
* This is equivalent to the number of non-empty spaces in |mObservers|.
*/
public int size() {
return mCount;
}
/**
* Returns true if the ObserverList contains no observers.
*/
public boolean isEmpty() {
return mCount == 0;
}
/**
* Compact the underlying list be removing null elements.
* <p/>
* Should only be called when mIterationDepth is zero.
*/
private void compact() {
assert mIterationDepth == 0;
for (int i = mObservers.size() - 1; i >= 0; i--) {
if (mObservers.get(i) == null) {
mObservers.remove(i);
}
}
}
private void incrementIterationDepth() {
mIterationDepth++;
}
private void decrementIterationDepthAndCompactIfNeeded() {
mIterationDepth--;
assert mIterationDepth >= 0;
if (mIterationDepth > 0) return;
if (!mNeedsCompact) return;
mNeedsCompact = false;
compact();
}
/**
* Returns the size of the underlying storage of the ObserverList.
* It will take into account the empty spaces inside |mObservers|.
*/
private int capacity() {
return mObservers.size();
}
private E getObserverAt(int index) {
return mObservers.get(index);
}
private class ObserverListIterator implements RewindableIterator<E> {
private int mListEndMarker;
private int mIndex;
private boolean mIsExhausted;
private ObserverListIterator() {
ObserverList.this.incrementIterationDepth();
mListEndMarker = ObserverList.this.capacity();
}
@Override
public void rewind() {
compactListIfNeeded();
ObserverList.this.incrementIterationDepth();
mListEndMarker = ObserverList.this.capacity();
mIsExhausted = false;
mIndex = 0;
}
@Override
public boolean hasNext() {
int lookupIndex = mIndex;
while (lookupIndex < mListEndMarker
&& ObserverList.this.getObserverAt(lookupIndex) == null) {
lookupIndex++;
}
if (lookupIndex < mListEndMarker) return true;
// We have reached the end of the list, allow for compaction.
compactListIfNeeded();
return false;
}
@Override
public E next() {
// Advance if the current element is null.
while (mIndex < mListEndMarker && ObserverList.this.getObserverAt(mIndex) == null) {
mIndex++;
}
if (mIndex < mListEndMarker) return ObserverList.this.getObserverAt(mIndex++);
// We have reached the end of the list, allow for compaction.
compactListIfNeeded();
throw new NoSuchElementException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private void compactListIfNeeded() {
if (!mIsExhausted) {
mIsExhausted = true;
ObserverList.this.decrementIterationDepthAndCompactIfNeeded();
}
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment