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