Import Cobalt 3.11337
diff --git a/src/base/file_util_starboard.cc b/src/base/file_util_starboard.cc
index 0c6511a..a2e9b2b 100644
--- a/src/base/file_util_starboard.cc
+++ b/src/base/file_util_starboard.cc
@@ -193,8 +193,8 @@
}
base::PlatformFile destination_file = base::CreatePlatformFileUnsafe(
- to_path, base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_WRITE, NULL,
- NULL);
+ to_path, base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_WRITE,
+ NULL, NULL);
if (destination_file == base::kInvalidPlatformFileValue) {
DPLOG(ERROR) << "CopyFile(): Unable to open destination file: "
<< to_path.value();
diff --git a/src/cobalt/base/base.gyp b/src/cobalt/base/base.gyp
index a2f0dda..a2822ab 100644
--- a/src/cobalt/base/base.gyp
+++ b/src/cobalt/base/base.gyp
@@ -31,6 +31,7 @@
'c_val.h',
'c_val_time_interval_entry_stats.h',
'c_val_time_interval_timer.h',
+ 'deep_link_event.h',
'do_main.h',
'do_main_starboard.h',
'event.h',
diff --git a/src/cobalt/base/c_val.h b/src/cobalt/base/c_val.h
index c4bcc0c..7331781 100644
--- a/src/cobalt/base/c_val.h
+++ b/src/cobalt/base/c_val.h
@@ -249,6 +249,7 @@
{10LL * 1024LL * 1024LL * 1024LL, 1024LL * 1024LL * 1024LL, "GB"},
{10LL * 1024LL * 1024LL, 1024LL * 1024LL, "MB"},
{10LL * 1024LL, 1024LL, "KB"},
+ {0LL, 1L, "B"},
};
return ThresholdList<cval::SizeInBytes>(thresholds, arraysize(thresholds));
}
@@ -291,21 +292,23 @@
if (negative) {
oss << "-";
}
+
+ oss << std::fixed << std::setprecision(1) << std::setfill('0');
if (value_in_us > kHour) {
- oss << value_in_us / kHour << ":" << std::setfill('0') << std::setw(2)
- << (value_in_us % kHour) / kMinute << ":" << std::setfill('0')
+ oss << value_in_us / kHour << ":" << std::setw(2)
+ << (value_in_us % kHour) / kMinute << ":"
<< std::setw(2) << (value_in_us % kMinute) / kSecond << "h";
} else if (value_in_us > kMinute) {
- oss << value_in_us / kMinute << ":" << std::setfill('0') << std::setw(2)
+ oss << value_in_us / kMinute << ":" << std::setw(2)
<< (value_in_us % kMinute) / kSecond << "m";
- } else if (value_in_us > kSecond * 10) {
- oss << value_in_us / kSecond << "s";
- } else if (value_in_us > kMillisecond * 2) {
- oss << value_in_us / kMillisecond << "ms";
- } else if (value_in_us > 0) {
- oss << value_in_us << "us";
+ } else if (value_in_us > kSecond) {
+ oss << std::setw(1)
+ << static_cast<double>(value_in_us) / kSecond << "s";
+ } else if (value_in_us > kMillisecond) {
+ oss << std::setw(1)
+ << static_cast<double>(value_in_us) / kMillisecond << "ms";
} else {
- oss << value_in_us;
+ oss << value_in_us << "us";
}
return oss.str();
diff --git a/src/cobalt/base/c_val_test.cc b/src/cobalt/base/c_val_test.cc
index 9751266..c1941d3 100644
--- a/src/cobalt/base/c_val_test.cc
+++ b/src/cobalt/base/c_val_test.cc
@@ -16,6 +16,7 @@
#include <limits>
+#include "base/time.h"
#include "cobalt/base/c_val.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -64,7 +65,27 @@
base::CValManager* cvm = base::CValManager::GetInstance();
base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
EXPECT_TRUE(result);
- EXPECT_EQ(*result, "4095MB");
+ EXPECT_EQ(*result, "4294M");
+}
+
+TEST(CValTest, RegisterAndPrintU32Zero) {
+ const std::string cval_name = "32-bit unsigned int";
+ const uint32_t cval_value = 0;
+ base::CVal<uint32_t> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "0");
+}
+
+TEST(CValTest, RegisterAndPrintU32K) {
+ const std::string cval_name = "32-bit unsigned int";
+ const uint32_t cval_value = 50000;
+ base::CVal<uint32_t> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "50K");
}
TEST(CValTest, RegisterAndPrintU64) {
@@ -74,7 +95,7 @@
base::CValManager* cvm = base::CValManager::GetInstance();
base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
EXPECT_TRUE(result);
- EXPECT_EQ(*result, "17592186044415MB");
+ EXPECT_EQ(*result, "18446744073709M");
}
TEST(CValTest, RegisterAndPrintS32) {
@@ -97,6 +118,266 @@
EXPECT_EQ(*result, "-9223372036854775808");
}
+TEST(CValTest, RegisterAndPrintSizeInBytesB) {
+ const std::string cval_name = "SizeInBytes";
+ cval::SizeInBytes cval_value(500);
+ base::CVal<cval::SizeInBytes> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "500B");
+}
+
+TEST(CValTest, RegisterAndPrintSizeInBytesZero) {
+ const std::string cval_name = "SizeInBytes";
+ cval::SizeInBytes cval_value(0);
+ base::CVal<cval::SizeInBytes> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "0B");
+}
+
+TEST(CValTest, RegisterAndPrintSizeInBytesKB) {
+ const std::string cval_name = "SizeInBytes";
+ cval::SizeInBytes cval_value(50000UL);
+ base::CVal<cval::SizeInBytes> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "48KB");
+}
+
+TEST(CValTest, RegisterAndPrintSizeInBytesMB) {
+ const std::string cval_name = "SizeInBytes";
+ cval::SizeInBytes cval_value(50000000UL);
+ base::CVal<cval::SizeInBytes> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "47MB");
+}
+
+TEST(CValTest, RegisterAndPrintSizeInBytesGB) {
+ const std::string cval_name = "SizeInBytes";
+ cval::SizeInBytes cval_value(50000000000UL);
+ base::CVal<cval::SizeInBytes> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "46GB");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaMicroseconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMicroseconds(50));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "50us");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaZero) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMicroseconds(0));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "0us");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaMilliseconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMilliseconds(50));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "50.0ms");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaMillisecondsSingleDigit) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMilliseconds(5));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "5.0ms");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaMillisecondsFraction) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMicroseconds(5500));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "5.5ms");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaSeconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromSeconds(50));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "50.0s");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaSecondsSingleDigit) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromSeconds(5));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "5.0s");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaSecondsFraction) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMilliseconds(5500));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "5.5s");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaMinutes) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMinutes(50));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "50:00m");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaMinutesSingleDigit) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMinutes(5));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "5:00m");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaMinutesAndSeconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromSeconds(92));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "1:32m");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaHours) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromHours(50));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "50:00:00h");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaHoursSingleDigit) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromHours(5));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "5:00:00h");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaHoursAndMinutes) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMinutes(92));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "1:32:00h");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaHoursAndMinutesAndSeconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromSeconds(92 * 60 + 32));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "1:32:32h");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaHoursAndSeconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromSeconds(60 * 60 + 32));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "1:00:32h");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaNegativeMicroseconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMicroseconds(-3));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "-3us");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaNegativeMilliseconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMilliseconds(-3));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "-3.0ms");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaNegativeSeconds) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromSeconds(-3));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "-3.0s");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaNegativeMinutes) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromMinutes(-3));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "-3:00m");
+}
+
+TEST(CValTest, RegisterAndPrintTimeDeltaNegativeHours) {
+ const std::string cval_name = "TimeDelta";
+ base::TimeDelta cval_value(base::TimeDelta::FromHours(-3));
+ base::CVal<base::TimeDelta> cval(cval_name, cval_value, "Description.");
+ base::CValManager* cvm = base::CValManager::GetInstance();
+ base::optional<std::string> result = cvm->GetValueAsPrettyString(cval_name);
+ EXPECT_TRUE(result);
+ EXPECT_EQ(*result, "-3:00:00h");
+}
+
TEST(CValTest, RegisterAndPrintFloat) {
const std::string cval_name = "float";
const float cval_value = 3.14159f;
diff --git a/src/cobalt/base/deep_link_event.h b/src/cobalt/base/deep_link_event.h
new file mode 100644
index 0000000..5e3df4b
--- /dev/null
+++ b/src/cobalt/base/deep_link_event.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_BASE_DEEP_LINK_EVENT_H_
+#define COBALT_BASE_DEEP_LINK_EVENT_H_
+
+#include <string>
+
+#include "base/compiler_specific.h"
+#include "cobalt/base/event.h"
+
+namespace base {
+
+class DeepLinkEvent : public Event {
+ public:
+ explicit DeepLinkEvent(const std::string& link) : link_(link) {}
+ const std::string& link() const { return link_; }
+
+ BASE_EVENT_SUBCLASS(DeepLinkEvent);
+
+ private:
+ std::string link_;
+};
+
+} // namespace base
+
+#endif // COBALT_BASE_DEEP_LINK_EVENT_H_
diff --git a/src/cobalt/base/init_cobalt.cc b/src/cobalt/base/init_cobalt.cc
index f9ed397..9cad4b4 100644
--- a/src/cobalt/base/init_cobalt.cc
+++ b/src/cobalt/base/init_cobalt.cc
@@ -16,6 +16,8 @@
#include "cobalt/base/init_cobalt.h"
+#include <string>
+
#include "base/at_exit.h"
#include "base/command_line.h"
#include "base/i18n/icu_util.h"
@@ -51,10 +53,15 @@
#endif
+base::LazyInstance<std::string> s_initial_deep_link = LAZY_INSTANCE_INITIALIZER;
+
} // namespace
-void InitCobalt(int argc, char* argv[]) {
+void InitCobalt(int argc, char* argv[], const char* link) {
CommandLine::Init(argc, argv);
+ if (link) {
+ s_initial_deep_link.Get() = link;
+ }
deprecated::PlatformDelegate::Init();
// Register a callback to be called during program termination.
@@ -74,4 +81,6 @@
LOG_IF(ERROR, !icu_initialized) << "ICU initialization failed.";
}
+const char* GetInitialDeepLink() { return s_initial_deep_link.Get().c_str(); }
+
} // namespace cobalt
diff --git a/src/cobalt/base/init_cobalt.h b/src/cobalt/base/init_cobalt.h
index 14aed0c..1ba9cdf 100644
--- a/src/cobalt/base/init_cobalt.h
+++ b/src/cobalt/base/init_cobalt.h
@@ -32,7 +32,10 @@
//
// ..RunSomeCode..
// }
-void InitCobalt(int argc, char* argv[]);
+void InitCobalt(int argc, char* argv[], const char* initial_deep_link);
+
+// Get the |initial_deep_link| string specified in |InitCobalt|.
+const char* GetInitialDeepLink();
} // namespace cobalt
diff --git a/src/cobalt/base/wrap_main.h b/src/cobalt/base/wrap_main.h
index 10aacce..193e379 100644
--- a/src/cobalt/base/wrap_main.h
+++ b/src/cobalt/base/wrap_main.h
@@ -42,7 +42,7 @@
typedef int (*MainFunction)(int argc, char** argv);
// A start-style function.
-typedef void (*StartFunction)(int argc, char** argv,
+typedef void (*StartFunction)(int argc, char** argv, const char* link,
const base::Closure& quit_closure);
// A function type that can be called at shutdown.
@@ -53,7 +53,7 @@
// No-operation function that can be passed into start_function if no start work
// is needed.
-void NoopStartFunction(int /*argc*/, char** /*argv*/,
+void NoopStartFunction(int /*argc*/, char** /*argv*/, const char* /*link*/,
const base::Closure& /*quit_closure*/) {}
// No-operation function that can be passed into event_function if no other
@@ -75,16 +75,16 @@
namespace wrap_main {
template <MainFunction main_function>
-int SimpleMain(int argc, char **argv) {
+int SimpleMain(int argc, char** argv) {
base::AtExitManager at_exit;
- InitCobalt(argc, argv);
+ InitCobalt(argc, argv, NULL);
return main_function(argc, argv);
}
template <StartFunction start_function, StopFunction stop_function>
-int BaseMain(int argc, char **argv) {
+int BaseMain(int argc, char** argv) {
base::AtExitManager at_exit;
- InitCobalt(argc, argv);
+ InitCobalt(argc, argv, NULL);
MessageLoopForUI message_loop;
base::PlatformThread::SetName("Main");
@@ -93,7 +93,7 @@
DCHECK(!message_loop.is_running());
base::RunLoop run_loop;
- start_function(argc, argv, run_loop.QuitClosure());
+ start_function(argc, argv, NULL, run_loop.QuitClosure());
run_loop.Run();
stop_function();
@@ -103,7 +103,7 @@
// Calls |main_function| at startup, creates an AtExitManager and calls
// InitCobalt, and terminates once it is completed.
#define COBALT_WRAP_SIMPLE_MAIN(main_function) \
- int main(int argc, char **argv) { \
+ int main(int argc, char** argv) { \
return ::cobalt::wrap_main::SimpleMain<main_function>(argc, argv); \
}
@@ -111,7 +111,7 @@
// terminates once the MessageLoop terminates, calling |stop_function| on the
// way out.
#define COBALT_WRAP_BASE_MAIN(start_function, stop_function) \
- int main(int argc, char **argv) { \
+ int main(int argc, char** argv) { \
return ::cobalt::wrap_main::BaseMain<start_function, stop_function>(argc, \
argv); \
}
diff --git a/src/cobalt/base/wrap_main_starboard.h b/src/cobalt/base/wrap_main_starboard.h
index 1a72bd7..dad3708 100644
--- a/src/cobalt/base/wrap_main_starboard.h
+++ b/src/cobalt/base/wrap_main_starboard.h
@@ -42,14 +42,14 @@
DCHECK(!g_at_exit);
g_at_exit = new base::AtExitManager();
- InitCobalt(data->argument_count, data->argument_values);
+ InitCobalt(data->argument_count, data->argument_values, data->link);
DCHECK(!g_loop);
g_loop = new MessageLoopForUI();
g_loop->set_thread_name("Main");
g_loop->Start();
- start_function(data->argument_count, data->argument_values,
+ start_function(data->argument_count, data->argument_values, data->link,
base::Bind(&SbSystemRequestStop, 0));
break;
}
@@ -78,7 +78,7 @@
template <MainFunction main_function>
int CobaltMainAddOns(int argc, char** argv) {
base::AtExitManager at_exit;
- cobalt::InitCobalt(argc, argv);
+ cobalt::InitCobalt(argc, argv, NULL);
return main_function(argc, argv);
}
diff --git a/src/cobalt/bindings/testing/get_opaque_root_test.cc b/src/cobalt/bindings/testing/get_opaque_root_test.cc
index ef72e1f..e60db42 100644
--- a/src/cobalt/bindings/testing/get_opaque_root_test.cc
+++ b/src/cobalt/bindings/testing/get_opaque_root_test.cc
@@ -24,8 +24,19 @@
namespace testing {
namespace {
-typedef InterfaceBindingsTest<GetOpaqueRootInterface>
- GetOpaqueRootInterfaceTest;
+class GetOpaqueRootInterfaceTest : public BindingsTestBase {
+ public:
+ GetOpaqueRootInterfaceTest()
+ : test_mock_(new ::testing::NiceMock<GetOpaqueRootInterface>()) {
+ global_environment_->Bind(
+ "test", make_scoped_refptr<GetOpaqueRootInterface>((test_mock_)));
+ }
+
+ GetOpaqueRootInterface& test_mock() { return *test_mock_.get(); }
+
+ private:
+ const scoped_refptr<GetOpaqueRootInterface> test_mock_;
+};
} // namespace
TEST_F(GetOpaqueRootInterfaceTest, CallsFunction) {
diff --git a/src/cobalt/browser/application.cc b/src/cobalt/browser/application.cc
index ac19432..27c9f1a 100644
--- a/src/cobalt/browser/application.cc
+++ b/src/cobalt/browser/application.cc
@@ -29,6 +29,8 @@
#include "build/build_config.h"
#include "cobalt/account/account_event.h"
#include "cobalt/base/cobalt_paths.h"
+#include "cobalt/base/deep_link_event.h"
+#include "cobalt/base/init_cobalt.h"
#include "cobalt/base/localized_strings.h"
#include "cobalt/base/user_log.h"
#include "cobalt/browser/switches.h"
@@ -290,6 +292,7 @@
BrowserModule::Options options;
options.web_module_options.name = "MainWebModule";
options.language = language;
+ options.initial_deep_link = GetInitialDeepLink();
options.network_module_options.preferred_language = language;
ApplyCommandLineSettingsToRendererOptions(&options.renderer_module_options);
diff --git a/src/cobalt/browser/browser_bindings.gyp b/src/cobalt/browser/browser_bindings.gyp
index 80049c1..cb9d50b 100644
--- a/src/cobalt/browser/browser_bindings.gyp
+++ b/src/cobalt/browser/browser_bindings.gyp
@@ -155,6 +155,7 @@
'../h5vcc/H5vccAudioConfigArray.idl',
'../h5vcc/H5vccCVal.idl',
'../h5vcc/H5vccCValKeyList.idl',
+ '../h5vcc/H5vccDeepLinkEventTarget.idl',
'../h5vcc/H5vccRuntime.idl',
'../h5vcc/H5vccRuntimeEventTarget.idl',
'../h5vcc/H5vccSettings.idl',
diff --git a/src/cobalt/browser/browser_module.cc b/src/cobalt/browser/browser_module.cc
index cd3c605..692ee5d 100644
--- a/src/cobalt/browser/browser_module.cc
+++ b/src/cobalt/browser/browser_module.cc
@@ -16,12 +16,16 @@
#include "cobalt/browser/browser_module.h"
+#include <vector>
+
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/stl_util.h"
+#include "base/string_number_conversions.h"
+#include "base/string_split.h"
#include "cobalt/base/cobalt_paths.h"
#include "cobalt/base/source_location.h"
#include "cobalt/base/tokens.h"
@@ -53,6 +57,15 @@
"activated or not. While activated, input will constantly and randomly be "
"generated and passed directly into the main web module.";
+const char kSetMediaConfigCommand[] = "set_media_config";
+const char kSetMediaConfigCommandShortHelp[] =
+ "Sets media module configuration.";
+const char kSetMediaConfigCommandLongHelp[] =
+ "This can be called in the form of set_media_config('name=value'), where "
+ "name is a string and value is an int. Refer to the implementation of "
+ "MediaModule::SetConfiguration() on individual platform for settings "
+ "supported on the particular platform.";
+
#if defined(ENABLE_SCREENSHOT)
// Command to take a screenshot.
const char kScreenshotCommand[] = "screenshot";
@@ -134,6 +147,10 @@
kFuzzerToggleCommand,
base::Bind(&BrowserModule::OnFuzzerToggle, base::Unretained(this)),
kFuzzerToggleCommandShortHelp, kFuzzerToggleCommandLongHelp)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(set_media_config_command_handler_(
+ kSetMediaConfigCommand,
+ base::Bind(&BrowserModule::OnSetMediaConfig, base::Unretained(this)),
+ kSetMediaConfigCommandShortHelp, kSetMediaConfigCommandLongHelp)),
#if defined(ENABLE_SCREENSHOT)
ALLOW_THIS_IN_INITIALIZER_LIST(screenshot_command_handler_(
kScreenshotCommand,
@@ -156,6 +173,7 @@
h5vcc_settings.network_module = &network_module_;
h5vcc_settings.account_manager = account_manager;
h5vcc_settings.event_dispatcher = system_window->event_dispatcher();
+ h5vcc_settings.initial_deep_link = options.initial_deep_link;
web_module_options_.injected_window_attributes["h5vcc"] =
base::Bind(&CreateH5VCC, h5vcc_settings);
@@ -330,6 +348,30 @@
}
}
+void BrowserModule::OnSetMediaConfig(const std::string& config) {
+ if (MessageLoop::current() != self_message_loop_) {
+ self_message_loop_->PostTask(
+ FROM_HERE,
+ base::Bind(&BrowserModule::OnSetMediaConfig, weak_this_, config));
+ return;
+ }
+
+ std::vector<std::string> tokens;
+ base::SplitString(config, '=', &tokens);
+
+ int value;
+ if (tokens.size() != 2 || !base::StringToInt(tokens[1], &value)) {
+ LOG(WARNING) << "Media configuration '" << config << "' is not in the"
+ << " form of '<string name>=<int value>'.";
+ return;
+ }
+ if (media_module_->SetConfiguration(tokens[0], value)) {
+ LOG(INFO) << "Successfully setting " << tokens[0] << " to " << value;
+ } else {
+ LOG(WARNING) << "Failed to set " << tokens[0] << " to " << value;
+ }
+}
+
void BrowserModule::OnDebugConsoleRenderTreeProduced(
const browser::WebModule::LayoutResults& layout_results) {
TRACE_EVENT0("cobalt::browser",
diff --git a/src/cobalt/browser/browser_module.h b/src/cobalt/browser/browser_module.h
index 742a4d3..df6da73 100644
--- a/src/cobalt/browser/browser_module.h
+++ b/src/cobalt/browser/browser_module.h
@@ -64,6 +64,7 @@
WebModule::Options web_module_options;
media::MediaModule::Options media_module_options;
std::string language;
+ std::string initial_deep_link;
base::Closure web_module_recreated_callback;
};
@@ -166,6 +167,10 @@
// Toggles the input fuzzer on/off. Ignores the parameter.
void OnFuzzerToggle(const std::string&);
+ // Use the config in the form of '<string name>=<int value>' to call
+ // MediaModule::SetConfiguration().
+ void OnSetMediaConfig(const std::string& config);
+
// Glue function to deal with the production of the debug console render tree,
// and will manage handing it off to the renderer.
void OnDebugConsoleRenderTreeProduced(
@@ -266,9 +271,12 @@
TraceManager trace_manager;
- // Command handler object for toggline the input fuzzer on/off.
+ // Command handler object for toggling the input fuzzer on/off.
base::ConsoleCommandManager::CommandHandler fuzzer_toggle_command_handler_;
+ // Command handler object for setting media module config.
+ base::ConsoleCommandManager::CommandHandler set_media_config_command_handler_;
+
#if defined(ENABLE_SCREENSHOT)
// Command handler object for screenshot command from the debug console.
base::ConsoleCommandManager::CommandHandler screenshot_command_handler_;
diff --git a/src/cobalt/browser/main.cc b/src/cobalt/browser/main.cc
index ef98aff..6b5fef2 100644
--- a/src/cobalt/browser/main.cc
+++ b/src/cobalt/browser/main.cc
@@ -27,7 +27,7 @@
cobalt::browser::Application* g_application = NULL;
-void StartApplication(int /*argc*/, char** /*argv*/,
+void StartApplication(int /*argc*/, char** /*argv*/, const char* /*link*/,
const base::Closure& quit_closure) {
DCHECK(!g_application);
g_application = cobalt::browser::CreateApplication(quit_closure).release();
diff --git a/src/cobalt/browser/snapshot_app_stats.cc b/src/cobalt/browser/snapshot_app_stats.cc
index 757e784..050914f 100644
--- a/src/cobalt/browser/snapshot_app_stats.cc
+++ b/src/cobalt/browser/snapshot_app_stats.cc
@@ -130,7 +130,7 @@
cobalt::browser::Application* g_application = NULL;
base::Thread* g_snapshot_thread = NULL;
-void StartApplication(int /*argc*/, char* /*argv*/ [],
+void StartApplication(int /*argc*/, char* /*argv*/ [], const char* /*link*/,
const base::Closure& quit_closure) {
logging::SetMinLogLevel(100);
diff --git a/src/cobalt/browser/starboard/event_handler.cc b/src/cobalt/browser/starboard/event_handler.cc
index 00808d7..be5e494 100644
--- a/src/cobalt/browser/starboard/event_handler.cc
+++ b/src/cobalt/browser/starboard/event_handler.cc
@@ -18,6 +18,7 @@
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
+#include "cobalt/base/deep_link_event.h"
#include "cobalt/network/network_event.h"
#include "cobalt/system_window/application_event.h"
#include "cobalt/system_window/starboard/system_window.h"
@@ -65,6 +66,9 @@
} else if (starboard_event->type == kSbEventTypeNetworkDisconnect) {
cobalt_event.reset(
new network::NetworkEvent(network::NetworkEvent::kDisconnection));
+ } else if (starboard_event->type == kSbEventTypeLink) {
+ const char* link = static_cast<const char*>(starboard_event->data);
+ cobalt_event.reset(new base::DeepLinkEvent(link));
}
// Dispatch the Cobalt event, if created.
diff --git a/src/cobalt/build/build.id b/src/cobalt/build/build.id
index 0e34392..eb49ee4 100644
--- a/src/cobalt/build/build.id
+++ b/src/cobalt/build/build.id
@@ -1 +1 @@
-11130
\ No newline at end of file
+11337
\ No newline at end of file
diff --git a/src/cobalt/dom/Window.idl b/src/cobalt/dom/Window.idl
index 3094309..b6a3e20 100644
--- a/src/cobalt/dom/Window.idl
+++ b/src/cobalt/dom/Window.idl
@@ -25,6 +25,7 @@
[Unforgeable] readonly attribute Document document;
[PutForwards=href, Unforgeable] readonly attribute Location location;
readonly attribute History history;
+ void close();
// other browsing contexts
[Replaceable] readonly attribute Window frames;
diff --git a/src/cobalt/dom/html_script_element.cc b/src/cobalt/dom/html_script_element.cc
index fcfdf35..b94ee93 100644
--- a/src/cobalt/dom/html_script_element.cc
+++ b/src/cobalt/dom/html_script_element.cc
@@ -53,7 +53,8 @@
load_option_(0),
inline_script_location_(GetSourceLocationName(), 1, 1),
is_sync_load_successful_(false),
- prevent_garbage_collection_count_(0) {
+ prevent_garbage_collection_count_(0),
+ should_execute_(true) {
DCHECK(document->html_element_context()->script_runner());
}
@@ -260,12 +261,9 @@
base::Unretained(this)));
if (is_sync_load_successful_) {
- html_element_context()->script_runner()->Execute(
- content_, base::SourceLocation(url_.spec(), 1, 1));
-
- // If the script is from an external file, fire a simple event named
- // load at the script element.
- DispatchEvent(new Event(base::Tokens::load()));
+ PreventGarbageCollection();
+ ExecuteExternal();
+ AllowGarbageCollection();
} else {
// Executing the script block must just consist of firing a simple event
// named error at the element.
@@ -497,6 +495,16 @@
void HTMLScriptElement::Execute(const std::string& content,
const base::SourceLocation& script_location,
bool is_external) {
+ // If should_execute_ is set to false for whatever reason, then we
+ // immediately exit.
+ // When inserted using the document.write() method, script elements execute
+ // (typically synchronously), but when inserted using innerHTML and
+ // outerHTML attributes, they do not execute at all.
+ // https://www.w3.org/TR/html5/scripting-1.html#the-script-element.
+ if (!should_execute_) {
+ return;
+ }
+
TRACE_EVENT0("cobalt::dom", "HTMLScriptElement::Execute()");
// Since error is already handled, it is guaranteed the load is successful.
diff --git a/src/cobalt/dom/html_script_element.h b/src/cobalt/dom/html_script_element.h
index 17ab6d4..aa523db 100644
--- a/src/cobalt/dom/html_script_element.h
+++ b/src/cobalt/dom/html_script_element.h
@@ -62,6 +62,10 @@
SetAttributeEventListener(base::Tokens::readystatechange(), event_listener);
}
+ void set_should_execute(bool should_execute) {
+ should_execute_ = should_execute;
+ }
+
// Custom, not in any spec.
//
// From Node.
@@ -133,6 +137,9 @@
std::string content_;
// Active requests disabling garbage collection.
int prevent_garbage_collection_count_;
+
+ // Whether or not the script should execute at all.
+ bool should_execute_;
};
} // namespace dom
diff --git a/src/cobalt/dom/parser.h b/src/cobalt/dom/parser.h
index 554643e..3bf7392 100644
--- a/src/cobalt/dom/parser.h
+++ b/src/cobalt/dom/parser.h
@@ -45,7 +45,8 @@
virtual ~Parser() {}
// Synchronous parsing interfaces.
//
- // Parses an HTML document and returns the created Document.
+ // Parses an HTML document and returns the created Document. No
+ // script elements within the HTML document will be executed.
virtual scoped_refptr<Document> ParseDocument(
const std::string& input, HTMLElementContext* html_element_context,
const base::SourceLocation& input_location) = 0;
@@ -56,7 +57,8 @@
const base::SourceLocation& input_location) = 0;
// Parses an HTML input and inserts new nodes in document under parent_node
- // before reference_node.
+ // before reference_node. No script elements within the HTML document will
+ // be executed.
virtual void ParseDocumentFragment(
const std::string& input, const scoped_refptr<Document>& document,
const scoped_refptr<Node>& parent_node,
@@ -74,7 +76,8 @@
// Asynchronous parsing interfaces.
//
// Parses an HTML document asynchronously, returns the decoder that can be
- // used in the parsing.
+ // used in the parsing. Script elements in the HTML document will be
+ // executed.
virtual scoped_ptr<loader::Decoder> ParseDocumentAsync(
const scoped_refptr<Document>& document,
const base::SourceLocation& input_location) = 0;
diff --git a/src/cobalt/dom/window.cc b/src/cobalt/dom/window.cc
index 8a57e10..3e23ed9 100644
--- a/src/cobalt/dom/window.cc
+++ b/src/cobalt/dom/window.cc
@@ -39,6 +39,9 @@
#include "cobalt/dom/storage.h"
#include "cobalt/dom/window_timers.h"
#include "cobalt/script/javascript_engine.h"
+#if defined(OS_STARBOARD)
+#include "starboard/system.h"
+#endif
namespace cobalt {
namespace dom {
@@ -135,6 +138,15 @@
const scoped_refptr<History>& Window::history() const { return history_; }
+// https://www.w3.org/TR/html5/browsers.html#dom-window-close
+void Window::Close() {
+#if defined(OS_STARBOARD)
+ SbSystemRequestStop(0);
+#else
+ LOG(WARNING) << "window.close is not supported on this platform.";
+#endif
+}
+
const scoped_refptr<Navigator>& Window::navigator() const { return navigator_; }
scoped_refptr<cssom::CSSStyleDeclaration> Window::GetComputedStyle(
diff --git a/src/cobalt/dom/window.h b/src/cobalt/dom/window.h
index 6b2e776..8240112 100644
--- a/src/cobalt/dom/window.h
+++ b/src/cobalt/dom/window.h
@@ -116,6 +116,7 @@
const scoped_refptr<Document>& document() const;
const scoped_refptr<Location>& location() const;
const scoped_refptr<History>& history() const;
+ void Close();
scoped_refptr<Window> frames() { return this; }
unsigned int length() { return 0; }
diff --git a/src/cobalt/dom_parser/html_decoder.cc b/src/cobalt/dom_parser/html_decoder.cc
index ddda365..f0b5585 100644
--- a/src/cobalt/dom_parser/html_decoder.cc
+++ b/src/cobalt/dom_parser/html_decoder.cc
@@ -30,12 +30,14 @@
const scoped_refptr<dom::Node>& reference_node,
const int dom_max_element_depth, const base::SourceLocation& input_location,
const base::Closure& done_callback,
- const base::Callback<void(const std::string&)>& error_callback)
+ const base::Callback<void(const std::string&)>& error_callback,
+ const bool should_run_scripts)
: libxml_html_parser_wrapper_(new LibxmlHTMLParserWrapper(
document, parent_node, reference_node, dom_max_element_depth,
- input_location, error_callback)),
+ input_location, error_callback, should_run_scripts)),
document_(document),
- done_callback_(done_callback) {}
+ done_callback_(done_callback),
+ should_run_scripts_(should_run_scripts) {}
HTMLDecoder::~HTMLDecoder() {}
diff --git a/src/cobalt/dom_parser/html_decoder.h b/src/cobalt/dom_parser/html_decoder.h
index 853fd47..db142e7 100644
--- a/src/cobalt/dom_parser/html_decoder.h
+++ b/src/cobalt/dom_parser/html_decoder.h
@@ -49,7 +49,8 @@
const int dom_max_element_depth,
const base::SourceLocation& input_location,
const base::Closure& done_callback,
- const base::Callback<void(const std::string&)>& error_callback);
+ const base::Callback<void(const std::string&)>& error_callback,
+ const bool should_run_scripts);
~HTMLDecoder();
@@ -68,6 +69,8 @@
base::ThreadChecker thread_checker_;
const base::Closure done_callback_;
+ const bool should_run_scripts_;
+
DISALLOW_COPY_AND_ASSIGN(HTMLDecoder);
};
diff --git a/src/cobalt/dom_parser/html_decoder_test.cc b/src/cobalt/dom_parser/html_decoder_test.cc
index ab5f6a1..92736ea 100644
--- a/src/cobalt/dom_parser/html_decoder_test.cc
+++ b/src/cobalt/dom_parser/html_decoder_test.cc
@@ -82,7 +82,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, document_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -104,7 +105,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, document_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(reinterpret_cast<char*>(temp), sizeof(temp));
html_decoder_->Finish();
@@ -122,7 +124,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, document_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -140,7 +143,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, document_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -158,7 +162,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, document_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -176,7 +181,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, document_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -199,7 +205,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -214,7 +221,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -238,7 +246,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -258,7 +267,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -276,7 +286,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -295,7 +306,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(reinterpret_cast<char*>(temp), sizeof(temp));
html_decoder_->Finish();
@@ -308,7 +320,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -335,7 +348,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -354,7 +368,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -376,7 +391,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -402,7 +418,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -420,7 +437,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -434,7 +452,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, root_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
@@ -457,7 +476,8 @@
html_decoder_.reset(new HTMLDecoder(
document_, document_, NULL, kDOMMaxElementDepth, source_location_,
base::Closure(), base::Bind(&MockErrorCallback::Run,
- base::Unretained(&mock_error_callback_))));
+ base::Unretained(&mock_error_callback_)),
+ true));
html_decoder_->DecodeChunk(input.c_str(), input.length());
html_decoder_->Finish();
diff --git a/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc b/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
index a50c295..e65ef8f 100644
--- a/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
+++ b/src/cobalt/dom_parser/libxml_html_parser_wrapper.cc
@@ -18,6 +18,8 @@
#include "base/basictypes.h"
#include "base/string_util.h"
+#include "cobalt/dom/element.h"
+#include "cobalt/dom/html_script_element.h"
namespace cobalt {
namespace dom_parser {
@@ -89,6 +91,17 @@
if (!IsFullDocument() && (name == "html" || name == "body")) {
return;
}
+
+ // If the top if the node stack is an html script element, then set its
+ // should_execute_ field to be our should_run_scripts_ field.
+ DCHECK(!node_stack().empty());
+ if (name == "script") {
+ scoped_refptr<dom::HTMLScriptElement> html_script_element =
+ node_stack().top()->AsElement()->AsHTMLElement()->AsHTMLScriptElement();
+ DCHECK(html_script_element);
+ html_script_element->set_should_execute(should_run_scripts_);
+ }
+
LibxmlParserWrapper::OnEndElement(name);
}
diff --git a/src/cobalt/dom_parser/libxml_html_parser_wrapper.h b/src/cobalt/dom_parser/libxml_html_parser_wrapper.h
index f553818..7114443 100644
--- a/src/cobalt/dom_parser/libxml_html_parser_wrapper.h
+++ b/src/cobalt/dom_parser/libxml_html_parser_wrapper.h
@@ -40,11 +40,13 @@
const scoped_refptr<dom::Node>& reference_node,
const int dom_max_element_depth,
const base::SourceLocation& input_location,
- const base::Callback<void(const std::string&)>& error_callback)
+ const base::Callback<void(const std::string&)>& error_callback,
+ const bool should_run_scripts)
: LibxmlParserWrapper(document, parent_node, reference_node,
dom_max_element_depth, input_location,
error_callback),
- html_parser_context_(NULL) {
+ html_parser_context_(NULL),
+ should_run_scripts_(should_run_scripts) {
DCHECK(!document->IsXMLDocument());
}
~LibxmlHTMLParserWrapper() OVERRIDE;
@@ -61,6 +63,8 @@
htmlParserCtxtPtr html_parser_context_;
+ const bool should_run_scripts_;
+
DISALLOW_COPY_AND_ASSIGN(LibxmlHTMLParserWrapper);
};
diff --git a/src/cobalt/dom_parser/parser.cc b/src/cobalt/dom_parser/parser.cc
index 507c187..5be05a6 100644
--- a/src/cobalt/dom_parser/parser.cc
+++ b/src/cobalt/dom_parser/parser.cc
@@ -32,7 +32,8 @@
scoped_refptr<dom::Document> document =
new dom::Document(html_element_context, dom::Document::Options());
HTMLDecoder html_decoder(document, document, NULL, dom_max_element_depth_,
- input_location, base::Closure(), error_callback_);
+ input_location, base::Closure(), error_callback_,
+ false);
html_decoder.DecodeChunk(input.c_str(), input.length());
html_decoder.Finish();
return document;
@@ -60,7 +61,7 @@
const base::SourceLocation& input_location) {
HTMLDecoder html_decoder(document, parent_node, reference_node,
dom_max_element_depth_, input_location,
- base::Closure(), error_callback_);
+ base::Closure(), error_callback_, false);
html_decoder.DecodeChunk(input.c_str(), input.length());
html_decoder.Finish();
}
@@ -84,7 +85,7 @@
const base::SourceLocation& input_location) {
return scoped_ptr<loader::Decoder>(
new HTMLDecoder(document, document, NULL, dom_max_element_depth_,
- input_location, base::Closure(), error_callback_));
+ input_location, base::Closure(), error_callback_, true));
}
scoped_ptr<loader::Decoder> Parser::ParseXMLDocumentAsync(
diff --git a/src/cobalt/h5vcc/H5vccDeepLinkEventTarget.idl b/src/cobalt/h5vcc/H5vccDeepLinkEventTarget.idl
new file mode 100644
index 0000000..8f823a9
--- /dev/null
+++ b/src/cobalt/h5vcc/H5vccDeepLinkEventTarget.idl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Custom interface to dispatch deep link events.
+// Modeled after Chrome's runtime onMessage target (and similar):
+// https://developer.chrome.com/apps/runtime#event-onMessage
+
+interface H5vccDeepLinkEventTarget {
+ void addListener(H5vccDeepLinkEventCallback callback);
+};
+
+callback H5vccDeepLinkEventCallback = void(DOMString link);
diff --git a/src/cobalt/h5vcc/H5vccRuntime.idl b/src/cobalt/h5vcc/H5vccRuntime.idl
index 18b8acb..02ed8ad 100644
--- a/src/cobalt/h5vcc/H5vccRuntime.idl
+++ b/src/cobalt/h5vcc/H5vccRuntime.idl
@@ -15,6 +15,8 @@
*/
interface H5vccRuntime {
+ readonly attribute DOMString initialDeepLink;
+ readonly attribute H5vccDeepLinkEventTarget onDeepLink;
readonly attribute H5vccRuntimeEventTarget onPause;
readonly attribute H5vccRuntimeEventTarget onResume;
};
diff --git a/src/cobalt/h5vcc/h5vcc.cc b/src/cobalt/h5vcc/h5vcc.cc
index b0cf530..9792fcb 100644
--- a/src/cobalt/h5vcc/h5vcc.cc
+++ b/src/cobalt/h5vcc/h5vcc.cc
@@ -23,7 +23,8 @@
account_info_ = new H5vccAccountInfo(settings.account_manager);
audio_config_array_ = new H5vccAudioConfigArray();
c_val_ = new H5vccCVal();
- runtime_ = new H5vccRuntime(settings.event_dispatcher);
+ runtime_ =
+ new H5vccRuntime(settings.event_dispatcher, settings.initial_deep_link);
settings_ = new H5vccSettings(settings.media_module);
storage_ = new H5vccStorage(settings.network_module);
system_ = new H5vccSystem();
diff --git a/src/cobalt/h5vcc/h5vcc.gyp b/src/cobalt/h5vcc/h5vcc.gyp
index 835c4f9..c208fd0 100644
--- a/src/cobalt/h5vcc/h5vcc.gyp
+++ b/src/cobalt/h5vcc/h5vcc.gyp
@@ -39,6 +39,9 @@
'h5vcc_c_val.h',
'h5vcc_c_val_key_list.cc',
'h5vcc_c_val_key_list.h',
+ 'h5vcc_deep_link_event_target.cc',
+ 'h5vcc_deep_link_event_target.h',
+ 'h5vcc_event_listener_container.h',
'h5vcc_runtime.cc',
'h5vcc_runtime.h',
'h5vcc_runtime_event_target.cc',
diff --git a/src/cobalt/h5vcc/h5vcc.h b/src/cobalt/h5vcc/h5vcc.h
index e8395b4..369cad3 100644
--- a/src/cobalt/h5vcc/h5vcc.h
+++ b/src/cobalt/h5vcc/h5vcc.h
@@ -17,6 +17,8 @@
#ifndef COBALT_H5VCC_H5VCC_H_
#define COBALT_H5VCC_H5VCC_H_
+#include <string>
+
#include "cobalt/base/event_dispatcher.h"
#include "cobalt/h5vcc/h5vcc_account_info.h"
#include "cobalt/h5vcc/h5vcc_audio_config_array.h"
@@ -42,6 +44,7 @@
network::NetworkModule* network_module;
account::AccountManager* account_manager;
base::EventDispatcher* event_dispatcher;
+ std::string initial_deep_link;
};
explicit H5vcc(const Settings& config);
diff --git a/src/cobalt/h5vcc/h5vcc_deep_link_event_target.cc b/src/cobalt/h5vcc/h5vcc_deep_link_event_target.cc
new file mode 100644
index 0000000..697688d
--- /dev/null
+++ b/src/cobalt/h5vcc/h5vcc_deep_link_event_target.cc
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cobalt/h5vcc/h5vcc_deep_link_event_target.h"
+
+namespace cobalt {
+namespace h5vcc {
+
+namespace {
+const std::string& OnGetArgument(const std::string& link) { return link; }
+} // namespace
+
+H5vccDeepLinkEventTarget::H5vccDeepLinkEventTarget()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(listeners_(this)) {}
+
+void H5vccDeepLinkEventTarget::AddListener(
+ const H5vccDeepLinkEventTarget::H5vccDeepLinkEventCallbackHolder&
+ callback_holder) {
+ listeners_.AddListener(callback_holder);
+}
+
+void H5vccDeepLinkEventTarget::DispatchEvent(const std::string& link) {
+ listeners_.DispatchEvent(base::Bind(OnGetArgument, link));
+}
+
+} // namespace h5vcc
+} // namespace cobalt
diff --git a/src/cobalt/h5vcc/h5vcc_deep_link_event_target.h b/src/cobalt/h5vcc/h5vcc_deep_link_event_target.h
new file mode 100644
index 0000000..4a6b706
--- /dev/null
+++ b/src/cobalt/h5vcc/h5vcc_deep_link_event_target.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_H5VCC_H5VCC_DEEP_LINK_EVENT_TARGET_H_
+#define COBALT_H5VCC_H5VCC_DEEP_LINK_EVENT_TARGET_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "cobalt/h5vcc/h5vcc_event_listener_container.h"
+#include "cobalt/script/callback_function.h"
+#include "cobalt/script/script_object.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace h5vcc {
+
+// This class implements the onDeepLink attribute of the h5vcc.runtime protocol,
+// modeled after the events in Chrome's runtime extension, e.g.
+// https://developer.chrome.com/apps/runtime#event-onMessage
+
+class H5vccDeepLinkEventTarget : public script::Wrappable {
+ public:
+ // Type for JavaScript event callback.
+ typedef script::CallbackFunction<void(const std::string&)>
+ H5vccDeepLinkEventCallback;
+ typedef script::ScriptObject<H5vccDeepLinkEventCallback>
+ H5vccDeepLinkEventCallbackHolder;
+
+ H5vccDeepLinkEventTarget();
+
+ // Called from JavaScript to register an event listener callback.
+ // May be called from any thread.
+ void AddListener(const H5vccDeepLinkEventCallbackHolder& callback_holder);
+
+ // Dispatches an event to the registered listeners.
+ // May be called from any thread.
+ void DispatchEvent(const std::string& link);
+
+ DEFINE_WRAPPABLE_TYPE(H5vccDeepLinkEventTarget);
+
+ private:
+ H5vccEventListenerContainer<const std::string&, H5vccDeepLinkEventCallback>
+ listeners_;
+
+ DISALLOW_COPY_AND_ASSIGN(H5vccDeepLinkEventTarget);
+};
+
+} // namespace h5vcc
+} // namespace cobalt
+
+#endif // COBALT_H5VCC_H5VCC_DEEP_LINK_EVENT_TARGET_H_
diff --git a/src/cobalt/h5vcc/h5vcc_event_listener_container.h b/src/cobalt/h5vcc/h5vcc_event_listener_container.h
new file mode 100644
index 0000000..6f91143
--- /dev/null
+++ b/src/cobalt/h5vcc/h5vcc_event_listener_container.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COBALT_H5VCC_H5VCC_EVENT_LISTENER_CONTAINER_H_
+#define COBALT_H5VCC_H5VCC_EVENT_LISTENER_CONTAINER_H_
+
+#include <string>
+#include <vector>
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+#include "base/synchronization/lock.h"
+#include "cobalt/script/callback_function.h"
+#include "cobalt/script/script_object.h"
+#include "cobalt/script/wrappable.h"
+
+namespace cobalt {
+namespace h5vcc {
+
+// Template class to implement a container of event listeners where the
+// listener callback can take an argument of any type, including none (void).
+// Callback type must be specified in addition to callback argument type,
+// as we cannot typedef a callback taking a void argument.
+template <class CallbackArgType, class CallbackType>
+class H5vccEventListenerContainer {
+ public:
+ typedef script::ScriptObject<CallbackType> CallbackHolderType;
+
+ // Type for a callback that returns the value of the argument to be passed
+ // to the callback for each listener.
+ typedef base::Callback<CallbackArgType()> GetArgumentCallback;
+
+ // Type for a listener.
+ // We store the message loop from which the listener was registered,
+ // so we can run the callback on the same loop.
+ struct Listener {
+ Listener(script::Wrappable* owner, const CallbackHolderType& cb)
+ : callback(owner, cb),
+ message_loop(base::MessageLoopProxy::current()) {}
+
+ // Notifies listener. Must be called on the same message loop the
+ // listener registered its callback from.
+ void Notify(GetArgumentCallback on_notify) {
+ DCHECK_EQ(base::MessageLoopProxy::current(), message_loop);
+ CallbackArgType arg = on_notify.Run();
+ callback.value().Run(arg);
+ }
+
+ typename CallbackHolderType::Reference callback;
+ scoped_refptr<base::MessageLoopProxy> message_loop;
+ };
+
+ explicit H5vccEventListenerContainer(script::Wrappable* owner)
+ : owner_(owner) {}
+
+ ~H5vccEventListenerContainer() {
+ // Delete all registered listeners.
+ for (typename ListenerVector::const_iterator it = listeners_.begin();
+ it != listeners_.end(); ++it) {
+ delete *it;
+ }
+ }
+
+ // Called from JavaScript to register an event listener. May be called from
+ // any thread, and event notification will be called on the same thread.
+ void AddListener(const CallbackHolderType& callback_holder) {
+ base::AutoLock auto_lock(lock_);
+ listeners_.push_back(new Listener(owner_, callback_holder));
+ }
+
+ // Dispatches an event to the registered listeners. May be called from any
+ // thread, and the callbacks will be invoked on the same thread each listener
+ // was registered on. |get_argument_callback| must be a function that
+ // returns the argument value for this event.
+ void DispatchEvent(GetArgumentCallback get_argument_callback) {
+ base::AutoLock auto_lock(lock_);
+ for (typename ListenerVector::iterator it = listeners_.begin();
+ it != listeners_.end(); ++it) {
+ Listener* listener = *it;
+ listener->message_loop->PostTask(
+ FROM_HERE, base::Bind(&Listener::Notify, base::Unretained(listener),
+ get_argument_callback));
+ }
+ }
+
+ private:
+ typedef std::vector<Listener*> ListenerVector;
+
+ script::Wrappable* owner_;
+ ListenerVector listeners_;
+ base::Lock lock_;
+};
+
+// Explicit template specialization for the no callback argument case, where
+// we don't need to call the |GetArgumentCallback| callback.
+template <>
+inline void
+ H5vccEventListenerContainer<void, script::CallbackFunction<void()> >::
+ Listener::Notify(GetArgumentCallback) {
+ DCHECK_EQ(base::MessageLoopProxy::current(), message_loop);
+ callback.value().Run();
+}
+
+} // namespace h5vcc
+} // namespace cobalt
+
+#endif // COBALT_H5VCC_H5VCC_EVENT_LISTENER_CONTAINER_H_
diff --git a/src/cobalt/h5vcc/h5vcc_runtime.cc b/src/cobalt/h5vcc/h5vcc_runtime.cc
index 09acb30..8b0f871 100644
--- a/src/cobalt/h5vcc/h5vcc_runtime.cc
+++ b/src/cobalt/h5vcc/h5vcc_runtime.cc
@@ -14,14 +14,18 @@
* limitations under the License.
*/
+#include "cobalt/base/deep_link_event.h"
#include "cobalt/base/polymorphic_downcast.h"
#include "cobalt/h5vcc/h5vcc_runtime.h"
#include "cobalt/system_window/application_event.h"
namespace cobalt {
namespace h5vcc {
-H5vccRuntime::H5vccRuntime(base::EventDispatcher* event_dispatcher)
+H5vccRuntime::H5vccRuntime(base::EventDispatcher* event_dispatcher,
+ const std::string& initial_deep_link)
: event_dispatcher_(event_dispatcher) {
+ initial_deep_link_ = initial_deep_link;
+ on_deep_link_ = new H5vccDeepLinkEventTarget;
on_pause_ = new H5vccRuntimeEventTarget;
on_resume_ = new H5vccRuntimeEventTarget;
@@ -30,11 +34,26 @@
base::Bind(&H5vccRuntime::OnApplicationEvent, base::Unretained(this));
event_dispatcher_->AddEventCallback(system_window::ApplicationEvent::TypeId(),
application_event_callback_);
+ deep_link_event_callback_ =
+ base::Bind(&H5vccRuntime::OnDeepLinkEvent, base::Unretained(this));
+ event_dispatcher_->AddEventCallback(base::DeepLinkEvent::TypeId(),
+ deep_link_event_callback_);
}
H5vccRuntime::~H5vccRuntime() {
event_dispatcher_->RemoveEventCallback(
system_window::ApplicationEvent::TypeId(), application_event_callback_);
+ event_dispatcher_->RemoveEventCallback(base::DeepLinkEvent::TypeId(),
+ deep_link_event_callback_);
+}
+
+const std::string& H5vccRuntime::initial_deep_link() const {
+ return initial_deep_link_;
+}
+
+const scoped_refptr<H5vccDeepLinkEventTarget>& H5vccRuntime::on_deep_link()
+ const {
+ return on_deep_link_;
}
const scoped_refptr<H5vccRuntimeEventTarget>& H5vccRuntime::on_pause() const {
@@ -56,5 +75,12 @@
on_resume()->DispatchEvent();
}
}
+
+void H5vccRuntime::OnDeepLinkEvent(const base::Event* event) {
+ const base::DeepLinkEvent* deep_link_event =
+ base::polymorphic_downcast<const base::DeepLinkEvent*>(event);
+ DLOG(INFO) << "Got deep link event: " << deep_link_event->link();
+ on_deep_link()->DispatchEvent(deep_link_event->link());
+}
} // namespace h5vcc
} // namespace cobalt
diff --git a/src/cobalt/h5vcc/h5vcc_runtime.h b/src/cobalt/h5vcc/h5vcc_runtime.h
index 8c14def..e51e87b 100644
--- a/src/cobalt/h5vcc/h5vcc_runtime.h
+++ b/src/cobalt/h5vcc/h5vcc_runtime.h
@@ -17,7 +17,10 @@
#ifndef COBALT_H5VCC_H5VCC_RUNTIME_H_
#define COBALT_H5VCC_H5VCC_RUNTIME_H_
+#include <string>
+
#include "cobalt/base/event_dispatcher.h"
+#include "cobalt/h5vcc/h5vcc_deep_link_event_target.h"
#include "cobalt/h5vcc/h5vcc_runtime_event_target.h"
#include "cobalt/script/wrappable.h"
@@ -26,26 +29,33 @@
class H5vccRuntime : public script::Wrappable {
public:
- explicit H5vccRuntime(base::EventDispatcher* event_dispatcher);
+ explicit H5vccRuntime(base::EventDispatcher* event_dispatcher,
+ const std::string& initial_deep_link);
~H5vccRuntime();
+ const std::string& initial_deep_link() const;
+ const scoped_refptr<H5vccDeepLinkEventTarget>& on_deep_link() const;
const scoped_refptr<H5vccRuntimeEventTarget>& on_pause() const;
const scoped_refptr<H5vccRuntimeEventTarget>& on_resume() const;
DEFINE_WRAPPABLE_TYPE(H5vccRuntime);
private:
- // Called by the event dispatcher to handle an application event.
+ // Called by the event dispatcher to handle various event types.
void OnApplicationEvent(const base::Event* event);
+ void OnDeepLinkEvent(const base::Event* event);
+ std::string initial_deep_link_;
+ scoped_refptr<H5vccDeepLinkEventTarget> on_deep_link_;
scoped_refptr<H5vccRuntimeEventTarget> on_pause_;
scoped_refptr<H5vccRuntimeEventTarget> on_resume_;
// Non-owned reference used to receive application event callbacks.
base::EventDispatcher* event_dispatcher_;
- // Application event callback.
+ // Event callbacks.
base::EventCallback application_event_callback_;
+ base::EventCallback deep_link_event_callback_;
DISALLOW_COPY_AND_ASSIGN(H5vccRuntime);
};
diff --git a/src/cobalt/h5vcc/h5vcc_runtime_event_target.cc b/src/cobalt/h5vcc/h5vcc_runtime_event_target.cc
index 32902e0..4174201 100644
--- a/src/cobalt/h5vcc/h5vcc_runtime_event_target.cc
+++ b/src/cobalt/h5vcc/h5vcc_runtime_event_target.cc
@@ -16,43 +16,23 @@
#include "cobalt/h5vcc/h5vcc_runtime_event_target.h"
-#include "base/location.h"
-
namespace cobalt {
namespace h5vcc {
-H5vccRuntimeEventTarget::H5vccRuntimeEventTarget() {}
+namespace {
+void OnGetArgument() {}
+} // namespace
-H5vccRuntimeEventTarget::~H5vccRuntimeEventTarget() {
- // Delete all registered listeners.
- for (ListenerVector::const_iterator it = listeners_.begin();
- it != listeners_.end(); ++it) {
- delete *it;
- }
-}
+H5vccRuntimeEventTarget::H5vccRuntimeEventTarget()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(listeners_(this)) {}
void H5vccRuntimeEventTarget::AddListener(
- const H5vccRuntimeEventTarget::H5vccRuntimeEventCallbackHolder& callback) {
- base::AutoLock auto_lock(lock_);
- listeners_.push_back(new ListenerInfo(this, callback));
+ const H5vccRuntimeEventCallbackHolder& callback_holder) {
+ listeners_.AddListener(callback_holder);
}
void H5vccRuntimeEventTarget::DispatchEvent() {
- base::AutoLock auto_lock(lock_);
-
- for (ListenerVector::const_iterator it = listeners_.begin();
- it != listeners_.end(); ++it) {
- const ListenerInfo* listener = *it;
- listener->message_loop->PostTask(
- FROM_HERE,
- base::Bind(&H5vccRuntimeEventTarget::NotifyListener, this, listener));
- }
-}
-
-void H5vccRuntimeEventTarget::NotifyListener(
- const H5vccRuntimeEventTarget::ListenerInfo* listener) {
- DCHECK_EQ(base::MessageLoopProxy::current(), listener->message_loop);
- listener->callback.value().Run();
+ listeners_.DispatchEvent(base::Bind(OnGetArgument));
}
} // namespace h5vcc
diff --git a/src/cobalt/h5vcc/h5vcc_runtime_event_target.h b/src/cobalt/h5vcc/h5vcc_runtime_event_target.h
index 344623e..fc2b491 100644
--- a/src/cobalt/h5vcc/h5vcc_runtime_event_target.h
+++ b/src/cobalt/h5vcc/h5vcc_runtime_event_target.h
@@ -22,6 +22,7 @@
#include "base/callback.h"
#include "base/message_loop_proxy.h"
#include "base/synchronization/lock.h"
+#include "cobalt/h5vcc/h5vcc_event_listener_container.h"
#include "cobalt/script/callback_function.h"
#include "cobalt/script/script_object.h"
#include "cobalt/script/wrappable.h"
@@ -40,21 +41,7 @@
typedef script::ScriptObject<H5vccRuntimeEventCallback>
H5vccRuntimeEventCallbackHolder;
- // Type for listener info.
- // We store the message loop from which the listener was registered,
- // so we can run the callback on the same loop.
- struct ListenerInfo {
- ListenerInfo(H5vccRuntimeEventTarget* const pause_event,
- const H5vccRuntimeEventCallbackHolder& cb)
- : callback(pause_event, cb),
- message_loop(base::MessageLoopProxy::current()) {}
-
- H5vccRuntimeEventCallbackHolder::Reference callback;
- scoped_refptr<base::MessageLoopProxy> message_loop;
- };
-
H5vccRuntimeEventTarget();
- ~H5vccRuntimeEventTarget();
// Called from JavaScript to register an event listener callback.
// May be called from any thread.
@@ -67,14 +54,7 @@
DEFINE_WRAPPABLE_TYPE(H5vccRuntimeEventTarget);
private:
- typedef std::vector<ListenerInfo*> ListenerVector;
-
- // Notifies a particular listener. Must be called on the same message loop the
- // listener registered its callback from.
- void NotifyListener(const ListenerInfo* listener);
-
- ListenerVector listeners_;
- base::Lock lock_;
+ H5vccEventListenerContainer<void, H5vccRuntimeEventCallback> listeners_;
DISALLOW_COPY_AND_ASSIGN(H5vccRuntimeEventTarget);
};
diff --git a/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-positioning-area-is-smaller-than-image-size.html b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-positioning-area-is-smaller-than-image-size.html
index 53de4b8..d727d74 100644
--- a/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-positioning-area-is-smaller-than-image-size.html
+++ b/src/cobalt/layout_tests/testdata/css3-background/14-2-1-background-positioning-area-is-smaller-than-image-size.html
@@ -20,7 +20,7 @@
}
var image = new Image();
- var image_name = 'legend-sprite-ps3.png';
+ var image_name = 'legend-sprite.png';
image.onload = function() {
var cobalt = document.getElementById('image');
diff --git a/src/cobalt/layout_tests/testdata/css3-background/legend-sprite.png b/src/cobalt/layout_tests/testdata/css3-background/legend-sprite.png
new file mode 100644
index 0000000..f566171
--- /dev/null
+++ b/src/cobalt/layout_tests/testdata/css3-background/legend-sprite.png
Binary files differ
diff --git a/src/cobalt/network/cookie_jar_impl.cc b/src/cobalt/network/cookie_jar_impl.cc
index 5ad5205..6e573de 100644
--- a/src/cobalt/network/cookie_jar_impl.cc
+++ b/src/cobalt/network/cookie_jar_impl.cc
@@ -18,7 +18,6 @@
#include "base/bind.h"
#include "base/synchronization/waitable_event.h"
-#include "base/threading/thread.h"
namespace cobalt {
namespace network {
@@ -27,10 +26,10 @@
class CookiesGetter {
public:
- CookiesGetter(const GURL& origin, net::CookieStore* cookie_store)
- : getter_thread_("CookiesGetter"), event_(true, false) {
- getter_thread_.Start();
- getter_thread_.message_loop()->PostTask(
+ CookiesGetter(const GURL& origin, net::CookieStore* cookie_store,
+ MessageLoop* get_cookies_message_loop)
+ : event_(true, false) {
+ get_cookies_message_loop->PostTask(
FROM_HERE, base::Bind(&net::CookieStore::GetCookiesWithOptionsAsync,
cookie_store, origin, net::CookieOptions(),
base::Bind(&CookiesGetter::CompletionCallback,
@@ -49,19 +48,20 @@
}
std::string cookies_;
- base::Thread getter_thread_;
base::WaitableEvent event_;
};
} // namespace
CookieJarImpl::CookieJarImpl(net::CookieStore* cookie_store)
- : cookie_store_(cookie_store) {
+ : get_cookies_thread_("CookiesGetter"), cookie_store_(cookie_store) {
DCHECK(cookie_store_);
+ get_cookies_thread_.Start();
}
std::string CookieJarImpl::GetCookies(const GURL& origin) {
- CookiesGetter cookies_getter(origin, cookie_store_);
+ CookiesGetter cookies_getter(
+ origin, cookie_store_, get_cookies_thread_.message_loop());
return cookies_getter.WaitForCookies();
}
diff --git a/src/cobalt/network/cookie_jar_impl.h b/src/cobalt/network/cookie_jar_impl.h
index 9d68e39..6c87a3f 100644
--- a/src/cobalt/network/cookie_jar_impl.h
+++ b/src/cobalt/network/cookie_jar_impl.h
@@ -20,6 +20,7 @@
#include <string>
#include "base/compiler_specific.h"
+#include "base/threading/thread.h"
#include "cobalt/network_bridge/cookie_jar.h"
#include "net/cookies/cookie_store.h"
@@ -34,6 +35,11 @@
void SetCookie(const GURL& origin, const std::string& cookie_line) OVERRIDE;
private:
+ // We use a dedicated thread for making GetCookiesWithOptionsAsync() calls in
+ // order to workaround GetCookiesWithOptionsAsync()'s default behavior of
+ // PostTask()ing the completion callback to the message loop that the
+ // GetCookiesWithOptionsAsync() call was made from.
+ base::Thread get_cookies_thread_;
net::CookieStore* cookie_store_;
DISALLOW_COPY_AND_ASSIGN(CookieJarImpl);
diff --git a/src/cobalt/renderer/backend/egl/texture_data_pbo.cc b/src/cobalt/renderer/backend/egl/texture_data_pbo.cc
index edc0e40..86fc108 100644
--- a/src/cobalt/renderer/backend/egl/texture_data_pbo.cc
+++ b/src/cobalt/renderer/backend/egl/texture_data_pbo.cc
@@ -32,7 +32,7 @@
TextureDataPBO::TextureDataPBO(ResourceContext* resource_context,
const math::Size& size, GLenum format)
: resource_context_(resource_context), size_(size), format_(format) {
- data_size_ = GetPitchInBytes() * size_.height();
+ data_size_ = static_cast<int64>(GetPitchInBytes()) * size_.height();
resource_context_->RunSynchronouslyWithinResourceContext(
base::Bind(&TextureDataPBO::InitAndMapPBO, base::Unretained(this)));
@@ -57,7 +57,7 @@
GL_PIXEL_UNPACK_BUFFER, 0, data_size_, GL_MAP_WRITE_BIT |
GL_MAP_INVALIDATE_BUFFER_BIT |
GL_MAP_UNSYNCHRONIZED_BIT));
- DCHECK(mapped_data_);
+ CHECK(mapped_data_);
DCHECK_EQ(GL_NO_ERROR, glGetError());
GL_CALL(glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0));
}
diff --git a/src/cobalt/renderer/backend/egl/texture_data_pbo.h b/src/cobalt/renderer/backend/egl/texture_data_pbo.h
index 56754ea..d99ca54 100644
--- a/src/cobalt/renderer/backend/egl/texture_data_pbo.h
+++ b/src/cobalt/renderer/backend/egl/texture_data_pbo.h
@@ -63,7 +63,7 @@
math::Size size_;
GLenum format_;
GLuint pixel_buffer_;
- int data_size_;
+ int64 data_size_;
GLubyte* mapped_data_;
};
diff --git a/src/cobalt/renderer/sandbox/renderer_sandbox_main.cc b/src/cobalt/renderer/sandbox/renderer_sandbox_main.cc
index 3fddb14..60e72e6 100644
--- a/src/cobalt/renderer/sandbox/renderer_sandbox_main.cc
+++ b/src/cobalt/renderer/sandbox/renderer_sandbox_main.cc
@@ -80,7 +80,7 @@
RendererSandbox* g_renderer_sandbox = NULL;
-void StartApplication(int /*argc*/, char** /*argv*/,
+void StartApplication(int /*argc*/, char** /*argv*/, const char* /*link*/,
const base::Closure& quit_closure) {
DCHECK(!g_renderer_sandbox);
g_renderer_sandbox = new RendererSandbox();
diff --git a/src/cobalt/script/exception_message.cc b/src/cobalt/script/exception_message.cc
index 73ac903..939b6b1 100644
--- a/src/cobalt/script/exception_message.cc
+++ b/src/cobalt/script/exception_message.cc
@@ -40,7 +40,7 @@
{kConvertToEnumFailed, kTypeError, "Failed to convert JS value to enum."},
{kStringifierProblem, kTypeError, "Stringifier problem."},
{kNotFunctionValue, kTypeError, "Value is not a function."},
- {kInvalidNumberOfArguments, kRangeError, "Invalid number of arguments."},
+ {kInvalidNumberOfArguments, kTypeError, "Invalid number of arguments."},
{kNotUnionType, kTypeError, "Value is not a member of the union type."},
{kOutsideBounds, kRangeError, "Offset is outside the object's bounds."},
{kInvalidLength, kRangeError, "Invalid length."},
diff --git a/src/cobalt/script/mozjs/mozjs_engine.cc b/src/cobalt/script/mozjs/mozjs_engine.cc
index b4d16ec..1b79cf7 100644
--- a/src/cobalt/script/mozjs/mozjs_engine.cc
+++ b/src/cobalt/script/mozjs/mozjs_engine.cc
@@ -28,8 +28,18 @@
namespace {
// After this many bytes have been allocated, the garbage collector will run.
const uint32_t kGarbageCollectionThresholdBytes = 8 * 1024 * 1024;
+
+JSBool CheckAccessStub(JSContext*, JS::Handle<JSObject*>, JS::Handle<jsid>,
+ JSAccessMode, JS::MutableHandle<JS::Value>) {
+ return true;
}
+JSSecurityCallbacks security_callbacks = {
+ CheckAccessStub,
+ MozjsGlobalEnvironment::CheckEval
+};
+} // namespace
+
MozjsEngine::MozjsEngine() {
// TODO: Investigate the benefit of helper threads and things like
// parallel compilation.
@@ -39,6 +49,8 @@
JS_SetRuntimePrivate(runtime_, this);
+ JS_SetSecurityCallbacks(runtime_, &security_callbacks);
+
// Use incremental garbage collection.
JS_SetGCParameter(runtime_, JSGC_MODE, JSGC_MODE_INCREMENTAL);
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.cc b/src/cobalt/script/mozjs/mozjs_global_environment.cc
index bf2648c..25fa34b 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.cc
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.cc
@@ -227,8 +227,10 @@
MozjsExceptionState exception_state(context_);
FromJSValue(context_, result_value, kNoConversionFlags, &exception_state,
out_result_utf8);
- } else {
+ } else if (last_error_message_) {
*out_result_utf8 = *last_error_message_;
+ } else {
+ DLOG(ERROR) << "Script execution failed.";
}
}
JS_RestoreExceptionState(context_, previous_exception_state);
diff --git a/src/cobalt/script/mozjs/mozjs_global_environment.h b/src/cobalt/script/mozjs/mozjs_global_environment.h
index a774cb2..54f75de 100644
--- a/src/cobalt/script/mozjs/mozjs_global_environment.h
+++ b/src/cobalt/script/mozjs/mozjs_global_environment.h
@@ -128,17 +128,17 @@
static MozjsGlobalEnvironment* GetFromContext(JSContext* context);
+ // This will be called every time an attempt is made to use eval() and
+ // friends. If it returns false, then the ReportErrorHandler will be fired
+ // with an error that eval() is disabled.
+ static JSBool CheckEval(JSContext* context);
+
protected:
static void ReportErrorHandler(JSContext* context, const char* message,
JSErrorReport* report);
static void TraceFunction(JSTracer* trace, void* data);
- // This will be called every time an attempt is made to use eval() and
- // friends. If it returns false, then the ReportErrorHandler will be fired
- // with an error that eval() is disabled.
- static JSBool CheckEval(JSContext* context);
-
// Helper struct to ensure the context is destroyed in the correct order
// relative to the MozjsGlobalEnvironment's other members.
struct ContextDestructor {
diff --git a/src/cobalt/storage/savegame_thread.cc b/src/cobalt/storage/savegame_thread.cc
index 423e52f..a36cf48 100644
--- a/src/cobalt/storage/savegame_thread.cc
+++ b/src/cobalt/storage/savegame_thread.cc
@@ -26,7 +26,8 @@
SavegameThread::SavegameThread(const Savegame::Options& options)
: options_(options),
initialized_(true /* manual reset */, false /* initially signalled */),
- thread_(new base::Thread("Savegame I/O")) {
+ thread_(new base::Thread("Savegame I/O")),
+ num_consecutive_flush_failures_(0) {
TRACE_EVENT0("cobalt::storage", __FUNCTION__);
thread_->Start();
thread_->message_loop()->PostTask(
@@ -88,7 +89,15 @@
DCHECK_EQ(thread_->message_loop(), MessageLoop::current());
if (raw_bytes_ptr->size() > 0) {
bool ret = savegame_->Write(*raw_bytes_ptr);
- DCHECK(ret);
+ if (ret) {
+ num_consecutive_flush_failures_ = 0;
+ } else {
+ DLOG(ERROR) << "Save failed.";
+ const int kMaxConsecutiveFlushFailures = 2;
+ DCHECK_LT(++num_consecutive_flush_failures_,
+ kMaxConsecutiveFlushFailures);
+ return;
+ }
}
if (!on_flush_complete.is_null()) {
diff --git a/src/cobalt/storage/savegame_thread.h b/src/cobalt/storage/savegame_thread.h
index 1f9b90f..1ffbd77 100644
--- a/src/cobalt/storage/savegame_thread.h
+++ b/src/cobalt/storage/savegame_thread.h
@@ -52,7 +52,7 @@
// I/O thread.
void ShutdownOnIOThread();
- // runs on the I/O thread to write the database to the savegame's persistent
+ // Runs on the I/O thread to write the database to the savegame's persistent
// storage.
void FlushOnIOThread(scoped_ptr<Savegame::ByteVector> raw_bytes_ptr,
const base::Closure& on_flush_complete);
@@ -74,6 +74,11 @@
// Interface to platform-specific savegame data.
scoped_ptr<Savegame> savegame_;
+
+ // How many flush failures have occurred since the last successful flush.
+ // Flushes (storage writes) may sometimes fail, but we want to make sure
+ // they're not consistently failing.
+ int num_consecutive_flush_failures_;
};
} // namespace storage
diff --git a/src/cobalt/version.h b/src/cobalt/version.h
index 28e8c13..7c390aa 100644
--- a/src/cobalt/version.h
+++ b/src/cobalt/version.h
@@ -17,6 +17,6 @@
#define COBALT_VERSION_H_
// Cobalt release number.
-#define COBALT_VERSION "2"
+#define COBALT_VERSION "3"
#endif // COBALT_VERSION_H_
diff --git a/src/net/tools/dump_cache/url_to_filename_encoder.cc b/src/net/tools/dump_cache/url_to_filename_encoder.cc
new file mode 100644
index 0000000..e928ee9
--- /dev/null
+++ b/src/net/tools/dump_cache/url_to_filename_encoder.cc
@@ -0,0 +1,292 @@
+// 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.
+
+#include <stdlib.h>
+
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "net/base/net_util.h"
+#include "net/tools/dump_cache/url_to_filename_encoder.h"
+
+using std::string;
+
+namespace {
+
+// Returns 1 if buf is prefixed by "num_digits" of hex digits
+// Teturns 0 otherwise.
+// The function checks for '\0' for string termination.
+int HexDigitsPrefix(const char* buf, int num_digits) {
+ for (int i = 0; i < num_digits; i++) {
+ if (!IsHexDigit(buf[i]))
+ return 0; // This also detects end of string as '\0' is not xdigit.
+ }
+ return 1;
+}
+
+#if defined(WIN32) || defined(__LB_XB1__) || defined(__LB_XB360__)
+#define strtoull _strtoui64
+#endif
+
+// A simple parser for long long values. Returns the parsed value if a
+// valid integer is found; else returns deflt
+// UInt64 and Int64 cannot handle decimal numbers with leading 0s.
+uint64 ParseLeadingHex64Value(const char *str, uint64 deflt) {
+ char *error = NULL;
+ const uint64 value = strtoull(str, &error, 16);
+ return (error == str) ? deflt : value;
+}
+
+}
+
+namespace net {
+
+// The escape character choice is made here -- all code and tests in this
+// directory are based off of this constant. However, our testdata
+// has tons of dependencies on this, so it cannot be changed without
+// re-running those tests and fixing them.
+const char UrlToFilenameEncoder::kEscapeChar = ',';
+const char UrlToFilenameEncoder::kTruncationChar = '-';
+const size_t UrlToFilenameEncoder::kMaximumSubdirectoryLength = 128;
+
+void UrlToFilenameEncoder::AppendSegment(string* segment, string* dest) {
+ CHECK(!segment->empty());
+ if ((*segment == ".") || (*segment == "..")) {
+ dest->append(1, kEscapeChar);
+ dest->append(*segment);
+ segment->clear();
+ } else {
+ size_t segment_size = segment->size();
+ if (segment_size > kMaximumSubdirectoryLength) {
+ // We need to inject ",-" at the end of the segment to signify that
+ // we are inserting an artificial '/'. This means we have to chop
+ // off at least two characters to make room.
+ segment_size = kMaximumSubdirectoryLength - 2;
+
+ // But we don't want to break up an escape sequence that happens to lie at
+ // the end. Escape sequences are at most 2 characters.
+ if ((*segment)[segment_size - 1] == kEscapeChar) {
+ segment_size -= 1;
+ } else if ((*segment)[segment_size - 2] == kEscapeChar) {
+ segment_size -= 2;
+ }
+ dest->append(segment->data(), segment_size);
+ dest->append(1, kEscapeChar);
+ dest->append(1, kTruncationChar);
+ segment->erase(0, segment_size);
+
+ // At this point, if we had segment_size=3, and segment="abcd",
+ // then after this erase, we will have written "abc,-" and set segment="d"
+ } else {
+ dest->append(*segment);
+ segment->clear();
+ }
+ }
+}
+
+void UrlToFilenameEncoder::EncodeSegment(const string& filename_prefix,
+ const string& escaped_ending,
+ char dir_separator,
+ string* encoded_filename) {
+ string filename_ending = UrlUtilities::Unescape(escaped_ending);
+
+ char encoded[3];
+ int encoded_len;
+ string segment;
+
+ // TODO(jmarantz): This code would be a bit simpler if we disallowed
+ // Instaweb allowing filename_prefix to not end in "/". We could
+ // then change the is routine to just take one input string.
+ size_t start_of_segment = filename_prefix.find_last_of(dir_separator);
+ if (start_of_segment == string::npos) {
+ segment = filename_prefix;
+ } else {
+ segment = filename_prefix.substr(start_of_segment + 1);
+ *encoded_filename = filename_prefix.substr(0, start_of_segment + 1);
+ }
+
+ size_t index = 0;
+ // Special case the first / to avoid adding a leading kEscapeChar.
+ if (!filename_ending.empty() && (filename_ending[0] == dir_separator)) {
+ encoded_filename->append(segment);
+ segment.clear();
+ encoded_filename->append(1, dir_separator);
+ ++index;
+ }
+
+ for (; index < filename_ending.length(); ++index) {
+ unsigned char ch = static_cast<unsigned char>(filename_ending[index]);
+
+ // Note: instead of outputing an empty segment, we let the second slash
+ // be escaped below.
+ if ((ch == dir_separator) && !segment.empty()) {
+ AppendSegment(&segment, encoded_filename);
+ encoded_filename->append(1, dir_separator);
+ segment.clear();
+ } else {
+ // After removing unsafe chars the only safe ones are _.=+- and alphanums.
+ if ((ch == '_') || (ch == '.') || (ch == '=') || (ch == '+') ||
+ (ch == '-') || (('0' <= ch) && (ch <= '9')) ||
+ (('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z'))) {
+ encoded[0] = ch;
+ encoded_len = 1;
+ } else {
+ encoded[0] = kEscapeChar;
+ encoded[1] = ch / 16;
+ encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0';
+ encoded[2] = ch % 16;
+ encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0';
+ encoded_len = 3;
+ }
+ segment.append(encoded, encoded_len);
+
+ // If segment is too big, we must chop it into chunks.
+ if (segment.size() > kMaximumSubdirectoryLength) {
+ AppendSegment(&segment, encoded_filename);
+ encoded_filename->append(1, dir_separator);
+ }
+ }
+ }
+
+ // Append "," to the leaf filename so the leaf can also be a branch., e.g.
+ // allow http://a/b/c and http://a/b/c/d to co-exist as files "/a/b/c," and
+ // /a/b/c/d". So we will rename the "d" here to "d,". If doing that pushed
+ // us over the 128 char limit, then we will need to append "/" and the
+ // remaining chars.
+ segment += kEscapeChar;
+ AppendSegment(&segment, encoded_filename);
+ if (!segment.empty()) {
+ // The last overflow segment is special, because we appended in
+ // kEscapeChar above. We won't need to check it again for size
+ // or further escaping.
+ encoded_filename->append(1, dir_separator);
+ encoded_filename->append(segment);
+ }
+}
+
+// Note: this decoder is not the exact inverse of the EncodeSegment above,
+// because it does not take into account a prefix.
+bool UrlToFilenameEncoder::Decode(const string& encoded_filename,
+ char dir_separator,
+ string* decoded_url) {
+ enum State {
+ kStart,
+ kEscape,
+ kFirstDigit,
+ kTruncate,
+ kEscapeDot
+ };
+ State state = kStart;
+ char hex_buffer[3];
+ hex_buffer[2] = '\0';
+ for (size_t i = 0; i < encoded_filename.size(); ++i) {
+ char ch = encoded_filename[i];
+ switch (state) {
+ case kStart:
+ if (ch == kEscapeChar) {
+ state = kEscape;
+ } else if (ch == dir_separator) {
+ decoded_url->append(1, '/'); // URLs only use '/' not '\\'
+ } else {
+ decoded_url->append(1, ch);
+ }
+ break;
+ case kEscape:
+ if (HexDigitsPrefix(&ch, 1) == 1) {
+ hex_buffer[0] = ch;
+ state = kFirstDigit;
+ } else if (ch == kTruncationChar) {
+ state = kTruncate;
+ } else if (ch == '.') {
+ decoded_url->append(1, '.');
+ state = kEscapeDot; // Look for at most one more dot.
+ } else if (ch == dir_separator) {
+ // Consider url "//x". This was once encoded to "/,/x,".
+ // This code is what skips the first Escape.
+ decoded_url->append(1, '/'); // URLs only use '/' not '\\'
+ state = kStart;
+ } else {
+ return false;
+ }
+ break;
+ case kFirstDigit:
+ if (HexDigitsPrefix(&ch, 1) == 1) {
+ hex_buffer[1] = ch;
+ uint64 hex_value = ParseLeadingHex64Value(hex_buffer, 0);
+ decoded_url->append(1, static_cast<char>(hex_value));
+ state = kStart;
+ } else {
+ return false;
+ }
+ break;
+ case kTruncate:
+ if (ch == dir_separator) {
+ // Skip this separator, it was only put in to break up long
+ // path segments, but is not part of the URL.
+ state = kStart;
+ } else {
+ return false;
+ }
+ break;
+ case kEscapeDot:
+ decoded_url->append(1, ch);
+ state = kStart;
+ break;
+ }
+ }
+
+ // All legal encoded filenames end in kEscapeChar.
+ return (state == kEscape);
+}
+
+// Escape the given input |path| and chop any individual components
+// of the path which are greater than kMaximumSubdirectoryLength characters
+// into two chunks.
+//
+// This legacy version has several issues with aliasing of different URLs,
+// inability to represent both /a/b/c and /a/b/c/d, and inability to decode
+// the filenames back into URLs.
+//
+// But there is a large body of slurped data which depends on this format,
+// so leave it as the default for spdy_in_mem_edsm_server.
+string UrlToFilenameEncoder::LegacyEscape(const string& path) {
+ string output;
+
+ // Note: We also chop paths into medium sized 'chunks'.
+ // This is due to the incompetence of the windows
+ // filesystem, which still hasn't figured out how
+ // to deal with long filenames.
+ int last_slash = 0;
+ for (size_t index = 0; index < path.length(); index++) {
+ char ch = path[index];
+ if (ch == 0x5C)
+ last_slash = index;
+ if ((ch == 0x2D) || // hyphen
+ (ch == 0x5C) || (ch == 0x5F) || // backslash, underscore
+ ((0x30 <= ch) && (ch <= 0x39)) || // Digits [0-9]
+ ((0x41 <= ch) && (ch <= 0x5A)) || // Uppercase [A-Z]
+ ((0x61 <= ch) && (ch <= 0x7A))) { // Lowercase [a-z]
+ output.append(&path[index], 1);
+ } else {
+ char encoded[3];
+ encoded[0] = 'x';
+ encoded[1] = ch / 16;
+ encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0';
+ encoded[2] = ch % 16;
+ encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0';
+ output.append(encoded, 3);
+ }
+ if (index - last_slash > kMaximumSubdirectoryLength) {
+#ifdef WIN32
+ char slash = '\\';
+#else
+ char slash = '/';
+#endif
+ output.append(&slash, 1);
+ last_slash = index;
+ }
+ }
+ return output;
+}
+
+} // namespace net
diff --git a/src/net/tools/dump_cache/url_to_filename_encoder.h b/src/net/tools/dump_cache/url_to_filename_encoder.h
new file mode 100644
index 0000000..b81a854
--- /dev/null
+++ b/src/net/tools/dump_cache/url_to_filename_encoder.h
@@ -0,0 +1,211 @@
+// 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.
+
+// URL filename encoder goals:
+//
+// 1. Allow URLs with arbitrary path-segment length, generating filenames
+// with a maximum of 128 characters.
+// 2. Provide a somewhat human readable filenames, for easy debugging flow.
+// 3. Provide reverse-mapping from filenames back to URLs.
+// 4. Be able to distinguish http://x from http://x/ from http://x/index.html.
+// Those can all be different URLs.
+// 5. Be able to represent http://a/b/c and http://a/b/c/d, a pattern seen
+// with Facebook Connect.
+//
+// We need an escape-character for representing characters that are legal
+// in URL paths, but not in filenames, such as '?'.
+//
+// We can pick any legal character as an escape, as long as we escape it too.
+// But as we have a goal of having filenames that humans can correlate with
+// URLs, we should pick one that doesn't show up frequently in URLs. Candidates
+// are ~`!@#$%^&()-=_+{}[],. but we would prefer to avoid characters that are
+// shell escapes or that various build tools use.
+//
+// .#&%-=_+ occur frequently in URLs.
+// <>:"/\|?* are illegal in Windows
+// See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+// ~`!$^&(){}[]'; are special to Unix shells
+// In addition, build tools do not like ^@#%
+//
+// Josh took a quick look at the frequency of some special characters in
+// Sadeesh's slurped directory from Fall 09 and found the following occurances:
+//
+// ^ 3 build tool doesn't like ^ in testdata filenames
+// @ 10 build tool doesn't like @ in testdata filenames
+// . 1676 too frequent in URLs
+// , 76 THE WINNER
+// # 0 build tool doesn't like it
+// & 487 Prefer to avoid shell escapes
+// % 374 g4 doesn't like it
+// = 579 very frequent in URLs -- leave unmodified
+// - 464 very frequent in URLs -- leave unmodified
+// _ 798 very frequent in URLs -- leave unmodified
+//
+//
+// The escaping algorithm is:
+// 1) Escape all unfriendly symbols as ,XX where XX is the hex code.
+// 2) Add a ',' at the end (We do not allow ',' at end of any directory name,
+// so this assures that e.g. /a and /a/b can coexist in the filesystem).
+// 3) Go through the path segment by segment (where a segment is one directory
+// or leaf in the path) and
+// 3a) If the segment is empty, escape the second slash. i.e. if it was
+// www.foo.com//a then we escape the second / like www.foo.com/,2Fa,
+// 3a) If it is "." or ".." prepend with ',' (so that we have a non-
+// empty and non-reserved filename).
+// 3b) If it is over 128 characters, break it up into smaller segments by
+// inserting ,-/ (Windows limits paths to 128 chars, other OSes also
+// have limits that would restrict us)
+//
+// For example:
+// URL File
+// / /,
+// /index.html /index.html,
+// /. /.,
+// /a/b /a/b,
+// /a/b/ /a/b/,
+// /a/b/c /a/b/c, Note: no prefix problem
+// /u?foo=bar /u,3Ffoo=bar,
+// // /,2F,
+// /./ /,./,
+// /../ /,../,
+// /, /,2C,
+// /,./ /,2C./,
+// /very...longname/ /very...long,-/name If very...long is about 126 long.
+
+// NOTE: we avoid using some classes here (like FilePath and GURL) because we
+// share this code with other projects externally.
+
+#ifndef NET_TOOLS_DUMP_CACHE_URL_TO_FILENAME_ENCODER_H_
+#define NET_TOOLS_DUMP_CACHE_URL_TO_FILENAME_ENCODER_H_
+
+#include <string>
+
+#include "base/string_util.h"
+#include "net/tools/dump_cache/url_utilities.h"
+
+namespace net {
+
+// Helper class for converting a URL into a filename.
+class UrlToFilenameEncoder {
+ public:
+ // Given a |url| and a |base_path|, returns a filename which represents this
+ // |url|. |url| may include URL escaping such as %21 for !
+ // |legacy_escape| indicates that this function should use the old-style
+ // of encoding.
+ // TODO(mbelshe): delete the legacy_escape code.
+ static std::string Encode(const std::string& url, std::string base_path,
+ bool legacy_escape) {
+ std::string filename;
+ if (!legacy_escape) {
+ std::string url_no_scheme = UrlUtilities::GetUrlHostPath(url);
+ EncodeSegment(base_path, url_no_scheme, '/', &filename);
+#ifdef WIN32
+ ReplaceAll(&filename, "/", "\\");
+#endif
+ } else {
+ std::string clean_url(url);
+ if (clean_url.length() && clean_url[clean_url.length()-1] == '/')
+ clean_url.append("index.html");
+
+ std::string host = UrlUtilities::GetUrlHost(clean_url);
+ filename.append(base_path);
+ filename.append(host);
+#ifdef WIN32
+ filename.append("\\");
+#else
+ filename.append("/");
+#endif
+
+ std::string url_filename = UrlUtilities::GetUrlPath(clean_url);
+ // Strip the leading '/'.
+ if (url_filename[0] == '/')
+ url_filename = url_filename.substr(1);
+
+ // Replace '/' with '\'.
+ ConvertToSlashes(&url_filename);
+
+ // Strip double back-slashes ("\\\\").
+ StripDoubleSlashes(&url_filename);
+
+ // Save path as filesystem-safe characters.
+ url_filename = LegacyEscape(url_filename);
+ filename.append(url_filename);
+
+#ifndef WIN32
+ // Last step - convert to native slashes.
+ const std::string slash("/");
+ const std::string backslash("\\");
+ ReplaceAll(&filename, backslash, slash);
+#endif
+ }
+
+ return filename;
+ }
+
+ // Rewrite HTML in a form that the SPDY in-memory server
+ // can read.
+ // |filename_prefix| is prepended without escaping.
+ // |escaped_ending| is the URL to be encoded into a filename. It may have URL
+ // escaped characters (like %21 for !).
+ // |dir_separator| is "/" on Unix, "\" on Windows.
+ // |encoded_filename| is the resultant filename.
+ static void EncodeSegment(
+ const std::string& filename_prefix,
+ const std::string& escaped_ending,
+ char dir_separator,
+ std::string* encoded_filename);
+
+ // Decodes a filename that was encoded with EncodeSegment,
+ // yielding back the original URL.
+ static bool Decode(const std::string& encoded_filename,
+ char dir_separator,
+ std::string* decoded_url);
+
+ static const char kEscapeChar;
+ static const char kTruncationChar;
+ static const size_t kMaximumSubdirectoryLength;
+
+ friend class UrlToFilenameEncoderTest;
+
+ private:
+ // Appends a segment of the path, special-casing "." and "..", and
+ // ensuring that the segment does not exceed the path length. If it does,
+ // it chops the end off the segment, writes the segment with a separator of
+ // ",-/", and then rewrites segment to contain just the truncated piece so
+ // it can be used in the next iteration.
+ // |segment| is a read/write parameter containing segment to write
+ // Note: this should not be called with empty segment.
+ static void AppendSegment(std::string* segment, std::string* dest);
+
+ // Allow reading of old slurped files.
+ static std::string LegacyEscape(const std::string& path);
+
+ // Replace all instances of |from| within |str| as |to|.
+ static void ReplaceAll(std::string* str, const std::string& from,
+ const std::string& to) {
+ std::string::size_type pos(0);
+ while ((pos = str->find(from, pos)) != std::string::npos) {
+ str->replace(pos, from.size(), to);
+ pos += from.size();
+ }
+ }
+
+ // Replace all instances of "/" with "\" in |path|.
+ static void ConvertToSlashes(std::string* path) {
+ const std::string slash("/");
+ const std::string backslash("\\");
+ ReplaceAll(path, slash, backslash);
+ }
+
+ // Replace all instances of "\\" with "%5C%5C" in |path|.
+ static void StripDoubleSlashes(std::string* path) {
+ const std::string doubleslash("\\\\");
+ const std::string escaped_doubleslash("%5C%5C");
+ ReplaceAll(path, doubleslash, escaped_doubleslash);
+ }
+};
+
+} // namespace net
+
+#endif // NET_TOOLS_DUMP_CACHE_URL_TO_FILENAME_ENCODER_H_
diff --git a/src/net/tools/dump_cache/url_to_filename_encoder_unittest.cc b/src/net/tools/dump_cache/url_to_filename_encoder_unittest.cc
new file mode 100644
index 0000000..2e09e0b
--- /dev/null
+++ b/src/net/tools/dump_cache/url_to_filename_encoder_unittest.cc
@@ -0,0 +1,341 @@
+// 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 "net/tools/dump_cache/url_to_filename_encoder.h"
+
+#include <string>
+#include <vector>
+
+#include "base/string_piece.h"
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::StringPiece;
+using std::string;
+
+namespace net {
+
+#ifdef WIN32
+char kDirSeparator = '\\';
+char kOtherDirSeparator = '/';
+#else
+char kDirSeparator = '/';
+char kOtherDirSeparator = '\\';
+#endif
+
+class UrlToFilenameEncoderTest : public ::testing::Test {
+ protected:
+ UrlToFilenameEncoderTest() : escape_(1, UrlToFilenameEncoder::kEscapeChar),
+ dir_sep_(1, kDirSeparator) {
+ }
+
+ void CheckSegmentLength(const StringPiece& escaped_word) {
+ std::vector<StringPiece> components;
+ Tokenize(escaped_word, StringPiece("/"), &components);
+ for (size_t i = 0; i < components.size(); ++i) {
+ EXPECT_GE(UrlToFilenameEncoder::kMaximumSubdirectoryLength,
+ components[i].size());
+ }
+ }
+
+ void CheckValidChars(const StringPiece& escaped_word, char invalid_slash) {
+ // These characters are invalid in Windows. We add in ', as that's pretty
+ // inconvenient in a Unix filename.
+ //
+ // See http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx
+ const string kInvalidChars = "<>:\"|?*'";
+ for (size_t i = 0; i < escaped_word.size(); ++i) {
+ char c = escaped_word[i];
+ EXPECT_EQ(string::npos, kInvalidChars.find(c));
+ EXPECT_NE(invalid_slash, c);
+ EXPECT_NE('\0', c); // only invalid character in Posix
+ EXPECT_GT(0x7E, c); // only English printable characters
+ }
+ }
+
+ void Validate(const string& in_word, const string& gold_word) {
+ string escaped_word, url;
+ UrlToFilenameEncoder::EncodeSegment("", in_word, '/', &escaped_word);
+ EXPECT_EQ(gold_word, escaped_word);
+ CheckSegmentLength(escaped_word);
+ CheckValidChars(escaped_word, '\\');
+ UrlToFilenameEncoder::Decode(escaped_word, '/', &url);
+ EXPECT_EQ(in_word, url);
+ }
+
+ void ValidateAllSegmentsSmall(const string& in_word) {
+ string escaped_word, url;
+ UrlToFilenameEncoder::EncodeSegment("", in_word, '/', &escaped_word);
+ CheckSegmentLength(escaped_word);
+ CheckValidChars(escaped_word, '\\');
+ UrlToFilenameEncoder::Decode(escaped_word, '/', &url);
+ EXPECT_EQ(in_word, url);
+ }
+
+ void ValidateNoChange(const string& word) {
+ // We always suffix the leaf with kEscapeChar, unless the leaf is empty.
+ Validate(word, word + escape_);
+ }
+
+ void ValidateEscaped(unsigned char ch) {
+ // We always suffix the leaf with kEscapeChar, unless the leaf is empty.
+ char escaped[100];
+ const char escape = UrlToFilenameEncoder::kEscapeChar;
+ base::snprintf(escaped, sizeof(escaped), "%c%02X%c", escape, ch, escape);
+ Validate(string(1, ch), escaped);
+ }
+
+ void ValidateUrl(const string& url, const string& base_path,
+ bool legacy_escape, const string& gold_filename) {
+ string encoded_filename = UrlToFilenameEncoder::Encode(
+ url, base_path, legacy_escape);
+ EXPECT_EQ(gold_filename, encoded_filename);
+ if (!legacy_escape) {
+ CheckSegmentLength(encoded_filename);
+ CheckValidChars(encoded_filename, kOtherDirSeparator);
+ string decoded_url;
+ UrlToFilenameEncoder::Decode(encoded_filename, kDirSeparator,
+ &decoded_url);
+ if (url != decoded_url) {
+ EXPECT_EQ(url, "http://" + decoded_url);
+ }
+ }
+ }
+
+ void ValidateUrlOldNew(const string& url, const string& gold_old_filename,
+ const string& gold_new_filename) {
+ ValidateUrl(url, "", true, gold_old_filename);
+ ValidateUrl(url, "", false, gold_new_filename);
+ }
+
+ void ValidateEncodeSame(const string& url1, const string& url2) {
+ string filename1 = UrlToFilenameEncoder::Encode(url1, "", false);
+ string filename2 = UrlToFilenameEncoder::Encode(url2, "", false);
+ EXPECT_EQ(filename1, filename2);
+ }
+
+ string escape_;
+ string dir_sep_;
+};
+
+TEST_F(UrlToFilenameEncoderTest, DoesNotEscape) {
+ ValidateNoChange("");
+ ValidateNoChange("abcdefg");
+ ValidateNoChange("abcdefghijklmnopqrstuvwxyz");
+ ValidateNoChange("ZYXWVUT");
+ ValidateNoChange("ZYXWVUTSRQPONMLKJIHGFEDCBA");
+ ValidateNoChange("01234567689");
+ ValidateNoChange("_.=+-");
+ ValidateNoChange("abcdefghijklmnopqrstuvwxyzZYXWVUTSRQPONMLKJIHGFEDCBA"
+ "01234567689_.=+-");
+ ValidateNoChange("index.html");
+ ValidateNoChange("/");
+ ValidateNoChange("/.");
+ ValidateNoChange(".");
+ ValidateNoChange("..");
+}
+
+TEST_F(UrlToFilenameEncoderTest, Escapes) {
+ const string bad_chars =
+ "<>:\"\\|?*" // Illegal on Windows
+ "~`!$^&(){}[]';" // Bad for Unix shells
+ "^@" // Build tool doesn't like
+ "#%" // Tool doesn't like
+ ","; // The escape char has to be escaped
+
+ for (size_t i = 0; i < bad_chars.size(); ++i) {
+ ValidateEscaped(bad_chars[i]);
+ }
+
+ // Check non-printable characters.
+ ValidateEscaped('\0');
+ for (size_t i = 127; i < 256; ++i) {
+ ValidateEscaped(static_cast<char>(i));
+ }
+}
+
+TEST_F(UrlToFilenameEncoderTest, DoesEscapeCorrectly) {
+ Validate("mysite.com&x", "mysite.com" + escape_ + "26x" + escape_);
+ Validate("/./", "/" + escape_ + "./" + escape_);
+ Validate("/../", "/" + escape_ + "../" + escape_);
+ Validate("//", "/" + escape_ + "2F" + escape_);
+ Validate("/./leaf", "/" + escape_ + "./leaf" + escape_);
+ Validate("/../leaf", "/" + escape_ + "../leaf" + escape_);
+ Validate("//leaf", "/" + escape_ + "2Fleaf" + escape_);
+ Validate("mysite/u?param1=x¶m2=y",
+ "mysite/u" + escape_ + "3Fparam1=x" + escape_ + "26param2=y" +
+ escape_);
+ Validate("search?q=dogs&go=&form=QBLH&qs=n", // from Latency Labs bing test.
+ "search" + escape_ + "3Fq=dogs" + escape_ + "26go=" + escape_ +
+ "26form=QBLH" + escape_ + "26qs=n" + escape_);
+ Validate("~joebob/my_neeto-website+with_stuff.asp?id=138&content=true",
+ "" + escape_ + "7Ejoebob/my_neeto-website+with_stuff.asp" + escape_ +
+ "3Fid=138" + escape_ + "26content=true" + escape_);
+}
+
+TEST_F(UrlToFilenameEncoderTest, EncodeUrlCorrectly) {
+ ValidateUrlOldNew("http://www.google.com/index.html",
+ "www.google.com" + dir_sep_ + "indexx2Ehtml",
+ "www.google.com" + dir_sep_ + "index.html" + escape_);
+ ValidateUrlOldNew("http://www.google.com/x/search?hl=en&q=dogs&oq=",
+ "www.google.com" + dir_sep_ + "x" + dir_sep_ +
+ "searchx3Fhlx3Denx26qx3Ddogsx26oqx3D",
+
+ "www.google.com" + dir_sep_ + "x" + dir_sep_ + "search" +
+ escape_ + "3Fhl=en" + escape_ + "26q=dogs" + escape_ +
+ "26oq=" + escape_);
+ ValidateUrlOldNew("http://www.foo.com/a//",
+ "www.foo.com" + dir_sep_ + "ax255Cx255Cindexx2Ehtml",
+ "www.foo.com" + dir_sep_ + "a" + dir_sep_ + escape_ + "2F" +
+ escape_);
+
+ // From bug: Double slash preserved.
+ ValidateUrl("http://www.foo.com/u?site=http://www.google.com/index.html",
+ "", false,
+ "www.foo.com" + dir_sep_ + "u" + escape_ + "3Fsite=http" +
+ escape_ + "3A" + dir_sep_ + escape_ + "2Fwww.google.com" +
+ dir_sep_ + "index.html" + escape_);
+ ValidateUrlOldNew(
+ "http://blogutils.net/olct/online.php?"
+ "site=http://thelwordfanfics.blogspot.&interval=600",
+
+ "blogutils.net" + dir_sep_ + "olct" + dir_sep_ + "onlinex2Ephpx3F"
+ "sitex3Dhttpx3Ax255Cx255Cthelwordfanficsx2Eblogspotx2Ex26intervalx3D600",
+
+ "blogutils.net" + dir_sep_ + "olct" + dir_sep_ + "online.php" + escape_ +
+ "3Fsite=http" + escape_ + "3A" + dir_sep_ + escape_ +
+ "2Fthelwordfanfics.blogspot." + escape_ + "26interval=600" + escape_);
+}
+
+// From bug: Escapes treated the same as normal char.
+TEST_F(UrlToFilenameEncoderTest, UnescapeUrlsBeforeEncode) {
+ for (int i = 0; i < 128; ++i) {
+ string unescaped(1, static_cast<char>(i));
+ string escaped = base::StringPrintf("%%%02X", i);
+ ValidateEncodeSame(unescaped, escaped);
+ }
+
+ ValidateEncodeSame(
+ "http://www.blogger.com/navbar.g?bName=God!&Mode=FOO&searchRoot"
+ "=http%3A%2F%2Fsurvivorscanthrive.blogspot.com%2Fsearch",
+
+ "http://www.blogger.com/navbar.g?bName=God%21&Mode=FOO&searchRoot"
+ "=http%3A%2F%2Fsurvivorscanthrive.blogspot.com%2Fsearch");
+}
+
+// From bug: Filename encoding is not prefix-free.
+TEST_F(UrlToFilenameEncoderTest, EscapeSecondSlash) {
+ Validate("/", "/" + escape_);
+ Validate("//", "/" + escape_ + "2F" + escape_);
+ Validate("///", "/" + escape_ + "2F" + "/" + escape_);
+}
+
+TEST_F(UrlToFilenameEncoderTest, LongTail) {
+ static char long_word[] =
+ "~joebob/briggs/12345678901234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890123456789012345678901234567890"
+ "1234567890123456789012345678901234567890123456789012345678901234567890";
+
+ // the long lines in the string below are 64 characters, so we can see
+ // the slashes every 128.
+ string gold_long_word =
+ escape_ + "7Ejoebob/briggs/"
+ "1234567890123456789012345678901234567890123456789012345678901234"
+ "56789012345678901234567890123456789012345678901234567890123456" +
+ escape_ + "-/"
+ "7890123456789012345678901234567890123456789012345678901234567890"
+ "12345678901234567890123456789012345678901234567890123456789012" +
+ escape_ + "-/"
+ "3456789012345678901234567890123456789012345678901234567890123456"
+ "78901234567890123456789012345678901234567890123456789012345678" +
+ escape_ + "-/"
+ "9012345678901234567890" + escape_;
+ EXPECT_LT(UrlToFilenameEncoder::kMaximumSubdirectoryLength,
+ sizeof(long_word));
+ Validate(long_word, gold_long_word);
+}
+
+TEST_F(UrlToFilenameEncoderTest, LongTailQuestion) {
+ // Here the '?' in the last path segment expands to @3F, making
+ // it hit 128 chars before the input segment gets that big.
+ static char long_word[] =
+ "~joebob/briggs/1234567?1234567?1234567?1234567?1234567?"
+ "1234567?1234567?1234567?1234567?1234567?1234567?1234567?"
+ "1234567?1234567?1234567?1234567?1234567?1234567?1234567?"
+ "1234567?1234567?1234567?1234567?1234567?1234567?1234567?"
+ "1234567?1234567?1234567?1234567?1234567?1234567?1234567?"
+ "1234567?1234567?1234567?1234567?1234567?1234567?1234567?";
+
+ // Notice that at the end of the third segment, we avoid splitting
+ // the (escape_ + "3F") that was generated from the "?", so that segment is
+ // only 127 characters.
+ string pattern = "1234567" + escape_ + "3F"; // 10 characters
+ string gold_long_word =
+ escape_ + "7Ejoebob/briggs/" +
+ pattern + pattern + pattern + pattern + pattern + pattern + "1234"
+ "567" + escape_ + "3F" + pattern + pattern + pattern + pattern + pattern +
+ "123456" + escape_ + "-/"
+ "7" + escape_ + "3F" + pattern + pattern + pattern + pattern + pattern +
+ pattern + pattern + pattern + pattern + pattern + pattern + pattern +
+ "12" +
+ escape_ + "-/"
+ "34567" + escape_ + "3F" + pattern + pattern + pattern + pattern + pattern
+ + "1234567" + escape_ + "3F" + pattern + pattern + pattern + pattern
+ + pattern + "1234567" +
+ escape_ + "-/" +
+ escape_ + "3F" + pattern + pattern + escape_;
+ EXPECT_LT(UrlToFilenameEncoder::kMaximumSubdirectoryLength,
+ sizeof(long_word));
+ Validate(long_word, gold_long_word);
+}
+
+TEST_F(UrlToFilenameEncoderTest, CornerCasesNearMaxLenNoEscape) {
+ // hit corner cases, +/- 4 characters from kMaxLen
+ for (int i = -4; i <= 4; ++i) {
+ string input;
+ input.append(i + UrlToFilenameEncoder::kMaximumSubdirectoryLength, 'x');
+ ValidateAllSegmentsSmall(input);
+ }
+}
+
+TEST_F(UrlToFilenameEncoderTest, CornerCasesNearMaxLenWithEscape) {
+ // hit corner cases, +/- 4 characters from kMaxLen. This time we
+ // leave off the last 'x' and put in a '.', which ensures that we
+ // are truncating with '/' *after* the expansion.
+ for (int i = -4; i <= 4; ++i) {
+ string input;
+ input.append(i + UrlToFilenameEncoder::kMaximumSubdirectoryLength - 1, 'x');
+ input.append(1, '.'); // this will expand to 3 characters.
+ ValidateAllSegmentsSmall(input);
+ }
+}
+
+TEST_F(UrlToFilenameEncoderTest, LeafBranchAlias) {
+ Validate("/a/b/c", "/a/b/c" + escape_); // c is leaf file "c,"
+ Validate("/a/b/c/d", "/a/b/c/d" + escape_); // c is directory "c"
+ Validate("/a/b/c/d/", "/a/b/c/d/" + escape_);
+}
+
+
+TEST_F(UrlToFilenameEncoderTest, BackslashSeparator) {
+ string long_word;
+ string escaped_word;
+ long_word.append(UrlToFilenameEncoder::kMaximumSubdirectoryLength + 1, 'x');
+ UrlToFilenameEncoder::EncodeSegment("", long_word, '\\', &escaped_word);
+
+ // check that one backslash, plus the escape ",-", and the ending , got added.
+ EXPECT_EQ(long_word.size() + 4, escaped_word.size());
+ ASSERT_LT(UrlToFilenameEncoder::kMaximumSubdirectoryLength,
+ escaped_word.size());
+ // Check that the backslash got inserted at the correct spot.
+ EXPECT_EQ('\\', escaped_word[
+ UrlToFilenameEncoder::kMaximumSubdirectoryLength]);
+}
+
+} // namespace net
+
diff --git a/src/net/tools/dump_cache/url_utilities.cc b/src/net/tools/dump_cache/url_utilities.cc
new file mode 100644
index 0000000..fe64bd9
--- /dev/null
+++ b/src/net/tools/dump_cache/url_utilities.cc
@@ -0,0 +1,126 @@
+// 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 "net/tools/dump_cache/url_utilities.h"
+
+#include "base/logging.h"
+#include "base/string_number_conversions.h"
+#include "base/string_util.h"
+
+namespace net {
+
+std::string UrlUtilities::GetUrlHost(const std::string& url) {
+ size_t b = url.find("//");
+ if (b == std::string::npos)
+ b = 0;
+ else
+ b += 2;
+ size_t next_slash = url.find_first_of('/', b);
+ size_t next_colon = url.find_first_of(':', b);
+ if (next_slash != std::string::npos
+ && next_colon != std::string::npos
+ && next_colon < next_slash) {
+ return std::string(url, b, next_colon - b);
+ }
+ if (next_slash == std::string::npos) {
+ if (next_colon != std::string::npos) {
+ return std::string(url, b, next_colon - b);
+ } else {
+ next_slash = url.size();
+ }
+ }
+ return std::string(url, b, next_slash - b);
+}
+
+std::string UrlUtilities::GetUrlHostPath(const std::string& url) {
+ size_t b = url.find("//");
+ if (b == std::string::npos)
+ b = 0;
+ else
+ b += 2;
+ return std::string(url, b);
+}
+
+std::string UrlUtilities::GetUrlPath(const std::string& url) {
+ size_t b = url.find("//");
+ if (b == std::string::npos)
+ b = 0;
+ else
+ b += 2;
+ b = url.find("/", b);
+ if (b == std::string::npos)
+ return "/";
+
+ size_t e = url.find("#", b+1);
+ if (e != std::string::npos)
+ return std::string(url, b, (e - b));
+ return std::string(url, b);
+}
+
+namespace {
+
+// Parsing states for UrlUtilities::Unescape
+enum UnescapeState {
+ NORMAL, // We are not in the middle of parsing an escape.
+ ESCAPE1, // We just parsed % .
+ ESCAPE2 // We just parsed %X for some hex digit X.
+};
+
+} // namespace
+
+std::string UrlUtilities::Unescape(const std::string& escaped_url) {
+ std::string unescaped_url, escape_text;
+ int escape_value;
+ UnescapeState state = NORMAL;
+ std::string::const_iterator iter = escaped_url.begin();
+ while (iter < escaped_url.end()) {
+ char c = *iter;
+ switch (state) {
+ case NORMAL:
+ if (c == '%') {
+ escape_text.clear();
+ state = ESCAPE1;
+ } else {
+ unescaped_url.push_back(c);
+ }
+ ++iter;
+ break;
+ case ESCAPE1:
+ if (IsHexDigit(c)) {
+ escape_text.push_back(c);
+ state = ESCAPE2;
+ ++iter;
+ } else {
+ // Unexpected, % followed by non-hex chars, pass it through.
+ unescaped_url.push_back('%');
+ state = NORMAL;
+ }
+ break;
+ case ESCAPE2:
+ if (IsHexDigit(c)) {
+ escape_text.push_back(c);
+ bool ok = base::HexStringToInt(escape_text, &escape_value);
+ DCHECK(ok);
+ unescaped_url.push_back(static_cast<unsigned char>(escape_value));
+ state = NORMAL;
+ ++iter;
+ } else {
+ // Unexpected, % followed by non-hex chars, pass it through.
+ unescaped_url.push_back('%');
+ unescaped_url.append(escape_text);
+ state = NORMAL;
+ }
+ break;
+ }
+ }
+ // Unexpected, % followed by end of string, pass it through.
+ if (state == ESCAPE1 || state == ESCAPE2) {
+ unescaped_url.push_back('%');
+ unescaped_url.append(escape_text);
+ }
+ return unescaped_url;
+}
+
+} // namespace net
+
diff --git a/src/net/tools/dump_cache/url_utilities.h b/src/net/tools/dump_cache/url_utilities.h
new file mode 100644
index 0000000..c9d8ea5
--- /dev/null
+++ b/src/net/tools/dump_cache/url_utilities.h
@@ -0,0 +1,35 @@
+// 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.
+
+#ifndef NET_TOOLS_DUMP_CACHE_URL_UTILITIES_H_
+#define NET_TOOLS_DUMP_CACHE_URL_UTILITIES_H_
+
+#include <string>
+
+namespace net {
+
+struct UrlUtilities {
+ // Gets the host from an url, strips the port number as well if the url
+ // has one.
+ // For example: calling GetUrlHost(www.foo.com:8080/boo) returns www.foo.com
+ static std::string GetUrlHost(const std::string& url);
+
+ // Get the host + path portion of an url
+ // e.g http://www.foo.com/path
+ // returns www.foo.com/path
+ static std::string GetUrlHostPath(const std::string& url);
+
+ // Gets the path portion of an url.
+ // e.g http://www.foo.com/path
+ // returns /path
+ static std::string GetUrlPath(const std::string& url);
+
+ // Unescape a url, converting all %XX to the the actual char 0xXX.
+ // For example, this will convert "foo%21bar" to "foo!bar".
+ static std::string Unescape(const std::string& escaped_url);
+};
+
+} // namespace net
+
+#endif // NET_TOOLS_DUMP_CACHE_URL_UTILITIES_H_
diff --git a/src/net/tools/dump_cache/url_utilities_unittest.cc b/src/net/tools/dump_cache/url_utilities_unittest.cc
new file mode 100644
index 0000000..0f9cb06
--- /dev/null
+++ b/src/net/tools/dump_cache/url_utilities_unittest.cc
@@ -0,0 +1,114 @@
+// 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 "net/tools/dump_cache/url_utilities.h"
+
+#include <string>
+
+#include "base/string_util.h"
+#include "base/stringprintf.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace net {
+
+TEST(UrlUtilitiesTest, GetUrlHost) {
+ EXPECT_EQ("www.foo.com",
+ UrlUtilities::GetUrlHost("http://www.foo.com"));
+ EXPECT_EQ("www.foo.com",
+ UrlUtilities::GetUrlHost("http://www.foo.com:80"));
+ EXPECT_EQ("www.foo.com",
+ UrlUtilities::GetUrlHost("http://www.foo.com:80/"));
+ EXPECT_EQ("www.foo.com",
+ UrlUtilities::GetUrlHost("http://www.foo.com/news"));
+ EXPECT_EQ("www.foo.com",
+ UrlUtilities::GetUrlHost("www.foo.com:80/news?q=hello"));
+ EXPECT_EQ("www.foo.com",
+ UrlUtilities::GetUrlHost("www.foo.com/news?q=a:b"));
+ EXPECT_EQ("www.foo.com",
+ UrlUtilities::GetUrlHost("www.foo.com:80"));
+}
+
+TEST(UrlUtilitiesTest, GetUrlHostPath) {
+ EXPECT_EQ("www.foo.com",
+ UrlUtilities::GetUrlHostPath("http://www.foo.com"));
+ EXPECT_EQ("www.foo.com:80",
+ UrlUtilities::GetUrlHostPath("http://www.foo.com:80"));
+ EXPECT_EQ("www.foo.com:80/",
+ UrlUtilities::GetUrlHostPath("http://www.foo.com:80/"));
+ EXPECT_EQ("www.foo.com/news",
+ UrlUtilities::GetUrlHostPath("http://www.foo.com/news"));
+ EXPECT_EQ("www.foo.com:80/news?q=hello",
+ UrlUtilities::GetUrlHostPath("www.foo.com:80/news?q=hello"));
+ EXPECT_EQ("www.foo.com/news?q=a:b",
+ UrlUtilities::GetUrlHostPath("www.foo.com/news?q=a:b"));
+ EXPECT_EQ("www.foo.com:80",
+ UrlUtilities::GetUrlHostPath("www.foo.com:80"));
+}
+
+TEST(UrlUtilitiesTest, GetUrlPath) {
+ EXPECT_EQ("/",
+ UrlUtilities::GetUrlPath("http://www.foo.com"));
+ EXPECT_EQ("/",
+ UrlUtilities::GetUrlPath("http://www.foo.com:80"));
+ EXPECT_EQ("/",
+ UrlUtilities::GetUrlPath("http://www.foo.com:80/"));
+ EXPECT_EQ("/news",
+ UrlUtilities::GetUrlPath("http://www.foo.com/news"));
+ EXPECT_EQ("/news?q=hello",
+ UrlUtilities::GetUrlPath("www.foo.com:80/news?q=hello"));
+ EXPECT_EQ("/news?q=a:b",
+ UrlUtilities::GetUrlPath("www.foo.com/news?q=a:b"));
+ EXPECT_EQ("/",
+ UrlUtilities::GetUrlPath("www.foo.com:80"));
+}
+
+TEST(UrlUtilitiesTest, Unescape) {
+ // Basic examples are left alone.
+ EXPECT_EQ("http://www.foo.com",
+ UrlUtilities::Unescape("http://www.foo.com"));
+ EXPECT_EQ("www.foo.com:80/news?q=hello",
+ UrlUtilities::Unescape("www.foo.com:80/news?q=hello"));
+
+ // All chars can be unescaped.
+ EXPECT_EQ("~`!@#$%^&*()_-+={[}]|\\:;\"'<,>.?/",
+ UrlUtilities::Unescape("%7E%60%21%40%23%24%25%5E%26%2A%28%29%5F%2D"
+ "%2B%3D%7B%5B%7D%5D%7C%5C%3A%3B%22%27%3C%2C"
+ "%3E%2E%3F%2F"));
+ for (int c = 0; c < 256; ++c) {
+ std::string unescaped_char(1, implicit_cast<unsigned char>(c));
+ std::string escaped_char = base::StringPrintf("%%%02X", c);
+ EXPECT_EQ(unescaped_char, UrlUtilities::Unescape(escaped_char))
+ << "escaped_char = " << escaped_char;
+ escaped_char = base::StringPrintf("%%%02x", c);
+ EXPECT_EQ(unescaped_char, UrlUtilities::Unescape(escaped_char))
+ << "escaped_char = " << escaped_char;
+ }
+
+ // All non-% chars are left alone.
+ EXPECT_EQ("~`!@#$^&*()_-+={[}]|\\:;\"'<,>.?/",
+ UrlUtilities::Unescape("~`!@#$^&*()_-+={[}]|\\:;\"'<,>.?/"));
+ for (int c = 0; c < 256; ++c) {
+ if (c != '%') {
+ std::string just_char(1, implicit_cast<unsigned char>(c));
+ EXPECT_EQ(just_char, UrlUtilities::Unescape(just_char));
+ }
+ }
+
+ // Some examples to unescape.
+ EXPECT_EQ("Hello, world!", UrlUtilities::Unescape("Hello%2C world%21"));
+
+ // Not actually escapes.
+ EXPECT_EQ("%", UrlUtilities::Unescape("%"));
+ EXPECT_EQ("%www", UrlUtilities::Unescape("%www"));
+ EXPECT_EQ("%foo", UrlUtilities::Unescape("%foo"));
+ EXPECT_EQ("%1", UrlUtilities::Unescape("%1"));
+ EXPECT_EQ("%1x", UrlUtilities::Unescape("%1x"));
+ EXPECT_EQ("%%", UrlUtilities::Unescape("%%"));
+ // Escapes following non-escapes.
+ EXPECT_EQ("%!", UrlUtilities::Unescape("%%21"));
+ EXPECT_EQ("%2!", UrlUtilities::Unescape("%2%21"));
+}
+
+} // namespace net
+
diff --git a/src/starboard/linux/x64directfb/xephyr_run.sh b/src/starboard/linux/x64directfb/xephyr_run.sh
index dc3181c..77bf969 100755
--- a/src/starboard/linux/x64directfb/xephyr_run.sh
+++ b/src/starboard/linux/x64directfb/xephyr_run.sh
@@ -13,44 +13,123 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-# This script can be used as a convenience to launch an app under a Xephyr
-# X server. It will first launch Xephyr, and then launch the executable given
-# on the command line such that it targets and will appear within the Xephyr
-# window. It shuts down Xephyr after the main executable finishes.
+# This script can be used as a convenience to launch an app under a new
+# X server. Xephyr requires a base X server, so will be launched if there is a
+# DISPLAY environment variable set. Xvfb does not, so it will be launched if
+# there is no DISPLAY variable set, which is likely to come from a remote shell
+# or background process. It will first launch the X server, and then launch the
+# executable given on the command line such that it targets the newly-launched
+# X server. It shuts down the X server after the main executable finishes.
-if [[ $# = 0 ]]; then
- echo
- echo "$0 will launch a given executable file under a Xephyr X server."
- echo
- echo "Usage:"
- echo " $0 PATH_TO_EXECUTABLE_FILE "
- echo
- exit 0
+# Standard stuff to get the real script name.
+script_file="$(readlink -f "${BASH_SOURCE[0]}")"
+script_name="$(basename "${script_file}")"
+
+if [[ -n "$DISPLAY" ]]; then
+ # The binary to run to start the X Server.
+ xserver_bin=Xephyr
+ # The command-line args to set the screen dimensions.
+ declare -a xserver_screen=(-screen 1920x1080)
+ # The package to install for this X Server.
+ xserver_package=xserver-xephyr
+else
+ xserver_bin=Xvfb
+ declare -a xserver_screen=(-screen 0 1920x1080x24)
+ xserver_package=xvfb
fi
-if ! hash Xephyr 2>/dev/null ; then
- echo
- echo "Xephyr is not installed. Please run:"
- echo " sudo apt-get install xserver-xephyr"
- echo
- exit 0
-fi
+# Use display 42 as it is unlikely to be used by another process on this host.
+# TODO: Scan displays for a free one.
+xserver_display=":42"
-# Open up a Xephyr display.
-Xephyr -screen 1920x1080 :10 2> /dev/null &
-xephyr_pid=$!
+function log() {
+ echo "${script_name}: $@"
+}
-# Setup a trap to clean up Xephyr upon termination of this script
-function kill1() {
+function deleteTempTrap() {
+ # If something interrupted the script, it might have printed a partial line.
+ echo
+ deleteDirectory "${temp_dir}"
+}
+
+function killServerTrap() {
+ echo
# Kill the Xephyr process.
- kill $xephyr_pid 2> /dev/null
+ kill "${xserver_pid}" &> /dev/null
# Waits for the kill to finish.
wait
+ log "${xserver_bin} (pid ${xserver_pid}) terminated."
+ deleteDirectory "${temp_dir}"
}
-trap kill1 EXIT
-# Give Xephyr some time to setup.
-sleep 0.5
+function deleteDirectory() {
+ if [[ -z "$1" ]]; then
+ log "deleteDirectory with no argument"
+ exit 1
+ fi
-# Launch the executable passed as a parameter on the Xephyr display.
-DISPLAY=:10 $@
\ No newline at end of file
+ # Only delete target if it is an existing directory.
+ if [[ -d "$1" ]]; then
+ rm -rf "$1"
+ fi
+}
+
+function main() {
+ if [[ "$#" = "0" ]]; then
+ echo
+ echo "${script_name}: Launches a given executable file under a ${xserver_bin} X server."
+ echo
+ echo "Usage:"
+ echo " ${script_name} PATH_TO_EXECUTABLE_FILE [ARGS...] "
+ echo
+ exit 1
+ fi
+
+ if ! hash "${xserver_bin}" 2>/dev/null ; then
+ log "${xserver_bin} is not installed. Please run:"
+ log " sudo apt-get install ${xserver_package}"
+ exit 1
+ fi
+
+ # Create an auth file that will allow the current user to access the display.
+ temp_dir="$(mktemp -dt "${script_name}.XXXXXXXXXX")"
+
+ # Delete the temporary directory on exit.
+ trap deleteTempTrap EXIT
+
+ # In this case, we don't want to try to tunnel authority through to remote
+ # connections, we want to use this X server with the client. So we create
+ # our own auth and forcibly pass it through both sides of the client and
+ # server. Apologies for the X11 magic here.
+ xserver_auth="${temp_dir}/XAuthority"
+ touch "${xserver_auth}"
+ token="$(dd if=/dev/urandom count=1 2> /dev/null | md5sum | cut -f1 -d' ')"
+ xauth -qf "${xserver_auth}" add "${xserver_display}" . "${token}"
+
+ xserver_log="${temp_dir}/${xserver_bin}.txt"
+ # Launch an X Server in the background at a new display.
+ "${xserver_bin}" "${xserver_display}" \
+ -auth "${xserver_auth}" \
+ "${xserver_screen[@]}" \
+ &> "${xserver_log}" &
+ xserver_pid=$!
+ log "${xserver_bin} (pid ${xserver_pid}) running on ${xserver_display}."
+
+ # Setup a trap to clean up Xephyr upon termination of this script
+ trap killServerTrap EXIT
+
+ # Give Xephyr some time to setup.
+ sleep 0.5
+
+ # Launch the executable passed as a parameter on the new display.
+ DISPLAY="${xserver_display}" XAUTHORITY="${xserver_auth}" "$@"
+ result=$?
+ if [[ "${result}" != "0" ]]; then
+ log "$1 result: ${result}"
+ log "--- ${xserver_bin} log ---------------------------------------"
+ cat "${xserver_log}"
+ fi
+ return ${result}
+}
+
+main "$@"
diff --git a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
index 6c7aec8..5bf56cd 100644
--- a/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
+++ b/src/starboard/shared/ffmpeg/ffmpeg_video_decoder.cc
@@ -150,6 +150,8 @@
SbThreadJoin(decoder_thread_, NULL);
}
+ avcodec_flush_buffers(codec_context_);
+
decoder_thread_ = kSbThreadInvalid;
stream_ended_ = false;
}
diff --git a/src/starboard/shared/starboard/player/player_worker.cc b/src/starboard/shared/starboard/player/player_worker.cc
index 5bab32b..ddb425f 100644
--- a/src/starboard/shared/starboard/player/player_worker.cc
+++ b/src/starboard/shared/starboard/player/player_worker.cc
@@ -16,6 +16,7 @@
#include <algorithm>
+#include "starboard/memory.h"
#include "starboard/shared/starboard/application.h"
#include "starboard/shared/starboard/drm/drm_system_internal.h"
#include "starboard/shared/starboard/player/audio_decoder_internal.h"
@@ -113,8 +114,11 @@
} else if (event->type == Event::kSetPause) {
running &= ProcessSetPauseEvent(event->data.set_pause);
} else if (event->type == Event::kSetBounds) {
- bounds = event->data.set_bounds;
- ProcessUpdateEvent(bounds);
+ if (SbMemoryCompare(&bounds, &event->data.set_bounds, sizeof(bounds)) !=
+ 0) {
+ bounds = event->data.set_bounds;
+ ProcessUpdateEvent(bounds);
+ }
} else if (event->type == Event::kStop) {
ProcessStopEvent();
running = false;
diff --git a/src/starboard/shared/starboard/player/video_frame_internal.cc b/src/starboard/shared/starboard/player/video_frame_internal.cc
index f13dc3b..d1a0099 100644
--- a/src/starboard/shared/starboard/player/video_frame_internal.cc
+++ b/src/starboard/shared/starboard/player/video_frame_internal.cc
@@ -30,7 +30,7 @@
int s_u_to_g[256];
int s_v_to_g[256];
int s_u_to_b[256];
-uint8_t s_clamp_table[256 * 3];
+uint8_t s_clamp_table[256 * 5];
void EnsureYUVToRGBLookupTableInitialized() {
if (s_yuv_to_rgb_lookup_table_initialized) {
@@ -47,10 +47,13 @@
//
// We optimize the conversion algorithm by creating two kinds of lookup
// tables. The color component table contains pre-calculated color component
- // values. The clamp table contains a map between |v| + 256 to the clamped
+ // values. The clamp table contains a map between |v| + 512 to the clamped
// |v| to avoid conditional operation.
- SbMemorySet(s_clamp_table, 0, 256);
- SbMemorySet(s_clamp_table + 512, 0xff, 256);
+ // The minimum value of |v| can be 2.112f * (-128) = -271, the maximum value
+ // of |v| can be 1.164f * 255 + 2.112f * 127 = 565. So we need 512 bytes at
+ // each side of the clamp buffer.
+ SbMemorySet(s_clamp_table, 0, 512);
+ SbMemorySet(s_clamp_table + 768, 0xff, 512);
for (int i = 0; i < 256; ++i) {
s_y_to_rgb[i] = (static_cast<uint8_t>(i) - 16) * 1.164f;
@@ -58,14 +61,14 @@
s_u_to_g[i] = (static_cast<uint8_t>(i) - 128) * -0.213;
s_v_to_g[i] = (static_cast<uint8_t>(i) - 128) * -0.533f;
s_u_to_b[i] = (static_cast<uint8_t>(i) - 128) * 2.112f;
- s_clamp_table[256 + i] = i;
+ s_clamp_table[512 + i] = i;
}
s_yuv_to_rgb_lookup_table_initialized = true;
}
uint8_t ClampColorComponent(int component) {
- return s_clamp_table[component + 256];
+ return s_clamp_table[component + 512];
}
} // namespace
diff --git a/src/starboard/shared/starboard/player/video_renderer_internal.cc b/src/starboard/shared/starboard/player/video_renderer_internal.cc
index da9ccf8..d3faa9f 100644
--- a/src/starboard/shared/starboard/player/video_renderer_internal.cc
+++ b/src/starboard/shared/starboard/player/video_renderer_internal.cc
@@ -70,6 +70,9 @@
seeking_ = true;
end_of_stream_written_ = false;
+ if (!frames_.empty()) {
+ seeking_frame_ = frames_.front();
+ }
frames_.clear();
}
@@ -77,8 +80,7 @@
ScopedLock lock(mutex_);
if (frames_.empty()) {
- // We cannot paint anything if there is no frames.
- return empty_frame_;
+ return seeking_frame_;
}
// Remove any frames with timestamps earlier than |media_time|, but always
// keep at least one of the frames.
@@ -122,7 +124,7 @@
frames_.push_back(*frame);
}
- if (seeking_ && frames_.size() > kPrerollFrames) {
+ if (seeking_ && frames_.size() >= kPrerollFrames) {
seeking_ = false;
}
}
diff --git a/src/starboard/shared/starboard/player/video_renderer_internal.h b/src/starboard/shared/starboard/player/video_renderer_internal.h
index f62cfda..0cf815a 100644
--- a/src/starboard/shared/starboard/player/video_renderer_internal.h
+++ b/src/starboard/shared/starboard/player/video_renderer_internal.h
@@ -70,7 +70,11 @@
bool seeking_;
Frames frames_;
- VideoFrame empty_frame_;
+ // During seeking, all frames inside |frames_| will be cleared but the app
+ // should still display the last frame it is rendering. This frame will be
+ // kept inside |seeking_frame_|. It is an empty/black frame before the video
+ // is started.
+ VideoFrame seeking_frame_;
SbMediaTime seeking_to_pts_;
bool end_of_stream_written_;
diff --git a/src/starboard/shared/x11/application_x11.cc b/src/starboard/shared/x11/application_x11.cc
index 3499bd8..bfde70b 100644
--- a/src/starboard/shared/x11/application_x11.cc
+++ b/src/starboard/shared/x11/application_x11.cc
@@ -587,6 +587,7 @@
// XLib is not thread-safe. Since we may be coming from another thread, we
// have to open another connection to the display to inject the wake-up event.
Display* display = XOpenDisplay(NULL);
+ SB_DCHECK(display);
XClientMessageEvent event = {0};
event.type = ClientMessage;
event.message_type = atom;
@@ -640,7 +641,9 @@
}
SbWindow ApplicationX11::CreateWindow(const SbWindowOptions* options) {
- EnsureX();
+ if (!EnsureX()) {
+ return kSbWindowInvalid;
+ }
SbWindow window = new SbWindowPrivate(display_, options);
windows_.push_back(window);
@@ -787,17 +790,25 @@
XSendAtom((*windows_.begin())->window, wake_up_atom_);
}
-void ApplicationX11::EnsureX() {
+bool ApplicationX11::EnsureX() {
// TODO: Consider thread-safety.
if (display_) {
- return;
+ return true;
}
XInitThreads();
XSetIOErrorHandler(IOErrorHandler);
XSetErrorHandler(ErrorHandler);
display_ = XOpenDisplay(NULL);
- SB_DCHECK(display_);
+ if (!display_) {
+ const char *display_environment = getenv("DISPLAY");
+ if (display_environment == NULL) {
+ SB_LOG(ERROR) << "Unable to open display, DISPLAY not set.";
+ } else {
+ SB_LOG(ERROR) << "Unable to open display " << display_environment << ".";
+ }
+ return false;
+ }
// Disable keyup events on auto-repeat to match Windows.
// Otherwise when holding down a key, we get a keyup event before
@@ -812,6 +823,7 @@
#if SB_IS(PLAYER_PUNCHED_OUT)
Composite();
#endif // SB_IS(PLAYER_PUNCHED_OUT)
+ return true;
}
void ApplicationX11::StopX() {
diff --git a/src/starboard/shared/x11/application_x11.h b/src/starboard/shared/x11/application_x11.h
index 4be2cab..11b4aca 100644
--- a/src/starboard/shared/x11/application_x11.h
+++ b/src/starboard/shared/x11/application_x11.h
@@ -80,8 +80,9 @@
};
#endif // SB_IS(PLAYER_PUNCHED_OUT)
- // Ensures that X is up, display is populated and connected.
- void EnsureX();
+ // Ensures that X is up, display is populated and connected, returning whether
+ // it succeeded.
+ bool EnsureX();
// Shuts X down.
void StopX();
diff --git a/src/third_party/mozjs/js/src/js.msg b/src/third_party/mozjs/js/src/js.msg
index 4047035..ff04140 100644
--- a/src/third_party/mozjs/js/src/js.msg
+++ b/src/third_party/mozjs/js/src/js.msg
@@ -297,7 +297,11 @@
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX, 244, 0, JSEXN_ERR, "invalid or out-of-range index")
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, 245, 1, JSEXN_ERR, "argument {0} must be >= 0")
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS, 246, 0, JSEXN_ERR, "invalid arguments")
+#if defined(STARBOARD)
+MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_EVALERR, "call to Function() blocked by CSP")
+#else
MSG_DEF(JSMSG_CSP_BLOCKED_FUNCTION, 247, 0, JSEXN_ERR, "call to Function() blocked by CSP")
+#endif
MSG_DEF(JSMSG_BAD_GET_SET_FIELD, 248, 1, JSEXN_TYPEERR, "property descriptor's {0} field is neither undefined nor a function")
MSG_DEF(JSMSG_BAD_PROXY_FIX, 249, 0, JSEXN_TYPEERR, "proxy was fixed while executing the handler")
MSG_DEF(JSMSG_INVALID_EVAL_SCOPE_ARG, 250, 0, JSEXN_EVALERR, "invalid eval scope argument")
@@ -338,7 +342,11 @@
MSG_DEF(JSMSG_DEBUG_COMPARTMENT_MISMATCH, 285, 2, JSEXN_TYPEERR, "{0}: descriptor .{1} property is an object in a different compartment than the target object")
MSG_DEF(JSMSG_DEBUG_NOT_SCRIPT_FRAME, 286, 0, JSEXN_ERR, "stack frame is not running JavaScript code")
MSG_DEF(JSMSG_CANT_WATCH_PROP, 287, 0, JSEXN_TYPEERR, "properties whose names are objects can't be watched")
+#if defined(STARBOARD)
+MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 288, 0, JSEXN_EVALERR, "call to eval() blocked by CSP")
+#else
MSG_DEF(JSMSG_CSP_BLOCKED_EVAL, 288, 0, JSEXN_ERR, "call to eval() blocked by CSP")
+#endif
MSG_DEF(JSMSG_DEBUG_NO_SCOPE_OBJECT, 289, 0, JSEXN_TYPEERR, "declarative Environments don't have binding objects")
MSG_DEF(JSMSG_EMPTY_CONSEQUENT, 290, 0, JSEXN_SYNTAXERR, "mistyped ; after conditional?")
MSG_DEF(JSMSG_NOT_ITERABLE, 291, 1, JSEXN_TYPEERR, "{0} is not iterable")
diff --git a/src/third_party/mozjs/js/src/jsapi.cpp b/src/third_party/mozjs/js/src/jsapi.cpp
index 1fae091..33b7991 100644
--- a/src/third_party/mozjs/js/src/jsapi.cpp
+++ b/src/third_party/mozjs/js/src/jsapi.cpp
@@ -93,6 +93,8 @@
#include "jit/Ion.h"
#endif
+#include "starboard/file.h"
+
using namespace js;
using namespace js::gc;
using namespace js::types;
@@ -5119,6 +5121,27 @@
return script;
}
+#if defined(STARBOARD)
+JSScript *
+JS::Compile(JSContext *cx, HandleObject obj, CompileOptions options,
+ SbFile file)
+{
+ SbFileInfo info;
+ bool success = SbFileGetInfo(file, &info);
+ if (!success) {
+ return NULL;
+ }
+ const int64_t kFileSize = info.size;
+ FileContents buffer(cx);
+ buffer.resize(kFileSize);
+ if (SbFileRead(file, buffer.begin(), kFileSize) < 0) {
+ return NULL;
+ }
+ JSScript *script = Compile(cx, obj, options, buffer.begin(),
+ buffer.length());
+ return script;
+}
+#else
JSScript *
JS::Compile(JSContext *cx, HandleObject obj, CompileOptions options, FILE *fp)
{
@@ -5129,15 +5152,26 @@
JSScript *script = Compile(cx, obj, options, buffer.begin(), buffer.length());
return script;
}
+#endif
JSScript *
JS::Compile(JSContext *cx, HandleObject obj, CompileOptions options, const char *filename)
{
+#if defined(STARBOARD)
+ starboard::ScopedFile file(filename, kSbFileOpenOnly | kSbFileRead, NULL,
+ NULL);
+ if (!file.IsValid()) {
+ return NULL;
+ }
+ options = options.setFileAndLine(filename, 1);
+ JSScript *script = Compile(cx, obj, options, file.file());
+#else
AutoFile file;
if (!file.open(cx, filename))
return NULL;
options = options.setFileAndLine(filename, 1);
JSScript *script = Compile(cx, obj, options, file.fp());
+#endif
return script;
}
@@ -7082,4 +7116,3 @@
return JSObject::preventExtensions(cx, obj);
}
-
diff --git a/src/third_party/mozjs/js/src/jsapi.h b/src/third_party/mozjs/js/src/jsapi.h
index 9fb1cec..768260e 100644
--- a/src/third_party/mozjs/js/src/jsapi.h
+++ b/src/third_party/mozjs/js/src/jsapi.h
@@ -31,6 +31,8 @@
#include "js/Value.h"
#include "js/Vector.h"
+#include "starboard/file.h"
+
/************************************************************************/
namespace JS {
@@ -3990,8 +3992,14 @@
Compile(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options,
const jschar *chars, size_t length);
+#if defined(STARBOARD)
+extern JS_PUBLIC_API(JSScript *)
+Compile(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options,
+ SbFile file);
+#else
extern JS_PUBLIC_API(JSScript *)
Compile(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options, FILE *file);
+#endif
extern JS_PUBLIC_API(JSScript *)
Compile(JSContext *cx, JS::Handle<JSObject*> obj, CompileOptions options, const char *filename);
diff --git a/src/third_party/mozjs/js/src/jsstarboard-time.cpp b/src/third_party/mozjs/js/src/jsstarboard-time.cpp
index bed499f..d5049e2 100644
--- a/src/third_party/mozjs/js/src/jsstarboard-time.cpp
+++ b/src/third_party/mozjs/js/src/jsstarboard-time.cpp
@@ -17,11 +17,11 @@
#include "mozilla/Assertions.h"
#include "unicode/timezone.h"
-SbTime get_dst_offset(SbTime utc_time) {
+SbTime getDSTOffset(int64_t utc_time_us) {
// UDate is in milliseconds from the epoch.
- UDate udate = utc_time / kSbTimeMillisecond;
+ UDate udate = utc_time_us / kSbTimeMillisecond;
- icu_46::TimeZone* current_zone = icu_46::TimeZone::createDefault();
+ icu::TimeZone* current_zone = icu::TimeZone::createDefault();
int32_t raw_offset_ms, dst_offset_ms;
UErrorCode error_code = U_ZERO_ERROR;
current_zone->getOffset(
@@ -30,7 +30,15 @@
if (U_SUCCESS(error_code)) {
MOZ_ASSERT(dst_offset_ms >= 0);
- return (raw_offset_ms + dst_offset_ms) * kSbTimeMillisecond;
+ return dst_offset_ms * kSbTimeMillisecond;
}
return 0;
}
+
+SbTime getTZOffset() {
+ icu::TimeZone* current_zone = icu::TimeZone::createDefault();
+ int32_t raw_offset_ms = current_zone->getRawOffset();
+ delete current_zone;
+ return raw_offset_ms * kSbTimeMillisecond;
+}
+
diff --git a/src/third_party/mozjs/js/src/jsstarboard-time.h b/src/third_party/mozjs/js/src/jsstarboard-time.h
index 0d20ad7..038dd2d 100644
--- a/src/third_party/mozjs/js/src/jsstarboard-time.h
+++ b/src/third_party/mozjs/js/src/jsstarboard-time.h
@@ -18,10 +18,14 @@
#include "starboard/time.h"
/*
- * Get the local time offset with DST applied using the current system's
- * timezone such that:
- * local_time = utc_time + get_dst_offset(utc_time)
+ * Get the DST offset at the given time. |utc_time_us| is relative to the posix
+ * epoch.
*/
-SbTime get_dst_offset(SbTime utc_time);
+SbTime getDSTOffset(int64_t utc_time_us);
+
+/*
+ * Get the TZ offset based on the system's current timezone setting.
+ */
+SbTime getTZOffset();
#endif // jsstarboardtime_h
diff --git a/src/third_party/mozjs/js/src/prmjtime.cpp b/src/third_party/mozjs/js/src/prmjtime.cpp
index 12b2f40..c1e67b5 100644
--- a/src/third_party/mozjs/js/src/prmjtime.cpp
+++ b/src/third_party/mozjs/js/src/prmjtime.cpp
@@ -193,11 +193,11 @@
int64_t
PRMJ_Now(void)
{
- // SbTime is in microseconds since the epoch.
- SbTime utcNowMicroseconds = SbTimeGetNow();
- SbTime dstOffsetMicroseconds = get_dst_offset(utcNowMicroseconds);
- return utcNowMicroseconds + dstOffsetMicroseconds;
+ // SbTime is in microseconds since the Starboard/Windows epoch, and PRMJ_Now
+ // should return microseconds since the Posix epoch.
+ return SbTimeToPosix(SbTimeGetNow());
}
+
#elif defined(XP_OS2)
int64_t
PRMJ_Now(void)
diff --git a/src/third_party/mozjs/js/src/shell/js.cpp b/src/third_party/mozjs/js/src/shell/js.cpp
index 7b1cf6b..4af5e8d 100644
--- a/src/third_party/mozjs/js/src/shell/js.cpp
+++ b/src/third_party/mozjs/js/src/shell/js.cpp
@@ -8,7 +8,9 @@
#include <errno.h>
#include <locale.h>
#include <math.h>
+#if !defined(STARBOARD)
#include <signal.h>
+#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -72,7 +74,7 @@
# include <direct.h>
# include "jswin.h"
# define PATH_MAX (MAX_PATH > _MAX_DIR ? MAX_PATH : _MAX_DIR)
-#else
+#elif !defined(STARBOARD)
# include <libgen.h>
#endif
@@ -89,6 +91,8 @@
#include "zlib.h"
#endif
+#include "starboard/client_porting/wrap_main/wrap_main.h"
+
using namespace js;
using namespace js::cli;
@@ -413,6 +417,13 @@
}
static void
+#if defined(STARBOARD)
+RunFile(JSContext *cx, Handle<JSObject*> obj, const char *filename, SbFile file,
+ bool compileOnly)
+{
+ // We may not handle lines starting with # correctly without
+ // adding more here.
+#else
RunFile(JSContext *cx, Handle<JSObject*> obj, const char *filename, FILE *file, bool compileOnly)
{
SkipUTF8BOM(file);
@@ -427,6 +438,7 @@
}
}
ungetc(ch, file);
+#endif
int64_t t1 = PRMJ_Now();
uint32_t oldopts = JS_GetOptions(cx);
@@ -531,8 +543,7 @@
if (hitEOF && buffer.empty())
break;
- if (!EvalAndPrint(cx, global, buffer.begin(), buffer.length(), startline, compileOnly,
- out))
+ if (!EvalAndPrint(cx, global, buffer.begin(), buffer.length(), startline, compileOnly, out))
{
// Catch the error, report it, and keep going.
JS_ReportPendingException(cx);
@@ -554,11 +565,31 @@
}
};
+#if defined(STARBOARD)
+static void
+Process(JSContext *cx, JSObject *obj_, const char *filename, bool forceTTY)
+{
+ // No interactive mode for Starboard : stdin not guaranteed.
+ if (forceTTY) {
+ return;
+ }
+ RootedObject obj(cx, obj_);
+ SbFile file = SbFileOpen(filename, kSbFileOpenOnly | kSbFileRead, NULL,
+ NULL);
+ if (!SbFileIsValid(file)) {
+ SbFileClose(file);
+ return;
+ }
+
+ SetContextOptions(cx);
+ RunFile(cx, obj, filename, file, compileOnly);
+ SbFileClose(file);
+}
+#else
static void
Process(JSContext *cx, JSObject *obj_, const char *filename, bool forceTTY)
{
RootedObject obj(cx, obj_);
-
FILE *file;
if (forceTTY || !filename || strcmp(filename, "-") == 0) {
file = stdin;
@@ -572,9 +603,7 @@
}
}
AutoCloseInputFile autoClose(file);
-
SetContextOptions(cx);
-
if (!forceTTY && !isatty(fileno(file))) {
// It's not interactive - just execute it.
RunFile(cx, obj, filename, file, compileOnly);
@@ -583,6 +612,7 @@
ReadEvalPrintLoop(cx, obj, file, gOutFile, compileOnly);
}
}
+#endif // STARBOARD
/*
* JSContext option name to flag map. The option names are in alphabetical
@@ -706,22 +736,37 @@
static char buffer[PATH_MAX+1];
if (scriptRelative) {
-#ifdef XP_WIN
+#if defined(STARBOARD)
+ // We do not expect relative path scripts to work in Starboard.
+ return NULL;
+#elif defined(XP_WIN)
// The docs say it can return EINVAL, but the compiler says it's void
_splitpath(script->filename(), NULL, buffer, NULL, NULL);
#else
strncpy(buffer, script->filename(), PATH_MAX+1);
if (buffer[PATH_MAX] != '\0')
return NULL;
-
// dirname(buffer) might return buffer, or it might return a
// statically-allocated string
memmove(buffer, dirname(buffer), strlen(buffer) + 1);
#endif
} else {
+#if defined(STARBOARD)
+ // For Starboard, the tests scripts are in
+ // content/dir_source_root/mozjs/tests
+ // so we let that play the role of the cwd.
+ bool result = SbSystemGetPath(kSbSystemPathSourceDirectory,
+ buffer, PATH_MAX + 1);
+ if (!result) {
+ return NULL;
+ }
+ // Append subdirectory /mozjs/tests.
+ SbStringConcat(buffer, "/mozjs/tests", PATH_MAX + 1);
+#else
const char *cwd = getcwd(buffer, PATH_MAX);
if (!cwd)
return NULL;
+#endif
}
size_t len = strlen(buffer);
@@ -771,7 +816,7 @@
}
}
if (!found)
- names = js_strdup("");
+ names = js_strdup((char*)"");
if (!names) {
JS_ReportOutOfMemory(cx);
return false;
@@ -2987,7 +3032,9 @@
static bool
ScheduleWatchdog(JSRuntime *rt, double t)
{
-#ifdef XP_WIN
+#if defined(STARBOARD)
+ return (t <= 0);
+#elif defined(XP_WIN)
if (gTimerHandle) {
DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
gTimerHandle = 0;
@@ -3311,6 +3358,7 @@
return ReadFile(cx, argc, vp, true);
}
+#if !defined(STARBOARD)
static JSBool
System(JSContext *cx, unsigned argc, jsval *vp)
{
@@ -3334,6 +3382,7 @@
JS_SET_RVAL(cx, vp, Int32Value(result));
return true;
}
+#endif
static bool
DecompileFunctionSomehow(JSContext *cx, unsigned argc, Value *vp,
@@ -3885,11 +3934,11 @@
JS_FN_HELP("setThrowHook", SetThrowHook, 1, 0,
"setThrowHook(f)",
" Set throw hook to f."),
-
+#if !defined(STARBOARD)
JS_FN_HELP("system", System, 1, 0,
"system(command)",
" Execute command on the current host, returning result code."),
-
+#endif
JS_FN_HELP("trap", Trap, 3, 0,
"trap([fun, [pc,]] exp)",
" Trap bytecode execution."),
@@ -4450,6 +4499,8 @@
JS_smprintf_free(waste);
#endif
}
+#elif defined(STARBOARD)
+ rv = -1;
#else
rv = setenv(idstr.getBytes(), valstr.getBytes(), 1);
#endif
@@ -4502,6 +4553,7 @@
return false;
name = idstr.getBytes();
+#if !defined(STARBOARD)
value = getenv(name);
if (value) {
valstr = JS_NewStringCopyZ(cx, value);
@@ -4513,6 +4565,7 @@
}
objp.set(obj);
}
+#endif
return true;
}
@@ -5160,10 +5213,14 @@
FILE* defaultOut,
FILE** outFile)
{
+#if defined(STARBOARD)
+ *outFile = defaultOut;
+#else
const char* outPath = getenv(envVar);
if (!outPath || !*outPath || !(*outFile = fopen(outPath, "w"))) {
*outFile = defaultOut;
}
+#endif
}
/* Set the initial counter to 1 so the principal will never be destroyed. */
@@ -5189,7 +5246,7 @@
}
int
-main(int argc, char **argv, char **envp)
+ShellMain(int argc, char **argv, char **envp)
{
int stackDummy;
JSRuntime *rt;
@@ -5382,7 +5439,6 @@
/* Must be done before creating the global object */
if (op.getBoolOption('D'))
JS_ToggleOptions(cx, JSOPTION_PCCOUNT);
-
result = Shell(cx, &op, envp);
#if defined(DEBUG) && !defined(JS_USE_CUSTOM_ALLOCATOR)
@@ -5401,3 +5457,9 @@
JS_ShutDown();
return result;
}
+
+int ShellMainNoEnv(int argc, char** argv) {
+ return ShellMain(argc, argv, NULL);
+}
+
+STARBOARD_WRAP_SIMPLE_MAIN(ShellMainNoEnv);
diff --git a/src/third_party/mozjs/js/src/shell/jsoptparse.cpp b/src/third_party/mozjs/js/src/shell/jsoptparse.cpp
index 204efa1..18be826 100644
--- a/src/third_party/mozjs/js/src/shell/jsoptparse.cpp
+++ b/src/third_party/mozjs/js/src/shell/jsoptparse.cpp
@@ -253,7 +253,7 @@
char *eq = strchr(argv[*i], '=');
if (eq) {
*value = eq + 1;
- if (value[0] == '\0')
+ if (*value[0] == '\0')
return error("A value is required for option %.*s", eq - argv[*i], argv[*i]);
return Okay;
}
diff --git a/src/third_party/mozjs/js/src/vm/DateTime.cpp b/src/third_party/mozjs/js/src/vm/DateTime.cpp
index 9c26bd9..cfca247 100644
--- a/src/third_party/mozjs/js/src/vm/DateTime.cpp
+++ b/src/third_party/mozjs/js/src/vm/DateTime.cpp
@@ -17,16 +17,7 @@
using mozilla::UnspecifiedNaN;
-#if defined(STARBOARD)
-static int32_t
-UTCToLocalStandardOffsetSeconds()
-{
- SbTimeZone timeZone = SbTimeZoneGetCurrent();
- // timeZone is in minutes
- return timeZone * 60;
-}
-
-#else
+#if !defined(STARBOARD)
static bool
ComputeLocalTime(time_t local, struct tm *ptm)
{
@@ -149,7 +140,11 @@
* The difference between local standard time and UTC will never change for
* a given time zone.
*/
+#if defined(STARBOARD)
+ utcToLocalStandardOffsetSeconds = getTZOffset() / kSbTimeSecond;
+#else
utcToLocalStandardOffsetSeconds = UTCToLocalStandardOffsetSeconds();
+#endif
double newTZA = utcToLocalStandardOffsetSeconds * msPerSecond;
if (newTZA == localTZA_)
@@ -192,7 +187,7 @@
MOZ_ASSERT(utcSeconds <= MaxUnixTimeT);
SbTime utcTimeMicroseconds = utcSeconds * kSbTimeSecond;
- SbTime offsetMicroseconds = get_dst_offset(utcTimeMicroseconds);
+ SbTime offsetMicroseconds = getDSTOffset(utcTimeMicroseconds);
return offsetMicroseconds / kSbTimeMillisecond;
}
diff --git a/src/third_party/mozjs/mozjs.gyp b/src/third_party/mozjs/mozjs.gyp
index c646032..b0c4497 100644
--- a/src/third_party/mozjs/mozjs.gyp
+++ b/src/third_party/mozjs/mozjs.gyp
@@ -205,7 +205,30 @@
'mozjs_lib',
'<(DEPTH)/starboard/starboard.gyp:starboard',
'<(DEPTH)/third_party/zlib/zlib.gyp:zlib',
- ]
+ ],
+ 'actions': [
+ {
+ 'action_name': 'copy_test_data',
+ 'variables': {
+ 'input_files': [
+ 'js/src/tests/',
+ ],
+ 'output_dir': 'mozjs/tests/',
+ },
+ 'includes': ['../../starboard/build/copy_test_data.gypi'],
+ },
+ ],
+ },
+ {
+ 'target_name': 'mozjs_shell_deploy',
+ 'type': 'none',
+ 'dependencies': [
+ 'mozjs_shell',
+ ],
+ 'variables': {
+ 'executable_name': 'mozjs_shell',
+ },
+ 'includes': [ '../../starboard/build/deploy.gypi' ],
},
{
# SpiderMonkey source expects to include files from a certain directory