summaryrefslogtreecommitdiffstats
path: root/components/network_hints/renderer
diff options
context:
space:
mode:
authorpmeenan <pmeenan@chromium.org>2015-01-30 13:59:59 -0800
committerCommit bot <commit-bot@chromium.org>2015-01-30 22:00:53 +0000
commited4b6f8b7392eb64fc664aa49f2627ecb5fdc073 (patch)
treec41b78ef68d5d37d5ec28ecaaa4037607524060d /components/network_hints/renderer
parent7a47c4214a06aa1778ced28dc6c57658326bbf3d (diff)
downloadchromium_src-ed4b6f8b7392eb64fc664aa49f2627ecb5fdc073.zip
chromium_src-ed4b6f8b7392eb64fc664aa49f2627ecb5fdc073.tar.gz
chromium_src-ed4b6f8b7392eb64fc664aa49f2627ecb5fdc073.tar.bz2
Renamed the dns_prefetch component to network_predictor
This is in preparation for adding preconnect support which goes through the same underlying plumbing. dns_prefetch was too restrictive of a component name and was only exposing the single method. There are no functionality changes in this CL, just the rename (the namespace was also renamed) BUG=450682 Review URL: https://codereview.chromium.org/848303005 Cr-Commit-Position: refs/heads/master@{#314001}
Diffstat (limited to 'components/network_hints/renderer')
-rw-r--r--components/network_hints/renderer/BUILD.gn21
-rw-r--r--components/network_hints/renderer/DEPS5
-rw-r--r--components/network_hints/renderer/dns_prefetch_queue.cc156
-rw-r--r--components/network_hints/renderer/dns_prefetch_queue.h93
-rw-r--r--components/network_hints/renderer/dns_prefetch_queue_unittest.cc262
-rw-r--r--components/network_hints/renderer/prescient_networking_dispatcher.cc27
-rw-r--r--components/network_hints/renderer/prescient_networking_dispatcher.h31
-rw-r--r--components/network_hints/renderer/renderer_dns_prefetch.cc165
-rw-r--r--components/network_hints/renderer/renderer_dns_prefetch.h109
-rw-r--r--components/network_hints/renderer/renderer_dns_prefetch_unittest.cc37
10 files changed, 906 insertions, 0 deletions
diff --git a/components/network_hints/renderer/BUILD.gn b/components/network_hints/renderer/BUILD.gn
new file mode 100644
index 0000000..8c56754
--- /dev/null
+++ b/components/network_hints/renderer/BUILD.gn
@@ -0,0 +1,21 @@
+# Copyright 2014 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.
+
+# GYP version: components/network_hints.gypi:predictor_renderer
+source_set("renderer") {
+ sources = [
+ "dns_prefetch_queue.cc",
+ "dns_prefetch_queue.h",
+ "prescient_networking_dispatcher.cc",
+ "prescient_networking_dispatcher.h",
+ "renderer_dns_prefetch.cc",
+ "renderer_dns_prefetch.h",
+ ]
+
+ public_deps = [
+ "//components/network_hints/common",
+ "//content/public/renderer",
+ "//third_party/WebKit/public:blink",
+ ]
+}
diff --git a/components/network_hints/renderer/DEPS b/components/network_hints/renderer/DEPS
new file mode 100644
index 0000000..d6ea0c4
--- /dev/null
+++ b/components/network_hints/renderer/DEPS
@@ -0,0 +1,5 @@
+include_rules = [
+ "+components/network_hints/common",
+ "+content/public/renderer",
+ "+third_party/WebKit/public",
+]
diff --git a/components/network_hints/renderer/dns_prefetch_queue.cc b/components/network_hints/renderer/dns_prefetch_queue.cc
new file mode 100644
index 0000000..8328df9
--- /dev/null
+++ b/components/network_hints/renderer/dns_prefetch_queue.cc
@@ -0,0 +1,156 @@
+// Copyright (c) 2006-2008 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.
+
+// See header file for description of DnsQueue class
+
+#include "components/network_hints/renderer/dns_prefetch_queue.h"
+
+#include "base/logging.h"
+#include "base/metrics/stats_counters.h"
+
+namespace network_hints {
+
+DnsQueue::DnsQueue(BufferSize size)
+ : buffer_(new char[size + 2]),
+ buffer_size_(size + 1),
+ buffer_sentinel_(size + 1),
+ size_(0) {
+ CHECK(0 < static_cast<BufferSize>(size + 3)); // Avoid overflow worries.
+ buffer_[buffer_sentinel_] = '\0'; // Guard byte to help reading data.
+ readable_ = writeable_ = 0; // Buffer starts empty.
+}
+
+DnsQueue::~DnsQueue(void) {
+}
+
+void DnsQueue::Clear() {
+ size_ = 0;
+ readable_ = writeable_;
+ DCHECK(Validate());
+}
+
+// Push takes an unterminated string plus its length.
+// The string must not contain a null terminator.
+// Exactly length chars are written, or nothing is written.
+// Returns true for success, false there was no room to push.
+DnsQueue::PushResult DnsQueue::Push(const char* source,
+ const size_t unsigned_length) {
+ BufferSize length = static_cast<BufferSize>(unsigned_length);
+ if (0 > length+1) // Avoid overflows in conversion to signed.
+ return OVERFLOW_PUSH;
+
+ // To save on sites with a LOT of links to the SAME domain, we have a
+ // a compaction hack that removes duplicates when we try to push() a
+ // match with the last push.
+ if (0 < size_ && readable_ + length < buffer_sentinel_ &&
+ 0 == strncmp(source, &buffer_[readable_], unsigned_length) &&
+ '\0' == buffer_[readable_ + unsigned_length]) {
+ SIMPLE_STATS_COUNTER("DNS.PrefetchDnsRedundantPush");
+
+ // We already wrote this name to the queue, so we'll skip this repeat.
+ return REDUNDANT_PUSH;
+ }
+
+ // Calling convention precludes nulls.
+ DCHECK(!length || '\0' != source[length - 1]);
+
+ DCHECK(Validate());
+
+ BufferSize available_space = readable_ - writeable_;
+
+ if (0 >= available_space) {
+ available_space += buffer_size_;
+ }
+
+ if (length + 1 >= available_space) {
+ SIMPLE_STATS_COUNTER("DNS.PrefetchDnsQueueFull");
+ return OVERFLOW_PUSH; // Not enough space to push.
+ }
+
+ BufferSize dest = writeable_;
+ BufferSize space_till_wrap = buffer_sentinel_ - dest;
+ if (space_till_wrap < length + 1) {
+ // Copy until we run out of room at end of buffer.
+ std::memcpy(&buffer_[dest], source, space_till_wrap);
+ // Ensure caller didn't have embedded '\0' and also
+ // ensure trailing sentinel was in place.
+ // Relies on sentinel.
+ DCHECK(static_cast<size_t>(space_till_wrap) == strlen(&buffer_[dest]));
+
+ length -= space_till_wrap;
+ source += space_till_wrap;
+ dest = 0; // Continue writing at start of buffer.
+ }
+
+ // Copy any remaining portion of source.
+ std::memcpy(&buffer_[dest], source, length);
+ DCHECK(dest + length < buffer_sentinel_);
+ buffer_[dest + length] = '\0'; // We need termination in our buffer.
+ // Preclude embedded '\0'.
+ DCHECK(static_cast<size_t>(length) == strlen(&buffer_[dest]));
+
+ dest += length + 1;
+ if (dest == buffer_sentinel_)
+ dest = 0;
+
+ writeable_ = dest;
+ size_++;
+ DCHECK(Validate());
+ return SUCCESSFUL_PUSH;
+}
+
+// Extracts the next available string from the buffer.
+// The returned string is null terminated, and hence has length
+// that is exactly one greater than the written string.
+// If the buffer is empty, then the Pop and returns false.
+bool DnsQueue::Pop(std::string* out_string) {
+ DCHECK(Validate());
+ // Sentinel will preclude memory reads beyond buffer's end.
+ DCHECK('\0' == buffer_[buffer_sentinel_]);
+
+ if (readable_ == writeable_) {
+ return false; // buffer was empty
+ }
+
+ // Constructor *may* rely on sentinel for null termination.
+ (*out_string) = &buffer_[readable_];
+ // Our sentinel_ at end of buffer precludes an overflow in cast.
+ BufferSize first_fragment_size = static_cast<BufferSize> (out_string->size());
+
+ BufferSize terminal_null;
+ if (readable_ + first_fragment_size >= buffer_sentinel_) {
+ // Sentinel was used, so we need the portion after the wrap.
+ out_string->append(&buffer_[0]); // Fragment at start of buffer.
+ // Sentinel precludes overflow in cast to signed type.
+ terminal_null = static_cast<BufferSize>(out_string->size())
+ - first_fragment_size;
+ } else {
+ terminal_null = readable_ + first_fragment_size;
+ }
+ DCHECK('\0' == buffer_[terminal_null]);
+
+ BufferSize new_readable = terminal_null + 1;
+ if (buffer_sentinel_ == new_readable)
+ new_readable = 0;
+
+ readable_ = new_readable;
+ size_--;
+ if (readable_ == writeable_ || 0 == size_) {
+ // Queue is empty, so reset to start of buffer to help with peeking.
+ readable_ = writeable_ = 0;
+ }
+ DCHECK(Validate());
+ return true;
+}
+
+bool DnsQueue::Validate() {
+ return (readable_ >= 0) &&
+ readable_ < buffer_sentinel_ &&
+ writeable_ >= 0 &&
+ writeable_ < buffer_sentinel_ &&
+ '\0' == buffer_[buffer_sentinel_] &&
+ ((0 == size_) == (readable_ == writeable_));
+}
+
+} // namespace network_hints
diff --git a/components/network_hints/renderer/dns_prefetch_queue.h b/components/network_hints/renderer/dns_prefetch_queue.h
new file mode 100644
index 0000000..72b4fad
--- /dev/null
+++ b/components/network_hints/renderer/dns_prefetch_queue.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2011 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.
+
+// DnsQueue is implemented as an almost FIFO circular buffer for text
+// strings that don't have embedded nulls ('\0'). The "almost" element is that
+// some duplicate strings may be removed (i.e., the string won't really be
+// pushed *if* the class happens to notice that a duplicate is already in the
+// queue).
+// The buffers internal format is null terminated character strings
+// (a.k.a., c_strings).
+// It is written to be as fast as possible during push() operations, so
+// that there will be minimal performance impact on a supplier thread.
+// The push() operation will not block, and no memory allocation is involved
+// (internally) during the push operations.
+// The one caveat is that if there is insufficient space in the buffer to
+// accept additional string via a push(), then the push() will fail, and
+// the buffer will be unmodified.
+
+// This class was designed for use in DNS prefetch operations. During
+// rendering, the supplier is the renderer (typically), and the consumer
+// is a thread that sends messages to an async DNS resolver.
+
+#ifndef COMPONENTS_NETWORK_HINTS_RENDERER_DNS_PREFETCH_QUEUE_H__
+#define COMPONENTS_NETWORK_HINTS_RENDERER_DNS_PREFETCH_QUEUE_H__
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+
+namespace network_hints {
+
+// A queue of DNS lookup requests for internal use within the network_hints
+// component.
+class DnsQueue {
+ public:
+ // BufferSize is a signed type used for indexing into a buffer.
+ typedef int32 BufferSize;
+
+ enum PushResult { SUCCESSFUL_PUSH, OVERFLOW_PUSH, REDUNDANT_PUSH };
+
+ // The size specified in the constructor creates a buffer large enough
+ // to hold at most one string of that length, or "many"
+ // strings of considerably shorter length. Note that strings
+ // are padded internally with a terminal '\0" while stored,
+ // so if you are trying to be precise and get N strings of
+ // length K to fit, you should actually construct a buffer with
+ // an internal size of N*(K+1).
+ explicit DnsQueue(BufferSize size);
+ ~DnsQueue(void);
+
+ size_t Size() const { return size_; }
+ void Clear();
+
+ // Push takes an unterminated string of the given length
+ // and inserts it into the queue for later
+ // extraction by read. For each successful push(), there
+ // can later be a corresponding read() to extracted the text.
+ // The string must not contain an embedded null terminator
+ // Exactly length chars are written, or the push fails (where
+ // "fails" means nothing is written).
+ // Returns true for success, false for failure (nothing written).
+ PushResult Push(const char* source, const size_t length);
+
+ PushResult Push(std::string source) {
+ return Push(source.c_str(), source.length());
+ }
+
+ // Extract the next available string from the buffer.
+ // If the buffer is empty, then return false.
+ bool Pop(std::string* out_string);
+
+ private:
+ bool Validate(); // Checks that all internal data is valid.
+
+ const scoped_ptr<char[]> buffer_; // Circular buffer, plus extra char ('\0').
+ const BufferSize buffer_size_; // Size one smaller than allocated space.
+ const BufferSize buffer_sentinel_; // Index of extra '\0' at end of buffer_.
+
+ // If writable_ == readable_, then the buffer is empty.
+ BufferSize readable_; // Next readable char in buffer_.
+ BufferSize writeable_; // The next space in buffer_ to push.
+
+ // Number of queued strings
+ size_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(DnsQueue);
+}; // class DnsQueue
+
+} // namespace network_hints
+
+#endif // COMPONENTS_NETWORK_HINTS_RENDERER_DNS_PREFETCH_QUEUE_H__
diff --git a/components/network_hints/renderer/dns_prefetch_queue_unittest.cc b/components/network_hints/renderer/dns_prefetch_queue_unittest.cc
new file mode 100644
index 0000000..cb808c1
--- /dev/null
+++ b/components/network_hints/renderer/dns_prefetch_queue_unittest.cc
@@ -0,0 +1,262 @@
+// Copyright (c) 2010 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 <sstream>
+
+#include "components/network_hints/renderer/dns_prefetch_queue.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Single threaded tests of DnsQueue functionality.
+
+namespace network_hints {
+
+class DnsQueueTest : public testing::Test {
+};
+
+// Define a helper class that does Push'es and Pop's of numbers.
+// This makes it easy to test a LOT of reads, and keep the expected Pop
+// value in sync with the Push value.
+class DnsQueueSequentialTester {
+ public:
+ DnsQueueSequentialTester(DnsQueue& buffer, int32 read_counter = 0,
+ int32 write_counter = 0);
+
+ // Return of false means buffer was full, or would not take entry.
+ bool Push(void); // Push the string value of next number.
+
+ // Return of false means buffer returned wrong value.
+ bool Pop(void); // Validate string value of next read.
+
+ private:
+ DnsQueue* buffer_;
+ int32 read_counter_; // expected value of next read string.
+ int32 write_counter_; // Numerical value to write next string.
+ DISALLOW_COPY_AND_ASSIGN(DnsQueueSequentialTester);
+};
+
+
+DnsQueueSequentialTester::DnsQueueSequentialTester(
+ DnsQueue& buffer, int32 read_counter, int32 write_counter)
+ : buffer_(&buffer),
+ read_counter_(read_counter),
+ write_counter_(write_counter) {
+}
+
+bool DnsQueueSequentialTester::Push(void) {
+ std::ostringstream value;
+ value << write_counter_;
+
+ // Exercise both write methods intermittently.
+ DnsQueue::PushResult result = (write_counter_ % 2) ?
+ buffer_->Push(value.str().c_str(), value.str().size()) :
+ buffer_->Push(value.str());
+ if (DnsQueue::SUCCESSFUL_PUSH == result)
+ write_counter_++;
+ return DnsQueue::OVERFLOW_PUSH != result;
+}
+
+bool DnsQueueSequentialTester::Pop(void) {
+ std::string string;
+ if (buffer_->Pop(&string)) {
+ std::ostringstream expected_value;
+ expected_value << read_counter_++;
+ EXPECT_STREQ(expected_value.str().c_str(), string.c_str())
+ << "Pop did not match write for value " << read_counter_;
+ return true;
+ }
+ return false;
+}
+
+
+TEST(DnsQueueTest, BufferUseCheck) {
+ // Use a small buffer so we can see that we can't write a string as soon as it
+ // gets longer than one less than the buffer size. The extra empty character
+ // is used to keep read and write pointers from overlapping when buffer is
+ // full. This shows the buffer size can constrain writes (and we're not
+ // scribbling all over memory).
+ const int buffer_size = 3; // Just room for 2 digts plus '\0' plus blank.
+ std::string string;
+ DnsQueue buffer(buffer_size);
+ DnsQueueSequentialTester tester(buffer);
+
+ EXPECT_FALSE(tester.Pop()) << "Pop from empty buffer succeeded";
+
+ int i;
+ for (i = 0; i < 102; i++) {
+ if (!tester.Push())
+ break; // String was too large.
+ EXPECT_TRUE(tester.Pop()) << "Unable to read back data " << i;
+ EXPECT_FALSE(buffer.Pop(&string))
+ << "read from empty buffer not flagged";
+ }
+
+ EXPECT_GE(i, 100) << "Can't write 2 digit strings in 4 character buffer";
+ EXPECT_LT(i, 101) << "We wrote 3 digit strings into a 4 character buffer";
+}
+
+TEST(DnsQueueTest, SubstringUseCheck) {
+ // Verify that only substring is written/read.
+ const int buffer_size = 100;
+ const char big_string[] = "123456789";
+ std::string string;
+ DnsQueue buffer(buffer_size);
+
+ EXPECT_FALSE(buffer.Pop(&string)) << "Initial buffer not empty";
+
+ EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 3))
+ << "Can't write string";
+ EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 0))
+ << "Can't write null string";
+ EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(big_string, 5))
+ << "Can't write string";
+
+ EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty";
+ EXPECT_STREQ(string.c_str(), "123") << "Can't read actual data";
+ EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty";
+ EXPECT_STREQ(string.c_str(), "") << "Can't read null string";
+ EXPECT_TRUE(buffer.Pop(&string)) << "Filled buffer marked as empty";
+ EXPECT_STREQ(string.c_str(), "12345") << "Can't read actual data";
+
+ EXPECT_FALSE(buffer.Pop(&string))
+ << "read from empty buffer not flagged";
+}
+
+TEST(DnsQueueTest, SizeCheck) {
+ // Verify that size is correctly accounted for in buffer.
+ const int buffer_size = 100;
+ std::string input_string = "Hello";
+ std::string string;
+ DnsQueue buffer(buffer_size);
+
+ EXPECT_EQ(0U, buffer.Size());
+ EXPECT_FALSE(buffer.Pop(&string));
+ EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(input_string));
+ EXPECT_EQ(1U, buffer.Size());
+ EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push("Hi There"));
+ EXPECT_EQ(2U, buffer.Size());
+ EXPECT_TRUE(buffer.Pop(&string));
+ EXPECT_EQ(1U, buffer.Size());
+ EXPECT_TRUE(buffer.Pop(&string));
+ EXPECT_EQ(0U, buffer.Size());
+ EXPECT_EQ(DnsQueue::SUCCESSFUL_PUSH, buffer.Push(input_string));
+ EXPECT_EQ(1U, buffer.Size());
+
+ // Check to see that the first string, if repeated, is discarded.
+ EXPECT_EQ(DnsQueue::REDUNDANT_PUSH, buffer.Push(input_string));
+ EXPECT_EQ(1U, buffer.Size());
+}
+
+TEST(DnsQueueTest, FillThenEmptyCheck) {
+ // Use a big buffer so we'll get a bunch of writes in.
+ // This tests to be sure the buffer holds many strings.
+ // We also make sure they all come out intact.
+ const size_t buffer_size = 1000;
+ size_t byte_usage_counter = 1; // Separation character between pointer.
+ DnsQueue buffer(buffer_size);
+ DnsQueueSequentialTester tester(buffer);
+
+ size_t write_success;
+ for (write_success = 0; write_success < buffer_size; write_success++) {
+ if (!tester.Push())
+ break;
+ EXPECT_EQ(buffer.Size(), write_success + 1);
+ if (write_success > 99)
+ byte_usage_counter += 4; // 3 digit plus '\0'.
+ else if (write_success > 9)
+ byte_usage_counter += 3; // 2 digits plus '\0'.
+ else
+ byte_usage_counter += 2; // Digit plus '\0'.
+ }
+ EXPECT_LE(byte_usage_counter, buffer_size)
+ << "Written data exceeded buffer size";
+ EXPECT_GE(byte_usage_counter, buffer_size - 4)
+ << "Buffer does not appear to have filled";
+
+ EXPECT_GE(write_success, 10U) << "Couldn't even write 10 one digit strings "
+ "in " << buffer_size << " byte buffer";
+
+
+ while (1) {
+ if (!tester.Pop())
+ break;
+ write_success--;
+ }
+ EXPECT_EQ(write_success, 0U) << "Push and Pop count were different";
+
+ EXPECT_FALSE(tester.Pop()) << "Read from empty buffer succeeded";
+}
+
+TEST(DnsQueueTest, ClearCheck) {
+ // Use a big buffer so we'll get a bunch of writes in.
+ const size_t buffer_size = 1000;
+ DnsQueue buffer(buffer_size);
+ std::string string("ABC");
+ DnsQueueSequentialTester tester(buffer);
+
+ size_t write_success;
+ for (write_success = 0; write_success < buffer_size; write_success++) {
+ if (!tester.Push())
+ break;
+ EXPECT_EQ(buffer.Size(), write_success + 1);
+ }
+
+ buffer.Clear();
+ EXPECT_EQ(buffer.Size(), 0U);
+
+ size_t write_success2;
+ for (write_success2 = 0; write_success2 < buffer_size; write_success2++) {
+ if (!tester.Push())
+ break;
+ EXPECT_EQ(buffer.Size(), write_success2 + 1);
+ }
+
+ for (; write_success2 > 0; write_success2--) {
+ EXPECT_EQ(buffer.Size(), write_success2);
+ EXPECT_TRUE(buffer.Pop(&string));
+ }
+
+ EXPECT_EQ(buffer.Size(), 0U);
+ buffer.Clear();
+ EXPECT_EQ(buffer.Size(), 0U);
+}
+
+TEST(DnsQueueTest, WrapOnVariousSubstrings) {
+ // Use a prime number for the allocated buffer size so that we tend
+ // to exercise all possible edge conditions (in circular text buffer).
+ // Once we're over 10 writes, all our strings are 2 digits long,
+ // with a '\0' terminator added making 3 characters per write.
+ // Since 3 is relatively prime to 23, we'll soon wrap (about
+ // every 6 writes). Hence after 18 writes, we'll have tested all
+ // edge conditions. We'll first do this where we empty the buffer
+ // after each write, and then again where there are some strings
+ // still in the buffer after each write.
+ const int prime_number = 23;
+ // Circular buffer needs an extra extra space to distinguish full from empty.
+ const int buffer_size = prime_number - 1;
+ DnsQueue buffer(buffer_size);
+ DnsQueueSequentialTester tester(buffer);
+
+ // First test empties between each write. Second loop
+ // has writes for each pop. Third has three pushes per pop.
+ // Third has two items pending during each write.
+ for (int j = 0; j < 3; j++) {
+ // Each group does 30 tests, which is more than 10+18
+ // which was needed to get into the thorough testing zone
+ // mentioned above.
+ for (int i = 0; i < 30; i++) {
+ EXPECT_TRUE(tester.Push()) << "write failed with only " << j
+ << " blocks in buffer";
+ EXPECT_TRUE(tester.Pop()) << "Unable to read back data ";
+ }
+ EXPECT_TRUE(tester.Push());
+ }
+
+ // Read back the accumulated 3 extra blocks.
+ EXPECT_TRUE(tester.Pop());
+ EXPECT_TRUE(tester.Pop());
+ EXPECT_TRUE(tester.Pop());
+ EXPECT_FALSE(tester.Pop());
+}
+
+}; // namespace network_hints
diff --git a/components/network_hints/renderer/prescient_networking_dispatcher.cc b/components/network_hints/renderer/prescient_networking_dispatcher.cc
new file mode 100644
index 0000000..ee02f019
--- /dev/null
+++ b/components/network_hints/renderer/prescient_networking_dispatcher.cc
@@ -0,0 +1,27 @@
+// Copyright 2014 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/network_hints/renderer/prescient_networking_dispatcher.h"
+
+#include "base/logging.h"
+
+namespace network_hints {
+
+PrescientNetworkingDispatcher::PrescientNetworkingDispatcher() {
+}
+
+PrescientNetworkingDispatcher::~PrescientNetworkingDispatcher() {
+}
+
+void PrescientNetworkingDispatcher::prefetchDNS(
+ const blink::WebString& hostname) {
+ VLOG(2) << "Prefetch DNS: " << hostname.utf8();
+ if (hostname.isEmpty())
+ return;
+
+ std::string hostname_utf8 = hostname.utf8();
+ dns_prefetch_.Resolve(hostname_utf8.data(), hostname_utf8.length());
+}
+
+} // namespace network_hints
diff --git a/components/network_hints/renderer/prescient_networking_dispatcher.h b/components/network_hints/renderer/prescient_networking_dispatcher.h
new file mode 100644
index 0000000..9b10739
--- /dev/null
+++ b/components/network_hints/renderer/prescient_networking_dispatcher.h
@@ -0,0 +1,31 @@
+// Copyright 2014 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_NETWORK_HINTS_RENDERER_PRESCIENT_NETWORKING_DISPATCHER_H_
+#define COMPONENTS_NETWORK_HINTS_RENDERER_PRESCIENT_NETWORKING_DISPATCHER_H_
+
+#include "base/macros.h"
+#include "components/network_hints/renderer/renderer_dns_prefetch.h"
+#include "third_party/WebKit/public/platform/WebPrescientNetworking.h"
+
+namespace network_hints {
+
+// The main entry point from blink for sending DNS prefetch requests to the
+// network stack.
+class PrescientNetworkingDispatcher : public blink::WebPrescientNetworking {
+ public:
+ PrescientNetworkingDispatcher();
+ ~PrescientNetworkingDispatcher() override;
+
+ void prefetchDNS(const blink::WebString& hostname) override;
+
+ private:
+ network_hints::RendererDnsPrefetch dns_prefetch_;
+
+ DISALLOW_COPY_AND_ASSIGN(PrescientNetworkingDispatcher);
+};
+
+} // namespace network_hints
+
+#endif // COMPONENTS_NETWORK_HINTS_RENDERER_PRESCIENT_NETWORKING_DISPATCHER_H_
diff --git a/components/network_hints/renderer/renderer_dns_prefetch.cc b/components/network_hints/renderer/renderer_dns_prefetch.cc
new file mode 100644
index 0000000..7098037
--- /dev/null
+++ b/components/network_hints/renderer/renderer_dns_prefetch.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2012 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.
+
+// See header file for description of RendererDnsPrefetch class
+
+#include "components/network_hints/renderer/renderer_dns_prefetch.h"
+
+#include <ctype.h>
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/message_loop/message_loop.h"
+#include "components/network_hints/common/network_hints_common.h"
+#include "components/network_hints/common/network_hints_messages.h"
+#include "components/network_hints/renderer/dns_prefetch_queue.h"
+#include "content/public/renderer/render_thread.h"
+
+using content::RenderThread;
+
+namespace network_hints {
+
+RendererDnsPrefetch::RendererDnsPrefetch()
+ : c_string_queue_(1000),
+ weak_factory_(this) {
+ Reset();
+}
+
+RendererDnsPrefetch::~RendererDnsPrefetch() {
+}
+
+void RendererDnsPrefetch::Reset() {
+ domain_map_.clear();
+ c_string_queue_.Clear();
+ buffer_full_discard_count_ = 0;
+ numeric_ip_discard_count_ = 0;
+ new_name_count_ = 0;
+}
+
+// Push names into queue quickly!
+void RendererDnsPrefetch::Resolve(const char* name, size_t length) {
+ if (!length)
+ return; // Don't store empty strings in buffer.
+ if (is_numeric_ip(name, length))
+ return; // Numeric IPs have no DNS lookup significance.
+
+ size_t old_size = c_string_queue_.Size();
+ DnsQueue::PushResult result = c_string_queue_.Push(name, length);
+ if (DnsQueue::SUCCESSFUL_PUSH == result) {
+ if (1 == c_string_queue_.Size()) {
+ DCHECK_EQ(old_size, 0u);
+ if (0 != old_size)
+ return; // Overkill safety net: Don't send too many InvokeLater's.
+ weak_factory_.InvalidateWeakPtrs();
+ RenderThread::Get()->GetTaskRunner()->PostDelayedTask(
+ FROM_HERE, base::Bind(&RendererDnsPrefetch::SubmitHostnames,
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(10));
+ }
+ return;
+ }
+ if (DnsQueue::OVERFLOW_PUSH == result) {
+ ++buffer_full_discard_count_;
+ return;
+ }
+ DCHECK(DnsQueue::REDUNDANT_PUSH == result);
+}
+
+// Extract data from the Queue, and then send it off the the Browser process
+// to be resolved.
+void RendererDnsPrefetch::SubmitHostnames() {
+ // Get all names out of the C_string_queue (into our map)
+ ExtractBufferedNames();
+ // TBD: IT could be that we should only extract about as many names as we are
+ // going to send to the browser. That would cause a "silly" page with a TON
+ // of URLs to start to overrun the DnsQueue, which will cause the names to
+ // be dropped (not stored in the queue). By fetching ALL names, we are
+ // taking on a lot of work, which may take a long time to process... perhaps
+ // longer than the page may be visible!?!?! If we implement a better
+ // mechanism for doing domain_map.clear() (see end of this method), then
+ // we'd automatically flush such pending work from a ridiculously link-filled
+ // page.
+
+ // Don't overload the browser DNS lookup facility, or take too long here,
+ // by only sending off kMaxDnsHostnamesPerRequest names to the Browser.
+ // This will help to avoid overloads when a page has a TON of links.
+ DnsPrefetchNames(kMaxDnsHostnamesPerRequest);
+ if (new_name_count_ > 0 || 0 < c_string_queue_.Size()) {
+ weak_factory_.InvalidateWeakPtrs();
+ RenderThread::Get()->GetTaskRunner()->PostDelayedTask(
+ FROM_HERE, base::Bind(&RendererDnsPrefetch::SubmitHostnames,
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(10));
+ } else {
+ // TODO(JAR): Should we only clear the map when we navigate, or reload?
+ domain_map_.clear();
+ }
+}
+
+// Pull some hostnames from the queue, and add them to our map.
+void RendererDnsPrefetch::ExtractBufferedNames(size_t size_goal) {
+ size_t count(0); // Number of entries to find (0 means find all).
+ if (size_goal > 0) {
+ if (size_goal <= domain_map_.size())
+ return; // Size goal was met.
+ count = size_goal - domain_map_.size();
+ }
+
+ std::string name;
+ while (c_string_queue_.Pop(&name)) {
+ DCHECK_NE(name.size(), 0u);
+ // We don't put numeric IP names into buffer.
+ DCHECK(!is_numeric_ip(name.c_str(), name.size()));
+ DomainUseMap::iterator it;
+ it = domain_map_.find(name);
+ if (domain_map_.end() == it) {
+ domain_map_[name] = kPending;
+ ++new_name_count_;
+ if (0 == count) continue; // Until buffer is empty.
+ if (1 == count) break; // We found size_goal.
+ DCHECK_GT(count, 1u);
+ --count;
+ } else {
+ DCHECK(kPending == it->second || kLookupRequested == it->second);
+ }
+ }
+}
+
+void RendererDnsPrefetch::DnsPrefetchNames(size_t max_count) {
+ // We are on the renderer thread, and just need to send things to the browser.
+ NameList names;
+ for (DomainUseMap::iterator it = domain_map_.begin();
+ it != domain_map_.end();
+ ++it) {
+ if (0 == (it->second & kLookupRequested)) {
+ it->second |= kLookupRequested;
+ names.push_back(it->first);
+ if (0 == max_count) continue; // Get all, independent of count.
+ if (1 == max_count) break;
+ --max_count;
+ DCHECK_GE(max_count, 1u);
+ }
+ }
+ DCHECK_GE(new_name_count_, names.size());
+ new_name_count_ -= names.size();
+
+ network_hints::LookupRequest request;
+ request.hostname_list = names;
+ RenderThread::Get()->Send(new DnsPrefetchMsg_RequestPrefetch(request));
+}
+
+// is_numeric_ip() checks to see if all characters in name are either numeric,
+// or dots. Such a name will not actually be passed to DNS, as it is an IP
+// address.
+bool RendererDnsPrefetch::is_numeric_ip(const char* name, size_t length) {
+ // Scan for a character outside our lookup list.
+ while (length-- > 0) {
+ if (!isdigit(*name) && '.' != *name)
+ return false;
+ ++name;
+ }
+ return true;
+}
+
+} // namespcae predictor
diff --git a/components/network_hints/renderer/renderer_dns_prefetch.h b/components/network_hints/renderer/renderer_dns_prefetch.h
new file mode 100644
index 0000000..9ee4f61
--- /dev/null
+++ b/components/network_hints/renderer/renderer_dns_prefetch.h
@@ -0,0 +1,109 @@
+// Copyright (c) 2011 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.
+
+// A RendererDnsPrefetch instance is maintained for each RenderThread.
+// URL strings are typically added to the embedded queue during rendering.
+// The first addition to the queue (transitioning from empty to having
+// some names) causes a processing task to be added to the Renderer Thread.
+// The processing task gathers all buffered names, and send them via IPC
+// to the browser, so that DNS lookups can be performed before the user attempts
+// to traverse a link.
+// This class removed some duplicates, and discards numeric IP addresss
+// (which wouldn't looked up in DNS anyway).
+// To limit the time during the processing task (and avoid stalling the Render
+// thread), several limits are placed on how much of the queue to process.
+// If the processing task is not able to completely empty the queue, it
+// schedules a future continuation of the task, and keeps the map of already
+// sent names. If the entire queue is processed, then the list of "sent names"
+// is cleared so that future gatherings might again pass along the same names.
+
+#ifndef COMPONENTS_NETWORK_HINTS_RENDERER_RENDERER_DNS_PREFETCH_H_
+#define COMPONENTS_NETWORK_HINTS_RENDERER_RENDERER_DNS_PREFETCH_H_
+
+#include <map>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/memory/weak_ptr.h"
+#include "components/network_hints/renderer/dns_prefetch_queue.h"
+
+namespace network_hints {
+
+// An internal interface to the network_hints component for efficiently sending
+// DNS prefetch requests to the net stack.
+class RendererDnsPrefetch {
+ public:
+ RendererDnsPrefetch();
+ ~RendererDnsPrefetch();
+
+ // Push a name into the queue to be resolved.
+ void Resolve(const char* name, size_t length);
+
+ // SubmitHosts processes the buffered names, and submits them for DNS
+ // prefetching.
+ // Note that browser process may decide which names should be looked up (to
+ // pre-warm the cache) based on what has been (or not been) looked up
+ // recently.
+ // If sending for DNS lookup is incomplete (queue is not empty, or not all
+ // names in map are sent, or ...) then a task to continue processing is
+ // sent to our thread loop.
+ void SubmitHostnames();
+
+ // The following is private, but exposed for testing purposes only.
+ static bool is_numeric_ip(const char* name, size_t length);
+
+ private:
+ // ExtractBufferedNames pulls names from queue into the map, reducing or
+ // eliminating a waiting queue.
+ // The size_goal argument can be used to reduce the amount of
+ // processing done in this method, and can leave some data
+ // in the buffer under some circumstances.
+ // If size_goal is zero, then extraction proceeds until
+ // the queue is empty. If size goal is positive, then
+ // extraction continues until the domain_map_ contains
+ // at least the specified number of names, or the buffer is empty.
+ void ExtractBufferedNames(size_t size_goal = 0);
+
+ // DnsPrefetchNames does not check the buffer, and just sends names
+ // that are already collected in the domain_map_ for DNS lookup.
+ // If max_count is zero, then all available names are sent; and
+ // if positive, then at most max_count names will be sent.
+ void DnsPrefetchNames(size_t max_count = 0);
+
+ // Reset() restores initial state provided after construction.
+ // This discards ALL queue entries, and map entries.
+ void Reset();
+
+ // We use c_string_queue_ to hold lists of names supplied typically) by the
+ // renderer. It queues the names, at minimal cost to the renderer's thread,
+ // and allows this class to process them when time permits (in a later task).
+ DnsQueue c_string_queue_;
+
+
+ // domain_map_ contains (for each domain) one of the next two constants,
+ // depending on whether we have asked the browser process to do the actual
+ // DNS lookup.
+ static const int kLookupRequested = 0x1;
+ static const int kPending = 0x0;
+ typedef std::map<std::string, int> DomainUseMap;
+ DomainUseMap domain_map_;
+
+ // Cache a tally of the count of names that haven't yet been sent
+ // for DNS pre-fetching. Note that we *could* recalculate this
+ // count by iterating over domain_map_, looking for even values.
+ size_t new_name_count_;
+
+ // We have some metrics to examine performance. We might use
+ // these metrics to modify buffer counts etc. some day.
+ int buffer_full_discard_count_;
+ int numeric_ip_discard_count_;
+
+ base::WeakPtrFactory<RendererDnsPrefetch> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(RendererDnsPrefetch);
+}; // class RendererDnsPrefetch
+
+} // namespace network_hints
+
+#endif // COMPONENTS_NETWORK_HINTS_RENDERER_RENDERER_DNS_PREFETCH_H_
diff --git a/components/network_hints/renderer/renderer_dns_prefetch_unittest.cc b/components/network_hints/renderer/renderer_dns_prefetch_unittest.cc
new file mode 100644
index 0000000..588383c
--- /dev/null
+++ b/components/network_hints/renderer/renderer_dns_prefetch_unittest.cc
@@ -0,0 +1,37 @@
+// Copyright (c) 2006-2010 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.
+
+// Single threaded tests of RendererDnsPrefetch functionality.
+
+#include "components/network_hints/renderer/renderer_dns_prefetch.h"
+
+#include <algorithm>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace network_hints {
+
+class RenderDnsMasterTest : public testing::Test {
+};
+
+TEST(RenderDnsMasterTest, NumericIpDiscardCheck) {
+ // Regular names.
+ const std::string A("a.com"), B("b.net"), C("www.other.uk");
+ // Combination of digits plus dots.
+ const std::string N1("1.3."), N2("5.5.7.12");
+
+#define TESTNAME(string) RendererDnsPrefetch::is_numeric_ip((string.data()), \
+ (string).size())
+
+ EXPECT_TRUE(TESTNAME(N1));
+ EXPECT_TRUE(TESTNAME(N2));
+
+ EXPECT_FALSE(TESTNAME(A));
+ EXPECT_FALSE(TESTNAME(B));
+ EXPECT_FALSE(TESTNAME(C));
+
+#undef TESTNAME
+}
+
+} // namespace network_hints