summaryrefslogtreecommitdiffstats
path: root/layout/inspector
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /layout/inspector
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'layout/inspector')
-rw-r--r--layout/inspector/inCSSValueSearch.cpp407
-rw-r--r--layout/inspector/inCSSValueSearch.h60
-rw-r--r--layout/inspector/inDOMUtils.cpp1351
-rw-r--r--layout/inspector/inDOMUtils.h42
-rw-r--r--layout/inspector/inDOMView.cpp1283
-rw-r--r--layout/inspector/inDOMView.h96
-rw-r--r--layout/inspector/inDeepTreeWalker.cpp442
-rw-r--r--layout/inspector/inDeepTreeWalker.h65
-rw-r--r--layout/inspector/inICSSValueSearch.idl29
-rw-r--r--layout/inspector/inIDOMUtils.idl213
-rw-r--r--layout/inspector/inIDOMView.idl24
-rw-r--r--layout/inspector/inIDeepTreeWalker.idl47
-rw-r--r--layout/inspector/inISearchObserver.idl21
-rw-r--r--layout/inspector/inISearchProcess.idl49
-rw-r--r--layout/inspector/inLayoutUtils.cpp71
-rw-r--r--layout/inspector/inLayoutUtils.h27
-rw-r--r--layout/inspector/inSearchLoop.cpp58
-rw-r--r--layout/inspector/inSearchLoop.h28
-rw-r--r--layout/inspector/moz.build49
-rw-r--r--layout/inspector/nsFontFace.cpp225
-rw-r--r--layout/inspector/nsFontFace.h37
-rw-r--r--layout/inspector/nsFontFaceList.cpp83
-rw-r--r--layout/inspector/nsFontFaceList.h34
-rw-r--r--layout/inspector/nsIDOMFontFace.idl32
-rw-r--r--layout/inspector/nsIDOMFontFaceList.idl14
-rw-r--r--layout/inspector/tests/bug1202095-2.css7
-rw-r--r--layout/inspector/tests/bug1202095.css7
-rw-r--r--layout/inspector/tests/bug856317.css23
-rw-r--r--layout/inspector/tests/chrome/GentiumPlus-R.woffbin0 -> 660480 bytes
-rw-r--r--layout/inspector/tests/chrome/chrome.ini12
-rw-r--r--layout/inspector/tests/chrome/test_bug467669.css8
-rw-r--r--layout/inspector/tests/chrome/test_bug467669.xul174
-rw-r--r--layout/inspector/tests/chrome/test_bug695639.css8
-rw-r--r--layout/inspector/tests/chrome/test_bug695639.xul80
-rw-r--r--layout/inspector/tests/chrome/test_bug708874.css33
-rw-r--r--layout/inspector/tests/chrome/test_bug708874.xul296
-rw-r--r--layout/inspector/tests/chrome/test_bug727834.css7
-rw-r--r--layout/inspector/tests/chrome/test_bug727834.xul88
-rw-r--r--layout/inspector/tests/file_bug522601.html17
-rw-r--r--layout/inspector/tests/mochitest.ini29
-rw-r--r--layout/inspector/tests/test_bug1006595.html118
-rw-r--r--layout/inspector/tests/test_bug462787.html100
-rw-r--r--layout/inspector/tests/test_bug462789.html94
-rw-r--r--layout/inspector/tests/test_bug522601.xhtml274
-rw-r--r--layout/inspector/tests/test_bug536379-2.html35
-rw-r--r--layout/inspector/tests/test_bug536379.html46
-rw-r--r--layout/inspector/tests/test_bug557726.html95
-rw-r--r--layout/inspector/tests/test_bug609549.xhtml67
-rw-r--r--layout/inspector/tests/test_bug806192.html26
-rw-r--r--layout/inspector/tests/test_bug856317.html84
-rw-r--r--layout/inspector/tests/test_bug877690.html260
-rw-r--r--layout/inspector/tests/test_color_to_rgba.html55
-rw-r--r--layout/inspector/tests/test_css_property_is_shorthand.html51
-rw-r--r--layout/inspector/tests/test_css_property_is_valid.html105
-rw-r--r--layout/inspector/tests/test_getCSSPseudoElementNames.html59
-rw-r--r--layout/inspector/tests/test_getRelativeRuleLine.html69
-rw-r--r--layout/inspector/tests/test_get_all_style_sheets.html44
-rw-r--r--layout/inspector/tests/test_is_valid_css_color.html91
-rw-r--r--layout/inspector/tests/test_isinheritableproperty.html38
-rw-r--r--layout/inspector/tests/test_parseStyleSheet.html34
-rw-r--r--layout/inspector/tests/test_parseStyleSheetImport.html83
-rw-r--r--layout/inspector/tests/test_selectormatcheselement.html90
62 files changed, 7494 insertions, 0 deletions
diff --git a/layout/inspector/inCSSValueSearch.cpp b/layout/inspector/inCSSValueSearch.cpp
new file mode 100644
index 000000000..ecde09993
--- /dev/null
+++ b/layout/inspector/inCSSValueSearch.cpp
@@ -0,0 +1,407 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "inCSSValueSearch.h"
+
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/dom/StyleSheetList.h"
+#include "nsIComponentManager.h"
+#include "nsIServiceManager.h"
+#include "nsReadableUtils.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMStyleSheetList.h"
+#include "nsIDOMCSSStyleSheet.h"
+#include "nsIDOMCSSRuleList.h"
+#include "nsIDOMCSSStyleRule.h"
+#include "nsIDOMCSSStyleDeclaration.h"
+#include "nsIDOMCSSImportRule.h"
+#include "nsIDOMCSSMediaRule.h"
+#include "nsIDOMCSSSupportsRule.h"
+#include "nsIURI.h"
+#include "nsIDocument.h"
+#include "nsNetUtil.h"
+
+using namespace mozilla;
+
+///////////////////////////////////////////////////////////////////////////////
+inCSSValueSearch::inCSSValueSearch()
+ : mResults(nullptr),
+ mProperties(nullptr),
+ mResultCount(0),
+ mPropertyCount(0),
+ mIsActive(false),
+ mHoldResults(true),
+ mReturnRelativeURLs(true),
+ mNormalizeChromeURLs(false)
+{
+ nsCSSProps::AddRefTable();
+ mProperties = new nsCSSPropertyID[100];
+}
+
+inCSSValueSearch::~inCSSValueSearch()
+{
+ delete[] mProperties;
+ delete mResults;
+ nsCSSProps::ReleaseTable();
+}
+
+NS_IMPL_ISUPPORTS(inCSSValueSearch, inISearchProcess, inICSSValueSearch)
+
+///////////////////////////////////////////////////////////////////////////////
+// inISearchProcess
+
+NS_IMETHODIMP
+inCSSValueSearch::GetIsActive(bool *aIsActive)
+{
+ *aIsActive = mIsActive;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::GetResultCount(int32_t *aResultCount)
+{
+ *aResultCount = mResultCount;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::GetHoldResults(bool *aHoldResults)
+{
+ *aHoldResults = mHoldResults;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SetHoldResults(bool aHoldResults)
+{
+ mHoldResults = aHoldResults;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SearchSync()
+{
+ InitSearch();
+
+ if (!mDocument) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(mDocument);
+ MOZ_ASSERT(document);
+
+ nsCOMPtr<nsIURI> baseURI = document->GetBaseURI();
+
+ RefPtr<dom::StyleSheetList> sheets = document->StyleSheets();
+ MOZ_ASSERT(sheets);
+
+ uint32_t length = sheets->Length();
+ for (uint32_t i = 0; i < length; ++i) {
+ RefPtr<StyleSheet> sheet = sheets->Item(i);
+ SearchStyleSheet(sheet, baseURI);
+ }
+
+ // XXX would be nice to search inline style as well.
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SearchAsync(inISearchObserver *aObserver)
+{
+ InitSearch();
+ mObserver = aObserver;
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+inCSSValueSearch::SearchStop()
+{
+ KillSearch(inISearchObserver::IN_INTERRUPTED);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SearchStep(bool* _retval)
+{
+
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+inCSSValueSearch::GetStringResultAt(int32_t aIndex, nsAString& _retval)
+{
+ if (mHoldResults) {
+ nsAutoString* result = mResults->ElementAt(aIndex);
+ _retval = *result;
+ } else if (aIndex == mResultCount-1) {
+ _retval = mLastResult;
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::GetIntResultAt(int32_t aIndex, int32_t *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::GetUIntResultAt(int32_t aIndex, uint32_t *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// inICSSValueSearch
+
+NS_IMETHODIMP
+inCSSValueSearch::GetDocument(nsIDOMDocument** aDocument)
+{
+ *aDocument = mDocument;
+ NS_IF_ADDREF(*aDocument);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SetDocument(nsIDOMDocument* aDocument)
+{
+ mDocument = aDocument;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::GetBaseURL(char16_t** aBaseURL)
+{
+ if (!(*aBaseURL = ToNewUnicode(mBaseURL)))
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SetBaseURL(const char16_t* aBaseURL)
+{
+ mBaseURL.Assign(aBaseURL);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::GetReturnRelativeURLs(bool* aReturnRelativeURLs)
+{
+ *aReturnRelativeURLs = mReturnRelativeURLs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SetReturnRelativeURLs(bool aReturnRelativeURLs)
+{
+ mReturnRelativeURLs = aReturnRelativeURLs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::GetNormalizeChromeURLs(bool *aNormalizeChromeURLs)
+{
+ *aNormalizeChromeURLs = mNormalizeChromeURLs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SetNormalizeChromeURLs(bool aNormalizeChromeURLs)
+{
+ mNormalizeChromeURLs = aNormalizeChromeURLs;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::AddPropertyCriteria(const char16_t *aPropName)
+{
+ nsCSSPropertyID prop =
+ nsCSSProps::LookupProperty(nsDependentString(aPropName),
+ CSSEnabledState::eIgnoreEnabledState);
+ mProperties[mPropertyCount] = prop;
+ mPropertyCount++;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::GetTextCriteria(char16_t** aTextCriteria)
+{
+ if (!(*aTextCriteria = ToNewUnicode(mTextCriteria)))
+ return NS_ERROR_OUT_OF_MEMORY;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inCSSValueSearch::SetTextCriteria(const char16_t* aTextCriteria)
+{
+ mTextCriteria.Assign(aTextCriteria);
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// inCSSValueSearch
+
+nsresult
+inCSSValueSearch::InitSearch()
+{
+ if (mHoldResults) {
+ mResults = new nsTArray<nsAutoString *>();
+ }
+
+ mResultCount = 0;
+
+ return NS_OK;
+}
+
+nsresult
+inCSSValueSearch::KillSearch(int16_t aResult)
+{
+ mIsActive = true;
+ mObserver->OnSearchEnd(this, aResult);
+
+ return NS_OK;
+}
+
+nsresult
+inCSSValueSearch::SearchStyleSheet(nsIDOMCSSStyleSheet* aStyleSheet, nsIURI* aBaseURL)
+{
+ nsCOMPtr<nsIURI> baseURL;
+ nsAutoString href;
+ aStyleSheet->GetHref(href);
+ if (href.IsEmpty())
+ baseURL = aBaseURL;
+ else
+ NS_NewURI(getter_AddRefs(baseURL), href, nullptr, aBaseURL);
+
+ nsCOMPtr<nsIDOMCSSRuleList> rules;
+ nsresult rv = aStyleSheet->GetCssRules(getter_AddRefs(rules));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return SearchRuleList(rules, baseURL);
+}
+
+nsresult
+inCSSValueSearch::SearchRuleList(nsIDOMCSSRuleList* aRuleList, nsIURI* aBaseURL)
+{
+ uint32_t length;
+ aRuleList->GetLength(&length);
+ for (uint32_t i = 0; i < length; ++i) {
+ nsCOMPtr<nsIDOMCSSRule> rule;
+ aRuleList->Item(i, getter_AddRefs(rule));
+ uint16_t type;
+ rule->GetType(&type);
+ switch (type) {
+ case nsIDOMCSSRule::STYLE_RULE: {
+ nsCOMPtr<nsIDOMCSSStyleRule> styleRule = do_QueryInterface(rule);
+ SearchStyleRule(styleRule, aBaseURL);
+ } break;
+ case nsIDOMCSSRule::IMPORT_RULE: {
+ nsCOMPtr<nsIDOMCSSImportRule> importRule = do_QueryInterface(rule);
+ nsCOMPtr<nsIDOMCSSStyleSheet> childSheet;
+ importRule->GetStyleSheet(getter_AddRefs(childSheet));
+ if (childSheet)
+ SearchStyleSheet(childSheet, aBaseURL);
+ } break;
+ case nsIDOMCSSRule::MEDIA_RULE: {
+ nsCOMPtr<nsIDOMCSSMediaRule> mediaRule = do_QueryInterface(rule);
+ nsCOMPtr<nsIDOMCSSRuleList> childRules;
+ mediaRule->GetCssRules(getter_AddRefs(childRules));
+ SearchRuleList(childRules, aBaseURL);
+ } break;
+ case nsIDOMCSSRule::SUPPORTS_RULE: {
+ nsCOMPtr<nsIDOMCSSSupportsRule> supportsRule = do_QueryInterface(rule);
+ nsCOMPtr<nsIDOMCSSRuleList> childRules;
+ supportsRule->GetCssRules(getter_AddRefs(childRules));
+ SearchRuleList(childRules, aBaseURL);
+ } break;
+ default:
+ // XXX handle nsIDOMCSSRule::PAGE_RULE if we ever support it
+ break;
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+inCSSValueSearch::SearchStyleRule(nsIDOMCSSStyleRule* aStyleRule, nsIURI* aBaseURL)
+{
+ nsCOMPtr<nsIDOMCSSStyleDeclaration> decl;
+ nsresult rv = aStyleRule->GetStyle(getter_AddRefs(decl));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ uint32_t length;
+ decl->GetLength(&length);
+ nsAutoString property, value;
+ for (uint32_t i = 0; i < length; ++i) {
+ decl->Item(i, property);
+ // XXX This probably ought to use GetPropertyCSSValue if it were
+ // implemented.
+ decl->GetPropertyValue(property, value);
+ SearchStyleValue(value, aBaseURL);
+ }
+ return NS_OK;
+}
+
+nsresult
+inCSSValueSearch::SearchStyleValue(const nsAFlatString& aValue, nsIURI* aBaseURL)
+{
+ if (StringBeginsWith(aValue, NS_LITERAL_STRING("url(")) &&
+ StringEndsWith(aValue, NS_LITERAL_STRING(")"))) {
+ const nsASingleFragmentString &url =
+ Substring(aValue, 4, aValue.Length() - 5);
+ // XXXldb Need to do more with |mReturnRelativeURLs|, perhaps?
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, aBaseURL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoCString spec;
+ rv = uri->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ nsAutoString *result = new NS_ConvertUTF8toUTF16(spec);
+ if (mReturnRelativeURLs)
+ EqualizeURL(result);
+ mResults->AppendElement(result);
+ ++mResultCount;
+ }
+
+ return NS_OK;
+}
+
+nsresult
+inCSSValueSearch::EqualizeURL(nsAutoString* aURL)
+{
+ if (mNormalizeChromeURLs) {
+ if (aURL->Find("chrome://", false, 0, 1) >= 0) {
+ uint32_t len = aURL->Length();
+ char16_t* result = new char16_t[len-8];
+ const char16_t* src = aURL->get();
+ uint32_t i = 9;
+ uint32_t milestone = 0;
+ uint32_t s = 0;
+ while (i < len) {
+ if (src[i] == '/') {
+ milestone += 1;
+ }
+ if (milestone != 1) {
+ result[i-9-s] = src[i];
+ } else {
+ s++;
+ }
+ i++;
+ }
+ result[i-9-s] = 0;
+
+ aURL->Assign(result);
+ delete [] result;
+ }
+ } else {
+ }
+
+ return NS_OK;
+}
diff --git a/layout/inspector/inCSSValueSearch.h b/layout/inspector/inCSSValueSearch.h
new file mode 100644
index 000000000..3741fd331
--- /dev/null
+++ b/layout/inspector/inCSSValueSearch.h
@@ -0,0 +1,60 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __inCSSValueSearch_h__
+#define __inCSSValueSearch_h__
+
+#include "inICSSValueSearch.h"
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsIDOMDocument.h"
+#include "inISearchObserver.h"
+#include "nsTArray.h"
+#include "nsCSSProps.h"
+
+class nsIDOMCSSStyleSheet;
+class nsIDOMCSSRuleList;
+class nsIDOMCSSStyleRule;
+class nsIURI;
+
+class inCSSValueSearch final : public inICSSValueSearch
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_INISEARCHPROCESS
+ NS_DECL_INICSSVALUESEARCH
+
+ inCSSValueSearch();
+
+protected:
+ virtual ~inCSSValueSearch();
+ nsCOMPtr<inISearchObserver> mObserver;
+ nsCOMPtr<nsIDOMDocument> mDocument;
+ nsTArray<nsAutoString *>* mResults;
+ nsCSSPropertyID* mProperties;
+ nsString mLastResult;
+ nsString mBaseURL;
+ nsString mTextCriteria;
+ int32_t mResultCount;
+ uint32_t mPropertyCount;
+ bool mIsActive;
+ bool mHoldResults;
+ bool mReturnRelativeURLs;
+ bool mNormalizeChromeURLs;
+
+ nsresult InitSearch();
+ nsresult KillSearch(int16_t aResult);
+ nsresult SearchStyleSheet(nsIDOMCSSStyleSheet* aStyleSheet, nsIURI* aBaseURI);
+ nsresult SearchRuleList(nsIDOMCSSRuleList* aRuleList, nsIURI* aBaseURI);
+ nsresult SearchStyleRule(nsIDOMCSSStyleRule* aStyleRule, nsIURI* aBaseURI);
+ nsresult SearchStyleValue(const nsAFlatString& aValue, nsIURI* aBaseURI);
+ nsresult EqualizeURL(nsAutoString* aURL);
+};
+
+// {4D977F60-FBE7-4583-8CB7-F5ED882293EF}
+#define IN_CSSVALUESEARCH_CID \
+{ 0x4d977f60, 0xfbe7, 0x4583, { 0x8c, 0xb7, 0xf5, 0xed, 0x88, 0x22, 0x93, 0xef } }
+
+#endif // __inCSSValueSearch_h__
diff --git a/layout/inspector/inDOMUtils.cpp b/layout/inspector/inDOMUtils.cpp
new file mode 100644
index 000000000..9f1dcaad3
--- /dev/null
+++ b/layout/inspector/inDOMUtils.cpp
@@ -0,0 +1,1351 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/EventStates.h"
+
+#include "inDOMUtils.h"
+#include "inLayoutUtils.h"
+
+#include "nsArray.h"
+#include "nsAutoPtr.h"
+#include "nsIServiceManager.h"
+#include "nsString.h"
+#include "nsIStyleSheetLinkingElement.h"
+#include "nsIContentInlines.h"
+#include "nsIDOMElement.h"
+#include "nsIDocument.h"
+#include "nsIPresShell.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMCharacterData.h"
+#include "nsRuleNode.h"
+#include "nsIStyleRule.h"
+#include "mozilla/css/StyleRule.h"
+#include "nsICSSStyleRuleDOMWrapper.h"
+#include "nsIDOMWindow.h"
+#include "nsXBLBinding.h"
+#include "nsXBLPrototypeBinding.h"
+#include "nsIMutableArray.h"
+#include "nsBindingManager.h"
+#include "ChildIterator.h"
+#include "nsComputedDOMStyle.h"
+#include "mozilla/EventStateManager.h"
+#include "nsIAtom.h"
+#include "nsRange.h"
+#include "nsContentList.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/dom/Element.h"
+#include "nsRuleWalker.h"
+#include "nsRuleProcessorData.h"
+#include "nsCSSPseudoClasses.h"
+#include "nsCSSRuleProcessor.h"
+#include "mozilla/dom/CSSLexer.h"
+#include "mozilla/dom/InspectorUtilsBinding.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsCSSParser.h"
+#include "nsCSSProps.h"
+#include "nsCSSValue.h"
+#include "nsColor.h"
+#include "nsStyleSet.h"
+#include "nsStyleUtil.h"
+#include "nsQueryObject.h"
+
+using namespace mozilla;
+using namespace mozilla::css;
+using namespace mozilla::dom;
+
+///////////////////////////////////////////////////////////////////////////////
+
+inDOMUtils::inDOMUtils()
+{
+}
+
+inDOMUtils::~inDOMUtils()
+{
+}
+
+NS_IMPL_ISUPPORTS(inDOMUtils, inIDOMUtils)
+
+///////////////////////////////////////////////////////////////////////////////
+// inIDOMUtils
+
+NS_IMETHODIMP
+inDOMUtils::GetAllStyleSheets(nsIDOMDocument *aDocument, uint32_t *aLength,
+ nsISupports ***aSheets)
+{
+ NS_ENSURE_ARG_POINTER(aDocument);
+
+ nsTArray<RefPtr<CSSStyleSheet>> sheets;
+
+ nsCOMPtr<nsIDocument> document = do_QueryInterface(aDocument);
+ MOZ_ASSERT(document);
+
+ // Get the agent, then user and finally xbl sheets in the style set.
+ nsIPresShell* presShell = document->GetShell();
+
+ if (presShell && presShell->StyleSet()->IsServo()) {
+ // XXXheycam ServoStyleSets don't have the ability to expose their
+ // sheets in a script-accessible way yet.
+ NS_ERROR("stylo: ServoStyleSets cannot expose their sheets to script yet");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (presShell) {
+ nsStyleSet* styleSet = presShell->StyleSet()->AsGecko();
+ SheetType sheetType = SheetType::Agent;
+ for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) {
+ sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i));
+ }
+ sheetType = SheetType::User;
+ for (int32_t i = 0; i < styleSet->SheetCount(sheetType); i++) {
+ sheets.AppendElement(styleSet->StyleSheetAt(sheetType, i));
+ }
+ AutoTArray<CSSStyleSheet*, 32> xblSheetArray;
+ styleSet->AppendAllXBLStyleSheets(xblSheetArray);
+
+ // The XBL stylesheet array will quite often be full of duplicates. Cope:
+ nsTHashtable<nsPtrHashKey<CSSStyleSheet>> sheetSet;
+ for (CSSStyleSheet* sheet : xblSheetArray) {
+ if (!sheetSet.Contains(sheet)) {
+ sheetSet.PutEntry(sheet);
+ sheets.AppendElement(sheet);
+ }
+ }
+ }
+
+ // Get the document sheets.
+ for (int32_t i = 0; i < document->GetNumberOfStyleSheets(); i++) {
+ // XXXheycam ServoStyleSets don't have the ability to expose their
+ // sheets in a script-accessible way yet.
+ sheets.AppendElement(document->GetStyleSheetAt(i)->AsGecko());
+ }
+
+ nsISupports** ret = static_cast<nsISupports**>(moz_xmalloc(sheets.Length() *
+ sizeof(nsISupports*)));
+
+ for (size_t i = 0; i < sheets.Length(); i++) {
+ NS_ADDREF(ret[i] = NS_ISUPPORTS_CAST(nsIDOMCSSStyleSheet*, sheets[i]));
+ }
+
+ *aLength = sheets.Length();
+ *aSheets = ret;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::IsIgnorableWhitespace(nsIDOMCharacterData *aDataNode,
+ bool *aReturn)
+{
+ NS_PRECONDITION(aReturn, "Must have an out parameter");
+
+ NS_ENSURE_ARG_POINTER(aDataNode);
+
+ *aReturn = false;
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aDataNode);
+ NS_ASSERTION(content, "Does not implement nsIContent!");
+
+ if (!content->TextIsOnlyWhitespace()) {
+ return NS_OK;
+ }
+
+ // Okay. We have only white space. Let's check the white-space
+ // property now and make sure that this isn't preformatted text...
+ nsIFrame* frame = content->GetPrimaryFrame();
+ if (frame) {
+ const nsStyleText* text = frame->StyleText();
+ *aReturn = !text->WhiteSpaceIsSignificant();
+ }
+ else {
+ // empty inter-tag text node without frame, e.g., in between <table>\n<tr>
+ *aReturn = true;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetParentForNode(nsIDOMNode* aNode,
+ bool aShowingAnonymousContent,
+ nsIDOMNode** aParent)
+{
+ NS_ENSURE_ARG_POINTER(aNode);
+
+ // First do the special cases -- document nodes and anonymous content
+ nsCOMPtr<nsIDocument> doc(do_QueryInterface(aNode));
+ nsCOMPtr<nsIDOMNode> parent;
+
+ if (doc) {
+ parent = inLayoutUtils::GetContainerFor(*doc);
+ } else if (aShowingAnonymousContent) {
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+ if (content) {
+ nsIContent* bparent = content->GetFlattenedTreeParent();
+ parent = do_QueryInterface(bparent);
+ }
+ }
+
+ if (!parent) {
+ // Ok, just get the normal DOM parent node
+ aNode->GetParentNode(getter_AddRefs(parent));
+ }
+
+ NS_IF_ADDREF(*aParent = parent);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetChildrenForNode(nsIDOMNode* aNode,
+ bool aShowingAnonymousContent,
+ nsIDOMNodeList** aChildren)
+{
+ NS_ENSURE_ARG_POINTER(aNode);
+ NS_PRECONDITION(aChildren, "Must have an out parameter");
+
+ nsCOMPtr<nsIDOMNodeList> kids;
+
+ if (aShowingAnonymousContent) {
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+ if (content) {
+ kids = content->GetChildren(nsIContent::eAllChildren);
+ }
+ }
+
+ if (!kids) {
+ aNode->GetChildNodes(getter_AddRefs(kids));
+ }
+
+ kids.forget(aChildren);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetCSSStyleRules(nsIDOMElement *aElement,
+ const nsAString& aPseudo,
+ nsIArrayExtensions **_retval)
+{
+ NS_ENSURE_ARG_POINTER(aElement);
+
+ *_retval = nullptr;
+
+ nsCOMPtr<nsIAtom> pseudoElt;
+ if (!aPseudo.IsEmpty()) {
+ pseudoElt = NS_Atomize(aPseudo);
+ }
+
+ nsRuleNode* ruleNode = nullptr;
+ nsCOMPtr<Element> element = do_QueryInterface(aElement);
+ NS_ENSURE_STATE(element);
+ RefPtr<nsStyleContext> styleContext;
+ GetRuleNodeForElement(element, pseudoElt, getter_AddRefs(styleContext), &ruleNode);
+ if (!ruleNode) {
+ // This can fail for elements that are not in the document or
+ // if the document they're in doesn't have a presshell. Bail out.
+ return NS_OK;
+ }
+
+ AutoTArray<nsRuleNode*, 16> ruleNodes;
+ while (!ruleNode->IsRoot()) {
+ ruleNodes.AppendElement(ruleNode);
+ ruleNode = ruleNode->GetParent();
+ }
+
+ nsCOMPtr<nsIMutableArray> rules = nsArray::Create();
+ for (nsRuleNode* ruleNode : Reversed(ruleNodes)) {
+ RefPtr<Declaration> decl = do_QueryObject(ruleNode->GetRule());
+ if (decl) {
+ RefPtr<mozilla::css::StyleRule> styleRule =
+ do_QueryObject(decl->GetOwningRule());
+ if (styleRule) {
+ nsCOMPtr<nsIDOMCSSRule> domRule = styleRule->GetDOMRule();
+ if (domRule) {
+ rules->AppendElement(domRule, /*weak =*/ false);
+ }
+ }
+ }
+ }
+
+ rules.forget(_retval);
+
+ return NS_OK;
+}
+
+static already_AddRefed<StyleRule>
+GetRuleFromDOMRule(nsIDOMCSSStyleRule *aRule, ErrorResult& rv)
+{
+ nsCOMPtr<nsICSSStyleRuleDOMWrapper> rule = do_QueryInterface(aRule);
+ if (!rule) {
+ rv.Throw(NS_ERROR_INVALID_POINTER);
+ return nullptr;
+ }
+
+ RefPtr<StyleRule> cssrule;
+ rv = rule->GetCSSStyleRule(getter_AddRefs(cssrule));
+ if (rv.Failed()) {
+ return nullptr;
+ }
+
+ if (!cssrule) {
+ rv.Throw(NS_ERROR_FAILURE);
+ }
+ return cssrule.forget();
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetRuleLine(nsIDOMCSSRule* aRule, uint32_t* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aRule);
+
+ Rule* rule = aRule->GetCSSRule();
+ if (!rule) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *_retval = rule->GetLineNumber();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetRuleColumn(nsIDOMCSSRule* aRule, uint32_t* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aRule);
+
+ Rule* rule = aRule->GetCSSRule();
+ if (!rule) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *_retval = rule->GetColumnNumber();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetRelativeRuleLine(nsIDOMCSSRule* aRule, uint32_t* _retval)
+{
+ NS_ENSURE_ARG_POINTER(aRule);
+
+ Rule* rule = aRule->GetCSSRule();
+ if (!rule) {
+ return NS_ERROR_FAILURE;
+ }
+
+ uint32_t lineNumber = rule->GetLineNumber();
+ CSSStyleSheet* sheet = rule->GetStyleSheet();
+ if (sheet && lineNumber != 0) {
+ nsINode* owningNode = sheet->GetOwnerNode();
+ if (owningNode) {
+ nsCOMPtr<nsIStyleSheetLinkingElement> link =
+ do_QueryInterface(owningNode);
+ if (link) {
+ lineNumber -= link->GetLineNumber() - 1;
+ }
+ }
+ }
+
+ *_retval = lineNumber;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetCSSLexer(const nsAString& aText, JSContext* aCx,
+ JS::MutableHandleValue aResult)
+{
+ MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
+ JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
+ nsAutoPtr<CSSLexer> lexer(new CSSLexer(aText));
+ if (!WrapNewBindingNonWrapperCachedObject(aCx, scope, lexer, aResult)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetSelectorCount(nsIDOMCSSStyleRule* aRule, uint32_t *aCount)
+{
+ ErrorResult rv;
+ RefPtr<StyleRule> rule = GetRuleFromDOMRule(aRule, rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+
+ uint32_t count = 0;
+ for (nsCSSSelectorList* sel = rule->Selector(); sel; sel = sel->mNext) {
+ ++count;
+ }
+ *aCount = count;
+ return NS_OK;
+}
+
+static nsCSSSelectorList*
+GetSelectorAtIndex(nsIDOMCSSStyleRule* aRule, uint32_t aIndex, ErrorResult& rv)
+{
+ RefPtr<StyleRule> rule = GetRuleFromDOMRule(aRule, rv);
+ if (rv.Failed()) {
+ return nullptr;
+ }
+
+ for (nsCSSSelectorList* sel = rule->Selector(); sel;
+ sel = sel->mNext, --aIndex) {
+ if (aIndex == 0) {
+ return sel;
+ }
+ }
+
+ // Ran out of selectors
+ rv.Throw(NS_ERROR_INVALID_ARG);
+ return nullptr;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetSelectorText(nsIDOMCSSStyleRule* aRule,
+ uint32_t aSelectorIndex,
+ nsAString& aText)
+{
+ ErrorResult rv;
+ nsCSSSelectorList* sel = GetSelectorAtIndex(aRule, aSelectorIndex, rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+
+ RefPtr<StyleRule> rule = GetRuleFromDOMRule(aRule, rv);
+ MOZ_ASSERT(!rv.Failed(), "How could we get a selector but not a rule?");
+
+ sel->mSelectors->ToString(aText, rule->GetStyleSheet(), false);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetSpecificity(nsIDOMCSSStyleRule* aRule,
+ uint32_t aSelectorIndex,
+ uint64_t* aSpecificity)
+{
+ ErrorResult rv;
+ nsCSSSelectorList* sel = GetSelectorAtIndex(aRule, aSelectorIndex, rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+
+ *aSpecificity = sel->mWeight;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::SelectorMatchesElement(nsIDOMElement* aElement,
+ nsIDOMCSSStyleRule* aRule,
+ uint32_t aSelectorIndex,
+ const nsAString& aPseudo,
+ bool* aMatches)
+{
+ nsCOMPtr<Element> element = do_QueryInterface(aElement);
+ NS_ENSURE_ARG_POINTER(element);
+
+ ErrorResult rv;
+ nsCSSSelectorList* tail = GetSelectorAtIndex(aRule, aSelectorIndex, rv);
+ if (rv.Failed()) {
+ return rv.StealNSResult();
+ }
+
+ // We want just the one list item, not the whole list tail
+ nsAutoPtr<nsCSSSelectorList> sel(tail->Clone(false));
+
+ // Do not attempt to match if a pseudo element is requested and this is not
+ // a pseudo element selector, or vice versa.
+ if (aPseudo.IsEmpty() == sel->mSelectors->IsPseudoElement()) {
+ *aMatches = false;
+ return NS_OK;
+ }
+
+ if (!aPseudo.IsEmpty()) {
+ // We need to make sure that the requested pseudo element type
+ // matches the selector pseudo element type before proceeding.
+ nsCOMPtr<nsIAtom> pseudoElt = NS_Atomize(aPseudo);
+ if (sel->mSelectors->PseudoType() != nsCSSPseudoElements::
+ GetPseudoType(pseudoElt, CSSEnabledState::eIgnoreEnabledState)) {
+ *aMatches = false;
+ return NS_OK;
+ }
+
+ // We have a matching pseudo element, now remove it so we can compare
+ // directly against |element| when proceeding into SelectorListMatches.
+ // It's OK to do this - we just cloned sel and nothing else is using it.
+ sel->RemoveRightmostSelector();
+ }
+
+ element->OwnerDoc()->FlushPendingLinkUpdates();
+ // XXXbz what exactly should we do with visited state here?
+ TreeMatchContext matchingContext(false,
+ nsRuleWalker::eRelevantLinkUnvisited,
+ element->OwnerDoc(),
+ TreeMatchContext::eNeverMatchVisited);
+ *aMatches = nsCSSRuleProcessor::SelectorListMatches(element, matchingContext,
+ sel);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::IsInheritedProperty(const nsAString &aPropertyName, bool *_retval)
+{
+ nsCSSPropertyID prop = nsCSSProps::
+ LookupProperty(aPropertyName, CSSEnabledState::eIgnoreEnabledState);
+ if (prop == eCSSProperty_UNKNOWN) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ if (prop == eCSSPropertyExtra_variable) {
+ *_retval = true;
+ return NS_OK;
+ }
+
+ if (nsCSSProps::IsShorthand(prop)) {
+ prop = nsCSSProps::SubpropertyEntryFor(prop)[0];
+ }
+
+ nsStyleStructID sid = nsCSSProps::kSIDTable[prop];
+ *_retval = !nsCachedStyleData::IsReset(sid);
+ return NS_OK;
+}
+
+extern const char* const kCSSRawProperties[];
+
+NS_IMETHODIMP
+inDOMUtils::GetCSSPropertyNames(uint32_t aFlags, uint32_t* aCount,
+ char16_t*** aProps)
+{
+ // maxCount is the largest number of properties we could have; our actual
+ // number might be smaller because properties might be disabled.
+ uint32_t maxCount;
+ if (aFlags & EXCLUDE_SHORTHANDS) {
+ maxCount = eCSSProperty_COUNT_no_shorthands;
+ } else {
+ maxCount = eCSSProperty_COUNT;
+ }
+
+ if (aFlags & INCLUDE_ALIASES) {
+ maxCount += (eCSSProperty_COUNT_with_aliases - eCSSProperty_COUNT);
+ }
+
+ char16_t** props =
+ static_cast<char16_t**>(moz_xmalloc(maxCount * sizeof(char16_t*)));
+
+#define DO_PROP(_prop) \
+ PR_BEGIN_MACRO \
+ nsCSSPropertyID cssProp = nsCSSPropertyID(_prop); \
+ if (nsCSSProps::IsEnabled(cssProp, CSSEnabledState::eForAllContent)) { \
+ props[propCount] = \
+ ToNewUnicode(nsDependentCString(kCSSRawProperties[_prop])); \
+ ++propCount; \
+ } \
+ PR_END_MACRO
+
+ // prop is the property id we're considering; propCount is how many properties
+ // we've put into props so far.
+ uint32_t prop = 0, propCount = 0;
+ for ( ; prop < eCSSProperty_COUNT_no_shorthands; ++prop) {
+ if (nsCSSProps::PropertyParseType(nsCSSPropertyID(prop)) !=
+ CSS_PROPERTY_PARSE_INACCESSIBLE) {
+ DO_PROP(prop);
+ }
+ }
+
+ if (!(aFlags & EXCLUDE_SHORTHANDS)) {
+ for ( ; prop < eCSSProperty_COUNT; ++prop) {
+ // Some shorthands are also aliases
+ if ((aFlags & INCLUDE_ALIASES) ||
+ !nsCSSProps::PropHasFlags(nsCSSPropertyID(prop),
+ CSS_PROPERTY_IS_ALIAS)) {
+ DO_PROP(prop);
+ }
+ }
+ }
+
+ if (aFlags & INCLUDE_ALIASES) {
+ for (prop = eCSSProperty_COUNT; prop < eCSSProperty_COUNT_with_aliases; ++prop) {
+ DO_PROP(prop);
+ }
+ }
+
+#undef DO_PROP
+
+ *aCount = propCount;
+ *aProps = props;
+
+ return NS_OK;
+}
+
+static void InsertNoDuplicates(nsTArray<nsString>& aArray,
+ const nsAString& aString)
+{
+ size_t i = aArray.IndexOfFirstElementGt(aString);
+ if (i > 0 && aArray[i-1].Equals(aString)) {
+ return;
+ }
+ aArray.InsertElementAt(i, aString);
+}
+
+static void GetKeywordsForProperty(const nsCSSPropertyID aProperty,
+ nsTArray<nsString>& aArray)
+{
+ if (nsCSSProps::IsShorthand(aProperty)) {
+ // Shorthand props have no keywords.
+ return;
+ }
+ const nsCSSProps::KTableEntry* keywordTable =
+ nsCSSProps::kKeywordTableTable[aProperty];
+ if (keywordTable) {
+ for (size_t i = 0; keywordTable[i].mKeyword != eCSSKeyword_UNKNOWN; ++i) {
+ nsCSSKeyword word = keywordTable[i].mKeyword;
+ InsertNoDuplicates(aArray,
+ NS_ConvertASCIItoUTF16(nsCSSKeywords::GetStringValue(word)));
+ }
+ }
+}
+
+static void GetColorsForProperty(const uint32_t aParserVariant,
+ nsTArray<nsString>& aArray)
+{
+ if (aParserVariant & VARIANT_COLOR) {
+ // GetKeywordsForProperty and GetOtherValuesForProperty assume aArray is sorted,
+ // and if aArray is not empty here, then it's not going to be sorted coming out.
+ MOZ_ASSERT(aArray.Length() == 0);
+ size_t size;
+ const char * const *allColorNames = NS_AllColorNames(&size);
+ nsString* utf16Names = aArray.AppendElements(size);
+ for (size_t i = 0; i < size; i++) {
+ CopyASCIItoUTF16(allColorNames[i], utf16Names[i]);
+ }
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("currentColor"));
+ }
+ return;
+}
+
+static void GetOtherValuesForProperty(const uint32_t aParserVariant,
+ nsTArray<nsString>& aArray)
+{
+ if (aParserVariant & VARIANT_AUTO) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("auto"));
+ }
+ if (aParserVariant & VARIANT_NORMAL) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("normal"));
+ }
+ if(aParserVariant & VARIANT_ALL) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("all"));
+ }
+ if (aParserVariant & VARIANT_NONE) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("none"));
+ }
+ if (aParserVariant & VARIANT_ELEMENT) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-element"));
+ }
+ if (aParserVariant & VARIANT_IMAGE_RECT) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-image-rect"));
+ }
+ if (aParserVariant & VARIANT_COLOR) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("rgb"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("hsl"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("rgba"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("hsla"));
+ }
+ if (aParserVariant & VARIANT_TIMING_FUNCTION) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("cubic-bezier"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("steps"));
+ }
+ if (aParserVariant & VARIANT_CALC) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("calc"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-calc"));
+ }
+ if (aParserVariant & VARIANT_URL) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("url"));
+ }
+ if (aParserVariant & VARIANT_GRADIENT) {
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("linear-gradient"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("radial-gradient"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("repeating-linear-gradient"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("repeating-radial-gradient"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-linear-gradient"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-radial-gradient"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-repeating-linear-gradient"));
+ InsertNoDuplicates(aArray, NS_LITERAL_STRING("-moz-repeating-radial-gradient"));
+ }
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetSubpropertiesForCSSProperty(const nsAString& aProperty,
+ uint32_t* aLength,
+ char16_t*** aValues)
+{
+ nsCSSPropertyID propertyID =
+ nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent);
+
+ if (propertyID == eCSSProperty_UNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (propertyID == eCSSPropertyExtra_variable) {
+ *aValues = static_cast<char16_t**>(moz_xmalloc(sizeof(char16_t*)));
+ (*aValues)[0] = ToNewUnicode(aProperty);
+ *aLength = 1;
+ return NS_OK;
+ }
+
+ if (!nsCSSProps::IsShorthand(propertyID)) {
+ *aValues = static_cast<char16_t**>(moz_xmalloc(sizeof(char16_t*)));
+ (*aValues)[0] = ToNewUnicode(nsCSSProps::GetStringValue(propertyID));
+ *aLength = 1;
+ return NS_OK;
+ }
+
+ // Count up how many subproperties we have.
+ size_t subpropCount = 0;
+ for (const nsCSSPropertyID *props = nsCSSProps::SubpropertyEntryFor(propertyID);
+ *props != eCSSProperty_UNKNOWN; ++props) {
+ ++subpropCount;
+ }
+
+ *aValues =
+ static_cast<char16_t**>(moz_xmalloc(subpropCount * sizeof(char16_t*)));
+ *aLength = subpropCount;
+ for (const nsCSSPropertyID *props = nsCSSProps::SubpropertyEntryFor(propertyID),
+ *props_start = props;
+ *props != eCSSProperty_UNKNOWN; ++props) {
+ (*aValues)[props-props_start] = ToNewUnicode(nsCSSProps::GetStringValue(*props));
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::CssPropertyIsShorthand(const nsAString& aProperty, bool *_retval)
+{
+ nsCSSPropertyID propertyID =
+ nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent);
+ if (propertyID == eCSSProperty_UNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (propertyID == eCSSPropertyExtra_variable) {
+ *_retval = false;
+ } else {
+ *_retval = nsCSSProps::IsShorthand(propertyID);
+ }
+ return NS_OK;
+}
+
+// A helper function that determines whether the given property
+// supports the given type.
+static bool
+PropertySupportsVariant(nsCSSPropertyID aPropertyID, uint32_t aVariant)
+{
+ if (nsCSSProps::IsShorthand(aPropertyID)) {
+ // We need a special case for border here, because while it resets
+ // border-image, it can't actually parse an image.
+ if (aPropertyID == eCSSProperty_border) {
+ return (aVariant & (VARIANT_COLOR | VARIANT_LENGTH)) != 0;
+ }
+
+ for (const nsCSSPropertyID* props = nsCSSProps::SubpropertyEntryFor(aPropertyID);
+ *props != eCSSProperty_UNKNOWN; ++props) {
+ if (PropertySupportsVariant(*props, aVariant)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // Properties that are parsed by functions must have their
+ // attributes hand-maintained here.
+ if (nsCSSProps::PropHasFlags(aPropertyID, CSS_PROPERTY_VALUE_PARSER_FUNCTION) ||
+ nsCSSProps::PropertyParseType(aPropertyID) == CSS_PROPERTY_PARSE_FUNCTION) {
+ // These must all be special-cased.
+ uint32_t supported;
+ switch (aPropertyID) {
+ case eCSSProperty_border_image_slice:
+ case eCSSProperty_grid_template:
+ case eCSSProperty_grid:
+ supported = VARIANT_PN;
+ break;
+
+ case eCSSProperty_border_image_outset:
+ supported = VARIANT_LN;
+ break;
+
+ case eCSSProperty_border_image_width:
+ case eCSSProperty_stroke_dasharray:
+ supported = VARIANT_LPN;
+ break;
+
+ case eCSSProperty_border_top_left_radius:
+ case eCSSProperty_border_top_right_radius:
+ case eCSSProperty_border_bottom_left_radius:
+ case eCSSProperty_border_bottom_right_radius:
+ case eCSSProperty_background_position:
+ case eCSSProperty_background_position_x:
+ case eCSSProperty_background_position_y:
+ case eCSSProperty_background_size:
+#ifdef MOZ_ENABLE_MASK_AS_SHORTHAND
+ case eCSSProperty_mask_position:
+ case eCSSProperty_mask_position_x:
+ case eCSSProperty_mask_position_y:
+ case eCSSProperty_mask_size:
+#endif
+ case eCSSProperty_grid_auto_columns:
+ case eCSSProperty_grid_auto_rows:
+ case eCSSProperty_grid_template_columns:
+ case eCSSProperty_grid_template_rows:
+ case eCSSProperty_object_position:
+ case eCSSProperty_scroll_snap_coordinate:
+ case eCSSProperty_scroll_snap_destination:
+ case eCSSProperty_transform_origin:
+ case eCSSProperty_perspective_origin:
+ case eCSSProperty__moz_outline_radius_topLeft:
+ case eCSSProperty__moz_outline_radius_topRight:
+ case eCSSProperty__moz_outline_radius_bottomLeft:
+ case eCSSProperty__moz_outline_radius_bottomRight:
+ supported = VARIANT_LP;
+ break;
+
+ case eCSSProperty_border_bottom_colors:
+ case eCSSProperty_border_left_colors:
+ case eCSSProperty_border_right_colors:
+ case eCSSProperty_border_top_colors:
+ supported = VARIANT_COLOR;
+ break;
+
+ case eCSSProperty_text_shadow:
+ case eCSSProperty_box_shadow:
+ supported = VARIANT_LENGTH | VARIANT_COLOR;
+ break;
+
+ case eCSSProperty_border_spacing:
+ supported = VARIANT_LENGTH;
+ break;
+
+ case eCSSProperty_content:
+ case eCSSProperty_cursor:
+ case eCSSProperty_clip_path:
+ case eCSSProperty_shape_outside:
+ supported = VARIANT_URL;
+ break;
+
+ case eCSSProperty_fill:
+ case eCSSProperty_stroke:
+ supported = VARIANT_COLOR | VARIANT_URL;
+ break;
+
+ case eCSSProperty_image_orientation:
+ supported = VARIANT_ANGLE;
+ break;
+
+ case eCSSProperty_filter:
+ supported = VARIANT_URL;
+ break;
+
+ case eCSSProperty_grid_column_start:
+ case eCSSProperty_grid_column_end:
+ case eCSSProperty_grid_row_start:
+ case eCSSProperty_grid_row_end:
+ case eCSSProperty_font_weight:
+ case eCSSProperty_initial_letter:
+ supported = VARIANT_NUMBER;
+ break;
+
+ default:
+ supported = 0;
+ break;
+ }
+
+ return (supported & aVariant) != 0;
+ }
+
+ return (nsCSSProps::ParserVariant(aPropertyID) & aVariant) != 0;
+}
+
+NS_IMETHODIMP
+inDOMUtils::CssPropertySupportsType(const nsAString& aProperty, uint32_t aType,
+ bool *_retval)
+{
+ nsCSSPropertyID propertyID =
+ nsCSSProps::LookupProperty(aProperty, CSSEnabledState::eForAllContent);
+ if (propertyID == eCSSProperty_UNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (propertyID >= eCSSProperty_COUNT) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ uint32_t variant;
+ switch (aType) {
+ case TYPE_LENGTH:
+ variant = VARIANT_LENGTH;
+ break;
+ case TYPE_PERCENTAGE:
+ variant = VARIANT_PERCENT;
+ break;
+ case TYPE_COLOR:
+ variant = VARIANT_COLOR;
+ break;
+ case TYPE_URL:
+ variant = VARIANT_URL;
+ break;
+ case TYPE_ANGLE:
+ variant = VARIANT_ANGLE;
+ break;
+ case TYPE_FREQUENCY:
+ variant = VARIANT_FREQUENCY;
+ break;
+ case TYPE_TIME:
+ variant = VARIANT_TIME;
+ break;
+ case TYPE_GRADIENT:
+ variant = VARIANT_GRADIENT;
+ break;
+ case TYPE_TIMING_FUNCTION:
+ variant = VARIANT_TIMING_FUNCTION;
+ break;
+ case TYPE_IMAGE_RECT:
+ variant = VARIANT_IMAGE_RECT;
+ break;
+ case TYPE_NUMBER:
+ // Include integers under "number"?
+ variant = VARIANT_NUMBER | VARIANT_INTEGER;
+ break;
+ default:
+ // Unknown type
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *_retval = PropertySupportsVariant(propertyID, variant);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetCSSValuesForProperty(const nsAString& aProperty,
+ uint32_t* aLength,
+ char16_t*** aValues)
+{
+ nsCSSPropertyID propertyID = nsCSSProps::
+ LookupProperty(aProperty, CSSEnabledState::eForAllContent);
+ if (propertyID == eCSSProperty_UNKNOWN) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsTArray<nsString> array;
+ // We start collecting the values, BUT colors need to go in first, because array
+ // needs to stay sorted, and the colors are sorted, so we just append them.
+ if (propertyID == eCSSPropertyExtra_variable) {
+ // No other values we can report.
+ } else if (!nsCSSProps::IsShorthand(propertyID)) {
+ // Property is longhand.
+ uint32_t propertyParserVariant = nsCSSProps::ParserVariant(propertyID);
+ // Get colors first.
+ GetColorsForProperty(propertyParserVariant, array);
+ if (propertyParserVariant & VARIANT_KEYWORD) {
+ GetKeywordsForProperty(propertyID, array);
+ }
+ GetOtherValuesForProperty(propertyParserVariant, array);
+ } else {
+ // Property is shorthand.
+ CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subproperty, propertyID,
+ CSSEnabledState::eForAllContent) {
+ // Get colors (once) first.
+ uint32_t propertyParserVariant = nsCSSProps::ParserVariant(*subproperty);
+ if (propertyParserVariant & VARIANT_COLOR) {
+ GetColorsForProperty(propertyParserVariant, array);
+ break;
+ }
+ }
+ CSSPROPS_FOR_SHORTHAND_SUBPROPERTIES(subproperty, propertyID,
+ CSSEnabledState::eForAllContent) {
+ uint32_t propertyParserVariant = nsCSSProps::ParserVariant(*subproperty);
+ if (propertyParserVariant & VARIANT_KEYWORD) {
+ GetKeywordsForProperty(*subproperty, array);
+ }
+ GetOtherValuesForProperty(propertyParserVariant, array);
+ }
+ }
+ // All CSS properties take initial, inherit and unset.
+ InsertNoDuplicates(array, NS_LITERAL_STRING("initial"));
+ InsertNoDuplicates(array, NS_LITERAL_STRING("inherit"));
+ InsertNoDuplicates(array, NS_LITERAL_STRING("unset"));
+
+ *aLength = array.Length();
+ char16_t** ret =
+ static_cast<char16_t**>(moz_xmalloc(*aLength * sizeof(char16_t*)));
+ for (uint32_t i = 0; i < *aLength; ++i) {
+ ret[i] = ToNewUnicode(array[i]);
+ }
+ *aValues = ret;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::ColorNameToRGB(const nsAString& aColorName, JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ nscolor color;
+ if (!NS_ColorNameToRGB(aColorName, &color)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ InspectorRGBTriple triple;
+ triple.mR = NS_GET_R(color);
+ triple.mG = NS_GET_G(color);
+ triple.mB = NS_GET_B(color);
+
+ if (!ToJSValue(aCx, triple, aValue)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::RgbToColorName(uint8_t aR, uint8_t aG, uint8_t aB,
+ nsAString& aColorName)
+{
+ const char* color = NS_RGBToColorName(NS_RGB(aR, aG, aB));
+ if (!color) {
+ aColorName.Truncate();
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ aColorName.AssignASCII(color);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::ColorToRGBA(const nsAString& aColorString, JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue)
+{
+ nscolor color = 0;
+ nsCSSParser cssParser;
+ nsCSSValue cssValue;
+
+ bool isColor = cssParser.ParseColorString(aColorString, nullptr, 0,
+ cssValue, true);
+
+ if (!isColor) {
+ aValue.setNull();
+ return NS_OK;
+ }
+
+ nsRuleNode::ComputeColor(cssValue, nullptr, nullptr, color);
+
+ InspectorRGBATuple tuple;
+ tuple.mR = NS_GET_R(color);
+ tuple.mG = NS_GET_G(color);
+ tuple.mB = NS_GET_B(color);
+ tuple.mA = nsStyleUtil::ColorComponentToFloat(NS_GET_A(color));
+
+ if (!ToJSValue(aCx, tuple, aValue)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::IsValidCSSColor(const nsAString& aColorString, bool *_retval)
+{
+ nsCSSParser cssParser;
+ nsCSSValue cssValue;
+ *_retval = cssParser.ParseColorString(aColorString, nullptr, 0, cssValue, true);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::CssPropertyIsValid(const nsAString& aPropertyName,
+ const nsAString& aPropertyValue,
+ bool *_retval)
+{
+ nsCSSPropertyID propertyID = nsCSSProps::
+ LookupProperty(aPropertyName, CSSEnabledState::eIgnoreEnabledState);
+
+ if (propertyID == eCSSProperty_UNKNOWN) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ if (propertyID == eCSSPropertyExtra_variable) {
+ *_retval = true;
+ return NS_OK;
+ }
+
+ // Get a parser, parse the property.
+ nsCSSParser parser;
+ *_retval = parser.IsValueValidForProperty(propertyID, aPropertyValue);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetBindingURLs(nsIDOMElement *aElement, nsIArray **_retval)
+{
+ NS_ENSURE_ARG_POINTER(aElement);
+
+ *_retval = nullptr;
+
+ nsCOMPtr<nsIMutableArray> urls = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (!urls)
+ return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
+ NS_ENSURE_ARG_POINTER(content);
+
+ nsXBLBinding *binding = content->GetXBLBinding();
+
+ while (binding) {
+ urls->AppendElement(binding->PrototypeBinding()->BindingURI(), false);
+ binding = binding->GetBaseBinding();
+ }
+
+ urls.forget(_retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::SetContentState(nsIDOMElement* aElement,
+ EventStates::InternalType aState,
+ bool* aRetVal)
+{
+ NS_ENSURE_ARG_POINTER(aElement);
+
+ RefPtr<EventStateManager> esm =
+ inLayoutUtils::GetEventStateManagerFor(aElement);
+ NS_ENSURE_TRUE(esm, NS_ERROR_INVALID_ARG);
+
+ nsCOMPtr<nsIContent> content;
+ content = do_QueryInterface(aElement);
+ NS_ENSURE_TRUE(content, NS_ERROR_INVALID_ARG);
+
+ *aRetVal = esm->SetContentState(content, EventStates(aState));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::RemoveContentState(nsIDOMElement* aElement,
+ EventStates::InternalType aState,
+ bool* aRetVal)
+{
+ NS_ENSURE_ARG_POINTER(aElement);
+
+ RefPtr<EventStateManager> esm =
+ inLayoutUtils::GetEventStateManagerFor(aElement);
+ NS_ENSURE_TRUE(esm, NS_ERROR_INVALID_ARG);
+
+ *aRetVal = esm->SetContentState(nullptr, EventStates(aState));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetContentState(nsIDOMElement* aElement,
+ EventStates::InternalType* aState)
+{
+ *aState = 0;
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
+ NS_ENSURE_ARG_POINTER(content);
+
+ // NOTE: if this method is removed,
+ // please remove GetInternalValue from EventStates
+ *aState = content->AsElement()->State().GetInternalValue();
+ return NS_OK;
+}
+
+/* static */ nsresult
+inDOMUtils::GetRuleNodeForElement(dom::Element* aElement,
+ nsIAtom* aPseudo,
+ nsStyleContext** aStyleContext,
+ nsRuleNode** aRuleNode)
+{
+ MOZ_ASSERT(aElement);
+
+ *aRuleNode = nullptr;
+ *aStyleContext = nullptr;
+
+ nsIDocument* doc = aElement->GetComposedDoc();
+ NS_ENSURE_TRUE(doc, NS_ERROR_UNEXPECTED);
+
+ nsIPresShell *presShell = doc->GetShell();
+ NS_ENSURE_TRUE(presShell, NS_ERROR_UNEXPECTED);
+
+ nsPresContext *presContext = presShell->GetPresContext();
+ NS_ENSURE_TRUE(presContext, NS_ERROR_UNEXPECTED);
+
+ presContext->EnsureSafeToHandOutCSSRules();
+
+ RefPtr<nsStyleContext> sContext =
+ nsComputedDOMStyle::GetStyleContextForElement(aElement, aPseudo, presShell);
+ if (sContext) {
+ *aRuleNode = sContext->RuleNode();
+ sContext.forget(aStyleContext);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetUsedFontFaces(nsIDOMRange* aRange,
+ nsIDOMFontFaceList** aFontFaceList)
+{
+ return static_cast<nsRange*>(aRange)->GetUsedFontFaces(aFontFaceList);
+}
+
+static EventStates
+GetStatesForPseudoClass(const nsAString& aStatePseudo)
+{
+ // An array of the states that are relevant for various pseudoclasses.
+ // XXXbz this duplicates code in nsCSSRuleProcessor
+ static const EventStates sPseudoClassStates[] = {
+#define CSS_PSEUDO_CLASS(_name, _value, _flags, _pref) \
+ EventStates(),
+#define CSS_STATE_PSEUDO_CLASS(_name, _value, _flags, _pref, _states) \
+ _states,
+#include "nsCSSPseudoClassList.h"
+#undef CSS_STATE_PSEUDO_CLASS
+#undef CSS_PSEUDO_CLASS
+
+ // Add more entries for our fake values to make sure we can't
+ // index out of bounds into this array no matter what.
+ EventStates(),
+ EventStates()
+ };
+ static_assert(MOZ_ARRAY_LENGTH(sPseudoClassStates) ==
+ static_cast<size_t>(CSSPseudoClassType::MAX),
+ "Length of PseudoClassStates array is incorrect");
+
+ nsCOMPtr<nsIAtom> atom = NS_Atomize(aStatePseudo);
+ CSSPseudoClassType type = nsCSSPseudoClasses::
+ GetPseudoType(atom, CSSEnabledState::eIgnoreEnabledState);
+
+ // Ignore :any-link so we don't give the element simultaneous
+ // visited and unvisited style state
+ if (type == CSSPseudoClassType::anyLink ||
+ type == CSSPseudoClassType::mozAnyLink) {
+ return EventStates();
+ }
+ // Our array above is long enough that indexing into it with
+ // NotPseudo is ok.
+ return sPseudoClassStates[static_cast<CSSPseudoClassTypeBase>(type)];
+}
+
+NS_IMETHODIMP
+inDOMUtils::GetCSSPseudoElementNames(uint32_t* aLength, char16_t*** aNames)
+{
+ nsTArray<nsIAtom*> array;
+
+ const CSSPseudoElementTypeBase pseudoCount =
+ static_cast<CSSPseudoElementTypeBase>(CSSPseudoElementType::Count);
+ for (CSSPseudoElementTypeBase i = 0; i < pseudoCount; ++i) {
+ CSSPseudoElementType type = static_cast<CSSPseudoElementType>(i);
+ if (nsCSSPseudoElements::IsEnabled(type, CSSEnabledState::eForAllContent)) {
+ nsIAtom* atom = nsCSSPseudoElements::GetPseudoAtom(type);
+ array.AppendElement(atom);
+ }
+ }
+
+ *aLength = array.Length();
+ char16_t** ret =
+ static_cast<char16_t**>(moz_xmalloc(*aLength * sizeof(char16_t*)));
+ for (uint32_t i = 0; i < *aLength; ++i) {
+ ret[i] = ToNewUnicode(nsDependentAtomString(array[i]));
+ }
+ *aNames = ret;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::AddPseudoClassLock(nsIDOMElement *aElement,
+ const nsAString &aPseudoClass)
+{
+ EventStates state = GetStatesForPseudoClass(aPseudoClass);
+ if (state.IsEmpty()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement);
+ NS_ENSURE_ARG_POINTER(element);
+
+ element->LockStyleStates(state);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::RemovePseudoClassLock(nsIDOMElement *aElement,
+ const nsAString &aPseudoClass)
+{
+ EventStates state = GetStatesForPseudoClass(aPseudoClass);
+ if (state.IsEmpty()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement);
+ NS_ENSURE_ARG_POINTER(element);
+
+ element->UnlockStyleStates(state);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::HasPseudoClassLock(nsIDOMElement *aElement,
+ const nsAString &aPseudoClass,
+ bool *_retval)
+{
+ EventStates state = GetStatesForPseudoClass(aPseudoClass);
+ if (state.IsEmpty()) {
+ *_retval = false;
+ return NS_OK;
+ }
+
+ nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement);
+ NS_ENSURE_ARG_POINTER(element);
+
+ EventStates locks = element->LockedStyleStates();
+
+ *_retval = locks.HasAllStates(state);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::ClearPseudoClassLocks(nsIDOMElement *aElement)
+{
+ nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(aElement);
+ NS_ENSURE_ARG_POINTER(element);
+
+ element->ClearStyleStateLocks();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMUtils::ParseStyleSheet(nsIDOMCSSStyleSheet *aSheet,
+ const nsAString& aInput)
+{
+ RefPtr<CSSStyleSheet> sheet = do_QueryObject(aSheet);
+ NS_ENSURE_ARG_POINTER(sheet);
+
+ return sheet->ReparseSheet(aInput);
+}
+
+NS_IMETHODIMP
+inDOMUtils::ScrollElementIntoView(nsIDOMElement *aElement)
+{
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
+ NS_ENSURE_ARG_POINTER(content);
+
+ nsIPresShell* presShell = content->OwnerDoc()->GetShell();
+ if (!presShell) {
+ return NS_OK;
+ }
+
+ presShell->ScrollContentIntoView(content,
+ nsIPresShell::ScrollAxis(),
+ nsIPresShell::ScrollAxis(),
+ nsIPresShell::SCROLL_OVERFLOW_HIDDEN);
+
+ return NS_OK;
+}
diff --git a/layout/inspector/inDOMUtils.h b/layout/inspector/inDOMUtils.h
new file mode 100644
index 000000000..e12915d16
--- /dev/null
+++ b/layout/inspector/inDOMUtils.h
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __inDOMUtils_h__
+#define __inDOMUtils_h__
+
+#include "inIDOMUtils.h"
+
+class nsRuleNode;
+class nsStyleContext;
+class nsIAtom;
+
+namespace mozilla {
+namespace dom {
+class Element;
+} // namespace dom
+} // namespace mozilla
+
+class inDOMUtils final : public inIDOMUtils
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_INIDOMUTILS
+
+ inDOMUtils();
+
+private:
+ virtual ~inDOMUtils();
+
+ // aStyleContext must be released by the caller once he's done with aRuleNode.
+ static nsresult GetRuleNodeForElement(mozilla::dom::Element* aElement,
+ nsIAtom* aPseudo,
+ nsStyleContext** aStyleContext,
+ nsRuleNode** aRuleNode);
+};
+
+// {0a499822-a287-4089-ad3f-9ffcd4f40263}
+#define IN_DOMUTILS_CID \
+ {0x0a499822, 0xa287, 0x4089, {0xad, 0x3f, 0x9f, 0xfc, 0xd4, 0xf4, 0x02, 0x63}}
+
+#endif // __inDOMUtils_h__
diff --git a/layout/inspector/inDOMView.cpp b/layout/inspector/inDOMView.cpp
new file mode 100644
index 000000000..967671ba3
--- /dev/null
+++ b/layout/inspector/inDOMView.cpp
@@ -0,0 +1,1283 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "inDOMView.h"
+#include "inIDOMUtils.h"
+
+#include "inLayoutUtils.h"
+
+#include "nsString.h"
+#include "nsReadableUtils.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMNodeFilter.h"
+#include "nsIDOMNodeList.h"
+#include "nsIDOMCharacterData.h"
+#include "nsIDOMAttr.h"
+#include "nsIDOMMozNamedAttrMap.h"
+#include "nsIDOMMutationEvent.h"
+#include "nsBindingManager.h"
+#include "nsNameSpaceManager.h"
+#include "nsIDocument.h"
+#include "nsIServiceManager.h"
+#include "nsITreeColumns.h"
+#include "nsITreeBoxObject.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/Services.h"
+
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+
+using namespace mozilla;
+
+////////////////////////////////////////////////////////////////////////
+// inDOMViewNode
+
+class inDOMViewNode
+{
+public:
+ inDOMViewNode() {}
+ explicit inDOMViewNode(nsIDOMNode* aNode);
+ ~inDOMViewNode();
+
+ nsCOMPtr<nsIDOMNode> node;
+
+ inDOMViewNode* parent;
+ inDOMViewNode* next;
+ inDOMViewNode* previous;
+
+ int32_t level;
+ bool isOpen;
+ bool isContainer;
+ bool hasAnonymous;
+ bool hasSubDocument;
+};
+
+inDOMViewNode::inDOMViewNode(nsIDOMNode* aNode) :
+ node(aNode),
+ parent(nullptr),
+ next(nullptr),
+ previous(nullptr),
+ level(0),
+ isOpen(false),
+ isContainer(false),
+ hasAnonymous(false),
+ hasSubDocument(false)
+{
+
+}
+
+inDOMViewNode::~inDOMViewNode()
+{
+}
+
+////////////////////////////////////////////////////////////////////////
+
+inDOMView::inDOMView() :
+ mShowAnonymous(false),
+ mShowSubDocuments(false),
+ mShowWhitespaceNodes(true),
+ mShowAccessibleNodes(false),
+ mWhatToShow(nsIDOMNodeFilter::SHOW_ALL)
+{
+}
+
+inDOMView::~inDOMView()
+{
+ SetRootNode(nullptr);
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_ISUPPORTS(inDOMView,
+ inIDOMView,
+ nsITreeView,
+ nsIMutationObserver)
+
+////////////////////////////////////////////////////////////////////////
+// inIDOMView
+
+NS_IMETHODIMP
+inDOMView::GetRootNode(nsIDOMNode** aNode)
+{
+ *aNode = mRootNode;
+ NS_IF_ADDREF(*aNode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetRootNode(nsIDOMNode* aNode)
+{
+ if (mTree)
+ mTree->BeginUpdateBatch();
+
+ if (mRootDocument) {
+ // remove previous document observer
+ nsCOMPtr<nsINode> doc(do_QueryInterface(mRootDocument));
+ if (doc)
+ doc->RemoveMutationObserver(this);
+ }
+
+ RemoveAllNodes();
+
+ mRootNode = aNode;
+
+ if (aNode) {
+ // If we are able to show element nodes, then start with the root node
+ // as the first node in the buffer
+ if (mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT) {
+ // allocate new node array
+ AppendNode(CreateNode(aNode, nullptr));
+ } else {
+ // place only the children of the root node in the buffer
+ ExpandNode(-1);
+ }
+
+ // store an owning reference to document so that it isn't
+ // destroyed before we are
+ mRootDocument = do_QueryInterface(aNode);
+ if (!mRootDocument) {
+ aNode->GetOwnerDocument(getter_AddRefs(mRootDocument));
+ }
+
+ // add document observer
+ nsCOMPtr<nsINode> doc(do_QueryInterface(mRootDocument));
+ if (doc)
+ doc->AddMutationObserver(this);
+ } else {
+ mRootDocument = nullptr;
+ }
+
+ if (mTree)
+ mTree->EndUpdateBatch();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetNodeFromRowIndex(int32_t rowIndex, nsIDOMNode **_retval)
+{
+ inDOMViewNode* viewNode = nullptr;
+ RowToNode(rowIndex, &viewNode);
+ if (!viewNode) return NS_ERROR_FAILURE;
+ *_retval = viewNode->node;
+ NS_IF_ADDREF(*_retval);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetRowIndexFromNode(nsIDOMNode *node, int32_t *_retval)
+{
+ NodeToRow(node, _retval);
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+inDOMView::GetShowAnonymousContent(bool *aShowAnonymousContent)
+{
+ *aShowAnonymousContent = mShowAnonymous;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetShowAnonymousContent(bool aShowAnonymousContent)
+{
+ mShowAnonymous = aShowAnonymousContent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetShowSubDocuments(bool *aShowSubDocuments)
+{
+ *aShowSubDocuments = mShowSubDocuments;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetShowSubDocuments(bool aShowSubDocuments)
+{
+ mShowSubDocuments = aShowSubDocuments;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetShowWhitespaceNodes(bool *aShowWhitespaceNodes)
+{
+ *aShowWhitespaceNodes = mShowWhitespaceNodes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetShowWhitespaceNodes(bool aShowWhitespaceNodes)
+{
+ mShowWhitespaceNodes = aShowWhitespaceNodes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetShowAccessibleNodes(bool *aShowAccessibleNodes)
+{
+ *aShowAccessibleNodes = mShowAccessibleNodes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetShowAccessibleNodes(bool aShowAccessibleNodes)
+{
+ mShowAccessibleNodes = aShowAccessibleNodes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetWhatToShow(uint32_t *aWhatToShow)
+{
+ *aWhatToShow = mWhatToShow;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetWhatToShow(uint32_t aWhatToShow)
+{
+ mWhatToShow = aWhatToShow;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::Rebuild()
+{
+ nsCOMPtr<nsIDOMNode> root;
+ GetRootNode(getter_AddRefs(root));
+ SetRootNode(root);
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsITreeView
+
+NS_IMETHODIMP
+inDOMView::GetRowCount(int32_t *aRowCount)
+{
+ *aRowCount = GetRowCount();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetRowProperties(int32_t index, nsAString& aProps)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetCellProperties(int32_t row, nsITreeColumn* col,
+ nsAString& aProps)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(row, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(node->node);
+ if (content && content->IsInAnonymousSubtree()) {
+ aProps.AppendLiteral("anonymous ");
+ }
+
+ uint16_t nodeType;
+ node->node->GetNodeType(&nodeType);
+ switch (nodeType) {
+ case nsIDOMNode::ELEMENT_NODE:
+ aProps.AppendLiteral("ELEMENT_NODE");
+ break;
+ case nsIDOMNode::ATTRIBUTE_NODE:
+ aProps.AppendLiteral("ATTRIBUTE_NODE");
+ break;
+ case nsIDOMNode::TEXT_NODE:
+ aProps.AppendLiteral("TEXT_NODE");
+ break;
+ case nsIDOMNode::CDATA_SECTION_NODE:
+ aProps.AppendLiteral("CDATA_SECTION_NODE");
+ break;
+ case nsIDOMNode::ENTITY_REFERENCE_NODE:
+ aProps.AppendLiteral("ENTITY_REFERENCE_NODE");
+ break;
+ case nsIDOMNode::ENTITY_NODE:
+ aProps.AppendLiteral("ENTITY_NODE");
+ break;
+ case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
+ aProps.AppendLiteral("PROCESSING_INSTRUCTION_NODE");
+ break;
+ case nsIDOMNode::COMMENT_NODE:
+ aProps.AppendLiteral("COMMENT_NODE");
+ break;
+ case nsIDOMNode::DOCUMENT_NODE:
+ aProps.AppendLiteral("DOCUMENT_NODE");
+ break;
+ case nsIDOMNode::DOCUMENT_TYPE_NODE:
+ aProps.AppendLiteral("DOCUMENT_TYPE_NODE");
+ break;
+ case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
+ aProps.AppendLiteral("DOCUMENT_FRAGMENT_NODE");
+ break;
+ case nsIDOMNode::NOTATION_NODE:
+ aProps.AppendLiteral("NOTATION_NODE");
+ break;
+ }
+
+#ifdef ACCESSIBILITY
+ if (mShowAccessibleNodes) {
+ nsAccessibilityService* accService = GetOrCreateAccService();
+ NS_ENSURE_TRUE(accService, NS_ERROR_FAILURE);
+
+ if (accService->HasAccessible(node->node))
+ aProps.AppendLiteral(" ACCESSIBLE_NODE");
+ }
+#endif
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetColumnProperties(nsITreeColumn* col, nsAString& aProps)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetImageSrc(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetProgressMode(int32_t row, nsITreeColumn* col, int32_t* _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetCellValue(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetCellText(int32_t row, nsITreeColumn* col, nsAString& _retval)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(row, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ nsIDOMNode* domNode = node->node;
+
+ nsAutoString colID;
+ col->GetId(colID);
+ if (colID.EqualsLiteral("colNodeName"))
+ domNode->GetNodeName(_retval);
+ else if (colID.EqualsLiteral("colLocalName"))
+ domNode->GetLocalName(_retval);
+ else if (colID.EqualsLiteral("colPrefix"))
+ domNode->GetPrefix(_retval);
+ else if (colID.EqualsLiteral("colNamespaceURI"))
+ domNode->GetNamespaceURI(_retval);
+ else if (colID.EqualsLiteral("colNodeType")) {
+ uint16_t nodeType;
+ domNode->GetNodeType(&nodeType);
+ nsAutoString temp;
+ temp.AppendInt(int32_t(nodeType));
+ _retval = temp;
+ } else if (colID.EqualsLiteral("colNodeValue"))
+ domNode->GetNodeValue(_retval);
+ else {
+ if (StringBeginsWith(colID, NS_LITERAL_STRING("col@"))) {
+ nsCOMPtr<nsIDOMElement> el = do_QueryInterface(node->node);
+ if (el) {
+ nsAutoString attr;
+ colID.Right(attr, colID.Length()-4); // have to use this because Substring is crashing on me!
+ el->GetAttribute(attr, _retval);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::IsContainer(int32_t index, bool *_retval)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(index, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ *_retval = node->isContainer;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::IsContainerOpen(int32_t index, bool *_retval)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(index, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ *_retval = node->isOpen;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::IsContainerEmpty(int32_t index, bool *_retval)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(index, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ *_retval = node->isContainer ? false : true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetLevel(int32_t index, int32_t *_retval)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(index, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ *_retval = node->level;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetParentIndex(int32_t rowIndex, int32_t *_retval)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(rowIndex, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ // GetParentIndex returns -1 if there is no parent
+ *_retval = -1;
+
+ inDOMViewNode* checkNode = nullptr;
+ int32_t i = rowIndex - 1;
+ do {
+ nsresult rv = RowToNode(i, &checkNode);
+ if (NS_FAILED(rv)) {
+ // No parent. Just break out.
+ break;
+ }
+
+ if (checkNode == node->parent) {
+ *_retval = i;
+ return NS_OK;
+ }
+ --i;
+ } while (checkNode);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::HasNextSibling(int32_t rowIndex, int32_t afterIndex, bool *_retval)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(rowIndex, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ *_retval = node->next != nullptr;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::ToggleOpenState(int32_t index)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(index, &node);
+ if (!node) return NS_ERROR_FAILURE;
+
+ int32_t oldCount = GetRowCount();
+ if (node->isOpen)
+ CollapseNode(index);
+ else
+ ExpandNode(index);
+
+ // Update the twisty.
+ mTree->InvalidateRow(index);
+
+ mTree->RowCountChanged(index+1, GetRowCount() - oldCount);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetTree(nsITreeBoxObject *tree)
+{
+ mTree = tree;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::GetSelection(nsITreeSelection * *aSelection)
+{
+ *aSelection = mSelection;
+ NS_IF_ADDREF(*aSelection);
+ return NS_OK;
+}
+
+NS_IMETHODIMP inDOMView::SetSelection(nsITreeSelection * aSelection)
+{
+ mSelection = aSelection;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SelectionChanged()
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetCellValue(int32_t row, nsITreeColumn* col, const nsAString& value)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::SetCellText(int32_t row, nsITreeColumn* col, const nsAString& value)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::CycleHeader(nsITreeColumn* col)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::CycleCell(int32_t row, nsITreeColumn* col)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::IsEditable(int32_t row, nsITreeColumn* col, bool *_retval)
+{
+ return NS_OK;
+}
+
+
+NS_IMETHODIMP
+inDOMView::IsSelectable(int32_t row, nsITreeColumn* col, bool *_retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::IsSeparator(int32_t index, bool *_retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::IsSorted(bool *_retval)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::CanDrop(int32_t index, int32_t orientation,
+ nsIDOMDataTransfer* aDataTransfer, bool *_retval)
+{
+ *_retval = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::Drop(int32_t row, int32_t orientation, nsIDOMDataTransfer* aDataTransfer)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::PerformAction(const char16_t *action)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::PerformActionOnRow(const char16_t *action, int32_t row)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDOMView::PerformActionOnCell(const char16_t* action, int32_t row, nsITreeColumn* col)
+{
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////
+// nsIMutationObserver
+
+void
+inDOMView::NodeWillBeDestroyed(const nsINode* aNode)
+{
+ NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
+}
+
+void
+inDOMView::AttributeChanged(nsIDocument* aDocument, dom::Element* aElement,
+ int32_t aNameSpaceID, nsIAtom* aAttribute,
+ int32_t aModType,
+ const nsAttrValue* aOldValue)
+{
+ if (!mTree) {
+ return;
+ }
+
+ if (!(mWhatToShow & nsIDOMNodeFilter::SHOW_ATTRIBUTE)) {
+ return;
+ }
+
+ nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
+
+ // get the dom attribute node, if there is any
+ nsCOMPtr<nsIDOMElement> el(do_QueryInterface(aElement));
+ nsCOMPtr<nsIDOMAttr> domAttr;
+ nsDependentAtomString attrStr(aAttribute);
+ if (aNameSpaceID) {
+ nsNameSpaceManager* nsm = nsNameSpaceManager::GetInstance();
+ if (!nsm) {
+ // we can't find out which attribute we want :(
+ return;
+ }
+ nsString attrNS;
+ nsresult rv = nsm->GetNameSpaceURI(aNameSpaceID, attrNS);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ (void)el->GetAttributeNodeNS(attrNS, attrStr, getter_AddRefs(domAttr));
+ } else {
+ (void)el->GetAttributeNode(attrStr, getter_AddRefs(domAttr));
+ }
+
+ if (aModType == nsIDOMMutationEvent::MODIFICATION) {
+ // No fancy stuff here, just invalidate the changed row
+ if (!domAttr) {
+ return;
+ }
+ int32_t row = 0;
+ NodeToRow(domAttr, &row);
+ mTree->InvalidateRange(row, row);
+ } else if (aModType == nsIDOMMutationEvent::ADDITION) {
+ if (!domAttr) {
+ return;
+ }
+ // get the number of attributes on this content node
+ nsCOMPtr<nsIDOMMozNamedAttrMap> attrs;
+ el->GetAttributes(getter_AddRefs(attrs));
+ uint32_t attrCount;
+ attrs->GetLength(&attrCount);
+
+ inDOMViewNode* contentNode = nullptr;
+ int32_t contentRow;
+ int32_t attrRow;
+ if (mRootNode == el &&
+ !(mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT)) {
+ // if this view has a root node but is not displaying it,
+ // it is ok to act as if the changed attribute is on the root.
+ attrRow = attrCount - 1;
+ } else {
+ if (NS_FAILED(NodeToRow(el, &contentRow))) {
+ return;
+ }
+ RowToNode(contentRow, &contentNode);
+ if (!contentNode->isOpen) {
+ return;
+ }
+ attrRow = contentRow + attrCount;
+ }
+
+ inDOMViewNode* newNode = CreateNode(domAttr, contentNode);
+ inDOMViewNode* insertNode = nullptr;
+ RowToNode(attrRow, &insertNode);
+ if (insertNode) {
+ if (contentNode &&
+ insertNode->level <= contentNode->level) {
+ RowToNode(attrRow-1, &insertNode);
+ InsertLinkAfter(newNode, insertNode);
+ } else
+ InsertLinkBefore(newNode, insertNode);
+ }
+ InsertNode(newNode, attrRow);
+ mTree->RowCountChanged(attrRow, 1);
+ } else if (aModType == nsIDOMMutationEvent::REMOVAL) {
+ // At this point, the attribute is already gone from the DOM, but is still represented
+ // in our mRows array. Search through the content node's children for the corresponding
+ // node and remove it.
+
+ // get the row of the content node
+ inDOMViewNode* contentNode = nullptr;
+ int32_t contentRow;
+ int32_t baseLevel;
+ if (NS_SUCCEEDED(NodeToRow(el, &contentRow))) {
+ RowToNode(contentRow, &contentNode);
+ baseLevel = contentNode->level;
+ } else {
+ if (mRootNode == el) {
+ contentRow = -1;
+ baseLevel = -1;
+ } else
+ return;
+ }
+
+ // search for the attribute node that was removed
+ inDOMViewNode* checkNode = nullptr;
+ int32_t row = 0;
+ for (row = contentRow+1; row < GetRowCount(); ++row) {
+ checkNode = GetNodeAt(row);
+ if (checkNode->level == baseLevel+1) {
+ domAttr = do_QueryInterface(checkNode->node);
+ if (domAttr) {
+ nsAutoString attrName;
+ domAttr->GetNodeName(attrName);
+ if (attrName.Equals(attrStr)) {
+ // we have found the row for the attribute that was removed
+ RemoveLink(checkNode);
+ RemoveNode(row);
+ mTree->RowCountChanged(row, -1);
+ break;
+ }
+ }
+ }
+ if (checkNode->level <= baseLevel)
+ break;
+ }
+
+ }
+}
+
+void
+inDOMView::ContentAppended(nsIDocument *aDocument,
+ nsIContent* aContainer,
+ nsIContent* aFirstNewContent,
+ int32_t /* unused */)
+{
+ if (!mTree) {
+ return;
+ }
+
+ for (nsIContent* cur = aFirstNewContent; cur; cur = cur->GetNextSibling()) {
+ // Our ContentInserted impl doesn't use the index
+ ContentInserted(aDocument, aContainer, cur, 0);
+ }
+}
+
+void
+inDOMView::ContentInserted(nsIDocument *aDocument, nsIContent* aContainer,
+ nsIContent* aChild, int32_t /* unused */)
+{
+ if (!mTree)
+ return;
+
+ nsresult rv;
+ nsCOMPtr<nsIDOMNode> childDOMNode(do_QueryInterface(aChild));
+ nsCOMPtr<nsIDOMNode> parent;
+ if (!mDOMUtils) {
+ mDOMUtils = services::GetInDOMUtils();
+ if (!mDOMUtils) {
+ return;
+ }
+ }
+ mDOMUtils->GetParentForNode(childDOMNode, mShowAnonymous,
+ getter_AddRefs(parent));
+
+ // find the inDOMViewNode for the parent of the inserted content
+ int32_t parentRow = 0;
+ if (NS_FAILED(rv = NodeToRow(parent, &parentRow)))
+ return;
+ inDOMViewNode* parentNode = nullptr;
+ if (NS_FAILED(rv = RowToNode(parentRow, &parentNode)))
+ return;
+
+ nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
+
+ if (!parentNode->isOpen) {
+ // Parent is not open, so don't bother creating tree rows for the
+ // kids. But do indicate that it's now a container, if needed.
+ if (!parentNode->isContainer) {
+ parentNode->isContainer = true;
+ mTree->InvalidateRow(parentRow);
+ }
+ return;
+ }
+
+ // get the previous sibling of the inserted content
+ nsCOMPtr<nsIDOMNode> previous;
+ GetRealPreviousSibling(childDOMNode, parent, getter_AddRefs(previous));
+ inDOMViewNode* previousNode = nullptr;
+
+ int32_t row = 0;
+ if (previous) {
+ // find the inDOMViewNode for the previous sibling of the inserted content
+ int32_t previousRow = 0;
+ if (NS_FAILED(rv = NodeToRow(previous, &previousRow)))
+ return;
+ if (NS_FAILED(rv = RowToNode(previousRow, &previousNode)))
+ return;
+
+ // get the last descendant of the previous row, which is the row
+ // after which to insert this new row
+ GetLastDescendantOf(previousNode, previousRow, &row);
+ ++row;
+ } else {
+ // there is no previous sibling, so the new row will be inserted after the parent
+ row = parentRow+1;
+ }
+
+ inDOMViewNode* newNode = CreateNode(childDOMNode, parentNode);
+
+ if (previous) {
+ InsertLinkAfter(newNode, previousNode);
+ } else {
+ int32_t firstChildRow;
+ if (NS_SUCCEEDED(GetFirstDescendantOf(parentNode, parentRow, &firstChildRow))) {
+ inDOMViewNode* firstChild;
+ RowToNode(firstChildRow, &firstChild);
+ InsertLinkBefore(newNode, firstChild);
+ }
+ }
+
+ // insert new node
+ InsertNode(newNode, row);
+
+ mTree->RowCountChanged(row, 1);
+}
+
+void
+inDOMView::ContentRemoved(nsIDocument *aDocument, nsIContent* aContainer,
+ nsIContent* aChild, int32_t aIndexInContainer,
+ nsIContent* aPreviousSibling)
+{
+ if (!mTree)
+ return;
+
+ nsresult rv;
+
+ // find the inDOMViewNode for the old child
+ nsCOMPtr<nsIDOMNode> oldDOMNode(do_QueryInterface(aChild));
+ int32_t row = 0;
+ if (NS_FAILED(rv = NodeToRow(oldDOMNode, &row)))
+ return;
+ inDOMViewNode* oldNode;
+ if (NS_FAILED(rv = RowToNode(row, &oldNode)))
+ return;
+
+ nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
+
+ // The parent may no longer be a container. Note that we don't want
+ // to access oldNode after calling RemoveNode, so do this now.
+ inDOMViewNode* parentNode = oldNode->parent;
+ bool isOnlyChild = oldNode->previous == nullptr && oldNode->next == nullptr;
+
+ // Keep track of how many rows we are removing. It's at least one,
+ // but if we're open it's more.
+ int32_t oldCount = GetRowCount();
+
+ if (oldNode->isOpen)
+ CollapseNode(row);
+
+ RemoveLink(oldNode);
+ RemoveNode(row);
+
+ if (isOnlyChild) {
+ // Fix up the parent
+ parentNode->isContainer = false;
+ parentNode->isOpen = false;
+ mTree->InvalidateRow(NodeToRow(parentNode));
+ }
+
+ mTree->RowCountChanged(row, GetRowCount() - oldCount);
+}
+
+///////////////////////////////////////////////////////////////////////
+// inDOMView
+
+//////// NODE MANAGEMENT
+
+inDOMViewNode*
+inDOMView::GetNodeAt(int32_t aRow)
+{
+ return mNodes.ElementAt(aRow);
+}
+
+int32_t
+inDOMView::GetRowCount()
+{
+ return mNodes.Length();
+}
+
+int32_t
+inDOMView::NodeToRow(inDOMViewNode* aNode)
+{
+ return mNodes.IndexOf(aNode);
+}
+
+inDOMViewNode*
+inDOMView::CreateNode(nsIDOMNode* aNode, inDOMViewNode* aParent)
+{
+ inDOMViewNode* viewNode = new inDOMViewNode(aNode);
+ viewNode->level = aParent ? aParent->level+1 : 0;
+ viewNode->parent = aParent;
+
+ nsCOMArray<nsIDOMNode> grandKids;
+ GetChildNodesFor(aNode, grandKids);
+ viewNode->isContainer = (grandKids.Count() > 0);
+ return viewNode;
+}
+
+bool
+inDOMView::RowOutOfBounds(int32_t aRow, int32_t aCount)
+{
+ return aRow < 0 || aRow >= GetRowCount() || aCount+aRow > GetRowCount();
+}
+
+void
+inDOMView::AppendNode(inDOMViewNode* aNode)
+{
+ mNodes.AppendElement(aNode);
+}
+
+void
+inDOMView::InsertNode(inDOMViewNode* aNode, int32_t aRow)
+{
+ if (RowOutOfBounds(aRow, 1))
+ AppendNode(aNode);
+ else
+ mNodes.InsertElementAt(aRow, aNode);
+}
+
+void
+inDOMView::RemoveNode(int32_t aRow)
+{
+ if (RowOutOfBounds(aRow, 1))
+ return;
+
+ delete GetNodeAt(aRow);
+ mNodes.RemoveElementAt(aRow);
+}
+
+void
+inDOMView::ReplaceNode(inDOMViewNode* aNode, int32_t aRow)
+{
+ if (RowOutOfBounds(aRow, 1))
+ return;
+
+ delete GetNodeAt(aRow);
+ mNodes.ElementAt(aRow) = aNode;
+}
+
+void
+inDOMView::InsertNodes(nsTArray<inDOMViewNode*>& aNodes, int32_t aRow)
+{
+ if (aRow < 0 || aRow > GetRowCount())
+ return;
+
+ mNodes.InsertElementsAt(aRow, aNodes);
+}
+
+void
+inDOMView::RemoveNodes(int32_t aRow, int32_t aCount)
+{
+ if (aRow < 0)
+ return;
+
+ int32_t rowCount = GetRowCount();
+ for (int32_t i = aRow; i < aRow+aCount && i < rowCount; ++i) {
+ delete GetNodeAt(i);
+ }
+
+ mNodes.RemoveElementsAt(aRow, aCount);
+}
+
+void
+inDOMView::RemoveAllNodes()
+{
+ int32_t rowCount = GetRowCount();
+ for (int32_t i = 0; i < rowCount; ++i) {
+ delete GetNodeAt(i);
+ }
+
+ mNodes.Clear();
+}
+
+void
+inDOMView::ExpandNode(int32_t aRow)
+{
+ inDOMViewNode* node = nullptr;
+ RowToNode(aRow, &node);
+
+ nsCOMArray<nsIDOMNode> kids;
+ GetChildNodesFor(node ? node->node : mRootNode,
+ kids);
+ int32_t kidCount = kids.Count();
+
+ nsTArray<inDOMViewNode*> list(kidCount);
+
+ inDOMViewNode* newNode = nullptr;
+ inDOMViewNode* prevNode = nullptr;
+
+ for (int32_t i = 0; i < kidCount; ++i) {
+ newNode = CreateNode(kids[i], node);
+ list.AppendElement(newNode);
+
+ if (prevNode)
+ prevNode->next = newNode;
+ newNode->previous = prevNode;
+ prevNode = newNode;
+ }
+
+ InsertNodes(list, aRow+1);
+
+ if (node)
+ node->isOpen = true;
+}
+
+void
+inDOMView::CollapseNode(int32_t aRow)
+{
+ inDOMViewNode* node = nullptr;
+ nsresult rv = RowToNode(aRow, &node);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ int32_t row = 0;
+ GetLastDescendantOf(node, aRow, &row);
+
+ RemoveNodes(aRow+1, row-aRow);
+
+ node->isOpen = false;
+}
+
+//////// NODE AND ROW CONVERSION
+
+nsresult
+inDOMView::RowToNode(int32_t aRow, inDOMViewNode** aNode)
+{
+ if (aRow < 0 || aRow >= GetRowCount())
+ return NS_ERROR_FAILURE;
+
+ *aNode = GetNodeAt(aRow);
+ return NS_OK;
+}
+
+nsresult
+inDOMView::NodeToRow(nsIDOMNode* aNode, int32_t* aRow)
+{
+ int32_t rowCount = GetRowCount();
+ for (int32_t i = 0; i < rowCount; ++i) {
+ if (GetNodeAt(i)->node == aNode) {
+ *aRow = i;
+ return NS_OK;
+ }
+ }
+
+ *aRow = -1;
+ return NS_ERROR_FAILURE;
+}
+
+//////// NODE HIERARCHY MUTATION
+
+void
+inDOMView::InsertLinkAfter(inDOMViewNode* aNode, inDOMViewNode* aInsertAfter)
+{
+ if (aInsertAfter->next)
+ aInsertAfter->next->previous = aNode;
+ aNode->next = aInsertAfter->next;
+ aInsertAfter->next = aNode;
+ aNode->previous = aInsertAfter;
+}
+
+void
+inDOMView::InsertLinkBefore(inDOMViewNode* aNode, inDOMViewNode* aInsertBefore)
+{
+ if (aInsertBefore->previous)
+ aInsertBefore->previous->next = aNode;
+ aNode->previous = aInsertBefore->previous;
+ aInsertBefore->previous = aNode;
+ aNode->next = aInsertBefore;
+}
+
+void
+inDOMView::RemoveLink(inDOMViewNode* aNode)
+{
+ if (aNode->previous)
+ aNode->previous->next = aNode->next;
+ if (aNode->next)
+ aNode->next->previous = aNode->previous;
+}
+
+void
+inDOMView::ReplaceLink(inDOMViewNode* aNewNode, inDOMViewNode* aOldNode)
+{
+ if (aOldNode->previous)
+ aOldNode->previous->next = aNewNode;
+ if (aOldNode->next)
+ aOldNode->next->previous = aNewNode;
+ aNewNode->next = aOldNode->next;
+ aNewNode->previous = aOldNode->previous;
+}
+
+//////// NODE HIERARCHY UTILITIES
+
+nsresult
+inDOMView::GetFirstDescendantOf(inDOMViewNode* aNode, int32_t aRow, int32_t* aResult)
+{
+ // get the first node that is a descendant of the previous sibling
+ int32_t row = 0;
+ inDOMViewNode* node;
+ for (row = aRow+1; row < GetRowCount(); ++row) {
+ node = GetNodeAt(row);
+ if (node->parent == aNode) {
+ *aResult = row;
+ return NS_OK;
+ }
+ if (node->level <= aNode->level)
+ break;
+ }
+ return NS_ERROR_FAILURE;
+}
+
+nsresult
+inDOMView::GetLastDescendantOf(inDOMViewNode* aNode, int32_t aRow, int32_t* aResult)
+{
+ // get the last node that is a descendant of the previous sibling
+ int32_t row = 0;
+ for (row = aRow+1; row < GetRowCount(); ++row) {
+ if (GetNodeAt(row)->level <= aNode->level)
+ break;
+ }
+ *aResult = row-1;
+ return NS_OK;
+}
+
+//////// DOM UTILITIES
+
+nsresult
+inDOMView::GetChildNodesFor(nsIDOMNode* aNode, nsCOMArray<nsIDOMNode>& aResult)
+{
+ NS_ENSURE_ARG(aNode);
+ // attribute nodes
+ if (mWhatToShow & nsIDOMNodeFilter::SHOW_ATTRIBUTE) {
+ nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aNode);
+ if (element) {
+ nsCOMPtr<nsIDOMMozNamedAttrMap> attrs;
+ element->GetAttributes(getter_AddRefs(attrs));
+ if (attrs) {
+ AppendAttrsToArray(attrs, aResult);
+ }
+ }
+ }
+
+ if (mWhatToShow & nsIDOMNodeFilter::SHOW_ELEMENT) {
+ nsCOMPtr<nsIDOMNodeList> kids;
+ if (!mDOMUtils) {
+ mDOMUtils = services::GetInDOMUtils();
+ if (!mDOMUtils) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ mDOMUtils->GetChildrenForNode(aNode, mShowAnonymous,
+ getter_AddRefs(kids));
+
+ if (kids) {
+ AppendKidsToArray(kids, aResult);
+ }
+ }
+
+ if (mShowSubDocuments) {
+ nsCOMPtr<nsIDOMNode> domdoc =
+ do_QueryInterface(inLayoutUtils::GetSubDocumentFor(aNode));
+ if (domdoc) {
+ aResult.AppendObject(domdoc);
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+inDOMView::GetRealPreviousSibling(nsIDOMNode* aNode, nsIDOMNode* aRealParent, nsIDOMNode** aSibling)
+{
+ // XXXjrh: This won't work for some cases during some situations where XBL insertion points
+ // are involved. Fix me!
+ aNode->GetPreviousSibling(aSibling);
+ return NS_OK;
+}
+
+nsresult
+inDOMView::AppendKidsToArray(nsIDOMNodeList* aKids,
+ nsCOMArray<nsIDOMNode>& aArray)
+{
+ uint32_t l = 0;
+ aKids->GetLength(&l);
+ nsCOMPtr<nsIDOMNode> kid;
+ uint16_t nodeType = 0;
+
+ // Try and get DOM Utils in case we don't have one yet.
+ if (!mShowWhitespaceNodes && !mDOMUtils) {
+ mDOMUtils = services::GetInDOMUtils();
+ }
+
+ for (uint32_t i = 0; i < l; ++i) {
+ aKids->Item(i, getter_AddRefs(kid));
+ kid->GetNodeType(&nodeType);
+
+ NS_ASSERTION(nodeType && nodeType <= nsIDOMNode::NOTATION_NODE,
+ "Unknown node type. "
+ "Were new types added to the spec?");
+ // As of DOM Level 2 Core and Traversal, each NodeFilter constant
+ // is defined as the lower nth bit in the NodeFilter bitmask,
+ // where n is the numeric constant of the nodeType it represents.
+ // If this invariant ever changes, we will need to update the
+ // following line.
+ uint32_t filterForNodeType = 1 << (nodeType - 1);
+
+ if (mWhatToShow & filterForNodeType) {
+ if ((nodeType == nsIDOMNode::TEXT_NODE ||
+ nodeType == nsIDOMNode::COMMENT_NODE) &&
+ !mShowWhitespaceNodes && mDOMUtils) {
+ nsCOMPtr<nsIDOMCharacterData> data = do_QueryInterface(kid);
+ NS_ASSERTION(data, "Does not implement nsIDOMCharacterData!");
+ bool ignore;
+ mDOMUtils->IsIgnorableWhitespace(data, &ignore);
+ if (ignore) {
+ continue;
+ }
+ }
+
+ aArray.AppendElement(kid.forget());
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult
+inDOMView::AppendAttrsToArray(nsIDOMMozNamedAttrMap* aAttributes,
+ nsCOMArray<nsIDOMNode>& aArray)
+{
+ uint32_t l = 0;
+ aAttributes->GetLength(&l);
+ nsCOMPtr<nsIDOMAttr> attribute;
+ for (uint32_t i = 0; i < l; ++i) {
+ aAttributes->Item(i, getter_AddRefs(attribute));
+ aArray.AppendElement(attribute.forget());
+ }
+ return NS_OK;
+}
diff --git a/layout/inspector/inDOMView.h b/layout/inspector/inDOMView.h
new file mode 100644
index 000000000..463d36316
--- /dev/null
+++ b/layout/inspector/inDOMView.h
@@ -0,0 +1,96 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __inDOMView_h__
+#define __inDOMView_h__
+
+#include "inIDOMView.h"
+#include "inIDOMUtils.h"
+
+#include "nsITreeView.h"
+#include "nsITreeSelection.h"
+#include "nsStubMutationObserver.h"
+#include "nsIDOMNode.h"
+#include "nsIDOMDocument.h"
+#include "nsTArray.h"
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+
+class inDOMViewNode;
+class nsIDOMMozNamedAttrMap;
+
+class inDOMView : public inIDOMView,
+ public nsITreeView,
+ public nsStubMutationObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_INIDOMVIEW
+ NS_DECL_NSITREEVIEW
+
+ inDOMView();
+
+ // nsIMutationObserver
+ NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
+ NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
+ NS_DECL_NSIMUTATIONOBSERVER_NODEWILLBEDESTROYED
+
+protected:
+ virtual ~inDOMView();
+
+ nsCOMPtr<nsITreeBoxObject> mTree;
+ nsCOMPtr<nsITreeSelection> mSelection;
+ nsCOMPtr<inIDOMUtils> mDOMUtils;
+
+ bool mShowAnonymous;
+ bool mShowSubDocuments;
+ bool mShowWhitespaceNodes;
+ bool mShowAccessibleNodes;
+ uint32_t mWhatToShow;
+
+ nsCOMPtr<nsIDOMNode> mRootNode;
+ nsCOMPtr<nsIDOMDocument> mRootDocument;
+
+ nsTArray<inDOMViewNode*> mNodes;
+
+ inDOMViewNode* GetNodeAt(int32_t aIndex);
+ int32_t GetRowCount();
+ int32_t NodeToRow(inDOMViewNode* aNode);
+ bool RowOutOfBounds(int32_t aRow, int32_t aCount);
+ inDOMViewNode* CreateNode(nsIDOMNode* aNode, inDOMViewNode* aParent);
+ void AppendNode(inDOMViewNode* aNode);
+ void InsertNode(inDOMViewNode* aNode, int32_t aIndex);
+ void RemoveNode(int32_t aIndex);
+ void ReplaceNode(inDOMViewNode* aNode, int32_t aIndex);
+ void InsertNodes(nsTArray<inDOMViewNode*>& aNodes, int32_t aIndex);
+ void RemoveNodes(int32_t aIndex, int32_t aCount);
+ void RemoveAllNodes();
+ void ExpandNode(int32_t aRow);
+ void CollapseNode(int32_t aRow);
+
+ nsresult RowToNode(int32_t aRow, inDOMViewNode** aNode);
+ nsresult NodeToRow(nsIDOMNode* aNode, int32_t* aRow);
+
+ void InsertLinkAfter(inDOMViewNode* aNode, inDOMViewNode* aInsertAfter);
+ void InsertLinkBefore(inDOMViewNode* aNode, inDOMViewNode* aInsertBefore);
+ void RemoveLink(inDOMViewNode* aNode);
+ void ReplaceLink(inDOMViewNode* aNewNode, inDOMViewNode* aOldNode);
+
+ nsresult GetChildNodesFor(nsIDOMNode* aNode, nsCOMArray<nsIDOMNode>& aResult);
+ nsresult AppendKidsToArray(nsIDOMNodeList* aKids, nsCOMArray<nsIDOMNode>& aArray);
+ nsresult AppendAttrsToArray(nsIDOMMozNamedAttrMap* aKids, nsCOMArray<nsIDOMNode>& aArray);
+ nsresult GetFirstDescendantOf(inDOMViewNode* aNode, int32_t aRow, int32_t* aResult);
+ nsresult GetLastDescendantOf(inDOMViewNode* aNode, int32_t aRow, int32_t* aResult);
+ nsresult GetRealPreviousSibling(nsIDOMNode* aNode, nsIDOMNode* aRealParent, nsIDOMNode** aSibling);
+};
+
+// {FB5C1775-1BBD-4b9c-ABB0-AE7ACD29E87E}
+#define IN_DOMVIEW_CID \
+{ 0xfb5c1775, 0x1bbd, 0x4b9c, { 0xab, 0xb0, 0xae, 0x7a, 0xcd, 0x29, 0xe8, 0x7e } }
+
+#endif // __inDOMView_h__
+
+
diff --git a/layout/inspector/inDeepTreeWalker.cpp b/layout/inspector/inDeepTreeWalker.cpp
new file mode 100644
index 000000000..a0c504980
--- /dev/null
+++ b/layout/inspector/inDeepTreeWalker.cpp
@@ -0,0 +1,442 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 sw=2 et tw=79: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "inDeepTreeWalker.h"
+#include "inLayoutUtils.h"
+
+#include "nsString.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMNodeFilter.h"
+#include "nsIDOMNodeList.h"
+#include "nsServiceManagerUtils.h"
+#include "inIDOMUtils.h"
+#include "nsIContent.h"
+#include "nsContentList.h"
+#include "ChildIterator.h"
+#include "mozilla/dom/Element.h"
+
+/*****************************************************************************
+ * This implementation does not currently operaate according to the W3C spec.
+ * In particular it does NOT handle DOM mutations during the walk. It also
+ * ignores whatToShow and the filter.
+ *****************************************************************************/
+
+////////////////////////////////////////////////////
+
+inDeepTreeWalker::inDeepTreeWalker()
+ : mShowAnonymousContent(false),
+ mShowSubDocuments(false),
+ mShowDocumentsAsNodes(false),
+ mWhatToShow(nsIDOMNodeFilter::SHOW_ALL)
+{
+}
+
+inDeepTreeWalker::~inDeepTreeWalker()
+{
+}
+
+NS_IMPL_ISUPPORTS(inDeepTreeWalker,
+ inIDeepTreeWalker)
+
+////////////////////////////////////////////////////
+// inIDeepTreeWalker
+
+NS_IMETHODIMP
+inDeepTreeWalker::GetShowAnonymousContent(bool *aShowAnonymousContent)
+{
+ *aShowAnonymousContent = mShowAnonymousContent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::SetShowAnonymousContent(bool aShowAnonymousContent)
+{
+ mShowAnonymousContent = aShowAnonymousContent;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::GetShowSubDocuments(bool *aShowSubDocuments)
+{
+ *aShowSubDocuments = mShowSubDocuments;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::SetShowSubDocuments(bool aShowSubDocuments)
+{
+ mShowSubDocuments = aShowSubDocuments;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::GetShowDocumentsAsNodes(bool *aShowDocumentsAsNodes)
+{
+ *aShowDocumentsAsNodes = mShowDocumentsAsNodes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::SetShowDocumentsAsNodes(bool aShowDocumentsAsNodes)
+{
+ mShowDocumentsAsNodes = aShowDocumentsAsNodes;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::Init(nsIDOMNode* aRoot, uint32_t aWhatToShow)
+{
+ if (!aRoot) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ mRoot = aRoot;
+ mCurrentNode = aRoot;
+ mWhatToShow = aWhatToShow;
+
+ mDOMUtils = do_GetService("@mozilla.org/inspector/dom-utils;1");
+ return mDOMUtils ? NS_OK : NS_ERROR_UNEXPECTED;
+}
+
+////////////////////////////////////////////////////
+// nsIDOMTreeWalker
+
+NS_IMETHODIMP
+inDeepTreeWalker::GetRoot(nsIDOMNode** aRoot)
+{
+ *aRoot = mRoot;
+ NS_IF_ADDREF(*aRoot);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::GetWhatToShow(uint32_t* aWhatToShow)
+{
+ *aWhatToShow = mWhatToShow;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::GetFilter(nsIDOMNodeFilter** aFilter)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::GetCurrentNode(nsIDOMNode** aCurrentNode)
+{
+ *aCurrentNode = mCurrentNode;
+ NS_IF_ADDREF(*aCurrentNode);
+ return NS_OK;
+}
+
+already_AddRefed<nsIDOMNode>
+inDeepTreeWalker::GetParent()
+{
+ if (mCurrentNode == mRoot) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIDOMNode> parent;
+ MOZ_ASSERT(mDOMUtils, "mDOMUtils should have been initiated already in Init");
+ mDOMUtils->GetParentForNode(mCurrentNode, mShowAnonymousContent,
+ getter_AddRefs(parent));
+
+ uint16_t nodeType = 0;
+ if (parent) {
+ parent->GetNodeType(&nodeType);
+ }
+ // For compatibility reasons by default we skip the document nodes
+ // from the walk.
+ if (!mShowDocumentsAsNodes &&
+ nodeType == nsIDOMNode::DOCUMENT_NODE &&
+ parent != mRoot) {
+ mDOMUtils->GetParentForNode(parent, mShowAnonymousContent,
+ getter_AddRefs(parent));
+ }
+
+ return parent.forget();
+}
+
+static already_AddRefed<nsINodeList>
+GetChildren(nsIDOMNode* aParent,
+ bool aShowAnonymousContent,
+ bool aShowSubDocuments)
+{
+ MOZ_ASSERT(aParent);
+
+ nsCOMPtr<nsINodeList> ret;
+ if (aShowSubDocuments) {
+ nsCOMPtr<nsIDOMDocument> domdoc = inLayoutUtils::GetSubDocumentFor(aParent);
+ if (domdoc) {
+ aParent = domdoc;
+ }
+ }
+
+ nsCOMPtr<nsIContent> parentAsContent = do_QueryInterface(aParent);
+ if (parentAsContent && aShowAnonymousContent) {
+ ret = parentAsContent->GetChildren(nsIContent::eAllChildren);
+ } else {
+ // If it's not a content, then it's a document (or an attribute but we can ignore that
+ // case here). If aShowAnonymousContent is false we also want to fall back to ChildNodes
+ // so we can skip any native anon content that GetChildren would return.
+ nsCOMPtr<nsINode> parentNode = do_QueryInterface(aParent);
+ MOZ_ASSERT(parentNode);
+ ret = parentNode->ChildNodes();
+ }
+ return ret.forget();
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::SetCurrentNode(nsIDOMNode* aCurrentNode)
+{
+ // mCurrentNode can only be null if init either failed, or has not been
+ // called yet.
+ if (!mCurrentNode || !aCurrentNode) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // If Document nodes are skipped by the walk, we should not allow
+ // one to set one as the current node either.
+ uint16_t nodeType = 0;
+ aCurrentNode->GetNodeType(&nodeType);
+ if (!mShowDocumentsAsNodes && nodeType == nsIDOMNode::DOCUMENT_NODE) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return SetCurrentNode(aCurrentNode, nullptr);
+}
+
+
+nsresult
+inDeepTreeWalker::SetCurrentNode(nsIDOMNode* aCurrentNode,
+ nsINodeList* aSiblings)
+{
+ MOZ_ASSERT(aCurrentNode);
+
+ // We want to store the original state so in case of error
+ // we can restore that.
+ nsCOMPtr<nsINodeList> tmpSiblings = mSiblings;
+ nsCOMPtr<nsIDOMNode> tmpCurrent = mCurrentNode;
+ mSiblings = aSiblings;
+ mCurrentNode = aCurrentNode;
+
+ // If siblings were not passed in as argument we have to
+ // get them from the parent node of aCurrentNode.
+ // Note: in the mShowDoucmentsAsNodes case when a sub document
+ // is set as the current, we don't want to get the children
+ // from the iframe accidentally here, so let's just skip this
+ // part for document nodes, they should never have siblings.
+ uint16_t nodeType = 0;
+ aCurrentNode->GetNodeType(&nodeType);
+ if (!mSiblings && nodeType != nsIDOMNode::DOCUMENT_NODE) {
+ nsCOMPtr<nsIDOMNode> parent = GetParent();
+ if (parent) {
+ mSiblings = GetChildren(parent,
+ mShowAnonymousContent,
+ mShowSubDocuments);
+ }
+ }
+
+ if (mSiblings && mSiblings->Length()) {
+ // We cached all the siblings (if there are any) of the current node, but we
+ // still have to set the index too, to be able to iterate over them.
+ nsCOMPtr<nsIContent> currentAsContent = do_QueryInterface(mCurrentNode);
+ MOZ_ASSERT(currentAsContent);
+ int32_t index = mSiblings->IndexOf(currentAsContent);
+ if (index < 0) {
+ // If someone tries to set current node to some value that is not reachable
+ // otherwise, let's throw. (For example mShowAnonymousContent is false and some
+ // XBL anon content was passed in)
+
+ // Restore state first.
+ mCurrentNode = tmpCurrent;
+ mSiblings = tmpSiblings;
+ return NS_ERROR_INVALID_ARG;
+ }
+ mCurrentIndex = index;
+ } else {
+ mCurrentIndex = -1;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::ParentNode(nsIDOMNode** _retval)
+{
+ *_retval = nullptr;
+ if (!mCurrentNode || mCurrentNode == mRoot) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDOMNode> parent = GetParent();
+
+ if (!parent) {
+ return NS_OK;
+ }
+
+ nsresult rv = SetCurrentNode(parent);
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ parent.forget(_retval);
+ return NS_OK;
+}
+
+// FirstChild and LastChild are very similar methods, this is the generic
+// version for internal use. With aReverse = true it returns the LastChild.
+nsresult
+inDeepTreeWalker::EdgeChild(nsIDOMNode** _retval, bool aFront)
+{
+ if (!mCurrentNode) {
+ return NS_ERROR_FAILURE;
+ }
+
+ *_retval = nullptr;
+
+ nsCOMPtr<nsIDOMNode> echild;
+ if (mShowSubDocuments && mShowDocumentsAsNodes) {
+ // GetChildren below, will skip the document node from
+ // the walk. But if mShowDocumentsAsNodes is set to true
+ // we want to include the (sub)document itself too.
+ echild = inLayoutUtils::GetSubDocumentFor(mCurrentNode);
+ }
+
+ nsCOMPtr<nsINodeList> children;
+ if (!echild) {
+ children = GetChildren(mCurrentNode,
+ mShowAnonymousContent,
+ mShowSubDocuments);
+ if (children && children->Length() > 0) {
+ nsINode* childNode = children->Item(aFront ? 0 : children->Length() - 1);
+ echild = childNode ? childNode->AsDOMNode() : nullptr;
+ }
+ }
+
+ if (echild) {
+ nsresult rv = SetCurrentNode(echild, children);
+ NS_ENSURE_SUCCESS(rv, rv);
+ NS_ADDREF(*_retval = mCurrentNode);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::FirstChild(nsIDOMNode** _retval)
+{
+ return EdgeChild(_retval, /* aFront = */ true);
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::LastChild(nsIDOMNode **_retval)
+{
+ return EdgeChild(_retval, /* aFront = */ false);
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::PreviousSibling(nsIDOMNode **_retval)
+{
+ *_retval = nullptr;
+ if (!mCurrentNode || !mSiblings || mCurrentIndex < 1) {
+ return NS_OK;
+ }
+
+ nsIContent* prev = mSiblings->Item(--mCurrentIndex);
+ mCurrentNode = prev->AsDOMNode();
+ NS_ADDREF(*_retval = mCurrentNode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::NextSibling(nsIDOMNode **_retval)
+{
+ *_retval = nullptr;
+ if (!mCurrentNode || !mSiblings ||
+ mCurrentIndex + 1 >= (int32_t) mSiblings->Length()) {
+ return NS_OK;
+ }
+
+ nsIContent* next = mSiblings->Item(++mCurrentIndex);
+ mCurrentNode = next->AsDOMNode();
+ NS_ADDREF(*_retval = mCurrentNode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::PreviousNode(nsIDOMNode **_retval)
+{
+ if (!mCurrentNode || mCurrentNode == mRoot) {
+ // Nowhere to go from here
+ *_retval = nullptr;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDOMNode> node;
+ PreviousSibling(getter_AddRefs(node));
+
+ if (!node) {
+ return ParentNode(_retval);
+ }
+
+ // Now we're positioned at our previous sibling. But since the DOM tree
+ // traversal is depth-first, the previous node is its most deeply nested last
+ // child. Just loop until LastChild() returns null; since the LastChild()
+ // call that returns null won't affect our position, we will then be
+ // positioned at the correct node.
+ while (node) {
+ LastChild(getter_AddRefs(node));
+ }
+
+ NS_ADDREF(*_retval = mCurrentNode);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+inDeepTreeWalker::NextNode(nsIDOMNode **_retval)
+{
+ if (!mCurrentNode) {
+ return NS_OK;
+ }
+
+ // First try our kids
+ FirstChild(_retval);
+
+ if (*_retval) {
+ return NS_OK;
+ }
+
+ // Now keep trying next siblings up the parent chain, but if we
+ // discover there's nothing else restore our state.
+#ifdef DEBUG
+ nsIDOMNode* origCurrentNode = mCurrentNode;
+#endif
+ uint32_t lastChildCallsToMake = 0;
+ while (1) {
+ NextSibling(_retval);
+
+ if (*_retval) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDOMNode> parent;
+ ParentNode(getter_AddRefs(parent));
+ if (!parent) {
+ // Nowhere else to go; we're done. Restore our state.
+ while (lastChildCallsToMake--) {
+ nsCOMPtr<nsIDOMNode> dummy;
+ LastChild(getter_AddRefs(dummy));
+ }
+ NS_ASSERTION(mCurrentNode == origCurrentNode,
+ "Didn't go back to the right node?");
+ *_retval = nullptr;
+ return NS_OK;
+ }
+ ++lastChildCallsToMake;
+ }
+
+ NS_NOTREACHED("how did we get here?");
+ return NS_OK;
+}
diff --git a/layout/inspector/inDeepTreeWalker.h b/layout/inspector/inDeepTreeWalker.h
new file mode 100644
index 000000000..652722808
--- /dev/null
+++ b/layout/inspector/inDeepTreeWalker.h
@@ -0,0 +1,65 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __inDeepTreeWalker_h___
+#define __inDeepTreeWalker_h___
+
+#include "inIDeepTreeWalker.h"
+
+#include "nsCOMPtr.h"
+#include "nsIDOMNode.h"
+#include "nsTArray.h"
+
+class nsINodeList;
+class inIDOMUtils;
+
+class inDeepTreeWalker final : public inIDeepTreeWalker
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_INIDEEPTREEWALKER
+
+ inDeepTreeWalker();
+
+ nsresult SetCurrentNode(nsIDOMNode* aCurrentNode,
+ nsINodeList* aSiblings);
+protected:
+ virtual ~inDeepTreeWalker();
+
+ already_AddRefed<nsIDOMNode> GetParent();
+ nsresult EdgeChild(nsIDOMNode** _retval, bool aReverse);
+
+ bool mShowAnonymousContent;
+ bool mShowSubDocuments;
+ bool mShowDocumentsAsNodes;
+
+ // The root node. previousNode and parentNode will return
+ // null from here.
+ nsCOMPtr<nsIDOMNode> mRoot;
+ nsCOMPtr<nsIDOMNode> mCurrentNode;
+ nsCOMPtr<inIDOMUtils> mDOMUtils;
+
+ // We cache the siblings of mCurrentNode as a list of nodes.
+ // Notes: normally siblings are all the children of the parent
+ // of mCurrentNode (that are interesting for use for the walk)
+ // and mCurrentIndex is the index of mCurrentNode in that list
+ // But if mCurrentNode is a (sub) document then instead of
+ // storing a list that has only one element (the document)
+ // and setting mCurrentIndex to null, we set mSibilings to null.
+ // The reason for this is purely technical, since nsINodeList is
+ // nsIContent based hence we cannot use it to store a document node.
+ nsCOMPtr<nsINodeList> mSiblings;
+
+ // Index of mCurrentNode in the mSiblings list.
+ int32_t mCurrentIndex;
+
+ // Currently unused. Should be a filter for nodes.
+ uint32_t mWhatToShow;
+};
+
+// {BFCB82C2-5611-4318-90D6-BAF4A7864252}
+#define IN_DEEPTREEWALKER_CID \
+{ 0xbfcb82c2, 0x5611, 0x4318, { 0x90, 0xd6, 0xba, 0xf4, 0xa7, 0x86, 0x42, 0x52 } }
+
+#endif // __inDeepTreeWalker_h___
diff --git a/layout/inspector/inICSSValueSearch.idl b/layout/inspector/inICSSValueSearch.idl
new file mode 100644
index 000000000..a67d72b08
--- /dev/null
+++ b/layout/inspector/inICSSValueSearch.idl
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "inISearchProcess.idl"
+
+interface nsIDOMDocument;
+
+[scriptable, uuid(e0d39e48-1dd1-11b2-81bd-9a0c117f0736)]
+interface inICSSValueSearch : inISearchProcess {
+
+ attribute nsIDOMDocument document;
+
+ // the base url for all returned URL results, if returnRelativeURLs is true
+ attribute wstring baseURL;
+
+ // strip off the baseURL for all URL results if true
+ attribute boolean returnRelativeURLs;
+
+ // correct the paths on a chrome url, such as turning global/skin/blah into global/blah
+ attribute boolean normalizeChromeURLs;
+
+ // add a css property to search for
+ void addPropertyCriteria(in wstring aPropName);
+
+ // set the text value to search for in the properties specified (optional)
+ attribute wstring textCriteria;
+
+};
diff --git a/layout/inspector/inIDOMUtils.idl b/layout/inspector/inIDOMUtils.idl
new file mode 100644
index 000000000..30c15003f
--- /dev/null
+++ b/layout/inspector/inIDOMUtils.idl
@@ -0,0 +1,213 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIArray;
+interface nsIArrayExtensions;
+interface nsIDOMCharacterData;
+interface nsIDOMElement;
+interface nsIDOMDocument;
+interface nsIDOMCSSRule;
+interface nsIDOMCSSStyleRule;
+interface nsIDOMNode;
+interface nsIDOMNodeList;
+interface nsIDOMFontFaceList;
+interface nsIDOMRange;
+interface nsIDOMCSSStyleSheet;
+
+[scriptable, uuid(362e98c3-82c2-4ad8-8dcb-00e8e4eab497)]
+interface inIDOMUtils : nsISupports
+{
+ // CSS utilities
+ void getAllStyleSheets (in nsIDOMDocument aDoc,
+ [optional] out unsigned long aLength,
+ [array, size_is (aLength), retval] out nsISupports aSheets);
+ nsIArrayExtensions getCSSStyleRules(in nsIDOMElement aElement, [optional] in DOMString aPseudo);
+
+ /**
+ * Get the line number of a rule.
+ *
+ * @param nsIDOMCSSRule aRule The rule.
+ * @return The rule's line number. Line numbers are 1-based.
+ */
+ unsigned long getRuleLine(in nsIDOMCSSRule aRule);
+
+ /**
+ * Get the column number of a rule.
+ *
+ * @param nsIDOMCSSRule aRule The rule.
+ * @return The rule's column number. Column numbers are 1-based.
+ */
+ unsigned long getRuleColumn(in nsIDOMCSSRule aRule);
+
+ /**
+ * Like getRuleLine, but if the rule is in a <style> element,
+ * returns a line number relative to the start of the element.
+ *
+ * @param nsIDOMCSSRule aRule the rule to examine
+ * @return the line number of the rule, possibly relative to the
+ * <style> element
+ */
+ unsigned long getRelativeRuleLine(in nsIDOMCSSRule aRule);
+
+ [implicit_jscontext]
+ jsval getCSSLexer(in DOMString aText);
+
+ // Utilities for working with selectors. We don't have a JS OM representation
+ // of a single selector or a selector list yet, but given a rule we can index
+ // into the selector list.
+ //
+ // This is a somewhat backwards API; once we move StyleRule to WebIDL we
+ // should consider using [ChromeOnly] APIs on that.
+ unsigned long getSelectorCount(in nsIDOMCSSStyleRule aRule);
+ // For all three functions below, aSelectorIndex is 0-based
+ AString getSelectorText(in nsIDOMCSSStyleRule aRule,
+ in unsigned long aSelectorIndex);
+ unsigned long long getSpecificity(in nsIDOMCSSStyleRule aRule,
+ in unsigned long aSelectorIndex);
+ // Note: This does not handle scoped selectors correctly, because it has no
+ // idea what the right scope is.
+ bool selectorMatchesElement(in nsIDOMElement aElement,
+ in nsIDOMCSSStyleRule aRule,
+ in unsigned long aSelectorIndex,
+ [optional] in DOMString aPseudo);
+
+ // Utilities for working with CSS properties
+ //
+ // Returns true if the string names a property that is inherited by default.
+ bool isInheritedProperty(in AString aPropertyName);
+
+ // Get a list of all our supported property names. Optionally
+ // shorthands can be excluded or property aliases included.
+ const unsigned long EXCLUDE_SHORTHANDS = (1<<0);
+ const unsigned long INCLUDE_ALIASES = (1<<1);
+ void getCSSPropertyNames([optional] in unsigned long aFlags,
+ [optional] out unsigned long aCount,
+ [retval, array, size_is(aCount)] out wstring aProps);
+
+ // Get a list of all valid keywords and colors for aProperty.
+ void getCSSValuesForProperty(in AString aProperty,
+ [optional] out unsigned long aLength,
+ [array, size_is(aLength), retval] out wstring aValues);
+
+ // Utilities for working with CSS colors
+ [implicit_jscontext]
+ jsval colorNameToRGB(in DOMString aColorName);
+ AString rgbToColorName(in octet aR, in octet aG, in octet aB);
+
+ // Convert a given CSS color string to rgba. Returns null on failure or an
+ // InspectorRGBATuple on success.
+ //
+ // NOTE: Converting a color to RGBA may be lossy when converting from some
+ // formats e.g. CMYK.
+ [implicit_jscontext]
+ jsval colorToRGBA(in DOMString aColorString);
+
+ // Check whether a given color is a valid CSS color.
+ bool isValidCSSColor(in AString aColorString);
+
+ // Utilities for obtaining information about a CSS property.
+
+ // Check whether a CSS property and value are a valid combination. If the
+ // property is pref-disabled it will still be processed.
+ // aPropertyName: A property name e.g. "color"
+ // aPropertyValue: A property value e.g. "red" or "red !important"
+ bool cssPropertyIsValid(in AString aPropertyName, in AString aPropertyValue);
+
+ // Get a list of the longhands corresponding to the given CSS property. If
+ // the property is a longhand already, just returns the property itself.
+ // Throws on unsupported property names.
+ void getSubpropertiesForCSSProperty(in AString aProperty,
+ [optional] out unsigned long aLength,
+ [array, size_is(aLength), retval] out wstring aValues);
+ // Check whether a given CSS property is a shorthand. Throws on unsupported
+ // property names.
+ bool cssPropertyIsShorthand(in AString aProperty);
+
+ // Check whether values of the given type are valid values for the property.
+ // For shorthands, checks whether there's a corresponding longhand property
+ // that accepts values of this type. Throws on unsupported properties or
+ // unknown types.
+ const unsigned long TYPE_LENGTH = 0;
+ const unsigned long TYPE_PERCENTAGE = 1;
+ const unsigned long TYPE_COLOR = 2;
+ const unsigned long TYPE_URL = 3;
+ const unsigned long TYPE_ANGLE = 4;
+ const unsigned long TYPE_FREQUENCY = 5;
+ const unsigned long TYPE_TIME = 6;
+ const unsigned long TYPE_GRADIENT = 7;
+ const unsigned long TYPE_TIMING_FUNCTION = 8;
+ const unsigned long TYPE_IMAGE_RECT = 9;
+ const unsigned long TYPE_NUMBER = 10;
+ bool cssPropertySupportsType(in AString aProperty, in unsigned long type);
+
+ // DOM Node utilities
+ boolean isIgnorableWhitespace(in nsIDOMCharacterData aDataNode);
+ // Returns the "parent" of a node. The parent of a document node is the
+ // frame/iframe containing that document. aShowingAnonymousContent says
+ // whether we are showing anonymous content.
+ nsIDOMNode getParentForNode(in nsIDOMNode aNode,
+ in boolean aShowingAnonymousContent);
+ nsIDOMNodeList getChildrenForNode(in nsIDOMNode aNode,
+ in boolean aShowingAnonymousContent);
+
+ // XBL utilities
+ nsIArray getBindingURLs(in nsIDOMElement aElement);
+
+ // content state utilities
+ unsigned long long getContentState(in nsIDOMElement aElement);
+ /**
+ * Setting and removing content state on an element. Both these functions
+ * calling EventStateManager::SetContentState internally, the difference is
+ * that for the remove case we simply pass in nullptr for the element.
+ * Use them accordingly.
+ *
+ * @return Returns true if the state was set successfully. See more details
+ * in EventStateManager.h SetContentState.
+ */
+ bool setContentState(in nsIDOMElement aElement, in unsigned long long aState);
+ bool removeContentState(in nsIDOMElement aElement, in unsigned long long aState);
+
+ nsIDOMFontFaceList getUsedFontFaces(in nsIDOMRange aRange);
+
+ /**
+ * Get the names of all the supported pseudo-elements.
+ * Pseudo-elements which are only accepted in UA style sheets are
+ * not included.
+ *
+ * @param {unsigned long} aCount the number of items returned
+ * @param {wstring[]} aNames the names
+ */
+ void getCSSPseudoElementNames([optional] out unsigned long aCount,
+ [retval, array, size_is(aCount)] out wstring aNames);
+
+ // pseudo-class style locking methods. aPseudoClass must be a valid pseudo-class
+ // selector string, e.g. ":hover". ":any-link" and non-event-state
+ // pseudo-classes are ignored.
+ void addPseudoClassLock(in nsIDOMElement aElement, in DOMString aPseudoClass);
+ void removePseudoClassLock(in nsIDOMElement aElement, in DOMString aPseudoClass);
+ bool hasPseudoClassLock(in nsIDOMElement aElement, in DOMString aPseudoClass);
+ void clearPseudoClassLocks(in nsIDOMElement aElement);
+
+ /**
+ * Parse CSS and update the style sheet in place.
+ *
+ * @param DOMCSSStyleSheet aSheet
+ * @param DOMString aInput
+ * The new source string for the style sheet.
+ */
+ void parseStyleSheet(in nsIDOMCSSStyleSheet aSheet, in DOMString aInput);
+ /**
+ * Scroll an element completely into view, if possible.
+ * This is similar to ensureElementIsVisible but for all ancestors.
+ *
+ * @param DOMElement aElement
+ */
+ void scrollElementIntoView(in nsIDOMElement aElement);
+};
+
+%{ C++
+#define IN_DOMUTILS_CONTRACTID "@mozilla.org/inspector/dom-utils;1"
+%}
diff --git a/layout/inspector/inIDOMView.idl b/layout/inspector/inIDOMView.idl
new file mode 100644
index 000000000..da8c543b1
--- /dev/null
+++ b/layout/inspector/inIDOMView.idl
@@ -0,0 +1,24 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMNode;
+
+[scriptable, uuid(FBB67442-27A3-483C-8EB2-29C3EED7514C)]
+interface inIDOMView : nsISupports
+{
+ attribute nsIDOMNode rootNode;
+
+ attribute boolean showAnonymousContent;
+ attribute boolean showSubDocuments;
+ attribute boolean showWhitespaceNodes;
+ attribute boolean showAccessibleNodes;
+ attribute unsigned long whatToShow;
+
+ nsIDOMNode getNodeFromRowIndex(in long rowIndex);
+ long getRowIndexFromNode(in nsIDOMNode node);
+
+ void rebuild();
+};
diff --git a/layout/inspector/inIDeepTreeWalker.idl b/layout/inspector/inIDeepTreeWalker.idl
new file mode 100644
index 000000000..389f3c86e
--- /dev/null
+++ b/layout/inspector/inIDeepTreeWalker.idl
@@ -0,0 +1,47 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMNode;
+interface nsIDOMNodeFilter;
+
+// Note: the iterator does not handle DOM mutations gracefully. So if
+// the underlying DOM we are iterating over is changed, the behavior
+// of the walker is undefined. (With the current implementation we
+// cache the siblings of the current node and this list is not updated
+// when a mutation occurs).
+
+[scriptable, uuid(6657e8eb-b646-48e7-993e-cfa6e96415b4)]
+interface inIDeepTreeWalker : nsISupports
+{
+ attribute boolean showAnonymousContent;
+ attribute boolean showSubDocuments;
+
+ // By default the walker skips document nodes from the iteration,
+ // by setting this flag to true this behavior can be altered.
+ attribute boolean showDocumentsAsNodes;
+
+ void init(in nsIDOMNode aRoot, in unsigned long aWhatToShow);
+
+ // Methods and attributes from nsIDOMTreeWalker, which is not scriptable.
+ // Note: normally parentNode cannot go further up on the tree once it reached
+ // the root, but setting currentNode does not have this limitation. If currentNode
+ // is set to a node that does not have the root as its ancestor the walk can be
+ // continued from there, and once we reach a node that is 'under' the root, the
+ // limitation for the parentNode will work again.
+ readonly attribute nsIDOMNode root;
+ readonly attribute unsigned long whatToShow;
+ readonly attribute nsIDOMNodeFilter filter;
+ attribute nsIDOMNode currentNode;
+
+ nsIDOMNode parentNode();
+ nsIDOMNode firstChild();
+ nsIDOMNode lastChild();
+ nsIDOMNode previousSibling();
+ nsIDOMNode nextSibling();
+ nsIDOMNode previousNode();
+ nsIDOMNode nextNode();
+};
+
diff --git a/layout/inspector/inISearchObserver.idl b/layout/inspector/inISearchObserver.idl
new file mode 100644
index 000000000..60de3246d
--- /dev/null
+++ b/layout/inspector/inISearchObserver.idl
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface inISearchProcess;
+
+[scriptable, uuid(46226D9B-E398-4106-8D9B-225D4D0589F5)]
+interface inISearchObserver : nsISupports
+{
+ // result codes which are sent to onSearchEnd
+ const short IN_SUCCESS = 1; // search completed successfully
+ const short IN_INTERRUPTED = 2; // search stopped due to user interruption
+ const short IN_ERROR = 3; // search stopped due to an error
+
+ void onSearchStart(in inISearchProcess aModule);
+ void onSearchResult(in inISearchProcess aModule);
+ void onSearchEnd(in inISearchProcess aModule, in short aResult);
+ void onSearchError(in inISearchProcess aModule, in AString aMessage);
+};
diff --git a/layout/inspector/inISearchProcess.idl b/layout/inspector/inISearchProcess.idl
new file mode 100644
index 000000000..d1e2f1ca2
--- /dev/null
+++ b/layout/inspector/inISearchProcess.idl
@@ -0,0 +1,49 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+
+#include "nsISupports.idl"
+
+interface nsAString;
+interface inISearchObserver;
+
+[scriptable, uuid(D5FA765B-2448-4686-B7C1-5FF13ACB0FC9)]
+interface inISearchProcess : nsISupports
+{
+ // indicates if an asynchronous search is in progress
+ readonly attribute boolean isActive;
+
+ // the number of results returned
+ readonly attribute long resultCount;
+
+ // for optimization when doing an async search, this will optionally
+ // destroy old results, assuming they will be fetched as soon as
+ // the observer is notified of their presence. If true, then indices
+ // pass to the get*ResultAt methods will return null for any index
+ // other than the most recent one, and getResults will return null always.
+ attribute boolean holdResults;
+
+ // start a synchronous search
+ void searchSync();
+
+ // start an asynchronous search
+ void searchAsync(in inISearchObserver aObserver);
+
+ // command an async process to stop immediately
+ void searchStop();
+
+ // performs a step in the asynchronous search loop
+ // return indicates true if loop is done, false if it should continue
+ // This is there only for the benefit of asynchronous search controllers,
+ // and is not for use by those who just wish to call searchAsync
+ boolean searchStep();
+
+ // methods for getting results of specific types
+
+ AString getStringResultAt(in long aIndex);
+
+ long getIntResultAt(in long aIndex);
+
+ unsigned long getUIntResultAt(in long aIndex);
+};
diff --git a/layout/inspector/inLayoutUtils.cpp b/layout/inspector/inLayoutUtils.cpp
new file mode 100644
index 000000000..eb502f905
--- /dev/null
+++ b/layout/inspector/inLayoutUtils.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "inLayoutUtils.h"
+
+#include "nsIDocument.h"
+#include "nsIDOMDocument.h"
+#include "nsIContent.h"
+#include "nsIContentViewer.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla;
+
+///////////////////////////////////////////////////////////////////////////////
+
+EventStateManager*
+inLayoutUtils::GetEventStateManagerFor(nsIDOMElement *aElement)
+{
+ NS_PRECONDITION(aElement, "Passing in a null element is bad");
+
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ aElement->GetOwnerDocument(getter_AddRefs(domDoc));
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
+
+ if (!doc) {
+ NS_WARNING("Could not get an nsIDocument!");
+ return nullptr;
+ }
+
+ nsIPresShell *shell = doc->GetShell();
+ if (!shell)
+ return nullptr;
+
+ return shell->GetPresContext()->EventStateManager();
+}
+
+nsIDOMDocument*
+inLayoutUtils::GetSubDocumentFor(nsIDOMNode* aNode)
+{
+ nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
+ if (content) {
+ nsCOMPtr<nsIDocument> doc = content->GetComposedDoc();
+ if (doc) {
+ nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(doc->GetSubDocumentFor(content)));
+
+ return domdoc;
+ }
+ }
+
+ return nullptr;
+}
+
+nsIDOMNode*
+inLayoutUtils::GetContainerFor(const nsIDocument& aDoc)
+{
+ nsPIDOMWindowOuter* pwin = aDoc.GetWindow();
+ if (!pwin) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIDOMNode> node = do_QueryInterface(pwin->GetFrameElementInternal());
+ return node;
+}
+
diff --git a/layout/inspector/inLayoutUtils.h b/layout/inspector/inLayoutUtils.h
new file mode 100644
index 000000000..e86e7d71c
--- /dev/null
+++ b/layout/inspector/inLayoutUtils.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __inLayoutUtils_h__
+#define __inLayoutUtils_h__
+
+class nsIDocument;
+class nsIDOMDocument;
+class nsIDOMElement;
+class nsIDOMNode;
+
+namespace mozilla {
+class EventStateManager;
+} // namespace mozilla
+
+class inLayoutUtils
+{
+public:
+ static mozilla::EventStateManager*
+ GetEventStateManagerFor(nsIDOMElement *aElement);
+ static nsIDOMDocument* GetSubDocumentFor(nsIDOMNode* aNode);
+ static nsIDOMNode* GetContainerFor(const nsIDocument& aDoc);
+};
+
+#endif // __inLayoutUtils_h__
diff --git a/layout/inspector/inSearchLoop.cpp b/layout/inspector/inSearchLoop.cpp
new file mode 100644
index 000000000..a2e46b932
--- /dev/null
+++ b/layout/inspector/inSearchLoop.cpp
@@ -0,0 +1,58 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "inSearchLoop.h"
+
+#include "nsITimer.h"
+#include "nsIServiceManager.h"
+///////////////////////////////////////////////////////////////////////////////
+
+inSearchLoop::inSearchLoop(inISearchProcess* aSearchProcess)
+{
+ mSearchProcess = aSearchProcess;
+ nsresult rv;
+ mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
+}
+
+inSearchLoop::~inSearchLoop()
+{
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// inSearchLoop
+
+nsresult
+inSearchLoop::Start()
+{
+ mTimer->InitWithFuncCallback(inSearchLoop::TimerCallback, (void*)this, 0, nsITimer::TYPE_REPEATING_SLACK);
+
+ return NS_OK;
+}
+
+nsresult
+inSearchLoop::Step()
+{
+ bool done = false;
+ mSearchProcess->SearchStep(&done);
+
+ if (done)
+ Stop();
+
+ return NS_OK;
+}
+
+nsresult
+inSearchLoop::Stop()
+{
+ mTimer->Cancel();
+
+ return NS_OK;
+}
+
+void
+inSearchLoop::TimerCallback(nsITimer *aTimer, void *aClosure)
+{
+ inSearchLoop* loop = (inSearchLoop*) aClosure;
+ loop->Step();
+}
diff --git a/layout/inspector/inSearchLoop.h b/layout/inspector/inSearchLoop.h
new file mode 100644
index 000000000..657ee4392
--- /dev/null
+++ b/layout/inspector/inSearchLoop.h
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __inSearchLoop_h__
+#define __inSearchLoop_h__
+
+#include "nsCOMPtr.h"
+#include "nsITimer.h"
+#include "inISearchProcess.h"
+
+class inSearchLoop
+{
+public:
+ explicit inSearchLoop(inISearchProcess* aSearchProcess);
+ virtual ~inSearchLoop();
+
+ nsresult Start();
+ nsresult Step();
+ nsresult Stop();
+ static void TimerCallback(nsITimer *aTimer, void *aClosure);
+
+protected:
+ nsCOMPtr<nsITimer> mTimer;
+ nsCOMPtr<inISearchProcess> mSearchProcess;
+};
+
+#endif
diff --git a/layout/inspector/moz.build b/layout/inspector/moz.build
new file mode 100644
index 000000000..1576b7d14
--- /dev/null
+++ b/layout/inspector/moz.build
@@ -0,0 +1,49 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+if CONFIG['ENABLE_TESTS']:
+ MOCHITEST_CHROME_MANIFESTS += ['tests/chrome/chrome.ini']
+ MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
+
+XPIDL_SOURCES += [
+ 'inICSSValueSearch.idl',
+ 'inIDeepTreeWalker.idl',
+ 'inIDOMUtils.idl',
+ 'inIDOMView.idl',
+ 'inISearchObserver.idl',
+ 'inISearchProcess.idl',
+ 'nsIDOMFontFace.idl',
+ 'nsIDOMFontFaceList.idl',
+]
+
+XPIDL_MODULE = 'inspector'
+
+EXPORTS += [
+ 'nsFontFace.h',
+ 'nsFontFaceList.h',
+]
+
+UNIFIED_SOURCES += [
+ 'inCSSValueSearch.cpp',
+ 'inDeepTreeWalker.cpp',
+ 'inDOMUtils.cpp',
+ 'inLayoutUtils.cpp',
+ 'inSearchLoop.cpp',
+ 'nsFontFace.cpp',
+ 'nsFontFaceList.cpp',
+]
+
+if CONFIG['MOZ_XUL']:
+ UNIFIED_SOURCES += [
+ 'inDOMView.cpp',
+ ]
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+ '../style',
+ '/dom/base',
+ '/dom/xbl',
+]
diff --git a/layout/inspector/nsFontFace.cpp b/layout/inspector/nsFontFace.cpp
new file mode 100644
index 000000000..a4a9231e2
--- /dev/null
+++ b/layout/inspector/nsFontFace.cpp
@@ -0,0 +1,225 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsFontFace.h"
+#include "nsIDOMCSSFontFaceRule.h"
+#include "nsCSSRules.h"
+#include "gfxTextRun.h"
+#include "gfxUserFontSet.h"
+#include "nsFontFaceLoader.h"
+#include "mozilla/gfx/2D.h"
+#include "decode.h"
+#include "zlib.h"
+#include "mozilla/dom/FontFaceSet.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+nsFontFace::nsFontFace(gfxFontEntry* aFontEntry,
+ gfxFontGroup* aFontGroup,
+ uint8_t aMatchType)
+ : mFontEntry(aFontEntry),
+ mFontGroup(aFontGroup),
+ mMatchType(aMatchType)
+{
+}
+
+nsFontFace::~nsFontFace()
+{
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_ISUPPORTS(nsFontFace, nsIDOMFontFace)
+
+////////////////////////////////////////////////////////////////////////
+// nsIDOMFontFace
+
+NS_IMETHODIMP
+nsFontFace::GetFromFontGroup(bool * aFromFontGroup)
+{
+ *aFromFontGroup =
+ (mMatchType & gfxTextRange::kFontGroup) != 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetFromLanguagePrefs(bool * aFromLanguagePrefs)
+{
+ *aFromLanguagePrefs =
+ (mMatchType & gfxTextRange::kPrefsFallback) != 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetFromSystemFallback(bool * aFromSystemFallback)
+{
+ *aFromSystemFallback =
+ (mMatchType & gfxTextRange::kSystemFallback) != 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetName(nsAString & aName)
+{
+ if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
+ NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
+ aName = mFontEntry->mUserFontData->mRealName;
+ } else {
+ aName = mFontEntry->RealFaceName();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetCSSFamilyName(nsAString & aCSSFamilyName)
+{
+ aCSSFamilyName = mFontEntry->FamilyName();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetRule(nsIDOMCSSFontFaceRule **aRule)
+{
+ // check whether this font entry is associated with an @font-face rule
+ // in the relevant font group's user font set
+ nsCSSFontFaceRule* rule = nullptr;
+ if (mFontEntry->IsUserFont()) {
+ FontFaceSet::UserFontSet* fontSet =
+ static_cast<FontFaceSet::UserFontSet*>(mFontGroup->GetUserFontSet());
+ if (fontSet) {
+ FontFaceSet* fontFaceSet = fontSet->GetFontFaceSet();
+ if (fontFaceSet) {
+ rule = fontFaceSet->FindRuleForEntry(mFontEntry);
+ }
+ }
+ }
+
+ NS_IF_ADDREF(*aRule = rule);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetSrcIndex(int32_t * aSrcIndex)
+{
+ if (mFontEntry->IsUserFont()) {
+ NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
+ *aSrcIndex = mFontEntry->mUserFontData->mSrcIndex;
+ } else {
+ *aSrcIndex = -1;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetURI(nsAString & aURI)
+{
+ aURI.Truncate();
+ if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
+ NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
+ if (mFontEntry->mUserFontData->mURI) {
+ nsAutoCString spec;
+ nsresult rv = mFontEntry->mUserFontData->mURI->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+ AppendUTF8toUTF16(spec, aURI);
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetLocalName(nsAString & aLocalName)
+{
+ if (mFontEntry->IsLocalUserFont()) {
+ NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
+ aLocalName = mFontEntry->mUserFontData->mLocalName;
+ } else {
+ aLocalName.Truncate();
+ }
+ return NS_OK;
+}
+
+static void
+AppendToFormat(nsAString & aResult, const char* aFormat)
+{
+ if (!aResult.IsEmpty()) {
+ aResult.Append(',');
+ }
+ aResult.AppendASCII(aFormat);
+}
+
+NS_IMETHODIMP
+nsFontFace::GetFormat(nsAString & aFormat)
+{
+ aFormat.Truncate();
+ if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
+ NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
+ uint32_t formatFlags = mFontEntry->mUserFontData->mFormat;
+ if (formatFlags & gfxUserFontSet::FLAG_FORMAT_OPENTYPE) {
+ AppendToFormat(aFormat, "opentype");
+ }
+ if (formatFlags & gfxUserFontSet::FLAG_FORMAT_TRUETYPE) {
+ AppendToFormat(aFormat, "truetype");
+ }
+ if (formatFlags & gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT) {
+ AppendToFormat(aFormat, "truetype-aat");
+ }
+ if (formatFlags & gfxUserFontSet::FLAG_FORMAT_EOT) {
+ AppendToFormat(aFormat, "embedded-opentype");
+ }
+ if (formatFlags & gfxUserFontSet::FLAG_FORMAT_SVG) {
+ AppendToFormat(aFormat, "svg");
+ }
+ if (formatFlags & gfxUserFontSet::FLAG_FORMAT_WOFF) {
+ AppendToFormat(aFormat, "woff");
+ }
+ if (formatFlags & gfxUserFontSet::FLAG_FORMAT_WOFF2) {
+ AppendToFormat(aFormat, "woff2");
+ }
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFace::GetMetadata(nsAString & aMetadata)
+{
+ aMetadata.Truncate();
+ if (mFontEntry->IsUserFont() && !mFontEntry->IsLocalUserFont()) {
+ NS_ASSERTION(mFontEntry->mUserFontData, "missing userFontData");
+ const gfxUserFontData* userFontData = mFontEntry->mUserFontData.get();
+ if (userFontData->mMetadata.Length() && userFontData->mMetaOrigLen) {
+ nsAutoCString str;
+ str.SetLength(userFontData->mMetaOrigLen);
+ if (str.Length() == userFontData->mMetaOrigLen) {
+ switch (userFontData->mCompression) {
+ case gfxUserFontData::kZlibCompression:
+ {
+ uLongf destLen = userFontData->mMetaOrigLen;
+ if (uncompress((Bytef *)(str.BeginWriting()), &destLen,
+ (const Bytef *)(userFontData->mMetadata.Elements()),
+ userFontData->mMetadata.Length()) == Z_OK &&
+ destLen == userFontData->mMetaOrigLen) {
+ AppendUTF8toUTF16(str, aMetadata);
+ }
+ }
+ break;
+ case gfxUserFontData::kBrotliCompression:
+ {
+ size_t decodedSize = userFontData->mMetaOrigLen;
+ if (BrotliDecompressBuffer(userFontData->mMetadata.Length(),
+ userFontData->mMetadata.Elements(),
+ &decodedSize,
+ (uint8_t*)str.BeginWriting()) == 1 &&
+ decodedSize == userFontData->mMetaOrigLen) {
+ AppendUTF8toUTF16(str, aMetadata);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ return NS_OK;
+}
diff --git a/layout/inspector/nsFontFace.h b/layout/inspector/nsFontFace.h
new file mode 100644
index 000000000..911df04c9
--- /dev/null
+++ b/layout/inspector/nsFontFace.h
@@ -0,0 +1,37 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsFontFace_h__
+#define __nsFontFace_h__
+
+#include "nsIDOMFontFace.h"
+
+class gfxFontEntry;
+class gfxFontGroup;
+
+class nsFontFace : public nsIDOMFontFace
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMFONTFACE
+
+ nsFontFace(gfxFontEntry* aFontEntry,
+ gfxFontGroup* aFontGroup,
+ uint8_t aMatchInfo);
+
+ gfxFontEntry* GetFontEntry() const { return mFontEntry.get(); }
+
+ void AddMatchType(uint8_t aMatchType) {
+ mMatchType |= aMatchType;
+ }
+
+protected:
+ virtual ~nsFontFace();
+
+ RefPtr<gfxFontEntry> mFontEntry;
+ RefPtr<gfxFontGroup> mFontGroup;
+ uint8_t mMatchType;
+};
+
+#endif // __nsFontFace_h__
diff --git a/layout/inspector/nsFontFaceList.cpp b/layout/inspector/nsFontFaceList.cpp
new file mode 100644
index 000000000..5f9b0ecb4
--- /dev/null
+++ b/layout/inspector/nsFontFaceList.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsFontFaceList.h"
+#include "nsFontFace.h"
+#include "nsFontFaceLoader.h"
+#include "nsIFrame.h"
+#include "gfxTextRun.h"
+#include "mozilla/gfx/2D.h"
+
+nsFontFaceList::nsFontFaceList()
+{
+}
+
+nsFontFaceList::~nsFontFaceList()
+{
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsISupports
+
+NS_IMPL_ISUPPORTS(nsFontFaceList, nsIDOMFontFaceList)
+
+////////////////////////////////////////////////////////////////////////
+// nsIDOMFontFaceList
+
+NS_IMETHODIMP
+nsFontFaceList::Item(uint32_t index, nsIDOMFontFace **_retval)
+{
+ NS_ENSURE_TRUE(index < mFontFaces.Count(), NS_ERROR_INVALID_ARG);
+
+ uint32_t current = 0;
+ nsIDOMFontFace* result = nullptr;
+ for (auto iter = mFontFaces.Iter(); !iter.Done(); iter.Next()) {
+ if (current == index) {
+ result = iter.UserData();
+ break;
+ }
+ current++;
+ }
+ NS_ASSERTION(result != nullptr, "null entry in nsFontFaceList?");
+ NS_IF_ADDREF(*_retval = result);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontFaceList::GetLength(uint32_t *aLength)
+{
+ *aLength = mFontFaces.Count();
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////
+// nsFontFaceList
+
+nsresult
+nsFontFaceList::AddFontsFromTextRun(gfxTextRun* aTextRun,
+ uint32_t aOffset, uint32_t aLength)
+{
+ gfxTextRun::Range range(aOffset, aOffset + aLength);
+ gfxTextRun::GlyphRunIterator iter(aTextRun, range);
+ while (iter.NextRun()) {
+ gfxFontEntry *fe = iter.GetGlyphRun()->mFont->GetFontEntry();
+ // if we have already listed this face, just make sure the match type is
+ // recorded
+ nsFontFace* existingFace =
+ static_cast<nsFontFace*>(mFontFaces.GetWeak(fe));
+ if (existingFace) {
+ existingFace->AddMatchType(iter.GetGlyphRun()->mMatchType);
+ } else {
+ // A new font entry we haven't seen before
+ RefPtr<nsFontFace> ff =
+ new nsFontFace(fe, aTextRun->GetFontGroup(),
+ iter.GetGlyphRun()->mMatchType);
+ mFontFaces.Put(fe, ff);
+ }
+ }
+
+ return NS_OK;
+}
diff --git a/layout/inspector/nsFontFaceList.h b/layout/inspector/nsFontFaceList.h
new file mode 100644
index 000000000..a17c4fc40
--- /dev/null
+++ b/layout/inspector/nsFontFaceList.h
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsFontFaceList_h__
+#define __nsFontFaceList_h__
+
+#include "nsIDOMFontFaceList.h"
+#include "nsIDOMFontFace.h"
+#include "nsCOMPtr.h"
+#include "nsInterfaceHashtable.h"
+#include "nsHashKeys.h"
+
+class gfxFontEntry;
+class gfxTextRun;
+
+class nsFontFaceList : public nsIDOMFontFaceList
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMFONTFACELIST
+
+ nsFontFaceList();
+
+ nsresult AddFontsFromTextRun(gfxTextRun* aTextRun,
+ uint32_t aOffset, uint32_t aLength);
+
+protected:
+ virtual ~nsFontFaceList();
+
+ nsInterfaceHashtable<nsPtrHashKey<gfxFontEntry>,nsIDOMFontFace> mFontFaces;
+};
+
+#endif // __nsFontFaceList_h__
diff --git a/layout/inspector/nsIDOMFontFace.idl b/layout/inspector/nsIDOMFontFace.idl
new file mode 100644
index 000000000..39164a7f9
--- /dev/null
+++ b/layout/inspector/nsIDOMFontFace.idl
@@ -0,0 +1,32 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMCSSFontFaceRule;
+interface nsIDOMCSSStyleDeclaration;
+
+[scriptable, uuid(9a3b1272-6585-4f41-b08f-fdc5da444cd0)]
+interface nsIDOMFontFace : nsISupports
+{
+ // An indication of how we found this font during font-matching.
+ // Note that the same physical font may have been found in multiple ways within a range.
+ readonly attribute boolean fromFontGroup;
+ readonly attribute boolean fromLanguagePrefs;
+ readonly attribute boolean fromSystemFallback;
+
+ // available for all fonts
+ readonly attribute DOMString name; // full font name as obtained from the font resource
+ readonly attribute DOMString CSSFamilyName; // a family name that could be used in CSS font-family
+ // (not necessarily the actual name that was used,
+ // due to aliases, generics, localized names, etc)
+
+ // meaningful only when the font is a user font defined using @font-face
+ readonly attribute nsIDOMCSSFontFaceRule rule; // null if no associated @font-face rule
+ readonly attribute long srcIndex; // index in the rule's src list, -1 if no @font-face rule
+ readonly attribute DOMString URI; // null if not a downloaded font, i.e. local
+ readonly attribute DOMString localName; // null if not a src:local(...) rule
+ readonly attribute DOMString format; // as per http://www.w3.org/TR/css3-webfonts/#referencing
+ readonly attribute DOMString metadata; // XML metadata from WOFF file (if any)
+};
diff --git a/layout/inspector/nsIDOMFontFaceList.idl b/layout/inspector/nsIDOMFontFaceList.idl
new file mode 100644
index 000000000..8a565a9b4
--- /dev/null
+++ b/layout/inspector/nsIDOMFontFaceList.idl
@@ -0,0 +1,14 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMFontFace;
+
+[scriptable, uuid(2538579c-9472-4fd9-8dc1-d44ce4c1b7ba)]
+interface nsIDOMFontFaceList : nsISupports
+{
+ nsIDOMFontFace item(in unsigned long index);
+ readonly attribute unsigned long length;
+};
diff --git a/layout/inspector/tests/bug1202095-2.css b/layout/inspector/tests/bug1202095-2.css
new file mode 100644
index 000000000..a484814eb
--- /dev/null
+++ b/layout/inspector/tests/bug1202095-2.css
@@ -0,0 +1,7 @@
+/*
+ * Bug 1202095 - parseStyleSheet should not re-load @imports
+ */
+
+body {
+ color: chartreuse;
+}
diff --git a/layout/inspector/tests/bug1202095.css b/layout/inspector/tests/bug1202095.css
new file mode 100644
index 000000000..fa8ef0feb
--- /dev/null
+++ b/layout/inspector/tests/bug1202095.css
@@ -0,0 +1,7 @@
+/*
+ * Bug 1202095 - parseStyleSheet should not re-load @imports
+ */
+
+body {
+ background-color: purple;
+}
diff --git a/layout/inspector/tests/bug856317.css b/layout/inspector/tests/bug856317.css
new file mode 100644
index 000000000..49691e3eb
--- /dev/null
+++ b/layout/inspector/tests/bug856317.css
@@ -0,0 +1,23 @@
+/*
+ * Bug 856317 - expose the column number of style rules via inIDOMUtils
+ */
+
+/* simplest possible */
+.alpha {
+}
+
+/* with leading whitespace */
+ #beta {
+ }
+
+/* with a comment before the rule */ #gamma {
+}
+
+/* mixed spaces and tab characters */
+ #delta {
+}
+
+/* long lines, like those produced by CSS minifiers
+ * (overflows a 16-bit unsigned int)
+ */
+ .epsilon{ background-image: url("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.png")}.zeta,.eta{}
diff --git a/layout/inspector/tests/chrome/GentiumPlus-R.woff b/layout/inspector/tests/chrome/GentiumPlus-R.woff
new file mode 100644
index 000000000..ebefd081a
--- /dev/null
+++ b/layout/inspector/tests/chrome/GentiumPlus-R.woff
Binary files differ
diff --git a/layout/inspector/tests/chrome/chrome.ini b/layout/inspector/tests/chrome/chrome.ini
new file mode 100644
index 000000000..ef4b74bda
--- /dev/null
+++ b/layout/inspector/tests/chrome/chrome.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files = GentiumPlus-R.woff
+
+[test_bug467669.css]
+[test_bug467669.xul]
+[test_bug695639.css]
+[test_bug695639.xul]
+[test_bug708874.css]
+[test_bug708874.xul]
+[test_bug727834.css]
+[test_bug727834.xul]
diff --git a/layout/inspector/tests/chrome/test_bug467669.css b/layout/inspector/tests/chrome/test_bug467669.css
new file mode 100644
index 000000000..fb050bf31
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_bug467669.css
@@ -0,0 +1,8 @@
+@font-face {
+ font-family: font-face-test-family;
+ src: url(bad/font/name.ttf), url(GentiumPlus-R.woff) format("woff");
+}
+
+.gentium {
+ font-family: font-face-test-family;
+}
diff --git a/layout/inspector/tests/chrome/test_bug467669.xul b/layout/inspector/tests/chrome/test_bug467669.xul
new file mode 100644
index 000000000..a5ecd32a7
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_bug467669.xul
@@ -0,0 +1,174 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<?xml-stylesheet type="text/css" href="test_bug467669.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=467669
+-->
+<window title="Mozilla Bug 467669"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="RunTest();">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 467669 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function RunTest() {
+ const CI = Components.interfaces;
+ const CC = Components.classes;
+
+ const kIsLinux = navigator.platform.indexOf("Linux") == 0;
+ const kIsMac = navigator.platform.indexOf("Mac") == 0;
+ const kIsWin = navigator.platform.indexOf("Win") == 0;
+
+ var domUtils =
+ CC["@mozilla.org/inspector/dom-utils;1"].getService(CI.inIDOMUtils);
+
+ var rng = document.createRange();
+ var elem, fonts, f;
+
+ elem = document.getElementById("test1");
+ rng.selectNode(elem);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts for simple Latin text");
+ f = fonts.item(0);
+ is(f.rule, null, "rule");
+ is(f.srcIndex, -1, "srcIndex");
+ is(f.localName, "", "local name");
+ is(f.URI, "", "URI");
+ is(f.format, "", "format string");
+ is(f.metadata, "", "metadata");
+// report(elem.id, fonts);
+
+ elem = document.getElementById("test2");
+ rng.selectNode(elem);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 3, "number of fonts for mixed serif, sans and monospaced text");
+// report(elem.id, fonts);
+
+ elem = document.getElementById("test3");
+ rng.selectNode(elem);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 2, "number of fonts for mixed Latin & Chinese");
+// report(elem.id, fonts);
+
+ // get properties of a @font-face font
+ elem = document.getElementById("test4");
+ rng.selectNode(elem);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts in @font-face test");
+ f = fonts.item(0);
+ isnot(f.rule, null, "missing rule");
+ is(f.srcIndex, 1, "srcIndex");
+ is(f.localName, "", "local name");
+ is(f.URI, "chrome://mochitests/content/chrome/layout/inspector/tests/chrome/GentiumPlus-R.woff", "bad URI");
+ is(f.format, "woff", "format");
+ is(/bukva:raz/.test(f.metadata), true, "metadata");
+// report(elem.id, fonts);
+
+ elem = document.getElementById("test5").childNodes[0];
+ // check that string length is as expected, including soft hyphens
+ is(elem.length, 42, "string length with soft hyphens");
+
+ // initial latin substring...
+ rng.setStart(elem, 0);
+ rng.setEnd(elem, 20); // "supercalifragilistic"
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts (Latin-only)");
+ f = fonts.item(0);
+ is(f.name, "Gentium Plus", "font name");
+ is(f.CSSFamilyName, "font-face-test-family", "family name");
+ is(f.fromFontGroup, true, "font matched in font group");
+
+ // extend to include a chinese character
+ rng.setEnd(elem, 21);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 2, "number of fonts (incl Chinese)");
+ if (kIsMac || kIsWin) { // these are only implemented by the Mac & Win font backends
+ var i;
+ for (i = 0; i < fonts.length; ++i) {
+ f = fonts.item(i);
+ if (f.rule) {
+ is(f.fromFontGroup, true, "@font-face font matched in group");
+ is(f.fromLanguagePrefs, false, "not from language prefs");
+ is(f.fromSystemFallback, false, "not from system fallback");
+ } else {
+ is(f.fromFontGroup, false, "not matched in group");
+ is(f.fromLanguagePrefs, true, "from language prefs");
+ is(f.fromSystemFallback, false, "not from system fallback");
+ }
+ }
+ }
+
+ // second half of the string includes &shy; chars to check original/skipped mapping;
+ // select just the final character
+ rng.setStart(elem, elem.length - 1);
+ rng.setEnd(elem, elem.length);
+ is(rng.toString(), "!", "content of range");
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts for last char");
+ f = fonts.item(0);
+ is(f.name, "Gentium Plus", "font name");
+
+ // include the preceding character as well
+ rng.setStart(elem, elem.length - 2);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 2, "number of fonts for last two chars");
+
+ // then trim the final one
+ rng.setEnd(elem, elem.length - 1);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts for Chinese char");
+ f = fonts.item(0);
+ isnot(f.name, "Gentium Plus", "font name for Chinese char");
+
+ rng.selectNode(elem);
+ fonts = domUtils.getUsedFontFaces(rng);
+// report("test5", fonts);
+
+ elem = document.getElementById("test6");
+ rng.selectNode(elem);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 2, "number of font faces for regular & italic");
+ is(fonts.item(0).CSSFamilyName, fonts.item(1).CSSFamilyName, "same family for regular & italic");
+ isnot(fonts.item(0).name, fonts.item(1).name, "different faces for regular & italic");
+// report(elem.id, fonts);
+
+ SimpleTest.finish();
+}
+
+// just for test-debugging purposes
+function report(e, f) {
+ var fontNames = "";
+ var i;
+ for (i = 0; i < f.length; ++i) {
+ if (i == 0) {
+ fontNames += e + " fonts: "
+ } else {
+ fontNames += ", ";
+ }
+ fontNames += f.item(i).name;
+ }
+ dump(fontNames + "\n");
+}
+
+ ]]>
+ </script>
+
+ <!-- html:body contains elements the test will inspect -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=467669"
+ target="_blank">Mozilla Bug 467669</a>
+ <div id="test1">Hello world</div>
+ <div id="test2" style="font-family:sans-serif"><span style="font-family:serif">Hello</span> <tt>cruel</tt> world</div>
+ <div id="test3">Hello, &#x4F60;&#x597D;</div>
+ <div id="test4" class="gentium">Hello Gentium Plus!</div>
+ <div id="test5" class="gentium">supercalifragilistic&#x4F60;ex&#xAD;pi&#xAD;a&#xAD;li&#xAD;do&#xAD;cious&#x597D;!</div>
+ <div id="test6" style="font-family:serif">regular and <em>italic</em> text</div>
+ </body>
+
+</window>
diff --git a/layout/inspector/tests/chrome/test_bug695639.css b/layout/inspector/tests/chrome/test_bug695639.css
new file mode 100644
index 000000000..549537498
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_bug695639.css
@@ -0,0 +1,8 @@
+@font-face {
+ font-family: gent;
+ src: url(GentiumPlus-R.woff) format("woff");
+}
+
+.test {
+ font-family: gent, sans-serif;
+}
diff --git a/layout/inspector/tests/chrome/test_bug695639.xul b/layout/inspector/tests/chrome/test_bug695639.xul
new file mode 100644
index 000000000..cc2c879c9
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_bug695639.xul
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<?xml-stylesheet type="text/css" href="test_bug695639.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=695639
+-->
+<window title="Mozilla Bug 695639"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="RunTest();">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 695639 - check that GetFontFacesForText handles wrapped lines properly **/
+
+SimpleTest.waitForExplicitFinish();
+
+function RunTest() {
+ const CI = Components.interfaces;
+ const CC = Components.classes;
+
+ var domUtils =
+ CC["@mozilla.org/inspector/dom-utils;1"].getService(CI.inIDOMUtils);
+
+ var rng = document.createRange();
+ var elem, fonts, f;
+
+ elem = document.getElementById("test").childNodes[0];
+ rng.setStart(elem, 0);
+ rng.setEnd(elem, 14);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 2, "number of fonts used for entire text");
+
+ // initial latin substring...
+ rng.setStart(elem, 0);
+ rng.setEnd(elem, 5); // "Hello"
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts (1)");
+ f = fonts.item(0);
+ is(f.name, "Gentium Plus", "font name (1)");
+
+ // the space (where the line wraps) should also be Gentium
+ rng.setStart(elem, 5);
+ rng.setEnd(elem, 6); // space
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts (2)");
+ f = fonts.item(0);
+ is(f.name, "Gentium Plus", "font name (2)");
+
+ // the Chinese text "ni hao" should NOT be in Gentium
+ rng.setStart(elem, 6);
+ rng.setEnd(elem, 8); // two Chinese characters on second line
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts (3)");
+ f = fonts.item(0);
+ isnot(f.name, "Gentium Plus", "font name (3)");
+
+ // space and "world" should be Gentium again
+ rng.setStart(elem, 8);
+ rng.setEnd(elem, 14);
+ fonts = domUtils.getUsedFontFaces(rng);
+ is(fonts.length, 1, "number of fonts (4)");
+ f = fonts.item(0);
+ is(f.name, "Gentium Plus", "font name (4)");
+
+ SimpleTest.finish();
+}
+ ]]>
+ </script>
+
+ <style type="text/css">
+ </style>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=695639"
+ target="_blank">Mozilla Bug 695639</a>
+ <div style="width: 2em;" class="test" id="test">Hello &#x4F60;&#x597D; world</div>
+ </body>
+
+</window>
diff --git a/layout/inspector/tests/chrome/test_bug708874.css b/layout/inspector/tests/chrome/test_bug708874.css
new file mode 100644
index 000000000..539c44538
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_bug708874.css
@@ -0,0 +1,33 @@
+#test-div {
+ color: rgb(0, 0, 0);
+ font-family: serif;
+ font-weight: 400;
+}
+
+#test-div:hover {
+ color: rgb(10, 0, 0);
+}
+
+#test-div:active {
+ font-family: Arial;
+}
+
+#test-div:focus {
+ font-weight: 800;
+}
+
+#test-button {
+ color: rgb(0, 0, 0);
+}
+
+#test-button:disabled {
+ color: rgb(40, 0, 0);
+}
+
+#test-link:visited {
+ color: rgb(20, 0, 0);
+}
+
+#test-link:link {
+ color: rgb(30, 0, 0);
+}
diff --git a/layout/inspector/tests/chrome/test_bug708874.xul b/layout/inspector/tests/chrome/test_bug708874.xul
new file mode 100644
index 000000000..0896d1eca
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_bug708874.xul
@@ -0,0 +1,296 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<?xml-stylesheet type="text/css" href="test_bug708874.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=708874
+-->
+<window title="Mozilla Bug 708874"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="RunTests();">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript">
+ <![CDATA[
+
+/** Test for Bug 708874 - API for locking pseudo-class state of an element **/
+
+var DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
+ .getService(Components.interfaces.inIDOMUtils);
+var DOMWindowUtils = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+ .getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+var defaultColor = "rgb(0, 0, 0)";
+var disabledColor = "rgb(40, 0, 0)";
+
+function RunTests() {
+ testLockEnabled();
+ testLockDisabled();
+ testVisited();
+ testMultiple();
+ testInvalid();
+ testNotElement();
+}
+
+function testLockEnabled() {
+ var button = document.getElementById("test-button");
+
+ /* starting state is enabled */
+ button.removeAttribute("disabled");
+
+ is(DOMUtils.hasPseudoClassLock(button, ":disabled"), false,
+ "doesn't have lock at start");
+
+ is(window.getComputedStyle(button).color, defaultColor,
+ "color is default color before locking on");
+
+ is(button.matches(":disabled"), false,
+ "doesn't match selector at start");
+
+ /* lock */
+ DOMUtils.addPseudoClassLock(button, ":disabled");
+
+ is(DOMUtils.hasPseudoClassLock(button, ":disabled"), true,
+ "hasPseudoClassLock is true after locking");
+
+ is(window.getComputedStyle(button).color, disabledColor,
+ ":disabled style applied after adding lock");
+
+ is(button.matches(":disabled"), true,
+ "matches selector after adding lock");
+
+ /* change state to disabled */
+ button.setAttribute("disabled", "disabled");
+
+ is(window.getComputedStyle(button).color, disabledColor,
+ ":disabled style still applied after really disabling");
+
+ is(button.matches(":disabled"), true,
+ "matches selector after adding lock");
+
+ /* remove lock */
+ DOMUtils.removePseudoClassLock(button, ":disabled");
+
+ is(DOMUtils.hasPseudoClassLock(button, ":disabled"), false,
+ "hasPseudoClassLock is false after removing on lock");
+
+ is(window.getComputedStyle(button).color, disabledColor,
+ ":disabled style still applied after removing lock");
+
+ is(button.matches(":disabled"), true,
+ "matches selector after removing lock");
+
+ /* change state to enabled */
+ button.removeAttribute("disabled");
+
+ is(window.getComputedStyle(button).color, defaultColor,
+ "back to default style after un-disabling");
+
+ is(button.matches(":disabled"), false,
+ "doesn't match selector after enabling");
+}
+
+
+function testLockDisabled() {
+ var button = document.getElementById("test-button");
+
+ /* starting state is disabled */
+ button.setAttribute("disabled", "disabled");
+
+ is(DOMUtils.hasPseudoClassLock(button, ":disabled"), false,
+ "doesn't have lock at start");
+
+ is(window.getComputedStyle(button).color, disabledColor,
+ "color is :disabled color before locking");
+
+ is(button.matches(":disabled"), true,
+ "matches selector before locking");
+
+ /* lock */
+ DOMUtils.addPseudoClassLock(button, ":disabled");
+
+ is(DOMUtils.hasPseudoClassLock(button, ":disabled"), true,
+ "hasPseudoClassLock is true after locking");
+
+ is(window.getComputedStyle(button).color, disabledColor,
+ ":disabled style still applied after adding on lock");
+
+ is(button.matches(":disabled"), true,
+ "matches selector after locking");
+
+ /* change state to enabled */
+ button.removeAttribute("disabled");
+
+ is(window.getComputedStyle(button).color, disabledColor,
+ ":disabled style applied after enabling");
+
+ is(button.matches(":disabled"), true,
+ "matches selector after enabling with lock on");
+
+ /* remove lock */
+ DOMUtils.removePseudoClassLock(button, ":disabled");
+
+ is(DOMUtils.hasPseudoClassLock(button, ":disabled"), false,
+ "hasPseudoClassLock is false after removing on lock");
+
+ is(window.getComputedStyle(button).color, defaultColor,
+ "default style applied after removing lock");
+
+ is(button.matches(":disabled"), false,
+ "doesn't match selector after unlocking");
+
+ /* change state to disabled */
+ button.setAttribute("disabled", "disabled");
+
+ is(window.getComputedStyle(button).color, disabledColor,
+ ":disabled style applied after disabling after unlocking");
+
+ is(button.matches(":disabled"), true,
+ "matches selector again after disabling");
+}
+
+function testVisited() {
+ var link = document.getElementById("test-link");
+ var visitedColor = "rgb(20, 0, 0)";
+ var unvisitedColor = "rgb(30, 0, 0)";
+
+ /* lock visited */
+ DOMUtils.addPseudoClassLock(link, ":visited");
+
+ is(DOMUtils.hasPseudoClassLock(link, ":visited"), true,
+ "hasPseudoClassLock is true after adding lock");
+
+ var color = DOMWindowUtils.getVisitedDependentComputedStyle(link,
+ null, "color");
+ is(color, visitedColor, "color is :visited color after locking");
+
+ /* lock unvisited */
+ DOMUtils.addPseudoClassLock(link, ":link");
+
+ is(DOMUtils.hasPseudoClassLock(link, ":link"), true,
+ "hasPseudoClassLock is true after adding :link lock");
+
+ is(DOMUtils.hasPseudoClassLock(link, ":visited"), false,
+ "hasPseudoClassLock is false for :visited after adding :link lock");
+
+ var color = DOMWindowUtils.getVisitedDependentComputedStyle(link,
+ null, "color");
+ is(color, unvisitedColor, "color is :link color after locking :link");
+
+ /* lock visited back on */
+ DOMUtils.addPseudoClassLock(link, ":visited");
+
+ is(DOMUtils.hasPseudoClassLock(link, ":visited"), true,
+ "hasPseudoClassLock is true after adding :visited lock");
+
+ is(DOMUtils.hasPseudoClassLock(link, ":link"), false,
+ "hasPseudoClassLock is false for :link after adding :visited lock");
+
+ var color = DOMWindowUtils.getVisitedDependentComputedStyle(link,
+ null, "color");
+ is(color, visitedColor, "color is :visited color after locking back on");
+}
+
+function testMultiple() {
+ var div = document.getElementById("test-div");
+
+ var styles = {
+ ":hover": {
+ property: "color",
+ value: "rgb(10, 0, 0)",
+ defaultValue: "rgb(0, 0, 0)"
+ },
+ ":active": {
+ property: "font-family",
+ value: "Arial",
+ defaultValue: "serif"
+ },
+ ":focus": {
+ property: "font-weight",
+ value: "800",
+ defaultValue: "400"
+ }
+ };
+
+ for (var pseudo in styles) {
+ DOMUtils.addPseudoClassLock(div, pseudo);
+ }
+
+ for (var pseudo in styles) {
+ is(DOMUtils.hasPseudoClassLock(div, pseudo), true,
+ "hasPseudoClassLock is true after locking");
+
+ var style = styles[pseudo];
+ is(window.getComputedStyle(div).getPropertyValue(style.property),
+ style.value, "style for pseudo-class is applied after locking");
+
+ is(div.matches(pseudo), true,
+ "matches selector after locking");
+ }
+
+ DOMUtils.clearPseudoClassLocks(div);
+
+ for (var pseudo in styles) {
+ is(DOMUtils.hasPseudoClassLock(div, pseudo), false,
+ "hasPseudoClassLock is false after clearing");
+
+ is(window.getComputedStyle(div).getPropertyValue(style.property),
+ style.defaultValue, "style is back to default after clearing");
+
+ is(div.matches(pseudo), false,
+ "doesn't match selector after unlocking");
+ }
+}
+
+function testInvalid() {
+ var div = document.getElementById("test-div");
+ var pseudos = ["not a valid pseudo-class", ":ny-link", ":first-child"];
+
+ for (var i = 0; i < pseudos.length; i++) {
+ var pseudo = pseudos[i];
+
+ // basically make sure these don't crash the browser.
+ DOMUtils.addPseudoClassLock(div, pseudo);
+
+ is(DOMUtils.hasPseudoClassLock(div, pseudo), false);
+
+ DOMUtils.removePseudoClassLock(div, pseudo);
+ }
+}
+
+function testNotElement() {
+ for (var value of [null, undefined, {}]) {
+ SimpleTest.doesThrow(() => DOMUtils.hasPseudoClassLock(value, ":hover"),
+ "hasPseudoClassLock should throw for " + value);
+ SimpleTest.doesThrow(() => DOMUtils.addPseudoClassLock(value, ":hover"),
+ "addPseudoClassLock should throw for " + value);
+ SimpleTest.doesThrow(() => DOMUtils.removePseudoClassLock(value, ":hover"),
+ "removePseudoClassLock should throw for " + value);
+ SimpleTest.doesThrow(() => DOMUtils.clearPseudoClassLocks(value),
+ "clearPseudoClassLocks should throw for " + value);
+ }
+}
+ ]]>
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=708874"
+ target="_blank">Mozilla Bug 708874</a>
+
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=708874">
+ Mozilla Bug 708874 - API for locking pseudo-class state of an element
+ </a>
+
+ <a id="test-link" href="http://notavisitedwebsite.com">
+ test link
+ </a>
+
+ <div id="test-div">
+ test div
+ </div>
+
+ <button id="test-button">
+ test button
+ </button>
+ </body>
+
+</window>
diff --git a/layout/inspector/tests/chrome/test_bug727834.css b/layout/inspector/tests/chrome/test_bug727834.css
new file mode 100644
index 000000000..f21f7a54c
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_bug727834.css
@@ -0,0 +1,7 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+body {
+ padding-top: 100px;
+}
diff --git a/layout/inspector/tests/chrome/test_bug727834.xul b/layout/inspector/tests/chrome/test_bug727834.xul
new file mode 100644
index 000000000..dde8e1b03
--- /dev/null
+++ b/layout/inspector/tests/chrome/test_bug727834.xul
@@ -0,0 +1,88 @@
+<?xml version="1.0"?>
+<!--
+vim: set ts=2 et sw=2 tw=80:
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<?xml-stylesheet type="text/css" href="test_bug727834.css"?>
+<window title="Mozilla Bug 727834"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="RunTests();">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"><![CDATA[
+/** Test for Bug 727834 - Add an API to (re)parse a style sheet in place **/
+
+function RunTests() {
+ SimpleTest.waitForExplicitFinish();
+
+ let DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
+ .getService(Components.interfaces.inIDOMUtils);
+ let body = document.querySelector("body");
+ let testSheet = document.styleSheets[2];
+ let rule = document.styleSheets[2].cssRules[0];
+
+ is(testSheet.cssRules.length, 1,
+ "style sheet has 1 rule");
+ is(rule.style.paddingTop, "100px",
+ "original first rule has padding-top 100px");
+ is(window.getComputedStyle(body).paddingTop, "100px",
+ "original first rule applies");
+
+ DOMUtils.parseStyleSheet(testSheet,
+ "@import url(test_bug727834.css); body{background: red;}");
+
+ is(testSheet.cssRules.length, 2,
+ "style sheet now has 2 rules");
+ is(window.getComputedStyle(body).backgroundColor, "rgb(255, 0, 0)",
+ "background is now red");
+
+ let exceptionName;
+ try {
+ rule.style.paddingLeft = "100px";
+ } catch (ex) {
+ exceptionName = ex.name;
+ } finally {
+ is(exceptionName, "NS_ERROR_NOT_AVAILABLE",
+ "original rule is not available for modification anymore");
+ }
+ is(window.getComputedStyle(body).paddingLeft, "0px",
+ "original rule does not apply to document");
+
+ rule = testSheet.cssRules[0];
+
+ is(rule.parentStyleSheet, testSheet,
+ "rule's parent style sheet is not null");
+
+ DOMUtils.parseStyleSheet(testSheet,
+ "body{background: lime;}");
+
+ is(testSheet.cssRules.length, 1,
+ "style sheet now has 1 rule");
+ is(window.getComputedStyle(body).backgroundColor, "rgb(0, 255, 0)",
+ "background is now lime");
+ is(rule.parentStyleSheet, null,
+ "detached rule's parent style sheet is null");
+
+ SimpleTest.executeSoon(function () {
+ DOMUtils.parseStyleSheet(testSheet,
+ "@import url(test_bug727834.css); body{background: blue;}");
+
+ is(testSheet.cssRules.length, 2,
+ "style sheet now has 2 rules");
+ is(window.getComputedStyle(body).backgroundColor, "rgb(0, 0, 255)",
+ "background is now blue");
+ is(testSheet.cssRules[0].parentStyleSheet, testSheet,
+ "parent style sheet is the test sheet");
+
+ SimpleTest.finish();
+ });
+}
+ ]]></script>
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=727834">
+ Mozilla Bug 727834 - Add an API to (re)parse a style sheet in place
+ </a>
+ </body>
+</window>
diff --git a/layout/inspector/tests/file_bug522601.html b/layout/inspector/tests/file_bug522601.html
new file mode 100644
index 000000000..c3e27a4b5
--- /dev/null
+++ b/layout/inspector/tests/file_bug522601.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+ "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=522601
+-->
+<head>
+ <title>Test for Bug 522601</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<div id="some content">
+ <h1>Blah</h1>
+</div>
+</body>
+</html>
diff --git a/layout/inspector/tests/mochitest.ini b/layout/inspector/tests/mochitest.ini
new file mode 100644
index 000000000..25209f57a
--- /dev/null
+++ b/layout/inspector/tests/mochitest.ini
@@ -0,0 +1,29 @@
+[DEFAULT]
+support-files =
+ bug1202095.css
+ bug1202095-2.css
+ bug856317.css
+ file_bug522601.html
+
+[test_bug462787.html]
+[test_bug462789.html]
+[test_bug522601.xhtml]
+[test_bug536379.html]
+[test_bug536379-2.html]
+[test_bug557726.html]
+[test_bug609549.xhtml]
+[test_bug806192.html]
+[test_bug856317.html]
+[test_bug877690.html]
+[test_bug1006595.html]
+[test_color_to_rgba.html]
+[test_css_property_is_shorthand.html]
+[test_css_property_is_valid.html]
+[test_getCSSPseudoElementNames.html]
+[test_getRelativeRuleLine.html]
+[test_get_all_style_sheets.html]
+[test_is_valid_css_color.html]
+[test_isinheritableproperty.html]
+[test_parseStyleSheet.html]
+[test_parseStyleSheetImport.html]
+[test_selectormatcheselement.html]
diff --git a/layout/inspector/tests/test_bug1006595.html b/layout/inspector/tests/test_bug1006595.html
new file mode 100644
index 000000000..009269d37
--- /dev/null
+++ b/layout/inspector/tests/test_bug1006595.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1006595
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1006595</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1006595 **/
+ function arraysEqual(arr1, arr2, message) {
+ is(arr1.length, arr2.length, message + " length");
+ for (var i = 0; i < arr1.length; ++i) {
+ is(arr1[i], arr2[i], message + " element at index " + i);
+ }
+ }
+ var utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ var paddingSubProps = utils.getSubpropertiesForCSSProperty("padding");
+ arraysEqual(paddingSubProps,
+ [ "padding-top",
+ "padding-right",
+ "padding-bottom",
+ "padding-left" ],
+ "'padding' subproperties");
+
+ var displaySubProps = utils.getSubpropertiesForCSSProperty("color");
+ arraysEqual(displaySubProps, [ "color" ],
+ "'color' subproperties");
+
+ var varProps = utils.getSubpropertiesForCSSProperty("--foo");
+ arraysEqual(varProps, ["--foo"], "'--foo' subproperties");
+
+ ok(utils.cssPropertyIsShorthand("padding"), "'padding' is a shorthand")
+ ok(!utils.cssPropertyIsShorthand("color"), "'color' is not a shorthand")
+
+ ok(utils.cssPropertySupportsType("padding", utils.TYPE_LENGTH),
+ "'padding' can be a length");
+ ok(!utils.cssPropertySupportsType("padding", utils.TYPE_COLOR),
+ "'padding' can't be a color");
+
+ ok(utils.cssPropertySupportsType("padding", utils.TYPE_PERCENTAGE),
+ "'padding' can be a percentage");
+ ok(!utils.cssPropertySupportsType("color", utils.TYPE_PERCENTAGE),
+ "'color' can't be a percentage");
+
+ ok(utils.cssPropertySupportsType("color", utils.TYPE_COLOR),
+ "'color' can be a color");
+ ok(utils.cssPropertySupportsType("background", utils.TYPE_COLOR),
+ "'background' can be a color");
+ ok(!utils.cssPropertySupportsType("background-image", utils.TYPE_COLOR),
+ "'background-image' can't be a color");
+
+ ok(utils.cssPropertySupportsType("background-image", utils.TYPE_URL),
+ "'background-image' can be a URL");
+ ok(utils.cssPropertySupportsType("background", utils.TYPE_URL),
+ "'background' can be a URL");
+ ok(!utils.cssPropertySupportsType("background-color", utils.TYPE_URL),
+ "'background-color' can't be a URL");
+
+ // There are no properties claiming to be of TYPE_ANGLE. image-orientation
+ // would be, but it doesn't use table-driven parsing.
+
+ // There are no properties claiming to be of TYPE_FREQUENCY
+
+ ok(utils.cssPropertySupportsType("transition", utils.TYPE_TIME),
+ "'transition' can be a time");
+ ok(utils.cssPropertySupportsType("transition-duration", utils.TYPE_TIME),
+ "'transition-duration' can be a time");
+ ok(!utils.cssPropertySupportsType("background-color", utils.TYPE_TIME),
+ "'background-color' can't be a time");
+
+ ok(utils.cssPropertySupportsType("background-image", utils.TYPE_GRADIENT),
+ "'background-image' can be a gradient");
+ ok(utils.cssPropertySupportsType("background", utils.TYPE_GRADIENT),
+ "'background' can be a gradient");
+ ok(!utils.cssPropertySupportsType("background-color", utils.TYPE_GRADIENT),
+ "'background-color' can't be a gradient");
+
+ ok(utils.cssPropertySupportsType("transition", utils.TYPE_TIMING_FUNCTION),
+ "'transition' can be a timing function");
+ ok(utils.cssPropertySupportsType("transition-timing-function",
+ utils.TYPE_TIMING_FUNCTION),
+ "'transition-duration' can be a timing function");
+ ok(!utils.cssPropertySupportsType("background-color",
+ utils.TYPE_TIMING_FUNCTION),
+ "'background-color' can't be a timing function");
+
+ ok(utils.cssPropertySupportsType("background-image", utils.TYPE_IMAGE_RECT),
+ "'background-image' can be an image rect");
+ ok(utils.cssPropertySupportsType("background", utils.TYPE_IMAGE_RECT),
+ "'background' can be an image rect");
+ ok(!utils.cssPropertySupportsType("background-color", utils.TYPE_IMAGE_RECT),
+ "'background-color' can't be an image rect");
+
+ ok(utils.cssPropertySupportsType("z-index", utils.TYPE_NUMBER),
+ "'z-index' can be a number");
+ ok(utils.cssPropertySupportsType("line-height", utils.TYPE_NUMBER),
+ "'line-height' can be a number");
+ ok(!utils.cssPropertySupportsType("background-color", utils.TYPE_NUMBER),
+ "'background-color' can't be a number");
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1006595">Mozilla Bug 1006595</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug462787.html b/layout/inspector/tests/test_bug462787.html
new file mode 100644
index 000000000..88c5c466f
--- /dev/null
+++ b/layout/inspector/tests/test_bug462787.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=462787
+-->
+<head>
+ <title>Test for Bug 462787</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462787">Mozilla Bug 462787</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 462787 **/
+
+function do_test() {
+ const INVALID_POINTER = SpecialPowers.Cr.NS_ERROR_INVALID_POINTER;
+
+ var utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+ try {
+ utils.getCSSStyleRules(null);
+ ok(false, "expected an exception");
+ }
+ catch(e) {
+ e = SpecialPowers.wrap(e);
+ is(e.result, INVALID_POINTER, "got the expected exception");
+ }
+
+ try {
+ utils.getRuleLine(null);
+ ok(false, "expected an exception");
+ }
+ catch(e) {
+ e = SpecialPowers.wrap(e);
+ is(e.result, INVALID_POINTER, "got the expected exception");
+ }
+
+ try {
+ utils.isIgnorableWhitespace(null);
+ ok(false, "expected an exception");
+ }
+ catch(e) {
+ e = SpecialPowers.wrap(e);
+ is(e.result, INVALID_POINTER, "got the expected exception");
+ }
+
+ try {
+ utils.getParentForNode(null, true);
+ ok(false, "expected an exception");
+ }
+ catch(e) {
+ e = SpecialPowers.wrap(e);
+ is(e.result, INVALID_POINTER, "got the expected exception");
+ }
+
+ try {
+ utils.getBindingURLs(null);
+ ok(false, "expected an exception");
+ }
+ catch(e) {
+ e = SpecialPowers.wrap(e);
+ is(e.result, INVALID_POINTER, "got the expected exception");
+ }
+
+ try {
+ utils.getContentState(null);
+ ok(false, "expected an exception");
+ }
+ catch(e) {
+ e = SpecialPowers.wrap(e);
+ is(e.result, INVALID_POINTER, "got the expected exception");
+ }
+
+ try {
+ utils.setContentState(null, false);
+ ok(false, "expected an exception");
+ }
+ catch(e) {
+ e = SpecialPowers.wrap(e);
+ is(e.result, INVALID_POINTER, "got the expected exception");
+ }
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(do_test);
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug462789.html b/layout/inspector/tests/test_bug462789.html
new file mode 100644
index 000000000..a027b85a6
--- /dev/null
+++ b/layout/inspector/tests/test_bug462789.html
@@ -0,0 +1,94 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=462789
+-->
+<head>
+ <title>Test for Bug 462789</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=462789">Mozilla Bug 462789</a>
+<p id="display"><iframe id="bug462789_iframe" src="data:text/html,<html><head><style>*{color:black;}</style></head><body>xxx" style="display: none;"></iframe></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 462789 **/
+
+function do_test() {
+ const ERROR_INVALID_ARG = 0x80070057;
+ const DOCUMENT_NODE_TYPE = 9;
+
+ var utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ var iframe = document.getElementById("bug462789_iframe");
+ var docElement = iframe.contentDocument.documentElement;
+ var body = docElement.children[1];
+ var rule = iframe.contentDocument.styleSheets[0].cssRules[0];
+ var text = body.firstChild;
+
+ try {
+ var res = utils.getCSSStyleRules(docElement);
+ is(res, null, "getCSSStyleRules");
+ res = utils.getCSSStyleRules(body);
+ is(res, null, "getCSSStyleRules");
+ }
+ catch(e) { ok(false, "got an unexpected exception:" + e); }
+
+ try {
+ var res = utils.getRuleLine(rule);
+ is(res, 1, "getRuleLine");
+ }
+ catch(e) { ok(false, "got an unexpected exception:" + e); }
+
+ try {
+ var res = utils.isIgnorableWhitespace(text);
+ is(res, false, "isIgnorableWhitespace");
+ }
+ catch(e) { ok(false, "got an unexpected exception:" + e); }
+
+ try {
+ var res = utils.getParentForNode(docElement, true);
+ is(res.nodeType, DOCUMENT_NODE_TYPE, "getParentForNode(docElement, true)");
+ res = utils.getParentForNode(text, true);
+ is(res.tagName, "BODY", "getParentForNode(text, true)");
+ }
+ catch(e) { ok(false, "got an unexpected exception:" + e); }
+
+ try {
+ var res = utils.getBindingURLs(docElement);
+ ok(SpecialPowers.call_Instanceof(res, SpecialPowers.Ci["nsIArray"]), "getBindingURLs result type");
+ is(res.length, 0, "getBindingURLs array length");
+ }
+ catch(e) { ok(false, "got an unexpected exception:" + e); }
+
+ try {
+ utils.getContentState(docElement);
+ ok(true, "Should not throw");
+ }
+ catch(e) { ok(false, "Got an exception: " + e); }
+
+ try {
+ utils.setContentState(docElement, false);
+ ok(false, "expected an exception");
+ }
+ catch(e) {
+ e = SpecialPowers.wrap(e);
+ is(e.result, ERROR_INVALID_ARG, "got the expected exception");
+ }
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(do_test);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug522601.xhtml b/layout/inspector/tests/test_bug522601.xhtml
new file mode 100644
index 000000000..7c5a9e79c
--- /dev/null
+++ b/layout/inspector/tests/test_bug522601.xhtml
@@ -0,0 +1,274 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=522601
+-->
+<head>
+ <title>Test for Bug 522601</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="testBinding">
+ <content><div><children/></div><children includes="span"/></content>
+ </binding>
+ </bindings>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=522601">Mozilla Bug 522601</a>
+<p id="display" style="-moz-binding: url(#testBinding)">
+ <span id="s">This is some text</span>
+ More text
+ <b id="b">Even more <i id="i1">Italic</i>text<i id="i2">And more italic</i></b></p>
+<div id="content" style="display: none">
+
+</div>
+<div id="subdoc">
+ <iframe id="frame1" src="file_bug522601.html">frame text</iframe>
+</div>
+<div id="test-shadow">
+ <h2 id="h2">light child</h2>
+</div>
+<pre id="test">
+<script type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 522601 **/
+SimpleTest.waitForExplicitFinish();
+
+function testFunc(walker, func, expectedNode, str) {
+ var oldCurrent = SpecialPowers.unwrap(walker.currentNode);
+ var newNode = SpecialPowers.unwrap(walker[func]());
+ is(newNode, expectedNode, "Unexpected node after " + str);
+ is(SpecialPowers.unwrap(walker.currentNode), newNode ? newNode : oldCurrent,
+ "Unexpected current node after " + str);
+}
+
+addLoadEvent(function() {
+ var walkerSubDocument =
+ SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+ .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+ walkerSubDocument.showAnonymousContent = false;
+ walkerSubDocument.showSubDocuments = true;
+ walkerSubDocument.init($("frame1"), NodeFilter.SHOW_ALL);
+
+ is(SpecialPowers.unwrap(walkerSubDocument.currentNode), $("frame1"), "Unexpected sub-doc root");
+ testFunc(walkerSubDocument, "firstChild", $("frame1").contentDocument.doctype,
+ "step to sub documents doctype");
+ testFunc(walkerSubDocument, "nextSibling", $("frame1").contentDocument.documentElement,
+ "step to sub documents documentElement");
+
+ walkerSubDocument =
+ SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+ .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+ walkerSubDocument.showAnonymousContent = false;
+ walkerSubDocument.showSubDocuments = true;
+ walkerSubDocument.showDocumentsAsNodes = true;
+ walkerSubDocument.init($("frame1"), NodeFilter.SHOW_ALL);
+
+ is(SpecialPowers.unwrap(walkerSubDocument.currentNode), $("frame1"), "Unexpected sub-doc root");
+ testFunc(walkerSubDocument, "firstChild", $("frame1").contentDocument,
+ "step to sub document");
+ testFunc(walkerSubDocument, "firstChild", $("frame1").contentDocument.doctype,
+ "step to sub documents doctype");
+ testFunc(walkerSubDocument, "nextSibling", $("frame1").contentDocument.documentElement,
+ "step to sub documents documentElement");
+
+ walkerSubDocument.currentNode = $("frame1").contentDocument;
+ is(SpecialPowers.unwrap(walkerSubDocument.currentNode), $("frame1").contentDocument,
+ "setting currentNode to sub document");
+ testFunc(walkerSubDocument, "nextSibling", null,
+ "nextSibling for sub document is null");
+
+ var walkerFrameChild =
+ SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+ .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+ walkerFrameChild.showAnonymousContent = false;
+ walkerFrameChild.showSubDocuments = false;
+ walkerFrameChild.init($("frame1"), NodeFilter.SHOW_ALL);
+
+ is(SpecialPowers.unwrap(walkerFrameChild.currentNode), $("frame1"), "Unexpected sub-doc root");
+ testFunc(walkerFrameChild, "firstChild", $("frame1").firstChild,
+ "step to frames child");
+
+ var walkerNonAnon =
+ SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+ .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+ walkerNonAnon.init($("display"), NodeFilter.SHOW_ALL);
+ walkerNonAnon.showAnonymousContent = false;
+
+ is(SpecialPowers.unwrap(walkerNonAnon.currentNode), $("display"), "Unexpected non-anon root");
+ testFunc(walkerNonAnon, "nextNode", $("s").previousSibling,
+ "step to some text");
+ testFunc(walkerNonAnon, "nextNode", $("s"), "step to span");
+ testFunc(walkerNonAnon, "nextNode", $("s").firstChild, "step to span text");
+ testFunc(walkerNonAnon, "nextNode", $("s").nextSibling, "step to more text");
+ testFunc(walkerNonAnon, "nextNode", $("b"), "step to bold");
+ testFunc(walkerNonAnon, "nextNode", $("b").firstChild, "step to bold text");
+ testFunc(walkerNonAnon, "nextNode", $("i1"), "step to first italic");
+ testFunc(walkerNonAnon, "nextNode", $("i1").firstChild,
+ "step to first italic text");
+ testFunc(walkerNonAnon, "nextNode", $("i1").nextSibling,
+ "step to more bold text");
+ testFunc(walkerNonAnon, "nextNode", $("i2"), "step to second italic");
+ testFunc(walkerNonAnon, "nextNode", $("i2").firstChild,
+ "step to second italic text");
+ testFunc(walkerNonAnon, "nextNode", null, "step past end");
+ testFunc(walkerNonAnon, "parentNode", $("i2"), "step up to second italic");
+ testFunc(walkerNonAnon, "parentNode", $("b"), "step up to bold");
+ testFunc(walkerNonAnon, "nextNode", $("b").firstChild, "step to bold text again");
+ testFunc(walkerNonAnon, "parentNode", $("b"), "step up to bold again");
+ testFunc(walkerNonAnon, "parentNode", $("display"), "step up to display");
+ testFunc(walkerNonAnon, "parentNode", null, "step up past root");
+ testFunc(walkerNonAnon, "firstChild", $("s").previousSibling,
+ "step firstChild to display first child");
+ testFunc(walkerNonAnon, "nextSibling", $("s"),
+ "step nextSibling to span");
+ testFunc(walkerNonAnon, "nextSibling", $("s").nextSibling,
+ "step nextSibling to more text");
+ testFunc(walkerNonAnon, "nextSibling", $("b"), "step nextSibling to bold");
+ testFunc(walkerNonAnon, "nextSibling", null, "step nextSibling past end");
+ testFunc(walkerNonAnon, "previousSibling", $("s").nextSibling,
+ "step previousSibling to more text");
+ testFunc(walkerNonAnon, "previousSibling", $("s"),
+ "step previousSibling to span");
+ testFunc(walkerNonAnon, "previousSibling", $("s").previousSibling,
+ "step previousSibling to display first child");
+ testFunc(walkerNonAnon, "previousSibling", null,
+ "step previousSibling past end");
+
+ // Move the walker over to the end
+ while (walkerNonAnon.nextNode()) { /* do nothing */ }
+ is(SpecialPowers.unwrap(walkerNonAnon.currentNode), $("i2").firstChild, "unexpected last node");
+ testFunc(walkerNonAnon, "previousNode", $("i2"), "step back to second italic");
+ testFunc(walkerNonAnon, "previousNode", $("i1").nextSibling,
+ "step back to more bold text");
+ testFunc(walkerNonAnon, "previousNode", $("i1").firstChild,
+ "step back to first italic text");
+ testFunc(walkerNonAnon, "previousNode", $("i1"), "step back to first italic");
+ testFunc(walkerNonAnon, "previousNode", $("b").firstChild,
+ "step back to bold text");
+ testFunc(walkerNonAnon, "previousNode", $("b"), "step back to bold");
+ testFunc(walkerNonAnon, "previousNode", $("s").nextSibling, "step back to more text");
+ testFunc(walkerNonAnon, "previousNode", $("s").firstChild, "step back to span text");
+ testFunc(walkerNonAnon, "previousNode", $("s"), "step back to span");
+ testFunc(walkerNonAnon, "previousNode", $("s").previousSibling,
+ "step back to some text");
+ testFunc(walkerNonAnon, "previousNode", $("display"),
+ "step back to root");
+ testFunc(walkerNonAnon, "previousNode", null,
+ "step back past root");
+
+ walkerNonAnon.currentNode = $("s");
+ is(SpecialPowers.unwrap(walkerNonAnon.currentNode), SpecialPowers.unwrap($("s")),
+ "Setting currentNode to span");
+
+ var anonDiv = SpecialPowers.unwrap(SpecialPowers.wrap(document).getAnonymousNodes($("display")))[0];
+
+ try {
+ walkerNonAnon.currentNode = anonDiv;
+ ok(false, "Setting current node to a node that is otherwise unreachable," +
+ " with the current visibility settings should throw");
+ } catch(e) {
+ ok(e.toString().indexOf("NS_ERROR_ILLEGAL_VALUE") > -1, "Setting current node to an anon node should throw" +
+ " NS_ERROR_ILLEGAL_VALUE if showAnonymousContent is set to false");
+ is(SpecialPowers.unwrap(walkerNonAnon.currentNode), SpecialPowers.unwrap($("s")),
+ "An unsuccessfull set currentNode should leave behind the old state");
+ testFunc(walkerNonAnon, "nextSibling", $("s").nextSibling, "nextSibling after set currentNode");
+ }
+
+ var walkerAnon =
+ SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+ .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+ walkerAnon.showAnonymousContent = true;
+ walkerAnon.init($("display"), NodeFilter.SHOW_ALL);
+
+ is(SpecialPowers.unwrap(walkerAnon.currentNode), $("display"), "Unexpected anon root");
+ testFunc(walkerAnon, "nextNode", anonDiv,
+ "step to anonymous div");
+ testFunc(walkerAnon, "nextNode", $("s").previousSibling,
+ "step to some text (anon)");
+ testFunc(walkerAnon, "nextNode", $("s").nextSibling, "step to more text (anon)");
+ testFunc(walkerAnon, "nextNode", $("b"), "step to bold (anon)");
+ testFunc(walkerAnon, "nextNode", $("b").firstChild, "step to bold text (anon)");
+ testFunc(walkerAnon, "nextNode", $("i1"), "step to first italic (anon)");
+ testFunc(walkerAnon, "nextNode", $("i1").firstChild,
+ "step to first italic text (anon)");
+ testFunc(walkerAnon, "nextNode", $("i1").nextSibling,
+ "step to more bold text (anon)");
+ testFunc(walkerAnon, "nextNode", $("i2"), "step to second italic (anon)");
+ testFunc(walkerAnon, "nextNode", $("i2").firstChild,
+ "step to second italic text (anon)");
+ testFunc(walkerAnon, "nextNode", $("s"), "step to span (anon)");
+ testFunc(walkerAnon, "nextNode", $("s").firstChild, "step to span text (anon)");
+ testFunc(walkerAnon, "nextNode", null, "step past end (anon)");
+ testFunc(walkerAnon, "parentNode", $("s"), "step up to span (anon)");
+ testFunc(walkerAnon, "parentNode", $("display"), "step up to display (anon)");
+ testFunc(walkerAnon, "nextNode", anonDiv, "step to anonymous div again");
+ testFunc(walkerAnon, "parentNode", $("display"),
+ "step up to display again (anon)");
+ testFunc(walkerAnon, "parentNode", null, "step up past root (anon)");
+ testFunc(walkerAnon, "firstChild", anonDiv,
+ "step firstChild to display first child (anon)");
+ testFunc(walkerAnon, "nextSibling", $("s"),
+ "step nextSibling to span (anon)");
+ testFunc(walkerAnon, "nextSibling", null, "step nextSibling past end (anon)");
+ testFunc(walkerAnon, "previousSibling", anonDiv,
+ "step previousSibling to anonymous div");
+ testFunc(walkerAnon, "previousSibling", null, "step previousSibling past end (anon)");
+
+ // Move the walker over to the end
+ while (walkerAnon.nextNode()) { /* do nothing */ }
+ testFunc(walkerAnon, "previousNode", $("s"), "step back to span (anon)");
+ testFunc(walkerAnon, "previousNode", $("i2").firstChild,
+ "step back to second italic text (anon)");
+ testFunc(walkerAnon, "previousNode", $("i2"), "step back to second italic (anon)");
+ testFunc(walkerAnon, "previousNode", $("i1").nextSibling,
+ "step back to more bold text (anon)");
+ testFunc(walkerAnon, "previousNode", $("i1").firstChild,
+ "step back to first italic text (anon)");
+ testFunc(walkerAnon, "previousNode", $("i1"), "step back to first italic (anon)");
+ testFunc(walkerAnon, "previousNode", $("b").firstChild, "step back to bold text (anon)");
+ testFunc(walkerAnon, "previousNode", $("b"), "step back to bold (anon)");
+ testFunc(walkerAnon, "previousNode", $("s").nextSibling, "step back to more text (anon)");
+ testFunc(walkerAnon, "previousNode", $("s").previousSibling,
+ "step back to some text (anon)");
+ testFunc(walkerAnon, "previousNode", anonDiv,
+ "step back to anonymous div");
+ testFunc(walkerAnon, "previousNode", $("display"), "step back to root (anon)");
+ testFunc(walkerAnon, "previousNode", null, "step back past root (anon)");
+
+ var shadowdiv = document.querySelector('#test-shadow');
+ var shadowRoot = shadowdiv.createShadowRoot();
+ var h = document.createElement("header");
+ var c = document.createElement("content");
+ c.setAttribute("select", "h2");
+ h.appendChild(c);
+ shadowRoot.appendChild(h);
+
+ var walkerShadow =
+ SpecialPowers.Cc["@mozilla.org/inspector/deep-tree-walker;1"]
+ .createInstance(SpecialPowers.Ci.inIDeepTreeWalker);
+ walkerShadow.showAnonymousContent = true;
+ walkerShadow.init($("test-shadow"), NodeFilter.SHOW_ALL);
+ var c1 = walkerShadow.nextNode();
+ var c2 = walkerShadow.nextNode();
+ var c3 = walkerShadow.nextNode();
+
+ walkerShadow.currentNode = c1;
+ is(SpecialPowers.unwrap(walkerShadow.currentNode), h,
+ "Unexpected shadow element 1");
+ walkerShadow.currentNode = c2;
+ is(SpecialPowers.unwrap(walkerShadow.currentNode), $("h2"),
+ "Unexpected shadow element 2");
+ walkerShadow.currentNode = c3;
+ is(SpecialPowers.unwrap(walkerShadow.currentNode), $("h2").firstChild,
+ "Unexpected shadow element 3");
+
+ SimpleTest.finish();
+});
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug536379-2.html b/layout/inspector/tests/test_bug536379-2.html
new file mode 100644
index 000000000..6fe3d8f8d
--- /dev/null
+++ b/layout/inspector/tests/test_bug536379-2.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=536379
+-->
+<head>
+ <title>Test for Bug 536379</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" type="text/css" href="data:text/css,@import 'data:text/css,p { color: green }'">
+ <link rel="stylesheet" type="text/css" href="data:text/css,@import &quot;data:text/css,@import 'data:text/css,p { color: green }'&quot;">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=536379">Mozilla Bug 536379</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 536379 **/
+
+const CI = SpecialPowers.Ci;
+const CC = SpecialPowers.Cc;
+
+var domUtils =
+ CC["@mozilla.org/inspector/dom-utils;1"].getService(CI.inIDOMUtils);
+var rules = domUtils.getCSSStyleRules(document.getElementById("display"));
+var firstPRule =
+ rules.GetElementAt(rules.Count() - 2).QueryInterface(CI.nsIDOMCSSStyleRule);
+firstPRule.style.removeProperty("color");
+ok(true, "should not crash");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug536379.html b/layout/inspector/tests/test_bug536379.html
new file mode 100644
index 000000000..8ac012953
--- /dev/null
+++ b/layout/inspector/tests/test_bug536379.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=536379
+-->
+<head>
+ <title>Test for Bug 536379</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" type="text/css" href="data:text/css,p { color: green }">
+ <link rel="stylesheet" type="text/css" href="data:text/css,p { color: green }">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=536379">Mozilla Bug 536379</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 536379 **/
+
+const CI = SpecialPowers.Ci;
+const CC = SpecialPowers.Cc;
+
+var domUtils =
+ CC["@mozilla.org/inspector/dom-utils;1"].getService(CI.inIDOMUtils);
+var rules = domUtils.getCSSStyleRules(document.getElementById("display"));
+var firstPRule =
+ rules.GetElementAt(rules.Count() - 2).QueryInterface(CI.nsIDOMCSSStyleRule);
+firstPRule.style.removeProperty("color");
+ok(true, "should not crash");
+
+var links = document.getElementsByTagName("link");
+is(links.length, 3, "links.length");
+is(SpecialPowers.unwrap(firstPRule.parentStyleSheet), links[1].sheet, "sheet match for first P rule");
+var secondPRule =
+ rules.GetElementAt(rules.Count() - 1).QueryInterface(CI.nsIDOMCSSStyleRule);
+is(SpecialPowers.unwrap(secondPRule.parentStyleSheet), links[2].sheet, "sheet match for second P rule");
+is(links[1].href, links[2].href, "links should have same href");
+isnot(links[1].sheet, links[2].sheet, "links should have different sheets");
+isnot(firstPRule, secondPRule, "rules should be different");
+isnot(firstPRule.cssText, secondPRule.cssText, "text should be different since property was removed from one");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug557726.html b/layout/inspector/tests/test_bug557726.html
new file mode 100644
index 000000000..eda46bdfd
--- /dev/null
+++ b/layout/inspector/tests/test_bug557726.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=557726
+-->
+<head>
+ <title>Test for Bug 557726</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style id="pseudo-style">
+ #div1 {
+ color: blue;
+ }
+
+ #div1:first-letter {
+ font-weight: bold;
+ }
+
+ #div1:before {
+ content: '"';
+ }
+
+ #div1:after {
+ content: '"';
+ }
+
+ #div1:after, #div1:before {
+ color: red;
+ }
+ </style>
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=557726">Mozilla Bug 557726</a>
+
+<div id="div1">
+ text with ::before, ::after, and ::first-letter pseudo-elements
+</div>
+
+<script type="application/javascript">
+
+/** Test for Bug 557726 **/
+
+function getSelectors (rules) {
+ var styleElement = document.getElementById("pseudo-style");
+ var selectors = [];
+ for (var i = 0; i < rules.Count(); i++) {
+ var rule = rules.GetElementAt(i).QueryInterface(SpecialPowers.Ci.nsIDOMCSSStyleRule);
+ if (SpecialPowers.unwrap(rule.parentStyleSheet.ownerNode) == styleElement) // no user agent rules
+ selectors.push(rule.selectorText);
+ }
+ return selectors;
+}
+
+var domUtils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+var div = document.getElementById("div1");
+
+/* empty or missing pseudo-element argument */
+var selectors = getSelectors(domUtils.getCSSStyleRules(div));
+is(selectors.length, 1, "psuedo-element argument should be optional");
+is(selectors[0], "#div1", "should only have the non-pseudo element rule");
+
+selectors = getSelectors(domUtils.getCSSStyleRules(div, null));
+is(selectors.length, 1, "pseudo-element argument can be null");
+is(selectors[0], "#div1", "should only have the non pseudo-element rule");
+
+selectors = getSelectors(domUtils.getCSSStyleRules(div, ""));
+is(selectors.length, 1, "pseudo-element argument can be empty string");
+is(selectors[0], "#div1", "should only have the non pseudo-element rule");
+
+
+/* invalid pseudo-element argument */
+var rules = domUtils.getCSSStyleRules(div, "not a valid pseudo element");
+is(rules, null, "invalid pseudo-element returns no rules list");
+
+
+/* valid pseudo-element argument */
+selectors = getSelectors(domUtils.getCSSStyleRules(div, ":first-letter"));
+is(selectors.length, 1, "pseudo-element argument can be used");
+is(selectors[0], "#div1::first-letter", "should only have the ::first-letter rule");
+
+selectors = getSelectors(domUtils.getCSSStyleRules(div, ":before"));
+is(selectors.length, 2, "::before pseudo-element has two matching rules");
+isnot(selectors.indexOf("#div1::after, #div1::before"), -1, "fetched rule for ::before")
+isnot(selectors.indexOf("#div1::before"), -1, "fetched rule for ::before")
+
+selectors = getSelectors(domUtils.getCSSStyleRules(div, ":first-line"));
+is(selectors.length, 0, "valid pseudo-element but no matching rules");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug609549.xhtml b/layout/inspector/tests/test_bug609549.xhtml
new file mode 100644
index 000000000..d7d115847
--- /dev/null
+++ b/layout/inspector/tests/test_bug609549.xhtml
@@ -0,0 +1,67 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=609549
+-->
+<head>
+ <title>Test for Bug 609549</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <bindings xmlns="http://www.mozilla.org/xbl">
+ <binding id="testBinding">
+ <!-- No linebreaks since this is html and whitespace is preserved. -->
+ <content><div anonid="box-A">x</div><div anonid="box-B"><children includes="span"/></div><div anonid="box-C">x</div><children/></content>
+ </binding>
+ </bindings>
+</head>
+
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=609549">Mozilla Bug 609549</a>
+<div id="bound" style="-moz-binding: url(#testBinding);"><p id="p">lorem ipsum dolor sit amet</p><span id="sandwiched">sandwiched</span></div>
+
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 609549 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ var domUtils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"].
+ getService(SpecialPowers.Ci.inIDOMUtils);
+ ok("getChildrenForNode" in domUtils, "domUtils has no getChildrenForNode");
+ var withoutAnons = SpecialPowers.unwrap(domUtils.getChildrenForNode($("bound"), false));
+
+ is(withoutAnons.length, $("bound").childNodes.length,
+ "withoutAnons should be the same length as childNodes");
+ is(withoutAnons[0], $("p"), "didn't get paragraph - without anons");
+ is(withoutAnons[1], $("sandwiched"),
+ "didn't get sandwiched span - without anons");
+
+ var withAnons = domUtils.getChildrenForNode($("bound"), true);
+
+ is(withAnons.length, 4, "bad withAnons.length");
+ is(withAnons[0].getAttribute("anonid"), "box-A",
+ "didn't get anonymous box-A");
+ is(withAnons[1].getAttribute("anonid"), "box-B",
+ "didn't get anonymous box-B");
+ is(withAnons[2].getAttribute("anonid"), "box-C",
+ "didn't get anonymous box-C");
+ is(withAnons[3].id, "p", "didn't get paragraph - with anons");
+
+ var bKids = domUtils.getChildrenForNode(withAnons[1], true);
+ is(bKids.length, 1, "bKids.length is bad");
+ is(SpecialPowers.unwrap(bKids[0]), $("sandwiched"),
+ "didn't get sandwiched span inserted into box-B");
+
+ SimpleTest.finish();
+});
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug806192.html b/layout/inspector/tests/test_bug806192.html
new file mode 100644
index 000000000..2a19ab633
--- /dev/null
+++ b/layout/inspector/tests/test_bug806192.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=806192
+-->
+<head>
+ <title>Test for Bug 806192</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+
+const utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+.getService(SpecialPowers.Ci.inIDOMUtils);
+
+try {
+ var res = utils.getBindingURLs({});
+ ok(false, "didn't get error");
+}
+catch(e) { ok(true, "got expected exception"); }
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug856317.html b/layout/inspector/tests/test_bug856317.html
new file mode 100644
index 000000000..b07b6ec1d
--- /dev/null
+++ b/layout/inspector/tests/test_bug856317.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=856317
+-->
+<head>
+ <title>Test for Bug 856317</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <link rel="stylesheet" type="text/css" href="bug856317.css"/>
+ <style type="text/css" title="bug856317-inline">
+/* simplest possible */
+.alpha {
+}
+
+/* with leading whitespace */
+ #beta {
+ }
+
+/* with a comment before the rule */ #gamma {
+}
+
+/* mixed spaces and tab characters */
+ #delta {
+}
+
+/* long lines, like those produced by CSS minifiers
+ * (overflows a 16-bit unsigned int)
+ */
+ .epsilon{ background-image: url("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.png")}.zeta,.eta{}
+ </style>
+</head>
+<body>
+<script type="application/javascript">
+
+var expectedResults = {
+ ".alpha": { line: 6, column: 1 },
+ "#beta": { line: 10, column: 5 },
+ "#gamma": { line: 13, column: 38 },
+ "#delta": { line: 17, column: 5 },
+ ".epsilon": { line: 23, column: 2 },
+ ".zeta, .eta": { line: 23, column: 65578 }
+};
+
+var domUtils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+// tests are run on both the external stylesheet and the embedded stylesheet
+function executeTests(lineOffset, stylesheetPredicate) {
+ var rules;
+ for (var i=0; i < document.styleSheets.length; ++i) {
+ var styleSheet = document.styleSheets.item(i);
+ if (stylesheetPredicate(styleSheet)) {
+ rules = styleSheet.cssRules;
+ break;
+ }
+ }
+ if (!rules) {
+ ok(false, "stylesheet document not found");
+ return;
+ }
+
+ for (var i=0; i < rules.length; ++i) {
+ var rule = rules.item(i);
+ expected = expectedResults[rule.selectorText];
+ is(domUtils.getRuleLine(rule), expected.line + lineOffset,
+ "line number for "+rule.selectorText);
+ is(domUtils.getRuleColumn(rule), expected.column,
+ "column number for "+rule.selectorText);
+ }
+}
+
+// run tests on external stylesheet
+executeTests(0, function (styleSheet) {
+ return styleSheet.href.indexOf("bug856317.css") > 0;
+});
+
+// run tests on embedded stylesheet
+executeTests(7, function (styleSheet) {
+ return styleSheet.title == "bug856317-inline";
+});
+</script>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_bug877690.html b/layout/inspector/tests/test_bug877690.html
new file mode 100644
index 000000000..0480656d4
--- /dev/null
+++ b/layout/inspector/tests/test_bug877690.html
@@ -0,0 +1,260 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=877690
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 877690</title>
+<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script type="application/javascript">
+
+/** Test for Bug 877690 **/
+
+// Returns true if array contains item. False otherwise. Raises an exception if
+// array is not an Array object. If the item is found in array, remove item.
+function contains(array, item) {
+ if (!array.indexOf) {
+ throw new "First argument is not an array";
+ }
+ var index = array.indexOf(item);
+ if (index == -1) {
+ return false;
+ }
+ array.splice(index, 1);
+ return true;
+}
+
+// Returns true if values contains all and only the expected values. False otherwise.
+function testValues(values, expected) {
+ expected.forEach(function (expectedValue) {
+ if (!contains(values, expectedValue)) {
+ return false;
+ }
+ });
+ return values.length === 0;
+}
+
+function do_test() {
+ var utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ var getCSSValuesForProperty = function(prop) {
+ return Array.from(utils.getCSSValuesForProperty(prop));
+ }
+
+ // test a property with keywords and colors
+ var prop = "color";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "initial", "inherit", "unset", "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
+ "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood",
+ "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", "currentColor",
+ "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey",
+ "darkkhaki", "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred",
+ "darksalmon", "darkseagreen", "darkslateblue", "darkslategray", "darkslategrey",
+ "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dimgrey", "dodgerblue",
+ "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold",
+ "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", "hotpink", "indianred",
+ "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
+ "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey",
+ "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey",
+ "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
+ "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen",
+ "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite",
+ "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen",
+ "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
+ "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen",
+ "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen",
+ "steelblue", "tan", "teal", "thistle", "tomato", "transparent", "turquoise", "violet", "wheat",
+ "white", "whitesmoke", "yellow", "yellowgreen", "rgb", "hsl", "rgba", "hsla" ];
+ ok(testValues(values, expected), "property color's values.");
+
+ // test a shorthand property
+ var prop = "background";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "initial", "inherit", "unset", "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure",
+ "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", "burlywood", "cadetblue",
+ "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", "currentColor", "cyan", "darkblue",
+ "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta",
+ "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue",
+ "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray",
+ "dimgrey", "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
+ "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", "hotpink", "indianred",
+ "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
+ "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink",
+ "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue",
+ "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine", "mediumblue",
+ "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen",
+ "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin",
+ "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod",
+ "palegreen", "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum",
+ "powderblue", "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown",
+ "seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "slategrey",
+ "snow", "springgreen", "steelblue", "tan", "teal", "thistle", "tomato", "transparent", "turquoise",
+ "violet", "wheat", "white", "whitesmoke", "yellow", "yellowgreen", "no-repeat", "repeat",
+ "repeat-x", "repeat-y", "space", "round", "fixed", "scroll", "local", "center", "top", "bottom", "left", "right",
+ "border-box", "padding-box", "content-box", "border-box", "padding-box", "content-box", "text", "contain",
+ "cover", "rgb", "hsl", "rgba", "hsla", "none", "-moz-element", "-moz-image-rect", "url", "linear-gradient",
+ "radial-gradient", "repeating-linear-gradient", "repeating-radial-gradient", "-moz-linear-gradient",
+ "-moz-radial-gradient", "-moz-repeating-linear-gradient", "-moz-repeating-radial-gradient" ];
+ ok(testValues(values, expected), "Shorthand property values.");
+
+ var prop = "border";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "-moz-calc", "initial", "unset", "aliceblue",
+ "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet",
+ "brown", "burlywood", "cadetblue", "calc", "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk",
+ "crimson", "currentColor", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey", "darkkhaki",
+ "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
+ "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "dashed", "deeppink",
+ "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "dotted", "double", "fill", "firebrick", "floralwhite",
+ "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "grey", "green", "greenyellow",
+ "groove", "hidden", "honeydew", "hotpink", "hsl", "hsla", "indianred", "indigo", "inherit", "inset", "ivory",
+ "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan",
+ "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink", "lightsalmon", "lightseagreen",
+ "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue", "lightyellow", "lime", "limegreen",
+ "linen", "logical", "magenta", "maroon", "medium", "mediumaquamarine", "mediumblue", "mediumorchid",
+ "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "none",
+ "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "outset", "palegoldenrod", "palegreen",
+ "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "physical", "pink", "plum", "powderblue",
+ "purple", "rebeccapurple", "red", "repeat", "rgb", "rgba", "ridge", "rosybrown", "round", "royalblue", "saddlebrown",
+ "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "slategrey",
+ "snow", "solid", "space", "springgreen", "steelblue", "stretch", "tan", "teal", "thick", "thin", "thistle", "tomato",
+ "transparent", "turquoise", "-moz-element", "-moz-image-rect", "url", "violet", "wheat", "white", "whitesmoke",
+ "yellow", "yellowgreen", "linear-gradient", "radial-gradient", "repeating-linear-gradient",
+ "repeating-radial-gradient", "-moz-linear-gradient", "-moz-radial-gradient", "-moz-repeating-linear-gradient",
+ "-moz-repeating-radial-gradient" ]
+ ok(testValues(values, expected), "Shorthand property values.");
+
+ // test keywords only
+ var prop = "border-top";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "initial", "inherit", "unset", "thin", "medium", "thick", "none", "hidden", "dotted",
+ "dashed", "solid", "double", "groove", "ridge", "inset", "outset",
+ "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black",
+ "blanchedalmond", "blue", "blueviolet", "brown", "burlywood", "cadetblue", "chartreuse",
+ "chocolate", "coral", "cornflowerblue", "cornsilk", "crimson", "currentColor", "cyan", "darkblue", "darkcyan",
+ "darkgoldenrod", "darkgray", "darkgreen", "darkgrey", "darkkhaki", "darkmagenta", "darkolivegreen",
+ "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", "darkslateblue", "darkslategray",
+ "darkslategrey", "darkturquoise", "darkviolet", "deeppink", "deepskyblue", "dimgray", "dimgrey",
+ "dodgerblue", "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite",
+ "gold", "goldenrod", "gray", "grey", "green", "greenyellow", "honeydew", "hotpink", "indianred",
+ "indigo", "ivory", "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
+ "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey",
+ "lightpink", "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", "lightslategrey",
+ "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
+ "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen",
+ "mediumturquoise", "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite",
+ "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "palegoldenrod", "palegreen",
+ "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue",
+ "purple", "rebeccapurple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", "sandybrown", "seagreen",
+ "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "slategrey", "snow", "springgreen",
+ "steelblue", "tan", "teal", "thistle", "tomato", "transparent", "turquoise", "violet", "wheat", "white",
+ "whitesmoke", "yellow", "yellowgreen", "calc", "-moz-calc", "rgb", "hsl", "rgba", "hsla" ];
+ ok(testValues(values, expected), "property border-top's values.");
+
+ // tests no keywords or colors
+ var prop = "padding-bottom";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "initial", "inherit", "unset", "calc", "-moz-calc" ];
+ ok(testValues(values, expected), "property padding-bottom's values.");
+
+ // test proprety
+ var prop = "display";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "initial", "inherit", "unset", "none", "inline", "block", "inline-block", "list-item",
+ "table", "inline-table", "table-row-group", "table-header-group", "table-footer-group", "table-row",
+ "table-column-group", "table-column", "table-cell", "table-caption", "-moz-box", "-moz-inline-box",
+ "-moz-grid", "-moz-inline-grid", "-moz-grid-group", "-moz-grid-line", "-moz-stack", "-moz-inline-stack",
+ "-moz-deck", "-moz-popup", "-moz-groupbox",
+ "flex", "inline-flex", "-webkit-box", "-webkit-inline-box",
+ "-webkit-flex", "-webkit-inline-flex",
+ "grid", "inline-grid",
+ "ruby", "ruby-base", "ruby-base-container", "ruby-text", "ruby-text-container",
+ "contents" ];
+ ok(testValues(values, expected), "property display's values.");
+
+ // test property
+ var prop = "float";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "initial", "inherit", "unset", "none", "left", "right", "inline-start", "inline-end" ];
+ ok(testValues(values, expected), "property float's values.");
+
+ // Test property with "auto"
+ var prop = "margin";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "-moz-calc", "initial", "unset", "auto", "calc", "inherit" ];
+ ok(testValues(values, expected), "property margin's values.");
+
+ // Test property with "normal"
+ var prop = "font-style";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "initial", "inherit", "unset", "italic", "normal", "oblique" ];
+ ok(testValues(values, expected), "property font-style's values.");
+
+ // Test property with "cubic-bezier" and "step".
+ var prop = "-moz-transition";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "initial", "all", "unset", "cubic-bezier", "ease", "ease-in", "ease-in-out",
+ "ease-out", "inherit", "linear", "none", "step-end", "step-start",
+ "steps" ];
+ ok(testValues(values, expected), "property -moz-transition's values.");
+
+ // test invalid property
+ var prop = "invalidProperty";
+ try {
+ getCSSValuesForProperty(prop);
+ ok(false, "invalid property should throw an exception");
+ }
+ catch(e) {
+ // test passed
+ }
+
+ // test border-image propery, for bug 973345
+ var prop = "border-image";
+ var values = getCSSValuesForProperty(prop);
+ var expected = [ "-moz-calc", "initial", "unset", "aliceblue",
+ "antiquewhite", "aqua", "aquamarine", "azure", "beige", "bisque", "black", "blanchedalmond", "blue", "blueviolet",
+ "brown", "burlywood", "cadetblue", "calc", "chartreuse", "chocolate", "coral", "cornflowerblue", "cornsilk",
+ "crimson", "currentColor", "cyan", "darkblue", "darkcyan", "darkgoldenrod", "darkgray", "darkgreen", "darkgrey", "darkkhaki",
+ "darkmagenta", "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen",
+ "darkslateblue", "darkslategray", "darkslategrey", "darkturquoise", "darkviolet", "dashed", "deeppink",
+ "deepskyblue", "dimgray", "dimgrey", "dodgerblue", "dotted", "double", "fill", "firebrick", "floralwhite",
+ "forestgreen", "fuchsia", "gainsboro", "ghostwhite", "gold", "goldenrod", "gray", "grey", "green", "greenyellow",
+ "groove", "hidden", "honeydew", "hotpink", "hsl", "hsla", "indianred", "indigo", "inherit", "inset", "ivory",
+ "khaki", "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", "lightcyan",
+ "lightgoldenrodyellow", "lightgray", "lightgreen", "lightgrey", "lightpink", "lightsalmon", "lightseagreen",
+ "lightskyblue", "lightslategray", "lightslategrey", "lightsteelblue", "lightyellow", "lime", "limegreen",
+ "linen", "logical", "magenta", "maroon", "medium", "mediumaquamarine", "mediumblue", "mediumorchid",
+ "mediumpurple", "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise",
+ "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", "navajowhite", "navy", "none",
+ "oldlace", "olive", "olivedrab", "orange", "orangered", "orchid", "outset", "palegoldenrod", "palegreen",
+ "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru", "physical", "pink", "plum", "powderblue",
+ "purple", "rebeccapurple", "red", "repeat", "rgb", "rgba", "ridge", "rosybrown", "round", "royalblue", "saddlebrown",
+ "salmon", "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", "slateblue", "slategray", "slategrey",
+ "snow", "solid", "springgreen", "steelblue", "stretch", "tan", "teal", "thick", "thin", "thistle", "tomato",
+ "transparent", "turquoise", "-moz-element", "-moz-image-rect", "url", "violet", "wheat", "white", "whitesmoke",
+ "yellow", "yellowgreen", "linear-gradient", "radial-gradient", "repeating-linear-gradient",
+ "repeating-radial-gradient", "-moz-linear-gradient", "-moz-radial-gradient", "-moz-repeating-linear-gradient",
+ "-moz-repeating-radial-gradient" ]
+ ok(testValues(values, expected), "property border-image's values.");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(do_test);
+
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=877690">Mozilla Bug 877690</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_color_to_rgba.html b/layout/inspector/tests/test_color_to_rgba.html
new file mode 100644
index 000000000..d8279e384
--- /dev/null
+++ b/layout/inspector/tests/test_color_to_rgba.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test inDOMUtils::ColorToRGBA</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript;version=1.8">
+ let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ testColor("red", {r:255, g:0, b:0, a:1});
+ testColor("#f00", {r:255, g:0, b:0, a:1});
+ testColor("#ff0000", {r:255, g:0, b:0, a:1});
+ testColor("ff0000", null);
+ testColor("rgb(255,0,0)", {r:255, g:0, b:0, a:1});
+ testColor("rgba(255,0,0)", {r:255, g:0, b:0, a:1});
+ testColor("rgb(255,0,0,0.7)", {r:255, g:0, b:0, a:0.7});
+ testColor("rgba(255,0,0,0.7)", {r:255, g:0, b:0, a:0.7});
+ testColor("rgb(50%,75%,60%)", {r:128, g:191, b:153, a:1});
+ testColor("rgba(50%,75%,60%)", {r:128, g:191, b:153, a:1});
+ testColor("rgb(100%,50%,25%,0.7)", {r:255, g:128, b:64, a:0.7});
+ testColor("rgba(100%,50%,25%,0.7)", {r:255, g:128, b:64, a:0.7});
+ testColor("hsl(320,30%,10%)", {r:33, g:17, b:28, a:1});
+ testColor("hsla(320,30%,10%)", {r:33, g:17, b:28, a:1});
+ testColor("hsl(170,60%,40%,0.9)", {r:40, g:163, b:142, a:0.9});
+ testColor("hsla(170,60%,40%,0.9)", {r:40, g:163, b:142, a:0.9});
+
+ function testColor(color, expected) {
+ let rgb = utils.colorToRGBA(color);
+
+ if (rgb === null) {
+ ok(expected === null, "color: " + color + " returns null");
+ return;
+ }
+
+ let {r, g, b, a} = rgb;
+
+ is(r, expected.r, "color: " + color + ", red component is converted correctly");
+ is(g, expected.g, "color: " + color + ", green component is converted correctly");
+ is(b, expected.b, "color: " + color + ", blue component is converted correctly");
+ is(Math.round(a * 10) / 10, expected.a, "color: " + color + ", alpha component is a converted correctly");
+ }
+ </script>
+</head>
+<body>
+<h1>Test inDOMUtils::ColorToRGBA</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_css_property_is_shorthand.html b/layout/inspector/tests/test_css_property_is_shorthand.html
new file mode 100644
index 000000000..e5fbcc1c9
--- /dev/null
+++ b/layout/inspector/tests/test_css_property_is_shorthand.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test inDOMUtils::CssPropertyIsShorthand</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript;version=1.8">
+ let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ let tests = [
+ {
+ property: "color",
+ expected: false
+ },
+ {
+ property: "background",
+ expected: true
+ },
+ {
+ property: "--anything",
+ expected: false
+ }
+ ];
+
+ for (let {property, expected} of tests) {
+ let result = utils.cssPropertyIsShorthand(property);
+ is(result, expected, "checking whether " + property + " is shorthand");
+ }
+
+ let sawException = false;
+ try {
+ let result = utils.cssPropertyIsShorthand("nosuchproperty");
+ } catch (e) {
+ sawException = true;
+ }
+ ok(sawException, "checking whether nosuchproperty throws");
+
+ </script>
+</head>
+<body>
+<h1>Test inDOMUtils::CssPropertyIsShorthand</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_css_property_is_valid.html b/layout/inspector/tests/test_css_property_is_valid.html
new file mode 100644
index 000000000..21f00bf18
--- /dev/null
+++ b/layout/inspector/tests/test_css_property_is_valid.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test inDOMUtils::CssPropertyIsValid</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript;version=1.8">
+ let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ let tests = [
+ {
+ property: "color",
+ value: "red",
+ expected: true
+ },
+ {
+ property: "display",
+ value: "none",
+ expected: true
+ },
+ {
+ property: "display",
+ value: "red",
+ expected: false
+ },
+ {
+ property: "displayx",
+ value: "none",
+ expected: false
+ },
+ {
+ property: "border",
+ value: "1px solid blue",
+ expected: true
+ },
+ {
+ property: "border",
+ value: "1 solid blue",
+ expected: false
+ },
+ {
+ property: "border",
+ value: "1px underline blue",
+ expected: false
+ },
+ {
+ property: "border",
+ value: "1px solid",
+ expected: true
+ },
+ {
+ property: "color",
+ value: "blue !important",
+ expected: true
+ },
+ {
+ property: "color",
+ value: "blue ! important",
+ expected: true
+ },
+ {
+ property: "color",
+ value: "blue !impoxxxrtant",
+ expected: false
+ },
+ {
+ property: "color",
+ value: "red; background:green;",
+ expected: false
+ },
+ {
+ property: "content",
+ value: "\"hello\"",
+ expected: true
+ },
+ {
+ property: "color",
+ value: "var(--some-kind-of-green)",
+ expected: true
+ }
+ ];
+
+ for (let {property, value, expected} of tests) {
+ let valid = utils.cssPropertyIsValid(property, value);
+
+ if (expected) {
+ ok(valid, property + ":" + value + " is valid");
+ } else {
+ ok(!valid, property + ":" + value + " is not valid");
+ }
+ }
+ </script>
+</head>
+<body>
+<h1>Test inDOMUtils::CssPropertyIsValid</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_getCSSPseudoElementNames.html b/layout/inspector/tests/test_getCSSPseudoElementNames.html
new file mode 100644
index 000000000..cdb4572d4
--- /dev/null
+++ b/layout/inspector/tests/test_getCSSPseudoElementNames.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test inDOMUtils::getCSSPseudoElementNames</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript;version=1.8">
+ let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ let expected = new Set([
+ ":after",
+ ":before",
+ ":backdrop",
+ ":first-letter",
+ ":first-line",
+ ":placeholder",
+ ":-moz-color-swatch",
+ ":-moz-focus-inner",
+ ":-moz-focus-outer",
+ ":-moz-list-bullet",
+ ":-moz-list-number",
+ ":-moz-math-anonymous",
+ ":-moz-meter-bar",
+ ":-moz-placeholder",
+ ":-moz-progress-bar",
+ ":-moz-range-progress",
+ ":-moz-range-thumb",
+ ":-moz-range-track",
+ ":-moz-selection",
+ ]);
+
+ let names = utils.getCSSPseudoElementNames();
+ for (let name of names) {
+ ok(expected.has(name), name + " is included");
+ expected.delete(name);
+ }
+
+ if (expected.size > 0) {
+ todo_is(expected.size, 0,
+ "ideally all pseudo-element names would be listed in this test");
+ for (let extra of expected) {
+ info("extra element: " + extra);
+ }
+ }
+
+ </script>
+</head>
+<body>
+<h1>Test inDOMUtils::getCSSPseudoElementNames</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_getRelativeRuleLine.html b/layout/inspector/tests/test_getRelativeRuleLine.html
new file mode 100644
index 000000000..a1481a49d
--- /dev/null
+++ b/layout/inspector/tests/test_getRelativeRuleLine.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test inDOMUtils::getRelativeRuleLine</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ @supports (not (whatever: 72 zq)) {
+ #test {
+ background-color: #f0c;
+ }
+ }
+
+ #test {
+ color: #f0c;
+ }
+ </style>
+ <style>#test { color: red; }</style>
+ <style>
+ @invalidatkeyword {
+ }
+
+ #test {
+ color: blue;
+ }
+ </style>
+ <script type="application/javascript;version=1.8">
+ let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ let tests = [
+ { sheetNo: 0, ruleNo: 0, lineNo: 1, columnNo: 1 },
+ { sheetNo: 1, ruleNo: 0, lineNo: 2, columnNo: 15 },
+ { sheetNo: 1, ruleNo: 1, lineNo: 8, columnNo: 5 },
+ { sheetNo: 2, ruleNo: 0, lineNo: 1, columnNo: 1 },
+ { sheetNo: 2, ruleNo: 1, lineNo: 0, columnNo: 1 },
+ { sheetNo: 3, ruleNo: 0, lineNo: 5, columnNo: 6 },
+ ];
+
+ function doTest() {
+ document.styleSheets[2].insertRule("body{}", 1);
+ for (let test of tests) {
+ let sheet = document.styleSheets[test.sheetNo];
+ let rule = sheet.cssRules[test.ruleNo];
+ let line = utils.getRelativeRuleLine(rule);
+ let column = utils.getRuleColumn(rule);
+ info("testing sheet " + test.sheetNo + ", rule " + test.ruleNo);
+ is(line, test.lineNo, "line number is correct");
+ is(column, test.columnNo, "column number is correct");
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+ </script>
+</head>
+<body>
+<h1>Test inDOMUtils::getRelativeRuleLine</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_get_all_style_sheets.html b/layout/inspector/tests/test_get_all_style_sheets.html
new file mode 100644
index 000000000..7e5a4d49b
--- /dev/null
+++ b/layout/inspector/tests/test_get_all_style_sheets.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=734861
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 734861</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 734861 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ var utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+ var res = utils.getAllStyleSheets(document);
+
+ var foundUA = false;
+ for (var i = 0; i < res.length; i++) {
+ if (res[i].href === "resource://gre-resources/ua.css") {
+ foundUA = true;
+ break;
+ }
+ }
+ ok(foundUA, "UA sheet should be returned with all the other sheets.");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="runTest();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=734861">Mozilla Bug 734861</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_is_valid_css_color.html b/layout/inspector/tests/test_is_valid_css_color.html
new file mode 100644
index 000000000..ef5eef32e
--- /dev/null
+++ b/layout/inspector/tests/test_is_valid_css_color.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test inDOMUtils::isValidCSSColor</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript;version=1.8">
+ let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ // Color names
+ let colors = utils.getCSSValuesForProperty("color");
+ let notColor = ["hsl", "hsla", "inherit", "initial", "rgb", "rgba",
+ "unset", "transparent", "currentColor"];
+ for (let color of colors) {
+ if (notColor.indexOf(color) !== -1) {
+ continue;
+ }
+ ok(utils.isValidCSSColor(color), color + " is a valid color");
+ ok(!utils.isValidCSSColor("xxx" + color), "xxx" + color + " is not a valid color");
+ }
+
+ // rgb(a)
+ for (let i = 0; i <= 265; i++) {
+ ok(utils.isValidCSSColor("rgb(" + i + ",0,0)"), "rgb(" + i + ",0,0) is a valid color");
+ ok(utils.isValidCSSColor("rgb(0," + i + ",0)"), "rgb(0," + i + ",0) is a valid color");
+ ok(utils.isValidCSSColor("rgb(0,0," + i + ")"), "rgb(0,0," + i + ") is a valid color");
+ ok(utils.isValidCSSColor("rgba(" + i + ",0,0,0.2)"), "rgba(" + i + ",0,0,0.2) is a valid color");
+ ok(utils.isValidCSSColor("rgba(0," + i + ",0,0.5)"), "rgba(0," + i + ",0,0.5) is a valid color");
+ ok(utils.isValidCSSColor("rgba(0,0," + i + ",0.7)"), "rgba(0,0," + i + ",0.7) is a valid color");
+
+ ok(!utils.isValidCSSColor("rgbxxx(" + i + ",0,0)"), "rgbxxx(" + i + ",0,0) is not a valid color");
+ ok(!utils.isValidCSSColor("rgbxxx(0," + i + ",0)"), "rgbxxx(0," + i + ",0) is not a valid color");
+ ok(!utils.isValidCSSColor("rgbxxx(0,0," + i + ")"), "rgbxxx(0,0," + i + ") is not a valid color");
+ }
+
+ // rgb(a) (%)
+ for (let i = 0; i <= 110; i++) {
+ ok(utils.isValidCSSColor("rgb(" + i + "%,0%,0%)"), "rgb(" + i + "%,0%,0%) is a valid color");
+ ok(utils.isValidCSSColor("rgb(0%," + i + "%,0%)"), "rgb(0%," + i + "%,0%) is a valid color");
+ ok(utils.isValidCSSColor("rgb(0%,0%," + i + "%)"), "rgb(0%,0%," + i + "%) is a valid color");
+ ok(utils.isValidCSSColor("rgba(" + i + "%,0%,0%,0.2)"), "rgba(" + i + "%,0%,0%,0.2) is a valid color");
+ ok(utils.isValidCSSColor("rgba(0%," + i + "%,0%,0.5)"), "rgba(0%," + i + "%,0%,0.5) is a valid color");
+ ok(utils.isValidCSSColor("rgba(0%,0%," + i + "%,0.7)"), "rgba(0%,0%," + i + "%,0.7) is a valid color");
+
+ ok(!utils.isValidCSSColor("rgbaxxx(" + i + "%,0%,0%,0.2)"), "rgbaxxx(" + i + "%,0%,0%,0.2) is not a valid color");
+ ok(!utils.isValidCSSColor("rgbaxxx(0%," + i + "%,0%,0.5)"), "rgbaxxx(0%," + i + "%,0%,0.5) is not a valid color");
+ ok(!utils.isValidCSSColor("rgbaxxx(0%,0%," + i + "%,0.7)"), "rgbaxxx(0%,0%," + i + "%,0.7) is not a valid color");
+ }
+
+ // hsl(a)
+ for (let i = 0; i <= 370; i++) {
+ ok(utils.isValidCSSColor("hsl(" + i + ",30%,10%)"), "rgb(" + i + ",30%,10%) is a valid color");
+ ok(utils.isValidCSSColor("hsla(" + i + ",60%,70%,0.2)"), "rgba(" + i + ",60%,70%,0.2) is a valid color");
+ }
+ for (let i = 0; i <= 110; i++) {
+ ok(utils.isValidCSSColor("hsl(100," + i + "%,20%)"), "hsl(100," + i + "%,20%) is a valid color");
+ ok(utils.isValidCSSColor("hsla(100,20%," + i + "%,0.6)"), "hsla(100,20%," + i + "%,0.6) is a valid color");
+ }
+
+ // hex
+ for (let i = 0; i <= 255; i++) {
+ let hex = (i).toString(16);
+ if (hex.length === 1) {
+ hex = 0 + hex;
+ }
+ ok(utils.isValidCSSColor("#" + hex + "7777"), "#" + hex + "7777 is a valid color");
+ ok(utils.isValidCSSColor("#77" + hex + "77"), "#77" + hex + "77 is a valid color");
+ ok(utils.isValidCSSColor("#7777" + hex), "#7777" + hex + " is a valid color");
+ }
+ ok(!utils.isValidCSSColor("#kkkkkk"), "#kkkkkk is not a valid color");
+
+ // short hex
+ for (let i = 0; i <= 16; i++) {
+ let hex = (i).toString(16);
+ ok(utils.isValidCSSColor("#" + hex + hex + hex), "#" + hex + hex + hex + " is a valid color");
+ }
+ ok(!utils.isValidCSSColor("#ggg"), "#ggg is not a valid color");
+ </script>
+</head>
+<body>
+<h1>Test inDOMUtils::isValidCSSColor</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_isinheritableproperty.html b/layout/inspector/tests/test_isinheritableproperty.html
new file mode 100644
index 000000000..6bbb1650f
--- /dev/null
+++ b/layout/inspector/tests/test_isinheritableproperty.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=699592
+-->
+<head>
+ <title>Test for nsIDOMUtils::isInheritedProperty</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+
+function do_test() {
+ var utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ is(utils.isInheritedProperty("font-size"), true, "font-size is inherited.");
+
+ is(utils.isInheritedProperty("min-width"), false, "min-width is not inherited.");
+
+ is(utils.isInheritedProperty("font"), true, "shorthand font property is inherited.");
+
+ is(utils.isInheritedProperty("border"), false, "shorthand border property not inherited.");
+ is(utils.isInheritedProperty("garbage"), false, "Unknown property isn't inherited.");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(do_test);
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/inspector/tests/test_parseStyleSheet.html b/layout/inspector/tests/test_parseStyleSheet.html
new file mode 100644
index 000000000..f7ddf6e9e
--- /dev/null
+++ b/layout/inspector/tests/test_parseStyleSheet.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1195978
+-->
+ <head>
+ <title>Test for Bug 1195978</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ body {
+ color: red;
+ }
+ </style>
+ </head>
+ <body>
+ <script type="application/javascript">
+var domUtils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+var sheet = document.styleSheets[1];
+is(domUtils.getRelativeRuleLine(sheet.cssRules[0]), 2,
+ "initial relative rule line");
+is(domUtils.getRuleLine(sheet.cssRules[0]), 11,
+ "initial rule line");
+
+domUtils.parseStyleSheet(sheet, "\nbody {\n color: blue;\n}\n");
+is(domUtils.getRelativeRuleLine(sheet.cssRules[0]), 2,
+ "relative rule line after reparsing");
+is(domUtils.getRuleLine(sheet.cssRules[0]), 11,
+ "relative rule line after reparsing");
+ </script>
+ </body>
+</html>
diff --git a/layout/inspector/tests/test_parseStyleSheetImport.html b/layout/inspector/tests/test_parseStyleSheetImport.html
new file mode 100644
index 000000000..73fef2d51
--- /dev/null
+++ b/layout/inspector/tests/test_parseStyleSheetImport.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1202095
+-->
+ <head>
+ <title>Test for Bug 1202095</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ @import url('bug1202095.css');
+ @import url('bug1202095-2.css');
+ </style>
+ </head>
+ <body>
+ <script type="application/javascript">
+var domUtils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+function do_test() {
+ var sheet = document.styleSheets[1];
+ var importRule = sheet.cssRules[0];
+ is(importRule.type, SpecialPowers.Ci.nsIDOMCSSRule.IMPORT_RULE,
+ "initial sheet has @import rule");
+
+ var importedSheet = importRule.styleSheet;
+ importedSheet.deleteRule(0);
+ is(importedSheet.cssRules.length, 0, "imported sheet now has no rules");
+
+ // "suffixed" refers to the "-2".
+ var suffixedSheet = sheet.cssRules[1].styleSheet;
+ domUtils.parseStyleSheet(suffixedSheet, "");
+ is(suffixedSheet.cssRules.length, 0, "second imported sheet now has no rules");
+
+ // Re-parse the style sheet, preserving the imports.
+ domUtils.parseStyleSheet(sheet, "@import url('bug1202095.css');" +
+ "@import url('bug1202095-2.css');");
+ is(sheet.cssRules[0].type, SpecialPowers.Ci.nsIDOMCSSRule.IMPORT_RULE,
+ "re-parsed sheet has @import rule");
+ is(sheet.cssRules[0].styleSheet, importedSheet,
+ "imported sheet has not changed");
+ is(sheet.cssRules[1].styleSheet, suffixedSheet,
+ "second imported sheet has not changed");
+
+ // Re-parse the style sheet, preserving both imports, but changing
+ // the order.
+ domUtils.parseStyleSheet(sheet, "@import url('bug1202095-2.css');" +
+ "@import url('bug1202095.css');");
+ is(sheet.cssRules[0].styleSheet, suffixedSheet,
+ "reordering preserved suffixed style sheet");
+ is(sheet.cssRules[1].styleSheet, importedSheet,
+ "reordering preserved unsuffixed style sheet");
+
+ // Re-parse the style sheet, removing the imports.
+ domUtils.parseStyleSheet(sheet, "");
+ is(sheet.cssRules.length, 0, "style sheet now has no rules");
+
+ // Re-parse the style sheet, adding one import back. This should
+ // not allow reuse.
+ domUtils.parseStyleSheet(sheet, "@import url('bug1202095.css');");
+ is(sheet.cssRules[0].type, SpecialPowers.Ci.nsIDOMCSSRule.IMPORT_RULE,
+ "re-re-re-parsed sheet has @import rule");
+ isnot(sheet.cssRules[0].styleSheet, importedSheet,
+ "imported sheet has changed now");
+
+ // Re-parse the style sheet, importing the same URL twice.
+ // The style sheet should be reused once, but not two times.
+ importedSheet = sheet.cssRules[0].styleSheet;
+ domUtils.parseStyleSheet(sheet, "@import url('bug1202095.css');" +
+ "@import url('bug1202095.css');");
+ is(sheet.cssRules[0].styleSheet, importedSheet,
+ "first imported sheet is reused");
+ isnot(sheet.cssRules[1].styleSheet, importedSheet,
+ "second imported sheet is reused");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(do_test);
+ </script>
+ </body>
+</html>
diff --git a/layout/inspector/tests/test_selectormatcheselement.html b/layout/inspector/tests/test_selectormatcheselement.html
new file mode 100644
index 000000000..a052f74d1
--- /dev/null
+++ b/layout/inspector/tests/test_selectormatcheselement.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1037519
+-->
+<head>
+ <title>Test for nsIDOMUtils::selectorMatchesElement</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style type="text/css">
+ #foo,
+ #bar,
+ #foo::before {
+ color: red;
+ }
+ #foo::before,
+ #bar::before {
+ content: 'foo-before';
+ color: green;
+ }
+ #foo::after,
+ #bar::after {
+ content: 'foo-after';
+ color: blue;
+ }
+ #foo::first-line,
+ #bar::first-line {
+ text-decoration: underline;
+ }
+ #foo::first-letter,
+ #bar::first-letter {
+ font-variant: small-caps;
+ }
+ </style>
+</head>
+<body>
+<div id="foo">foo content</div>
+<pre id="test">
+<script type="application/javascript">
+
+function do_test() {
+ var utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+ .getService(SpecialPowers.Ci.inIDOMUtils);
+
+ var element = document.querySelector("#foo");
+
+ var elementRules = utils.getCSSStyleRules(element);
+ var elementRule = elementRules.GetElementAt(elementRules.Count() - 1);
+
+ is (utils.selectorMatchesElement(element, elementRule, 0), true,
+ "Matches #foo");
+ is (utils.selectorMatchesElement(element, elementRule, 1), false,
+ "Doesn't match #bar");
+ is (utils.selectorMatchesElement(element, elementRule, 0, ":bogus"), false,
+ "Doesn't match #foo with a bogus pseudo");
+ is (utils.selectorMatchesElement(element, elementRule, 2, ":bogus"), false,
+ "Doesn't match #foo::before with bogus pseudo");
+ is (utils.selectorMatchesElement(element, elementRule, 0, ":after"), false,
+ "Does match #foo::before with the :after pseudo");
+
+ checkPseudo(":before");
+ checkPseudo(":after");
+ checkPseudo(":first-letter");
+ checkPseudo(":first-line");
+
+ SimpleTest.finish();
+
+ function checkPseudo(pseudo) {
+ var rules = utils.getCSSStyleRules(element, pseudo);
+ var rule = rules.GetElementAt(rules.Count() - 1);
+
+ is (utils.selectorMatchesElement(element, rule, 0), false,
+ "Doesn't match without " + pseudo);
+ is (utils.selectorMatchesElement(element, rule, 1), false,
+ "Doesn't match without " + pseudo);
+
+ is (utils.selectorMatchesElement(element, rule, 0, pseudo), true,
+ "Matches on #foo" + pseudo);
+ is (utils.selectorMatchesElement(element, rule, 1, pseudo), false,
+ "Doesn't match on #bar" + pseudo);
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(do_test);
+
+</script>
+</pre>
+</body>
+</html>