author | David Anderson <danderson@mozilla.com> |
Mon, 19 Mar 2012 14:13:34 -0700 | |
changeset 105996 | a4916f9d8d2f217b2d452a5802f8ace5d44f396b |
parent 105995 | b5b6e6aebb36140047dd6563c8a4507d942999c4 (current diff) |
parent 89722 | c22568c8cf0e147e78989f31f678377327141057 (diff) |
child 105997 | e96d5b1f47b8bd29a8d7f7f1149482b8b8660a91 |
push id | 23447 |
push user | danderson@mozilla.com |
push date | Tue, 11 Sep 2012 17:34:27 +0000 |
treeherder | mozilla-central@fdfaef738a00 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 14.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/accessible/src/base/nsAccUtils.cpp +++ b/accessible/src/base/nsAccUtils.cpp @@ -317,30 +317,16 @@ nsAccUtils::GetSelectableContainer(nsAcc nsAccessible* parent = aAccessible; while ((parent = parent->Parent()) && !parent->IsSelect()) { if (Role(parent) == nsIAccessibleRole::ROLE_PANE) return nsnull; } return parent; } -nsAccessible* -nsAccUtils::GetMultiSelectableContainer(nsINode* aNode) -{ - nsAccessible* accessible = GetAccService()->GetAccessible(aNode, nsnull); - if (accessible) { - nsAccessible* container = GetSelectableContainer(accessible, - accessible->State()); - if (container && container->State() & states::MULTISELECTABLE) - return container; - } - - return nsnull; -} - bool nsAccUtils::IsARIASelected(nsAccessible *aAccessible) { return aAccessible->GetContent()-> AttrValueIs(kNameSpaceID_None, nsGkAtoms::aria_selected, nsGkAtoms::_true, eCaseMatters); }
--- a/accessible/src/base/nsAccUtils.h +++ b/accessible/src/base/nsAccUtils.h @@ -192,21 +192,16 @@ public: * * @param aAccessible [in] the item accessible * @param aState [in] the state of the item accessible */ static nsAccessible* GetSelectableContainer(nsAccessible* aAccessible, PRUint64 aState); /** - * Return multi selectable container for the given item. - */ - static nsAccessible *GetMultiSelectableContainer(nsINode *aNode); - - /** * Return true if the DOM node of given accessible has aria-selected="true" * attribute. */ static bool IsARIASelected(nsAccessible *aAccessible); /** * Return text accessible containing focus point of the given selection. * Used for normal and misspelling selection changes processing.
--- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -293,29 +293,30 @@ nsAccessibilityService::CreateHTMLFileIn NS_ADDREF(accessible); return accessible; } already_AddRefed<nsAccessible> nsAccessibilityService::CreateHTMLImageAccessible(nsIContent* aContent, nsIPresShell* aPresShell) { - nsAutoString mapElmName; - aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usemap, mapElmName); - nsCOMPtr<nsIDOMHTMLMapElement> mapElm; - if (nsIDocument* document = aContent->GetCurrentDoc()) { - mapElm = do_QueryInterface(document->FindImageMap(mapElmName)); - } + nsAccessible* accessible = + new nsHTMLImageAccessibleWrap(aContent, + nsAccUtils::GetDocAccessibleFor(aPresShell)); + NS_ADDREF(accessible); + return accessible; +} - nsAccessible* accessible = mapElm ? - new nsHTMLImageMapAccessible(aContent, - nsAccUtils::GetDocAccessibleFor(aPresShell), - mapElm) : - new nsHTMLImageAccessibleWrap(aContent, - nsAccUtils::GetDocAccessibleFor(aPresShell)); +already_AddRefed<nsAccessible> +nsAccessibilityService::CreateHTMLImageMapAccessible(nsIContent* aContent, + nsIPresShell* aPresShell) +{ + nsAccessible* accessible = + new nsHTMLImageMapAccessible(aContent, + nsAccUtils::GetDocAccessibleFor(aPresShell)); NS_ADDREF(accessible); return accessible; } already_AddRefed<nsAccessible> nsAccessibilityService::CreateHTMLGroupboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell) { @@ -607,16 +608,38 @@ nsAccessibilityService::UpdateListBullet nsHTMLLIAccessible* listItem = accessible->AsHTMLListItem(); if (listItem) listItem->UpdateBullet(aHasBullet); } } } void +nsAccessibilityService::UpdateImageMap(nsImageFrame* aImageFrame) +{ + nsIPresShell* presShell = aImageFrame->PresContext()->PresShell(); + nsDocAccessible* document = GetDocAccessible(presShell->GetDocument()); + if (document) { + nsAccessible* accessible = + document->GetAccessible(aImageFrame->GetContent()); + if (accessible) { + nsHTMLImageMapAccessible* imageMap = accessible->AsImageMap(); + if (imageMap) { + imageMap->UpdateChildAreas(); + return; + } + + // If image map was initialized after we created an accessible (that'll + // be an image accessible) then recreate it. + RecreateAccessible(presShell, aImageFrame->GetContent()); + } + } +} + +void nsAccessibilityService::PresShellDestroyed(nsIPresShell *aPresShell) { // Presshell destruction will automatically destroy shells for descendant // documents, so no need to worry about those. Just shut down the accessible // for this one document. That keeps us from having bad behavior in case of // deep bushy subtrees. // When document subtree containing iframe is hidden then we don't get // pagehide event for the iframe's underlying document and its presshell is
--- a/accessible/src/base/nsAccessibilityService.h +++ b/accessible/src/base/nsAccessibilityService.h @@ -43,16 +43,18 @@ #include "a11yGeneric.h" #include "nsAccDocManager.h" #include "mozilla/a11y/FocusManager.h" #include "nsIObserver.h" +class nsImageFrame; + namespace mozilla { namespace a11y { /** * Return focus manager. */ FocusManager* FocusMgr(); @@ -104,16 +106,18 @@ public: already_AddRefed<nsAccessible> CreateHTMLFileInputAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLGroupboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLHRAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLImageAccessible(nsIContent* aContent, nsIPresShell* aPresShell); + already_AddRefed<nsAccessible> + CreateHTMLImageMapAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLLabelAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLLIAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLListboxAccessible(nsIContent* aContent, nsIPresShell* aPresShell); virtual already_AddRefed<nsAccessible> CreateHTMLMediaAccessible(nsIContent* aContent, nsIPresShell* aPresShell); @@ -150,16 +154,21 @@ public: /** * Update list bullet accessible. */ virtual void UpdateListBullet(nsIPresShell* aPresShell, nsIContent* aHTMLListItemContent, bool aHasBullet); + /** + * Update the image map. + */ + void UpdateImageMap(nsImageFrame* aImageFrame); + virtual void NotifyOfAnchorJumpTo(nsIContent *aTarget); virtual void PresShellDestroyed(nsIPresShell* aPresShell); /** * Notify that presshell is activated. */ virtual void PresShellActivated(nsIPresShell* aPresShell);
--- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -1037,57 +1037,52 @@ nsAccessible::GetBounds(PRInt32* aX, PRI // helpers nsIFrame* nsAccessible::GetBoundsFrame() { return GetFrame(); } -/* void removeSelection (); */ NS_IMETHODIMP nsAccessible::SetSelected(bool aSelect) { - // Add or remove selection if (IsDefunct()) return NS_ERROR_FAILURE; - if (State() & states::SELECTABLE) { - nsAccessible* multiSelect = - nsAccUtils::GetMultiSelectableContainer(mContent); - if (!multiSelect) { - return aSelect ? TakeFocus() : NS_ERROR_FAILURE; + nsAccessible* select = nsAccUtils::GetSelectableContainer(this, State()); + if (select) { + if (select->State() & states::MULTISELECTABLE) { + if (mRoleMapEntry) { + if (aSelect) { + return mContent->SetAttr(kNameSpaceID_None, + nsGkAtoms::aria_selected, + NS_LITERAL_STRING("true"), true); + } + return mContent->UnsetAttr(kNameSpaceID_None, + nsGkAtoms::aria_selected, true); + } + + return NS_OK; } - if (mRoleMapEntry) { - if (aSelect) { - return mContent->SetAttr(kNameSpaceID_None, - nsGkAtoms::aria_selected, - NS_LITERAL_STRING("true"), true); - } - return mContent->UnsetAttr(kNameSpaceID_None, - nsGkAtoms::aria_selected, true); - } + return aSelect ? TakeFocus() : NS_ERROR_FAILURE; } return NS_OK; } -/* void takeSelection (); */ NS_IMETHODIMP nsAccessible::TakeSelection() { - // Select only this item if (IsDefunct()) return NS_ERROR_FAILURE; - if (State() & states::SELECTABLE) { - nsAccessible* multiSelect = - nsAccUtils::GetMultiSelectableContainer(mContent); - if (multiSelect) - multiSelect->ClearSelection(); - + nsAccessible* select = nsAccUtils::GetSelectableContainer(this, State()); + if (select) { + if (select->State() & states::MULTISELECTABLE) + select->ClearSelection(); return SetSelected(true); } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsAccessible::TakeFocus()
--- a/accessible/src/base/nsAccessible.h +++ b/accessible/src/base/nsAccessible.h @@ -57,16 +57,17 @@ class AccEvent; class AccGroupInfo; class EmbeddedObjCollector; class KeyBinding; class nsAccessible; class nsHyperTextAccessible; class nsHTMLImageAccessible; +class nsHTMLImageMapAccessible; class nsHTMLLIAccessible; struct nsRoleMapEntry; class Relation; class nsTextAccessible; struct nsRect; class nsIContent; class nsIFrame; @@ -449,20 +450,23 @@ public: inline bool IsHyperText() const { return mFlags & eHyperTextAccessible; } nsHyperTextAccessible* AsHyperText(); inline bool IsHTMLFileInput() const { return mFlags & eHTMLFileInputAccessible; } inline bool IsHTMLListItem() const { return mFlags & eHTMLListItemAccessible; } nsHTMLLIAccessible* AsHTMLListItem(); - + inline bool IsImageAccessible() const { return mFlags & eImageAccessible; } nsHTMLImageAccessible* AsImage(); + bool IsImageMapAccessible() const { return mFlags & eImageMapAccessible; } + nsHTMLImageMapAccessible* AsImageMap(); + inline bool IsListControl() const { return mFlags & eListControlAccessible; } inline bool IsMenuButton() const { return mFlags & eMenuButtonAccessible; } inline bool IsMenuPopup() const { return mFlags & eMenuPopupAccessible; } inline bool IsRoot() const { return mFlags & eRootAccessible; } nsRootAccessible* AsRoot(); @@ -684,21 +688,22 @@ protected: eAutoCompleteAccessible = 1 << 3, eAutoCompletePopupAccessible = 1 << 4, eComboboxAccessible = 1 << 5, eDocAccessible = 1 << 6, eHyperTextAccessible = 1 << 7, eHTMLFileInputAccessible = 1 << 8, eHTMLListItemAccessible = 1 << 9, eImageAccessible = 1 << 10, - eListControlAccessible = 1 << 11, - eMenuButtonAccessible = 1 << 12, - eMenuPopupAccessible = 1 << 13, - eRootAccessible = 1 << 14, - eTextLeafAccessible = 1 << 15 + eImageMapAccessible = 1 << 11, + eListControlAccessible = 1 << 12, + eMenuButtonAccessible = 1 << 13, + eMenuPopupAccessible = 1 << 14, + eRootAccessible = 1 << 15, + eTextLeafAccessible = 1 << 16 }; ////////////////////////////////////////////////////////////////////////////// // Miscellaneous helpers /** * Return ARIA role (helper method). */
--- a/accessible/src/html/nsHTMLImageMapAccessible.cpp +++ b/accessible/src/html/nsHTMLImageMapAccessible.cpp @@ -47,25 +47,26 @@ #include "nsIServiceManager.h" #include "nsIDOMElement.h" #include "nsIDOMHTMLAreaElement.h" #include "nsIFrame.h" #include "nsImageFrame.h" #include "nsImageMap.h" using namespace mozilla::a11y; + //////////////////////////////////////////////////////////////////////////////// // nsHTMLImageMapAccessible //////////////////////////////////////////////////////////////////////////////// nsHTMLImageMapAccessible:: - nsHTMLImageMapAccessible(nsIContent* aContent, nsDocAccessible* aDoc, - nsIDOMHTMLMapElement* aMapElm) : - nsHTMLImageAccessibleWrap(aContent, aDoc), mMapElement(aMapElm) + nsHTMLImageMapAccessible(nsIContent* aContent, nsDocAccessible* aDoc) : + nsHTMLImageAccessibleWrap(aContent, aDoc) { + mFlags |= eImageMapAccessible; } //////////////////////////////////////////////////////////////////////////////// // nsHTMLImageMapAccessible: nsISupports NS_IMPL_ISUPPORTS_INHERITED0(nsHTMLImageMapAccessible, nsHTMLImageAccessible) //////////////////////////////////////////////////////////////////////////////// @@ -99,49 +100,85 @@ nsHTMLImageMapAccessible::AnchorURIAt(PR if (!area) return nsnull; nsIContent* linkContent = area->GetContent(); return linkContent ? linkContent->GetHrefURI() : nsnull; } //////////////////////////////////////////////////////////////////////////////// -// nsHTMLImageMapAccessible: nsAccessible protected +// nsHTMLImageMapAccessible: public -void -nsHTMLImageMapAccessible::CacheChildren() +void +nsHTMLImageMapAccessible::UpdateChildAreas(bool aDoFireEvents) { - if (!mMapElement) - return; + nsImageFrame* imageFrame = do_QueryFrame(mContent->GetPrimaryFrame()); - nsCOMPtr<nsIDOMHTMLCollection> mapAreas; - mMapElement->GetAreas(getter_AddRefs(mapAreas)); - if (!mapAreas) + // If image map is not initialized yet then we trigger one time more later. + nsImageMap* imageMapObj = imageFrame->GetExistingImageMap(); + if (!imageMapObj) return; - nsDocAccessible* document = Document(); + bool doReorderEvent = false; + + // Remove areas that are not a valid part of the image map anymore. + for (PRInt32 childIdx = mChildren.Length() - 1; childIdx >= 0; childIdx--) { + nsAccessible* area = mChildren.ElementAt(childIdx); + if (area->GetContent()->GetPrimaryFrame()) + continue; - PRUint32 areaCount = 0; - mapAreas->GetLength(&areaCount); + if (aDoFireEvents) { + nsRefPtr<AccEvent> event = new AccHideEvent(area, area->GetContent()); + mDoc->FireDelayedAccessibleEvent(event); + doReorderEvent = true; + } + + RemoveChild(area); + } - for (PRUint32 areaIdx = 0; areaIdx < areaCount; areaIdx++) { - nsCOMPtr<nsIDOMNode> areaNode; - mapAreas->Item(areaIdx, getter_AddRefs(areaNode)); - if (!areaNode) - return; + // Insert new areas into the tree. + PRUint32 areaElmCount = imageMapObj->AreaCount(); + for (PRUint32 idx = 0; idx < areaElmCount; idx++) { + nsIContent* areaContent = imageMapObj->GetAreaAt(idx); + + nsAccessible* area = mChildren.SafeElementAt(idx); + if (!area || area->GetContent() != areaContent) { + nsRefPtr<nsAccessible> area = new nsHTMLAreaAccessible(areaContent, mDoc); + if (!mDoc->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent))) + break; - nsCOMPtr<nsIContent> areaContent(do_QueryInterface(areaNode)); - nsRefPtr<nsAccessible> area = - new nsHTMLAreaAccessible(areaContent, mDoc); + if (!InsertChildAt(idx, area)) { + mDoc->UnbindFromDocument(area); + break; + } - if (!document->BindToDocument(area, nsAccUtils::GetRoleMapEntry(areaContent)) || - !AppendChild(area)) { - return; + if (aDoFireEvents) { + nsRefPtr<AccEvent> event = new AccShowEvent(area, areaContent); + mDoc->FireDelayedAccessibleEvent(event); + doReorderEvent = true; + } } } + + // Fire reorder event if needed. + if (doReorderEvent) { + nsRefPtr<AccEvent> reorderEvent = + new AccEvent(nsIAccessibleEvent::EVENT_REORDER, mContent, + eAutoDetect, AccEvent::eCoalesceFromSameSubtree); + mDoc->FireDelayedAccessibleEvent(reorderEvent); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// nsHTMLImageMapAccessible: nsAccessible protected + +void +nsHTMLImageMapAccessible::CacheChildren() +{ + UpdateChildAreas(false); } //////////////////////////////////////////////////////////////////////////////// // nsHTMLAreaAccessible //////////////////////////////////////////////////////////////////////////////// nsHTMLAreaAccessible:: @@ -222,16 +259,27 @@ nsHTMLAreaAccessible::GetBounds(PRInt32 nsIntRect orgRectPixels = frame->GetScreenRectExternal(); *aX += orgRectPixels.x; *aY += orgRectPixels.y; return NS_OK; } //////////////////////////////////////////////////////////////////////////////// +// nsHTMLAreaAccessible: nsAccessNode public + +bool +nsHTMLAreaAccessible::IsPrimaryForNode() const +{ + // Make HTML area DOM element not accessible. HTML image map accessible + // manages its tree itself. + return false; +} + +//////////////////////////////////////////////////////////////////////////////// // nsHTMLAreaAccessible: nsAccessible public PRUint64 nsHTMLAreaAccessible::NativeState() { // Bypass the link states specialization for non links. if (mRoleMapEntry && mRoleMapEntry->role != roles::NOTHING &&
--- a/accessible/src/html/nsHTMLImageMapAccessible.h +++ b/accessible/src/html/nsHTMLImageMapAccessible.h @@ -46,54 +46,68 @@ #include "nsIDOMHTMLMapElement.h" /** * Used for HTML image maps. */ class nsHTMLImageMapAccessible : public nsHTMLImageAccessibleWrap { public: - nsHTMLImageMapAccessible(nsIContent* aContent, nsDocAccessible* aDoc, - nsIDOMHTMLMapElement* aMapElm); + nsHTMLImageMapAccessible(nsIContent* aContent, nsDocAccessible* aDoc); + virtual ~nsHTMLImageMapAccessible() { } // nsISupports and cycle collector NS_DECL_ISUPPORTS_INHERITED // nsAccessible virtual mozilla::a11y::role NativeRole(); // HyperLinkAccessible virtual PRUint32 AnchorCount(); virtual nsAccessible* AnchorAt(PRUint32 aAnchorIndex); virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex); + /** + * Update area children of the image map. + */ + void UpdateChildAreas(bool aDoFireEvents = true); + protected: // nsAccessible virtual void CacheChildren(); +}; -private: - // Reference on linked map element if any. - nsCOMPtr<nsIDOMHTMLMapElement> mMapElement; -}; +//////////////////////////////////////////////////////////////////////////////// +// nsAccessible downcasting method + +inline nsHTMLImageMapAccessible* +nsAccessible::AsImageMap() +{ + return IsImageMapAccessible() ? + static_cast<nsHTMLImageMapAccessible*>(this) : nsnull; +} /** * Accessible for image map areas - must be child of image. */ class nsHTMLAreaAccessible : public nsHTMLLinkAccessible { public: nsHTMLAreaAccessible(nsIContent* aContent, nsDocAccessible* aDoc); // nsIAccessible NS_IMETHOD GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height); + // nsAccessNode + virtual bool IsPrimaryForNode() const; + // nsAccessible virtual void Description(nsString& aDescription); virtual nsresult GetNameInternal(nsAString& aName); virtual PRUint64 NativeState(); virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY, EWhichChildAtPoint aWhichChild); // HyperLinkAccessible
--- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -481,16 +481,31 @@ function testDefunctAccessible(aAcc, aNo try { aAcc.parent; } catch (e) { success = (e.result == Components.results.NS_ERROR_FAILURE); } ok(success, "parent" + msg); } +/** + * Ensure that image map accessible tree is created. + */ +function ensureImageMapTree(aID) +{ + // XXX: We send a useless mouse move to the image to force it to setup its + // image map, because flushing layout won't do it. Hopefully bug 135040 + // will make this not suck. + synthesizeMouse(getNode(aID), 10, 10, { type: "mousemove" }); + + // XXX This may affect a11y more than other code because imagemaps may not + // get drawn or have an mouse event over them. Bug 570322 tracks a11y + // dealing with this. + todo(false, "Need to remove this image map workaround."); +} /** * Convert role to human readable string. */ function roleToString(aRole) { return gAccRetrieval.getStringRole(aRole); }
--- a/accessible/tests/mochitest/hyperlink/test_general.html +++ b/accessible/tests/mochitest/hyperlink/test_general.html @@ -4,16 +4,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418368 --> <head> <title>nsIHyperLinkAccessible chrome tests</title> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> <script type="application/javascript" src="../common.js"></script> <script type="application/javascript" src="../role.js"></script> <script type="application/javascript" src="../states.js"></script> <script type="application/javascript" @@ -83,16 +85,18 @@ https://bugzilla.mozilla.org/show_bug.cg // ARIA hyperlink with status invalid var invalidAriaHyperlinkAcc = getAccessible("InvalidAriaHyperlink", [nsIAccessibleHyperLink]); is(invalidAriaHyperlinkAcc.valid, false, "Should not be valid!"); testStates(invalidAriaHyperlinkAcc, STATE_LINKED, EXT_STATE_HORIZONTAL); ////////////////////////////////////////////////////////////////////////// // image map and its link children + ensureImageMapTree("imgmap"); + var imageMapHyperlinkAcc = getAccessible("imgmap", [nsIAccessibleHyperLink]); testThis("imgmap", imageMapHyperlinkAcc, ROLE_IMAGE_MAP, 2, "b", true, 79, 80); is(imageMapHyperlinkAcc.getURI(0).spec, "http://www.bbc.co.uk/radio4/atoz/index.shtml#b", "URI wrong!"); is(imageMapHyperlinkAcc.getURI(1).spec, "http://www.bbc.co.uk/radio4/atoz/index.shtml#a", "URI wrong!"); @@ -263,17 +267,17 @@ https://bugzilla.mozilla.org/show_bug.cg ><area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a" coords="0,0,13,14" alt="a" shape="rect"></area ></map ><img width="447" id="imgmap" height="15" usemap="#atoz_map" - src="letters.gif"><br>Empty link:<br + src="../letters.gif"><br>Empty link:<br ><a id="emptyLink" href=""><img src=""></a ><br>Link with embedded span<br ><a id="LinkWithSpan" href="http://www.heise.de/"><span lang="de">Heise Online</span></a ><br>Named anchor, must not have "linked" state for it to be exposed correctly:<br ><a id="namedAnchor" name="named_anchor">This should never be of state_linked</a ><br>Link having no attributes, must not have "linked" state:<a id="noLink" >This should never be of state_linked</a ><br>Link with registered 'click' event: <a id="linkWithClick" onclick="var clicked = true;"
--- a/accessible/tests/mochitest/hypertext/test_general.html +++ b/accessible/tests/mochitest/hypertext/test_general.html @@ -4,16 +4,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=428248 --> <head> <title>nsIHyper>TextAccessible chrome tests</title> <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> <script type="application/javascript" src="../common.js"></script> <script type="application/javascript"> var gParagraphAcc; function testLinkIndexAtOffset(aID, aOffset, aIndex) @@ -48,16 +50,17 @@ https://bugzilla.mozilla.org/show_bug.cg // ARIA hyperlink testThis("AriaHyperlink", 27, 1, "Mozilla Foundation Home"); // ARIA hyperlink with status invalid testThis("InvalidAriaHyperlink", 63, 2, "Invalid link"); // image map, but not its link children. They are not part of hypertext. + ensureImageMapTree("imgmap"); testThis("imgmap", 76, 3, "b"); // empty hyperlink testThis("emptyLink", 90, 4, null); // normal hyperlink with embedded span testThis("LinkWithSpan", 116, 5, "Heise Online"); @@ -126,17 +129,17 @@ https://bugzilla.mozilla.org/show_bug.cg shape="rect"></area ><area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#a" coords="0,0,13,14" alt="a" shape="rect"></area></map ><img width="447" id="imgmap" height="15" usemap="#atoz_map" - src="letters.gif"></img><br + src="../letters.gif"></img><br >Empty link:<br ><a id="emptyLink" href=""><img src=""></img></a><br >Link with embedded span<br ><a id="LinkWithSpan" href="http://www.heise.de/"><span lang="de">Heise Online</span></a><br >Named anchor, must not have "linked" state for it to be exposed correctly:<br ><a id="namedAnchor" name="named_anchor">This should never be of state_linked</a> </p> <p id="p2"><a href="http://mozilla.org">mozilla.org</a></p>
--- a/accessible/tests/mochitest/states/test_aria_imgmap.html +++ b/accessible/tests/mochitest/states/test_aria_imgmap.html @@ -14,31 +14,32 @@ <script type="application/javascript" src="../role.js"></script> <script type="application/javascript" src="../states.js"></script> <script type="application/javascript"> function doTest() { - //XXX We send a useless mouse move to the image to force it to setup its - // image map, because flushing layout won't do it. Hopefully bug 135040 - // will make this not suck. - synthesizeMouse($("imagemap"), 10, 10, { type: "mousemove" }); - //XXX This may affect a11y more than other code because imagemaps may not - // get drawn or have an mouse event over them. Bug 570322 tracks a11y - // dealing with this. - todo(false, "Need to remove this image map workaround."); + ensureImageMapTree("imagemap"); + + var imageMap = getAccessible("imagemap"); - testStates("t1", 0, EXT_STATE_EDITABLE, STATE_LINKED); - testStates("t2", 0, EXT_STATE_EDITABLE, STATE_LINKED); - testStates("rb1", (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED); - testStates("rb2", STATE_CHECKABLE, 0, STATE_CHECKED, STATE_LINKED); - testStates("cb1", (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED); - testStates("cbox", (STATE_HASPOPUP | STATE_COLLAPSED), + var t1 = imageMap.getChildAt(0); + testStates(t1, 0, EXT_STATE_EDITABLE, STATE_LINKED); + var t2 = imageMap.getChildAt(1); + testStates(t2, 0, EXT_STATE_EDITABLE, STATE_LINKED); + var rb1 = imageMap.getChildAt(2); + testStates(rb1, (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED); + var rb2 = imageMap.getChildAt(3); + testStates(rb2, STATE_CHECKABLE, 0, STATE_CHECKED, STATE_LINKED); + var cb1 = imageMap.getChildAt(4); + testStates(cb1, (STATE_CHECKABLE | STATE_CHECKED), 0, STATE_LINKED); + var cbox = imageMap.getChildAt(5); + testStates(cbox, (STATE_HASPOPUP | STATE_COLLAPSED), EXT_STATE_EXPANDABLE, STATE_LINKED); SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); addA11yLoadEvent(doTest); </script>
--- a/accessible/tests/mochitest/tree/test_aria_imgmap.html +++ b/accessible/tests/mochitest/tree/test_aria_imgmap.html @@ -14,24 +14,17 @@ <script type="application/javascript" src="../role.js"></script> <script type="application/javascript" src="../states.js"></script> <script type="application/javascript"> function doTest() { - //XXX We send a useless mouse move to the image to force it to setup its - // image map, because flushing layout won't do it. Hopefully bug 135040 - // will make this not suck. - synthesizeMouse($("imagemap"), 10, 10, { type: "mousemove" }); - //XXX This may affect a11y more than other code because imagemaps may not - // get drawn or have an mouse event over them. Bug 570322 tracks a11y - // dealing with this. - todo(false, "Need to remove this image map workaround."); + ensureImageMapTree("imagemap"); var accTree = { role: ROLE_IMAGE_MAP, children: [ { role: ROLE_ENTRY, name: "first name" },
--- a/accessible/tests/mochitest/treeupdate/Makefile.in +++ b/accessible/tests/mochitest/treeupdate/Makefile.in @@ -49,16 +49,17 @@ include $(topsrcdir)/config/rules.mk test_ariadialog.html \ test_canvas.html \ test_colorpicker.xul \ test_cssoverflow.html \ test_contextmenu.xul \ test_doc.html \ test_gencontent.html \ test_hidden.html \ + test_imagemap.html \ test_list_editabledoc.html \ test_list.html \ test_menu.xul \ test_menubutton.xul \ test_recreation.html \ test_select.html \ test_textleaf.html \ test_visibility.html \
new file mode 100644 --- /dev/null +++ b/accessible/tests/mochitest/treeupdate/test_imagemap.html @@ -0,0 +1,394 @@ +<!DOCTYPE html> +<html> +<head> + <title>HTML img map accessible tree update tests</title> + <link rel="stylesheet" type="text/css" + href="chrome://mochikit/content/tests/SimpleTest/test.css" /> + + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script> + + <script type="application/javascript" + src="../common.js"></script> + <script type="application/javascript" + src="../role.js"></script> + <script type="application/javascript" + src="../events.js"></script> + + <script type="application/javascript"> + function insertArea(aImageMapID, aMapID) + { + this.imageMap = getAccessible(aImageMapID); + this.mapNode = getNode(aMapID); + + function getInsertedArea(aThisObj) + { + return aThisObj.imageMap.firstChild; + } + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getInsertedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap) + ]; + + this.invoke = function insertArea_invoke() + { + var areaElm = document.createElement("area"); + areaElm.setAttribute("href", + "http://www.bbc.co.uk/radio4/atoz/index.shtml#a"); + areaElm.setAttribute("coords", "0,0,13,14"); + areaElm.setAttribute("alt", "a"); + areaElm.setAttribute("shape", "rect"); + + this.mapNode.insertBefore(areaElm, this.mapNode.firstChild); + } + + this.finalCheck = function insertArea_finalCheck() + { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "a", + children: [ ] + }, + { + role: ROLE_LINK, + name: "b", + children: [ ] + }, + ] }; + testAccessibleTree(this.imageMap, accTree); + } + + this.getID = function insertArea_getID() + { + return "insert area element"; + } + } + + function appendArea(aImageMapID, aMapID) + { + this.imageMap = getAccessible(aImageMapID); + this.mapNode = getNode(aMapID); + + function getAppendedArea(aThisObj) + { + return aThisObj.imageMap.lastChild; + } + + this.eventSeq = [ + new invokerChecker(EVENT_SHOW, getAppendedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap) + ]; + + this.invoke = function appendArea_invoke() + { + var areaElm = document.createElement("area"); + areaElm.setAttribute("href", + "http://www.bbc.co.uk/radio4/atoz/index.shtml#c"); + areaElm.setAttribute("coords", "34,0,47,14"); + areaElm.setAttribute("alt", "c"); + areaElm.setAttribute("shape", "rect"); + + this.mapNode.appendChild(areaElm); + } + + this.finalCheck = function appendArea_finalCheck() + { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "a", + children: [ ] + }, + { + role: ROLE_LINK, + name: "b", + children: [ ] + }, + { + role: ROLE_LINK, + name: "c", + children: [ ] + } + ] }; + testAccessibleTree(this.imageMap, accTree); + } + + this.getID = function appendArea_getID() + { + return "append area element"; + } + } + + function removeArea(aImageMapID, aMapID) + { + this.imageMap = getAccessible(aImageMapID); + this.area = null; + this.mapNode = getNode(aMapID); + + function getRemovedArea(aThisObj) + { + return aThisObj.area; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getRemovedArea, this), + new invokerChecker(EVENT_REORDER, this.imageMap) + ]; + + this.invoke = function removeArea_invoke() + { + this.area = this.imageMap.firstChild; + this.mapNode.removeChild(this.mapNode.firstElementChild); + } + + this.finalCheck = function removeArea_finalCheck() + { + var accTree = + { IMAGE_MAP: [ + { + role: ROLE_LINK, + name: "b", + children: [ ] + }, + { + role: ROLE_LINK, + name: "c", + children: [ ] + } + ] }; + testAccessibleTree(this.imageMap, accTree); + } + + this.getID = function removeArea_getID() + { + return "remove area element"; + } + } + + function removeNameOnMap(aImageMapContainerID, aImageMapID, aMapID) + { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = getAccessible(aImageMapID); + this.imgNode = this.imageMap.DOMNode; + this.mapNode = getNode(aMapID); + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, this.imageMap), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function removeNameOnMap_invoke() + { + this.mapNode.removeAttribute("name"); + } + + this.finalCheck = function removeNameOnMap_finalCheck() + { + var accTree = + { SECTION: [ + { GRAPHIC: [ ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function removeNameOnMap_getID() + { + return "remove @name on map element"; + } + } + + function restoreNameOnMap(aImageMapContainerID, aImageMapID, aMapID) + { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = null; + this.imgNode = getNode(aImageMapID); + this.mapNode = getNode(aMapID); + + function getImageMap(aThisObj) + { + return aThisObj.imageMap; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImageMap, this), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function restoreNameOnMap_invoke() + { + this.imageMap = getAccessible(aImageMapID); + this.mapNode.setAttribute("name", "atoz_map"); + } + + this.finalCheck = function removeNameOnMap_finalCheck() + { + var accTree = + { SECTION: [ + { IMAGE_MAP: [ + { LINK: [ ] }, + { LINK: [ ] } + ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function removeNameOnMap_getID() + { + return "restore @name on map element"; + } + } + + function removeMap(aImageMapContainerID, aImageMapID, aMapID) + { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.imageMap = null; + this.imgNode = getNode(aImageMapID); + this.mapNode = getNode(aMapID); + + function getImageMap(aThisObj) + { + return aThisObj.imageMap; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImageMap, this), + new invokerChecker(EVENT_SHOW, this.imgNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function removeMap_invoke() + { + this.imageMap = getAccessible(aImageMapID); + this.mapNode.parentNode.removeChild(this.mapNode); + } + + this.finalCheck = function removeMap_finalCheck() + { + var accTree = + { SECTION: [ + { GRAPHIC: [ ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function removeMap_getID() + { + return "remove map element"; + } + } + + function insertMap(aImageMapContainerID, aImageID) + { + this.container = getAccessible(aImageMapContainerID); + this.containerNode = this.container.DOMNode; + this.image = null; + this.imgMapNode = getNode(aImageID); + + function getImage(aThisObj) + { + return aThisObj.image; + } + + this.eventSeq = [ + new invokerChecker(EVENT_HIDE, getImage, this), + new invokerChecker(EVENT_SHOW, this.imgMapNode), + new invokerChecker(EVENT_REORDER, this.container) + ]; + + this.invoke = function insertMap_invoke() + { + this.image = getAccessible(aImageID); + + var map = document.createElement("map"); + map.setAttribute("name", "atoz_map"); + map.setAttribute("id", "map"); + + var area = document.createElement("area") + area.setAttribute("href", + "http://www.bbc.co.uk/radio4/atoz/index.shtml#b"); + area.setAttribute("coords", "17,0,30,14"); + area.setAttribute("alt", "b"); + area.setAttribute("shape", "rect"); + + map.appendChild(area); + + this.containerNode.appendChild(map); + + ensureImageMapTree(aImageID); + } + + this.finalCheck = function insertMap_finalCheck() + { + var accTree = + { SECTION: [ + { IMAGE_MAP: [ + { LINK: [ ] } + ] } + ] }; + testAccessibleTree(this.container, accTree); + } + + this.getID = function insertMap_getID() + { + return "insert map element"; + } + } + + //gA11yEventDumpToConsole = true; + + var gQueue = null; + function doTest() + { + gQueue = new eventQueue(); + + gQueue.push(new insertArea("imgmap", "map")); + gQueue.push(new appendArea("imgmap", "map")); + gQueue.push(new removeArea("imgmap", "map")); + gQueue.push(new removeNameOnMap("container", "imgmap", "map")); + gQueue.push(new restoreNameOnMap("container", "imgmap", "map")); + gQueue.push(new removeMap("container", "imgmap", "map")); + gQueue.push(new insertMap("container", "imgmap")); + + gQueue.invoke(); // Will call SimpleTest.finish(); + } + + SimpleTest.waitForExplicitFinish(); + addA11yLoadEvent(doTest); + </script> +</head> +<body> + + <a target="_blank" + title="Image map accessible tree is not updated when image map is changed" + href="https://bugzilla.mozilla.org/show_bug.cgi?id=732389"> + Mozilla Bug 732389 + </a> + <p id="display"></p> + <div id="content" style="display: none"></div> + <pre id="test"> + </pre> + + <map name="atoz_map" id="map"> + <area href="http://www.bbc.co.uk/radio4/atoz/index.shtml#b" + coords="17,0,30,14" alt="b" shape="rect"> + </map> + + <div id="container"> + <img id="imgmap" width="447" height="15" + usemap="#atoz_map" + src="../letters.gif"> + </div> + +</body> +</html>
--- a/aclocal.m4 +++ b/aclocal.m4 @@ -14,16 +14,17 @@ builtin(include, build/autoconf/mozprog. builtin(include, build/autoconf/mozheader.m4)dnl builtin(include, build/autoconf/mozcommonheader.m4)dnl builtin(include, build/autoconf/acwinpaths.m4)dnl builtin(include, build/autoconf/lto.m4)dnl builtin(include, build/autoconf/gcc-pr49911.m4)dnl builtin(include, build/autoconf/frameptr.m4)dnl builtin(include, build/autoconf/compiler-opts.m4)dnl builtin(include, build/autoconf/expandlibs.m4)dnl +builtin(include, build/autoconf/arch.m4)dnl MOZ_PROG_CHECKMSYS() # Read the user's .mozconfig script. We can't do this in # configure.in: autoconf puts the argument parsing code above anything # expanded from configure.in, and we need to get the configure options # from .mozconfig in place before that argument parsing code. MOZ_READ_MOZCONFIG(.)
--- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -142,17 +142,17 @@ pref("browser.formfill.enable", true); /* spellcheck */ pref("layout.spellcheckDefault", 0); /* block popups by default, and notify the user about blocked popups */ pref("dom.disable_open_during_load", true); pref("privacy.popups.showBrowserMessage", true); pref("keyword.enabled", true); -pref("keyword.URL", "http://www.google.com/m?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q="); +pref("keyword.URL", "https://www.google.com/m?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q="); pref("accessibility.typeaheadfind", false); pref("accessibility.typeaheadfind.timeout", 5000); pref("accessibility.typeaheadfind.flashBar", 1); pref("accessibility.typeaheadfind.linksonly", false); pref("accessibility.typeaheadfind.casesensitive", 0); // pointer to the default engine name
--- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -12,16 +12,17 @@ const Cr = Components.results; const LocalFile = CC('@mozilla.org/file/local;1', 'nsILocalFile', 'initWithPath'); Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); Cu.import('resource://gre/modules/ContactService.jsm'); +Cu.import('resource://gre/modules/Webapps.jsm'); XPCOMUtils.defineLazyGetter(Services, 'env', function() { return Cc['@mozilla.org/process/environment;1'] .getService(Ci.nsIEnvironment); }); XPCOMUtils.defineLazyGetter(Services, 'ss', function() { return Cc['@mozilla.org/content/style-sheet-service;1'] @@ -61,17 +62,17 @@ function startupHttpd(baseDir, port) { // FIXME Bug 707625 // until we have a proper security model, add some rights to // the pre-installed web applications // XXX never grant 'content-camera' to non-gaia apps function addPermissions(urls) { let permissions = [ 'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app', - 'content-camera', 'webcontacts-manage', 'wifi-manage' + 'content-camera', 'webcontacts-manage', 'wifi-manage', 'desktop-notification' ]; urls.forEach(function(url) { let uri = Services.io.newURI(url, null, null); let allow = Ci.nsIPermissionManager.ALLOW_ACTION; permissions.forEach(function(permission) { Services.perms.add(uri, permission, allow); }); @@ -160,16 +161,20 @@ var shell = { // Load webapi.js as a frame script let frameScriptUrl = 'chrome://browser/content/webapi.js'; try { messageManager.loadFrameScript(frameScriptUrl, true); } catch (e) { dump('Error loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n'); } + CustomEventManager.init(); + + WebappsHelper.init(); + let browser = this.contentBrowser; browser.homePage = homeURL; browser.goHome(); }, stop: function shell_stop() { window.removeEventListener('MozApplicationManifest', this); window.removeEventListener('mozfullscreenchange', this); @@ -416,8 +421,75 @@ Services.obs.addObserver(function onCons let serverPort = Services.prefs.getIntPref('b2g.remote-js.port'); let serverSocket = Cc['@mozilla.org/network/server-socket;1'] .createInstance(Ci.nsIServerSocket); serverSocket.init(serverPort, true, -1); dump('Opened socket on ' + serverSocket.port + '\n'); serverSocket.asyncListen(listener); })(); +CustomEventManager = { + init: function custevt_init() { + window.addEventListener("ContentStart", (function(evt) { + content.addEventListener("mozContentEvent", this, false, true); + }).bind(this), false); + }, + + handleEvent: function custevt_handleEvent(evt) { + let detail = evt.detail; + dump("XXX FIXME : Got a mozContentEvent: " + detail.type); + + switch(detail.type) { + case "desktop-notification-click": + case "desktop-notification-close": + AlertsHelper.handleEvent(detail); + break; + } + } +} + +AlertsHelper = { + _listeners: {}, + _count: 0, + + handleEvent: function alert_handleEvent(detail) { + if (!detail || !detail.id) + return; + + let listener = this._listeners[detail.id]; + let topic = detail.type == "desktop-notification-click" ? "alertclickcallback" : "alertfinished"; + listener.observer.observe(null, topic, listener.cookie); + + // we're done with this notification + if (topic === "alertfinished") + delete this._listeners[detail.id]; + }, + + registerListener: function alert_registerListener(cookie, alertListener) { + let id = "alert" + this._count++; + this._listeners[id] = { observer: alertListener, cookie: cookie }; + return id; + }, + + showAlertNotification: function alert_showAlertNotification(imageUrl, title, text, textClickable, + cookie, alertListener, name) { + let id = this.registerListener(cookie, alertListener); + shell.sendEvent(content, "mozChromeEvent", { type: "desktop-notification", id: id, icon: imageUrl, + title: title, text: text } ); + } +} + +WebappsHelper = { + init: function webapps_init() { + Services.obs.addObserver(this, "webapps-launch", false); + }, + + observe: function webapps_observe(subject, topic, data) { + let json = JSON.parse(data); + DOMApplicationRegistry.getManifestFor(json.origin, function(aManifest) { + if (!aManifest) + return; + + let manifest = new DOMApplicationManifest(aManifest, json.origin); + shell.sendEvent(content, "mozChromeEvent", { type: "webapps-launch", url: manifest.fullLaunchPath(), origin: json.origin }); + }); + } +}
new file mode 100644 --- /dev/null +++ b/b2g/components/AlertsService.js @@ -0,0 +1,27 @@ +/* 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/. */ + +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +// ----------------------------------------------------------------------- +// Alerts Service +// ----------------------------------------------------------------------- + +function AlertsService() { } + +AlertsService.prototype = { + classID: Components.ID("{5dce03b2-8faa-4b6e-9242-6ddb0411750c}"), + QueryInterface: XPCOMUtils.generateQI([Ci.nsIAlertsService]), + + showAlertNotification: function(aImageUrl, aTitle, aText, aTextClickable, aCookie, aAlertListener, aName) { + let browser = Services.wm.getMostRecentWindow("navigator:browser"); + browser.AlertsHelper.showAlertNotification(aImageUrl, aTitle, aText, aTextClickable, aCookie, aAlertListener, aName); + } +}; + +const NSGetFactory = XPCOMUtils.generateNSGetFactory([AlertsService]);
--- a/b2g/components/B2GComponents.manifest +++ b/b2g/components/B2GComponents.manifest @@ -1,7 +1,15 @@ # Scrollbars category agent-style-sheets browser-content-stylesheet chrome://browser/content/content.css # CameraContent.js component {eff4231b-abce-4f7f-a40a-d646e8fde3ce} CameraContent.js contract @mozilla.org/b2g-camera-content;1 {eff4231b-abce-4f7f-a40a-d646e8fde3ce} category JavaScript-navigator-property mozCamera @mozilla.org/b2g-camera-content;1 + +# AlertsService.js +component {5dce03b2-8faa-4b6e-9242-6ddb0411750c} AlertsService.js +contract @mozilla.org/alerts-service;1 {5dce03b2-8faa-4b6e-9242-6ddb0411750c} + +# ContentPermissionPrompt.js +component {8c719f03-afe0-4aac-91ff-6c215895d467} ContentPermissionPrompt.js +contract @mozilla.org/content-permission/prompt;1 {8c719f03-afe0-4aac-91ff-6c215895d467}
--- a/b2g/components/CameraContent.js +++ b/b2g/components/CameraContent.js @@ -57,17 +57,17 @@ CameraContent.prototype = { init: function(aWindow) { let principal = aWindow.document.nodePrincipal; let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); let perm = principal == secMan.getSystemPrincipal() ? Ci.nsIPermissionManager.ALLOW_ACTION : Services.perms.testExactPermission(principal.URI, "content-camera"); //only pages with perm set and chrome pages can use the camera in content - this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome"); + this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION; Services.obs.addObserver(this, "inner-window-destroyed", false); let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); this.innerWindowID = util.currentInnerWindowID; }, classID: Components.ID("{eff4231b-abce-4f7f-a40a-d646e8fde3ce}"),
new file mode 100644 --- /dev/null +++ b/b2g/components/ContentPermissionPrompt.js @@ -0,0 +1,45 @@ +/* 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/. */ + +const Ci = Components.interfaces; +const Cr = Components.results; +const Cu = Components.utils; +const Cc = Components.classes; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/Services.jsm"); + +function ContentPermissionPrompt() {} + +ContentPermissionPrompt.prototype = { + + handleExistingPermission: function handleExistingPermission(request) { + let result = Services.perms.testExactPermission(request.uri, request.type); + if (result == Ci.nsIPermissionManager.ALLOW_ACTION) { + request.allow(); + return true; + } + if (result == Ci.nsIPermissionManager.DENY_ACTION) { + request.cancel(); + return true; + } + return false; + }, + + prompt: function(request) { + // returns true if the request was handled + if (this.handleExistingPermission(request)) + return; + + // TODO : show UI to grant or deny permission + }, + + classID: Components.ID("{8c719f03-afe0-4aac-91ff-6c215895d467}"), + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]) +}; + + +//module initialization +const NSGetFactory = XPCOMUtils.generateNSGetFactory([ContentPermissionPrompt]);
--- a/b2g/components/Makefile.in +++ b/b2g/components/Makefile.in @@ -46,11 +46,13 @@ XPIDL_MODULE = B2GComponents XPIDLSRCS = \ b2g.idl \ $(NULL) EXTRA_PP_COMPONENTS = \ B2GComponents.manifest \ CameraContent.js \ + AlertsService.js \ + ContentPermissionPrompt.js \ $(NULL) include $(topsrcdir)/config/rules.mk
--- a/b2g/config/mozconfigs/linux32/debug +++ b/b2g/config/mozconfigs/linux32/debug @@ -1,15 +1,15 @@ #GONK_TOOLCHAIN_VERSION=0 #export GONK_PRODUCT=generic #gonk="/home/cjones/mozilla/gonk-toolchain-$GONK_TOOLCHAIN_VERSION" -mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/objdir-prof-gonk +mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-b2g -mk_add_options MOZ_MAKE_FLAGS="-s -j16" +mk_add_options MOZ_MAKE_FLAGS="-j8" ac_add_options --enable-application=b2g ac_add_options --target=arm-android-eabi ac_add_options --with-gonk="$gonk" ac_add_options --with-endian=little ac_add_options --disable-elf-hack ac_add_options --enable-debug-symbols
--- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -139,16 +139,17 @@ @BINPATH@/components/content_htmldoc.xpt @BINPATH@/components/content_html.xpt @BINPATH@/components/content_xslt.xpt @BINPATH@/components/content_xtf.xpt @BINPATH@/components/cookie.xpt @BINPATH@/components/directory.xpt @BINPATH@/components/docshell.xpt @BINPATH@/components/dom.xpt +@BINPATH@/components/dom_apps.xpt @BINPATH@/components/dom_base.xpt #ifdef MOZ_B2G_RIL @BINPATH@/components/dom_telephony.xpt @BINPATH@/components/dom_wifi.xpt @BINPATH@/components/dom_system_gonk.xpt #endif @BINPATH@/components/dom_battery.xpt #ifdef MOZ_B2G_BT @@ -285,17 +286,16 @@ @BINPATH@/components/xpcom_threads.xpt @BINPATH@/components/xpcom_xpti.xpt @BINPATH@/components/xpconnect.xpt @BINPATH@/components/xulapp.xpt @BINPATH@/components/xul.xpt @BINPATH@/components/xuldoc.xpt @BINPATH@/components/xultmpl.xpt @BINPATH@/components/zipwriter.xpt -@BINPATH@/components/webapps.xpt ; JavaScript components @BINPATH@/components/ConsoleAPI.manifest @BINPATH@/components/ConsoleAPI.js @BINPATH@/components/ContactManager.js @BINPATH@/components/ContactManager.manifest @BINPATH@/components/FeedProcessor.manifest @BINPATH@/components/FeedProcessor.js @@ -402,20 +402,20 @@ @BINPATH@/components/messageWakeupService.manifest @BINPATH@/components/nsFilePicker.js @BINPATH@/components/nsFilePicker.manifest #ifdef MOZ_B2G_RIL @BINPATH@/components/RadioInterfaceLayer.manifest @BINPATH@/components/RadioInterfaceLayer.js @BINPATH@/components/SmsDatabaseService.manifest @BINPATH@/components/SmsDatabaseService.js -@BINPATH@/components/nsWifiWorker.js -@BINPATH@/components/nsWifiWorker.manifest -@BINPATH@/components/nsDOMWifiManager.js -@BINPATH@/components/nsDOMWifiManager.manifest +@BINPATH@/components/WifiWorker.js +@BINPATH@/components/WifiWorker.manifest +@BINPATH@/components/DOMWifiManager.js +@BINPATH@/components/DOMWifiManager.manifest #endif #ifdef XP_MACOSX @BINPATH@/components/libalerts.dylib #endif #ifdef MOZ_ENABLE_DBUS @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@ #endif @BINPATH@/components/nsINIProcessor.manifest @@ -425,16 +425,18 @@ #ifdef MOZ_SERVICES_SYNC @BINPATH@/components/SyncComponents.manifest @BINPATH@/components/Weave.js @BINPATH@/components/WeaveCrypto.manifest @BINPATH@/components/WeaveCrypto.js #endif @BINPATH@/components/TelemetryPing.js @BINPATH@/components/TelemetryPing.manifest +@BINPATH@/components/Webapps.js +@BINPATH@/components/Webapps.manifest ; Modules @BINPATH@/modules/* ; Safe Browsing @BINPATH@/components/nsURLClassifier.manifest @BINPATH@/components/nsUrlClassifierHashCompleter.js @BINPATH@/components/nsUrlClassifierListManager.js @@ -610,8 +612,10 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL [b2g] @BINPATH@/chrome/icons/ @BINPATH@/chrome/chrome@JAREXT@ @BINPATH@/chrome/chrome.manifest @BINPATH@/components/B2GComponents.manifest @BINPATH@/components/B2GComponents.xpt @BINPATH@/components/CameraContent.js +@BINPATH@/components/AlertsService.js +@BINPATH@/components/ContentPermissionPrompt.js
--- a/browser/app/blocklist.xml +++ b/browser/app/blocklist.xml @@ -1,10 +1,10 @@ <?xml version="1.0"?> -<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1331241604000"> +<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1331848989000"> <emItems> <emItem blockID="i58" id="webmaster@buzzzzvideos.info"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i41" id="{99079a25-328f-4bd4-be04-00955acaa0a7}"> <versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1"> </versionRange> @@ -60,18 +60,18 @@ </versionRange> </emItem> <emItem blockID="i13" id="{E8E88AB0-7182-11DF-904E-6045E0D72085}"> </emItem> <emItem blockID="i64" id="royal@facebook.com"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> - <emItem blockID="i72" id="{4ED1F68A-5463-4931-9384-8FFF5ED91D92}"> - <versionRange minVersion="0" maxVersion="3.4.1.194" severity="1"> + <emItem blockID="i72" os="WINNT" id="{4ED1F68A-5463-4931-9384-8FFF5ED91D92}"> + <versionRange minVersion="3.4.1" maxVersion="3.4.1.194" severity="1"> </versionRange> </emItem> <emItem blockID="i40" id="{28387537-e3f9-4ed7-860c-11e69af4a8a0}"> <versionRange minVersion="0.1" maxVersion="4.3.1.00" severity="1"> </versionRange> </emItem> <emItem blockID="i53" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}"> <versionRange minVersion="2.0.3" maxVersion="2.0.3"> @@ -148,16 +148,20 @@ <emItem blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}"> <versionRange minVersion="1.1b1" maxVersion="1.1b1"> </versionRange> </emItem> <emItem blockID="i3" id="langpack-vi-VN@firefox.mozilla.org"> <versionRange minVersion="2.0" maxVersion="2.0"> </versionRange> </emItem> + <emItem blockID="i73" id="a1g0a9g219d@a1.com"> + <versionRange minVersion="0" maxVersion="*"> + </versionRange> + </emItem> <emItem blockID="i51" id="admin@youtubeplayer.com"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem> <emItem blockID="i52" id="ff-ext@youtube"> <versionRange minVersion="0" maxVersion="*"> </versionRange> </emItem>
deleted file mode 100644 index e6b80682e5f4a46023977fb26b65e36095832cce..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
deleted file mode 100644 index 82b2bb7f51fe39a9f2fce05d7e310c2378f46785..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@<O00001
rename from browser/base/content/aboutHome.css rename to browser/base/content/abouthome/aboutHome.css --- a/browser/base/content/aboutHome.css +++ b/browser/base/content/abouthome/aboutHome.css @@ -19,16 +19,17 @@ * The Initial Developer of the Original Code is the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Marco Bonardo <mak77@bonardo.net> (original author) * Mihai Sucan <mihai.sucan@gmail.com> * Stephen Horlander <shorlander@mozilla.com> + * Frank Yan <fyan@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -36,349 +37,326 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ %endif html { - font-family: sans-serif; - background: -moz-Field; - color: -moz-FieldText; + font: message-box; + font-size: 100%; + background-color: hsl(0,0%,90%); + background-image: url(chrome://browser/content/abouthome/noise.png), + -moz-linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,.4)); + background-attachment: fixed; + color: #000; height: 100%; } body { - display: inline-block; - position: relative; margin: 0; + height: 100%; +} + +#container { + display: -moz-box; + -moz-box-orient: vertical; width: 100%; height: 100%; } +input, +button { + font-size: inherit; + font-family: inherit; +} + a { + color: -moz-nativehyperlinktext; text-decoration: none; } -a:hover { - text-decoration: underline; +.spacer { + -moz-box-flex: 1; } -#brandStart { +#topSection { text-align: center; - height: 19%; - max-height: 256px; - min-height: 92px; } -#brandStartSpacer { - height: 6.5%; -} - -#brandStartLogo { - height: 100%; +#brandLogo { + height: 154px; + margin: 22px 0 31px; } -#searchContainer { - height: 15%; - min-height: 90px; -} - -#searchContainer::before { - content: " "; - display: block; - height: 23%; +#searchForm, +#snippets { + width: 470px; } #searchForm { - display: table; - width: 100%; - max-width: 1830px; - margin: 0 auto; -} - -@media all and (max-height: 700px) { - #searchContainer { height: 20% } -} - -@media all and (max-height: 500px) { - #searchContainer { height: 25% } -} - -@media all and (max-height: 370px) { - #searchContainer { height: 30% } + display: -moz-box; } #searchLogoContainer { - display: table-cell; - width: 30%; - text-align: end; - line-height: 32px; + display: -moz-box; + -moz-box-align: center; + padding-top: 2px; + -moz-padding-end: 8px; } #searchEngineLogo { - -moz-margin-end: 2.5%; - vertical-align: middle; -} - -#searchInputContainer { - display: table-cell; - width: 38%; - max-width: 700px; - min-width: 150px; + display: inline-block; } #searchText { - width: 100%; - height: 24px; - padding: 3px 6px; - border-radius: 2px; - border: 1px solid rgb(150,150,150); - border-top-color: rgb(100,100,100); - box-shadow: 0 1px 0 rgba(255,255,255,0.5); - font-size: 1.2em; -} - -#searchButtons { - display: table-cell; - width: 31%; - -moz-padding-start: 13px; - vertical-align: top; + -moz-box-flex: 1; + padding: 6px 8px; + background: hsla(0,0%,100%,.9) padding-box; + border: 1px solid; + border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); + box-shadow: 0 1px 0 hsla(210,65%,9%,.02) inset, + 0 0 2px hsla(210,65%,9%,.1) inset, + 0 1px 0 hsla(0,0%,100%,.2); + border-radius: 2.5px 0 0 2.5px; } -@media all and (max-width: 470px) { - #searchLogoContainer { width: 10% } - #searchButtons { width: 11% } - #searchInputContainer { width: 40% } +body[dir=rtl] #searchText { + border-radius: 0 2.5px 2.5px 0; } -@media all and (min-width: 470px) and (max-width: 600px) { - #searchLogoContainer { width: 15% } - #searchButtons { width: 16%; white-space: nowrap } - #searchInputContainer { width: 45% } -} - -@media all and (min-width: 600px) and (max-width: 850px) { - #searchLogoContainer { width: 20% } - #searchButtons { width: 21%; white-space: nowrap } - #searchInputContainer { width: 49% } +#searchText:focus { + border-color: hsla(206,100%,60%,.6) hsla(206,76%,52%,.6) hsla(204,100%,40%,.6); } #searchSubmit { - background: -moz-linear-gradient(#f1f1f1, #dfdfdf); - padding: 4px 8px; - height: 32px; - border: 1px solid #ccc; - border-top-color: #ccc; - border-bottom-color: #999; - -moz-border-start-color: #afafaf; - -moz-border-end-color: #999; - box-shadow: 1px 1px 0 #e7e7e7, - 0 1px 0 #fcfcfc inset, - 0 -1px 0 #d7d7d7 inset; - font-size: 1em; - color: #000; + -moz-margin-start: -1px; + background: -moz-linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.1)) padding-box; + padding: 0 9px; + border: 1px solid; + border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); + -moz-border-start: 1px solid transparent; + border-radius: 0 2.5px 2.5px 0; + box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset, + 0 1px 0 hsla(0,0%,100%,.2); cursor: pointer; + -moz-transition-property: background-color, border-color, box-shadow; + -moz-transition-duration: 150ms; } body[dir=rtl] #searchSubmit { - box-shadow: -1px 1px 0 #e7e7e7, - 0 1px 0 #fcfcfc inset, - 0 -1px 0 #d7d7d7 inset; + border-radius: 2.5px 0 0 2.5px; +} + +#searchText:focus + #searchSubmit, +#searchText + #searchSubmit:hover { + border-color: #59b5fc #45a3e7 #3294d5; + color: white; } -#searchSubmit:active { - background: -moz-linear-gradient(#c5c5c5, #c5c5c5); - box-shadow: 1px 1px 0 #e7e7e7; +#searchText:focus + #searchSubmit { + background-image: -moz-linear-gradient(#4cb1ff, #1793e5); + box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, + 0 0 0 1px hsla(0,0%,100%,.1) inset, + 0 1px 0 hsla(210,54%,20%,.03); } -body[dir=rtl] #searchSubmit:active { - box-shadow: -1px 1px 0 #e7e7e7; +#searchText + #searchSubmit:hover { + background-image: -moz-linear-gradient(#66bdff, #0d9eff); + box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset, + 0 0 0 1px hsla(0,0%,100%,.1) inset, + 0 1px 0 hsla(210,54%,20%,.03), + 0 0 4px hsla(206,100%,20%,.2); } -#contentContainer { - height: 30%; - background-image: -moz-radial-gradient(center top, ellipse farthest-side, rgba(16,83,130,.5), rgba(16,83,130,0) 75%), - -moz-radial-gradient(center top, ellipse farthest-side, rgba(180,218,244,.5), rgba(180,218,244,0)), - -moz-radial-gradient(center top, ellipse farthest-side, rgba(180,218,244,.3), rgba(180,218,244,0)); - background-size: 100% 5px, - 100% 50px, - 100% 100%; - background-repeat: no-repeat; +#searchText + #searchSubmit:hover:active { + box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset, + 0 0 1px hsla(211,79%,6%,.2) inset; + -moz-transition-duration: 0ms; } -@media all and (max-height: 400px) { - #contentContainer { height: 20% } +#defaultSnippet1, +#defaultSnippet2 { + display: block; + min-height: 38px; + background: 30px center no-repeat; + padding: 6px 0; + -moz-padding-start: 79px; } -#snippetContainer { - position: relative; - top: -24px; - text-align: center; +body[dir=rtl] #defaultSnippet1, +body[dir=rtl] #defaultSnippet2 { + background-position: right 30px center; +} + +#defaultSnippet1 { + background-image: url("chrome://browser/content/abouthome/snippet1.png"); +} + +#defaultSnippet2 { + background-image: url("chrome://browser/content/abouthome/snippet2.png"); } #snippets { display: inline-block; - padding: 14px; - width: 30%; - max-width: 600px; - background-image: -moz-linear-gradient(rgba(255,255,255,.8), rgba(255,255,255,.1)); - background-color: rgb(250,250,250); - border-radius: 4px; - box-shadow: 0 1px 0 rgba(255,255,255,.8) inset, - 0 -2px 0 rgba(0,0,0,.1) inset, - 0 0 10px rgba(255,255,255,.5) inset, - 0 0 0 1px rgba(0,0,0,.1), - 0 2px 4px rgba(0,0,0,.2); - color: rgb(60,60,60); - font-size: .85em; - cursor: pointer; + text-align: start; + margin: 12px 0; + color: #3c3c3c; + font-size: 75%; } -#snippets:empty { - visibility: hidden; -} - -@media all and (max-width: 470px) { - #snippets { width: 65% } +#launcher { + display: -moz-box; + -moz-box-align: center; + -moz-box-pack: center; + width: 100%; + background-color: hsla(0,0%,0%,.03); + border-top: 1px solid hsla(0,0%,0%,.03); + box-shadow: 0 1px 2px hsla(0,0%,0%,.02) inset, + 0 -1px 0 hsla(0,0%,100%,.25); } -@media all and (min-width: 470px) and (max-width: 850px) { - #snippets { width: 45% } -} - -#snippets:hover { - background-color: rgb(255,255,255); - box-shadow: 0 1px 0 rgba(255,255,255,.8) inset, - 0 -2px 0 rgba(0,0,0,.1) inset, - 0 0 10px rgba(255,255,255,.5) inset, - 0 0 5px rgba(0,0,0,.1), - 0 0 0 1px rgba(0,0,0,.1), - 0 2px 4px rgba(0,0,0,.2); +#launcher:not([session]), +body[narrow] #launcher[session] { + display: block; /* display separator and restore button on separate lines */ + text-align: center; + white-space: nowrap; /* prevent navigational buttons from wrapping */ } -#snippets:hover:active { - background-color: rgb(210,210,210); - box-shadow: 0 2px 3px rgba(0,0,0,.3) inset, - 0 1px 0 rgba(255,255,255,.5); +.launchButton { + display: -moz-box; + -moz-box-orient: vertical; + margin: 16px 1px; + padding: 14px 6px; + min-width: 88px; + max-width: 176px; + max-height: 85px; + vertical-align: top; + white-space: normal; + background: transparent padding-box; + border: 1px solid transparent; + border-radius: 2.5px; + color: #525c66; + font-size: 75%; + cursor: pointer; + -moz-transition-property: background-color, border-color, box-shadow; + -moz-transition-duration: 150ms; } -#defaultSnippet1, -#defaultSnippet2 { - display: table-row; - text-align: start; +body[narrow] #launcher[session] > .launchButton { + margin: 4px 1px; } -#defaultSnippet1::before, -#defaultSnippet2::before { - display: table-cell; - vertical-align: middle; - -moz-padding-end: 1em; +.launchButton:hover { + background-color: hsla(211,79%,6%,.03); + border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2); } -#defaultSnippet1::before { - content: url("chrome://browser/content/aboutHome-snippet1.png"); +.launchButton:hover:active { + background-image: -moz-linear-gradient(hsla(211,79%,6%,.02), hsla(211,79%,6%,.05)); + border-color: hsla(210,54%,20%,.2) hsla(210,54%,20%,.23) hsla(210,54%,20%,.25); + box-shadow: 0 1px 1px hsla(211,79%,6%,.05) inset, + 0 0 1px hsla(211,79%,6%,.1) inset; + -moz-transition-duration: 0ms; } -#defaultSnippet2::before { - content: url("chrome://browser/content/aboutHome-snippet2.png"); + +#launcher:not([session]) > #restorePreviousSessionSeparator, +#launcher:not([session]) > #restorePreviousSession { + display: none; } -#sessionRestoreContainer { - padding-top: 1.5%; - text-align: center; +#restorePreviousSessionSeparator { + width: 3px; + height: 116px; + margin: 0 10px; + background-image: -moz-linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)), + -moz-linear-gradient(hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)), + -moz-linear-gradient(hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)); + background-position: left top, center, right bottom; + background-size: 1px auto; + background-repeat: no-repeat; } -@media all and (max-height: 500px) { - #sessionRestoreContainer { - position: relative; - top: -15px; - padding-top: 0; - } +body[narrow] #restorePreviousSessionSeparator { + margin: 0 auto; + width: 512px; + height: 3px; + background-image: -moz-linear-gradient(0, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)), + -moz-linear-gradient(0, hsla(211,79%,6%,0), hsla(211,79%,6%,.2), hsla(211,79%,6%,0)), + -moz-linear-gradient(0, hsla(0,0%,100%,0), hsla(0,0%,100%,.35), hsla(0,0%,100%,0)); + background-size: auto 1px; } #restorePreviousSession { - padding: 10px; - border: 0; - border-radius: 4px; - box-shadow: 0 0 0 1px rgba(9,37,59,0), - 0 1px 2px rgba(9,37,59,0), - 0 0 10px rgba(255,255,255,0), - 0 -3px 0 rgba(180,194,212,0) inset; - -moz-transition-property: background-color, box-shadow; - -moz-transition-duration: 0.25s; - -moz-transition-timing-function: ease-out; - background: transparent; - color: rgb(50,50,50); - font-weight: bold; - font-size: 1em; - cursor: pointer; + max-width: none; + font-size: 90%; +} + +body[narrow] #restorePreviousSession { + font-size: 80%; +} + +.launchButton::before { + display: block; + margin-bottom: 6px; + line-height: 0; /* remove extra vertical space due to non-zero font-size */ +} + +#bookmarks::before { + content: url("chrome://browser/content/abouthome/bookmarks.png"); +} + +#history::before { + content: url("chrome://browser/content/abouthome/history.png"); +} + +#settings::before { + content: url("chrome://browser/content/abouthome/settings.png"); +} + +#addons::before { + content: url("chrome://browser/content/abouthome/addons.png"); +} + +#downloads::before { + content: url("chrome://browser/content/abouthome/downloads.png"); +} + +#sync::before { + content: url("chrome://browser/content/abouthome/sync.png"); } #restorePreviousSession::before { - display: inline-block; - content: url("chrome://browser/content/aboutHome-restore-icon.png"); - -moz-margin-end: 10px; + content: url("chrome://browser/content/abouthome/restore-large.png"); + display: inline-block; /* display on same line as text label */ vertical-align: middle; - height: 66px; /* Needed to avoid a blank space under the image */ + margin-bottom: 0; + -moz-margin-end: 8px; } body[dir=rtl] #restorePreviousSession::before { -moz-transform: scaleX(-1); } -@media all and (max-height: 500px) { - #restorePreviousSession::before { - content: url("chrome://browser/content/aboutHome-restore-icon-small.png"); - height: 41px; - } +body[narrow] #restorePreviousSession::before { + content: url("chrome://browser/content/abouthome/restore.png"); } -@media all and (max-width: 500px) { - #restorePreviousSession::before { - content: url("chrome://browser/content/aboutHome-restore-icon-small.png"); - height: 41px; - } -} - -#restorePreviousSession:disabled { - display: none; -} - -#restorePreviousSession:hover { - background-image: -moz-linear-gradient(rgba(255,255,255,.7), rgba(255,255,255,.2)); - border-radius: 4px; - box-shadow: 0 0 0 1px rgba(9,37,59,.2), - 0 1px 2px rgba(9,37,59,.2), - 0 0 10px rgba(255,255,255,.4), - 0 -3px 0 rgba(180,194,212,.3) inset; +#aboutMozilla { + display: block; + position: relative; /* pin wordmark to edge of document, not of viewport */ + -moz-box-ordinal-group: 0; + opacity: .5; + -moz-transition: opacity 150ms; } -#restorePreviousSession:hover:active { - background-image: -moz-linear-gradient(rgba(255,255,255,.0), rgba(255,255,255,.2)); - background-color: rgba(23,75,115,.1); - box-shadow: 0 0 0 1px rgba(9,37,59,.2), - 0 1px 2px rgba(9,37,59,.4) inset, - 0 1px 5px rgba(9,37,59,.15) inset; +#aboutMozilla:hover { + opacity: 1; } -#bottomSection { +#aboutMozilla::before { + content: url("chrome://browser/content/abouthome/mozilla.png"); + display: block; position: absolute; - color: rgb(150,150,150); - font-size: .8em; - width: 100%; - text-align: center; - bottom: 2%; + top: 12px; + right: 12px; } - -#syncLinksContainer { - padding-top: 1em; -} - -.sync-link { - padding: 1em; -} - -@media all and (max-height: 370px) { - #bottomSection { - visibility: hidden; - } -}
rename from browser/base/content/aboutHome.js rename to browser/base/content/abouthome/aboutHome.js --- a/browser/base/content/aboutHome.js +++ b/browser/base/content/abouthome/aboutHome.js @@ -17,16 +17,17 @@ * * The Initial Developer of the Original Code is the Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Marco Bonardo <mak77@bonardo.net> (original author) * Mihai Sucan <mihai.sucan@gmail.com> + * Frank Yan <fyan@mozilla.com> * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -150,16 +151,19 @@ const SNIPPETS_UPDATE_INTERVAL_MS = 8640 let gSearchEngine; function onLoad(event) { setupSearchEngine(); document.getElementById("searchText").focus(); loadSnippets(); + + fitToWidth(); + window.addEventListener("resize", fitToWidth); } function onSearchSubmit(aEvent) { let searchTerms = document.getElementById("searchText").value; if (gSearchEngine && searchTerms.length > 0) { const SEARCH_TOKENS = { @@ -205,23 +209,27 @@ function setupSearchEngine() function loadSnippets() { // Check last snippets update. let lastUpdate = localStorage["snippets-last-update"]; let updateURL = localStorage["snippets-update-url"]; if (updateURL && (!lastUpdate || Date.now() - lastUpdate > SNIPPETS_UPDATE_INTERVAL_MS)) { + // Try to update from network. + let xhr = new XMLHttpRequest(); + try { + xhr.open("GET", updateURL, true); + } catch (ex) { + showSnippets(); + return; + } // Even if fetching should fail we don't want to spam the server, thus // set the last update time regardless its results. Will retry tomorrow. localStorage["snippets-last-update"] = Date.now(); - - // Try to update from network. - let xhr = new XMLHttpRequest(); - xhr.open('GET', updateURL, true); xhr.onerror = function (event) { showSnippets(); }; xhr.onload = function (event) { if (xhr.status == 200) { localStorage["snippets"] = xhr.responseText; } @@ -255,41 +263,31 @@ function showSnippets() // Bad content, continue to show default snippets. } } // Show default snippets otherwise. let defaultSnippetsElt = document.getElementById("defaultSnippets"); let entries = defaultSnippetsElt.querySelectorAll("span"); // Choose a random snippet. Assume there is always at least one. - let randIndex = Math.round(Math.random() * (entries.length - 1)); + let randIndex = Math.floor(Math.random() * entries.length); let entry = entries[randIndex]; // Inject url in the eventual link. if (DEFAULT_SNIPPETS_URLS[randIndex]) { let links = entry.getElementsByTagName("a"); // Default snippets can have only one link, otherwise something is messed // up in the translation. if (links.length == 1) { links[0].href = DEFAULT_SNIPPETS_URLS[randIndex]; - activateSnippetsButtonClick(entry); } } // Move the default snippet to the snippets element. snippetsElt.appendChild(entry); } -/** - * Searches a single link element in aElt and binds its href to the click - * action of the snippets button. - * - * @param aElt - * Element to search the link into. - */ -function activateSnippetsButtonClick(aElt) { - let links = aElt.getElementsByTagName("a"); - if (links.length == 1) { - document.getElementById("snippets") - .addEventListener("click", function(aEvent) { - if (aEvent.target.nodeName != "a") - window.location = links[0].href; - }, false); +function fitToWidth() { + if (window.scrollMaxX) { + document.body.setAttribute("narrow", "true"); + } else if (document.body.hasAttribute("narrow")) { + document.body.removeAttribute("narrow"); + fitToWidth(); } }
rename from browser/base/content/aboutHome.xhtml rename to browser/base/content/abouthome/aboutHome.xhtml --- a/browser/base/content/aboutHome.xhtml +++ b/browser/base/content/abouthome/aboutHome.xhtml @@ -20,16 +20,17 @@ # The Initial Developer of the Original Code is the Mozilla Foundation. # Portions created by the Initial Developer are Copyright (C) 2010 # the Initial Developer. All Rights Reserved. # # Contributor(s): # Marco Bonardo <mak77@bonardo.net> (original author) # Mihai Sucan <mihai.sucan@gmail.com> # Stephen Horlander <shorlander@mozilla.com> +# Frank Yan <fyan@mozilla.com> # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your @@ -55,57 +56,53 @@ <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>&abouthome.pageTitle;</title> <link rel="icon" type="image/png" id="favicon" href="chrome://branding/content/icon16.png"/> <link rel="stylesheet" type="text/css" media="all" - href="chrome://browser/content/aboutHome.css"/> + href="chrome://browser/content/abouthome/aboutHome.css"/> <script type="text/javascript;version=1.8" - src="chrome://browser/content/aboutHome.js"/> + src="chrome://browser/content/abouthome/aboutHome.js"/> </head> <body dir="&locale.dir;" onload="onLoad(event)"> - <div id="brandStartSpacer" /> - <div id="brandStart"> - <img id="brandStartLogo" src="chrome://branding/content/about-logo.png" alt="" /> - </div> + <div id="container"> + <div class="spacer"/> + <div id="topSection"> + <img id="brandLogo" src="chrome://branding/content/about-logo.png" alt=""/> - <div id="searchContainer"> - <form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)"> - <div id="searchLogoContainer"><img id="searchEngineLogo" /></div> - <div id="searchInputContainer"> - <input type="text" name="q" value="" id="searchText" maxlength="256" /> + <div id="searchContainer"> + <form name="searchForm" id="searchForm" onsubmit="onSearchSubmit(event)"> + <div id="searchLogoContainer"><img id="searchEngineLogo"/></div> + <input type="text" name="q" value="" id="searchText" maxlength="256"/> + <input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;"/> + </form> </div> - <div id="searchButtons"> - <input id="searchSubmit" type="submit" value="&abouthome.searchEngineButton.label;" /> - </div> - </form> - </div> - <div id="contentContainer"> - <div id="snippetContainer"> - <div id="defaultSnippets" hidden="true"> - <span id="defaultSnippet1">&abouthome.defaultSnippet1.v1;</span> - <span id="defaultSnippet2">&abouthome.defaultSnippet2.v1;</span> + <div id="snippetContainer"> + <div id="defaultSnippets" hidden="true"> + <span id="defaultSnippet1">&abouthome.defaultSnippet1.v1;</span> + <span id="defaultSnippet2">&abouthome.defaultSnippet2.v1;</span> + </div> + <div id="snippets"/> </div> - <div id="snippets"/> + </div> + <div class="spacer"/> + + <div id="launcher" session="true"> + <button class="launchButton" id="bookmarks">&abouthome.bookmarksButton.label;</button> + <button class="launchButton" id="history">&abouthome.historyButton.label;</button> + <button class="launchButton" id="settings">&abouthome.settingsButton.label;</button> + <button class="launchButton" id="addons">&abouthome.addonsButton.label;</button> + <button class="launchButton" id="downloads">&abouthome.downloadsButton.label;</button> + <button class="launchButton" id="sync">&syncBrand.shortName.label;</button> + <div id="restorePreviousSessionSeparator"/> + <button class="launchButton" id="restorePreviousSession">&historyRestoreLastSession.label;</button> </div> - <div id="sessionRestoreContainer"> - <button id="restorePreviousSession">&historyRestoreLastSession.label;</button> - </div> - </div> - - <div id="bottomSection"> - <div id="aboutMozilla"> - <a href="http://www.mozilla.com/about/">&abouthome.aboutMozilla;</a> - </div> - <div id="syncLinksContainer"> - <a href="javascript:void(0);" class="sync-link" id="setupSyncLink">&abouthome.syncSetup.label;</a> - <a href="javascript:void(0);" class="sync-link" id="pairDeviceLink">&abouthome.pairDevice.label;</a> - </div> + <a id="aboutMozilla" href="http://www.mozilla.com/about/"/> </div> </body> </html>
new file mode 100644 index 0000000000000000000000000000000000000000..41519ce4982a0385b7668eaad8c20e1d06070b7b GIT binary patch literal 1444 zc$@*G1zY-wP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU%MoC0LR9M5+msw0yR~W}_oBG%{pPQyW z7}KOSJT$T3N>M}<6&Us)%Z$jtFayIZch);|@66oUR~Q&TmX<|g!$VD46GHI8Hce|y zO=;g~OoM1`o7Ae2G+)2(hM5i{57%TcDakMQaF%=i=R5!JoIAUA{U830me-vut!i1S zXt?wUS;O+pqvf^7UdiOe=Niw{EBZif5rA>UM5tWTu7Trc8xHKafCfeHmqwh_KQs;D z(W@d<?q`EgCszwQE}+NcTIM2S5E+}#Kq4&OLC~|l;EoGOUYRjlak9|JTt+}(cm_=F z*ux)w{Q2OH3jlz({fY4<VPGn=1r(8)YoGhDZTBlNVmLWrb%sZ9@;MQkZxCF;!OFZ% zP*hsA|4?al!J$*t1x2OR2eRt-l$>vBRP@;%(5h)02uCOdtt!)R*ruT|g(oZD*zCVI z{T`%DvVe*@`B3%6Zm5^{LQPu_R5o|Pg{DriELU4-zrl-J5K7I8P?_p3_1S?^v<myv z7+Gr%)M0wowyB|_UU6Hp0ENc<m7WWOJuoDO*Ww)zp)wa41@GV#8gCkcsTmO}`;wmk z9~~3NShM=m|3*Fn8WVq0vVhAv_MR(tMSO8SAQ1*<U{<n#E<N{`keGx39x(a9A`F0; z5BsDGFmV5n0Dtn+ykK(2L2vV#B@5^_@*Bdyq`0s7z~qTTx1J4U&w*J{dE|7B^Kh9= zi&^u|76EFD=b<}3E}D(-Bqo47K?bBJiA%Rhfjl7BSk{Uw8p?1&S(&W$cCB0kE!{?F z>eRv6dc{K&v_B)j!iR2Hgao+I)f^r;W>0)Wh>hMFo|v1*=QnnAw}%9h5DCzG^v)2p zsTgSKGJuM4iD_&1C&i4xh{L(Yj;k2~zuf!vV^?Ts$?T4<dSfGtckcdV#AEg-@;-9h zpBP(o1cyPu$Vh?2@)$izOEHykQB;?1qq>$Z{iE#j{!OG1IgfmVyp_F0k>rGqaYb?P zL|)*+qjZRENH(c~^)>ag0a3kFXd5VMEc$_>;YKf|+njimtBl;*mPravT#%tLl7~vG zn1dzfx45)LsSD~ILGdM05Pjs1J)fLg7@z&@`Vhm1|Ddqb#3YLR;fs%4$xLvhOeU{t zR>DQ49vVBebdEnp_jaJDg5ziEn$f+_(k7JxO~!|kOSqN$kaySaKWO*GM!v8K@fC|Z zx-vR7KaTb9Wdzi;cCYl}cb*j|LFJ$^dqgp{co~t4`|W<9OkmN)1cxi(ag{Dg(`_)| z3i-eQpXIUL$cM=3P4;aGf2)ewpcJ9KqVQ3eXvK7?kooAz`Uh5oXlhxAjVzm7(e<>u zX-qDCHXkq!U#Ilju-7W?54|uK%3C3lTHaJ%L_GfcNrT;+xSJM{3q?@Fa0=<Sq1OJ~ zfo`SH=3>Na+RO(NIiG2-b9gvlB(@zx0+d>-TDpLK+n;tnxpepijC>gV(0?bp|KYOg zJ<UqpI^&9LJEqqvz~w%(Q~GVFV^_(5PTc!ksJ^|&1jov2W$FH>sxDqv>KtIh?b&XO z7Cj*EF{WPPdw~7k<c^~GhF*XTBtdO<gRDil@ZXOW&F=;-B(`lgM&}6NryR2&{l|yF zd4u+ve2|9HjaF#Et5t=Uhk^Ab)BW{!VUbozZRzyP9!G~(yUO^rWC1#>C)KHA#e65U zb{{>TFkEQW=81kgK7B1k7gM_b)2Gi4rzYpdvF$s(Bd|_lGGno<wR!I=)Xc4y`SI}q yBF~|D^KvRi_U80|ZS#~XL29-cTaBEMGWWm2aTm|u=>tjt0000<MNUMnLSTZ;x6I7|
new file mode 100644 index 0000000000000000000000000000000000000000..5c7e194a61c1e669b24c382603269a093989f517 GIT binary patch literal 1276 zc$@+J1OxktP)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F8000ETNkl<Zc-qC5 zYfPJE6vz8b6E9P;O`>KW{op4P6J!B81jcoY%55<2TFRwR3!|l_w9vQhTUvT=DYQVj z?7|$&V$3o#B%4E+gn({16tkEZql+I*jBKfd#T?IhMS@y{7SIpB0MB{O|NPH$d*K1V z|4P^i@#zJ;gp2}S;)$ZD)VzvCWFCJ%fMXcjmc21>EUSnoD6Ji8P#8ems)wAC8ZVCh zHwLKF<8PH8sX5cRX7r2z>mCMCvt|{?qA|Ab2C(~6yuvC;pOSThWAGA(l666TrP#^G zSbTc^zp-lr*nL^FsJUact7jBkLzlrB7zbJj91Erh$_`<d`)eStLhMu+Twot~kBW2H z`Y%GO-U)dX_1*kTT!pm!E!d@j6hY~s>LzuNbr0dvlYvVF_dAUVF_w~362*0!mKTcM z8OX&BTXhbw`p0PRH^Ys|F(xRj(<fvU^5U>q2qHI-oKu3`Yk~%60PMKr4%~vx&=|#p z()NeRCr=-U--Q9XYOmwtQiekMwEW5|*~QgM<@It<b$P(zJCFN+F(eeD8&Os#1IoeN zaneAo)1<lOA>?Nk#^C;rpC}S8=a$z)adi_^HYh-JrUN7@BghyVbXfYp+<O+6`T`!h zv5-*A<Q=8Clta0sL0X(9X=fD)m#7xjd~L%3zG^?!$rzAnZJ;uGpwr?5ovoi+Qd7?e zboZVEs}BqN$94qGrJOF$2x*WOX_7Wqqqap0f|8mC8wMI%nQv%<3GcS0?>q_Y3#3go zH0EAtkTc6025!%NUZ*m;e=&N7p}X%K?d85e+Ek;Bb^d(!-d9zd58<<)%+4#@jP4b@ z&opsgKy5mtEAw~0Xa<0%{(cLhZcfi?RIDp#Bon=7_Y~F@0MvdoJ-g6G4R3V~4^DhM zqi<tfYm{L2j_wJhg_^<1Ter2;=yvbG3s<k->~3S-Yk%B=(Op3rN`q_d!^tTFY9HR| zOZejD53W1#U_Qp}psN@WluH`#Uz@a`X7sVl!mys=BbJ`P1Y`5#eIAYot<?wi-r=jL z^)ereA59U2_bq5tFxS+k9_l0_)cABul)5WP*~MJ(*yNn>42Y!d_cdg|6^IDR6-ib1 z60=TG@$e1cxBpjMMmoh2LAlt(Lt241usE`F1Ne`{OSLVXptI9AVK5>nmo)HG9>p0* zI=OQLr6Orsqe2gQGT;~p1;w_FlSV;BJwG;F165*~tV!JkdPo01U~E26n>_TU2iuLV zRgJla#zM}Owj1H?26@BL;{skxQufXb2pbiHvQ7(iu>oWCaR*DTwXT`n10Q|=;79Sd z-~AvlIs0$m*kg+6Z2rIGv~+X>YI);Qb9pguhGF0xiDF8jx8pwgx$ZP(Ds1`9VDFn= zTE5=`0I|pu$dgFi;+J15bXM<-oUy|Cig3B~xA0x%L_&Igcm}Gaszrs){%6OaHG3BD zE2hR6A9<PtX<zAn^D_!4=NTk%ZsBf6r^)?UD`Vqw>8DVloX0ug8DMQ*Z=*uDqUmzp zxivFK?|2IG90^fT659-otv|KTBdIs0K4sgDjzzJYd1Q9^ZQ&XqUPa20H00p=7>R5J mo$}2_4kPJE1@g*PXW)02(gT@f=I})T0000<MNUMnLSTZFLTTOr
new file mode 100644 index 0000000000000000000000000000000000000000..3d4d10e7abb472690b37dbd8cea26c79ec8205ea GIT binary patch literal 898 zc$@)(1AY97P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F80009?Nkl<Zc-qyM z%TE(g6vm116(5QEzqoKEaVH^>D2N6W!B8WlQh~mN&UE^qkLk2SdDHUr0irJ5xR4bD zO$kUVg|wy>(^6WRX<4`clIuN9N-$1f3Ns6D@=KcD@1FCWd(XX1i;Di2alQ2ZwVM@n zD|c!dQTZb|FE9rh(28!LyzW`JnRFn6?nji<!wbxT2DG9ZXiyuGW8e*PkGw}+qaS#I zInaPsbOTUX=kU8+l7j#ZXhk>BN->8f&jVV~4YcT)Ty;VKtpo<(2&M!9T1k3<9hwq^ zBt6hf(1`WE6$EHSH}IUWBbRql5TF&^fI??OPS1oOKr6n1c5Gm9LJ*)8-9S^D6*2gg z69Q-@Fkr#Y>}x@QR+1*9`(Fv7;3h0CsbotkYBRU0o}e-w9z0c|HcJ<x`(6qHw6KOR z7UU?sTlc%T^xov2fvS3iU#>MHjfq8CbLT(kY(1#MJ&f$V<AMM!tl^6VITY<yR4r?c z?irYwo0lmmCT(<ik+pja*?Pt$0b-ljA*9kXTc1A9wCov3rBWAt-~3G)GmA}luy7PF z7!wtUX=1&22VB|NIiEJ0&0hJd55UCYQc$ijI#UMcAhL9g<Q9nm#Dc<9MkYJ^bwQcQ zWG?eIV=|e%5LjMq)S6rg12d32OIUmyAO;kqHaIy*&T-tO18+nkkvJb*4a?M&BThN` zQAg(xvVf5z0BfNTHN|lA3x36+$@3!?i=7LFqK~obHInW_W*i7ADFj$U;p8d<lZNDQ z#09+#kx1lhFdVK`QOp`)@69z31Qh~k^?3J`lq0>k^j%(X^8F?vk)Jhc0~00iy*A-S zGJ}x=0F55+no93TL2~G;V?Dv4Q0PqbSG-zlWPa#u-ME_vafKdUV2-4Fk&2=>pqY=I zJU;?~!0FifdZpIj4C$=c1nz36bU*MNiH}ODrxX6=l?G^LQGbklKHsTiI$f?YFu^v9 z8(*z~T*Ux<hTTz;ws;^ImWiG`Kc=UrPi|~(-Ubsd@VgFY4Og|T!;M-AYaDNTJ=`Ek z|D=Kmj@v4;yS*P<2|L$9*tc{R_DeXjRx5e({2)p4#P;^~)!p6QD$K*3ot+!dtVfCU Y2Tou=l3c_Nga7~l07*qoM6N<$f;t<JX#fBK
new file mode 100644 index 0000000000000000000000000000000000000000..ae742b1aa8acd6115533186c86f7ac37828681f7 GIT binary patch literal 1654 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 AM*si-
new file mode 100644 index 0000000000000000000000000000000000000000..f2c348d13566a943593afc93934e5503661deaa9 GIT binary patch literal 2684 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
copy from browser/themes/winstripe/newtab/noise.png copy to browser/base/content/abouthome/noise.png
new file mode 100644 index 0000000000000000000000000000000000000000..ef593e6e14c87b6774b39361f9ce31b1c908dbed GIT binary patch literal 2841 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
new file mode 100644 index 0000000000000000000000000000000000000000..5c3d6f4376c24bfe320ccac1e2b09f896c83a2a9 GIT binary patch literal 1796 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
new file mode 100644 index 0000000000000000000000000000000000000000..4b0c30990933f748f41f8424ff81bd0622c05b18 GIT binary patch literal 1557 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 Hu0mjf%%}sx
rename from browser/base/content/aboutHome-snippet1.png rename to browser/base/content/abouthome/snippet1.png
rename from browser/base/content/aboutHome-snippet2.png rename to browser/base/content/abouthome/snippet2.png
new file mode 100644 index 0000000000000000000000000000000000000000..11e40cc93755ab65b0dcd4a51a58c87f6c096278 GIT binary patch literal 1879 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 RBdh=b002ovPDHLkV1m-`bcp}}
--- a/browser/base/content/browser-syncui.js +++ b/browser/base/content/browser-syncui.js @@ -170,31 +170,17 @@ let gSyncUI = { }, onLoginFinish: function SUI_onLoginFinish() { // Clear out any login failure notifications let title = this._stringBundle.GetStringFromName("error.login.title"); this.clearError(title); }, - // Set visibility of "Setup Sync" link - showSetupSyncAboutHome: function SUI_showSetupSyncAboutHome(toShow) { - let browsers = gBrowser.browsers; - for (let i = 0; i < browsers.length; i++) { - let b = browsers[i]; - if ("about:home" == b.currentURI.spec) { - b.contentDocument.getElementById("setupSyncLink").hidden = !toShow; - } - } - }, - onSetupComplete: function SUI_onSetupComplete() { - // Remove "setup sync" link in about:home if it is open. - this.showSetupSyncAboutHome(false); - onLoginFinish(); }, onLoginError: function SUI_onLoginError() { // if login fails, any other notifications are essentially moot Weave.Notifications.removeAll(); // if we haven't set up the client, don't show errors @@ -232,18 +218,16 @@ let gSyncUI = { }, onLogout: function SUI_onLogout() { this.updateUI(); }, onStartOver: function SUI_onStartOver() { this.clearError(); - // Make "setup sync" link visible in about:home if it is open. - this.showSetupSyncAboutHome(true); }, onQuotaNotice: function onQuotaNotice(subject, data) { let title = this._stringBundle.GetStringFromName("warning.sync.quota.label"); let description = this._stringBundle.GetStringFromName("warning.sync.quota.description"); let buttons = []; buttons.push(new Weave.NotificationButton( this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
--- a/browser/base/content/browser-thumbnails.js +++ b/browser/base/content/browser-thumbnails.js @@ -105,16 +105,25 @@ let gBrowserThumbnails = { return false; // Don't take screenshots of about: pages. if (aBrowser.currentURI.schemeIs("about")) return false; let channel = aBrowser.docShell.currentDocumentChannel; + // No valid document channel. We shouldn't take a screenshot. + if (!channel) + return false; + + // Don't take screenshots of internally redirecting about: pages. + // This includes error pages. + if (channel.originalURI.schemeIs("about")) + return false; + try { // If the channel is a nsIHttpChannel get its http status code. let httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); // Continue only if we have a 2xx status code. return Math.floor(httpChannel.responseStatus / 100) == 2; } catch (e) { // Not a http channel, we just assume a success status code.
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -51,16 +51,17 @@ # Dietrich Ayala <dietrich@mozilla.com> # Gavin Sharp <gavin@gavinsharp.com> # Justin Dolske <dolske@mozilla.com> # Rob Campbell <rcampbell@mozilla.com> # David Dahl <ddahl@mozilla.com> # Patrick Walton <pcwalton@mozilla.com> # Mihai Sucan <mihai.sucan@gmail.com> # Victor Porof <vporof@mozilla.com> +# Frank Yan <fyan@mozilla.com> # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), # in which case the provisions of the GPL or the LGPL are applicable instead # of those above. If you wish to allow use of your version of this file only # under the terms of either the GPL or the LGPL, and not to allow others to # use your version of this file under the terms of the MPL, indicate your @@ -2701,99 +2702,93 @@ function PageProxyClickHandler(aEvent) * Handle load of some pages (about:*) so that we can make modifications * to the DOM for unprivileged pages. */ function BrowserOnAboutPageLoad(document) { if (/^about:home$/i.test(document.documentURI)) { let ss = Components.classes["@mozilla.org/browser/sessionstore;1"]. getService(Components.interfaces.nsISessionStore); if (!ss.canRestoreLastSession) - document.getElementById("sessionRestoreContainer").hidden = true; - // Sync-related links - if (Services.prefs.prefHasUserValue("services.sync.username")) { - document.getElementById("setupSyncLink").hidden = true; - } + document.getElementById("launcher").removeAttribute("session"); } } /** * Handle command events bubbling up from error page content */ function BrowserOnClick(event) { - // Don't trust synthetic events - if (!event.isTrusted || - (event.target.localName != "button" && - event.target.className != "sync-link")) + if (!event.isTrusted || // Don't trust synthetic events + event.button == 2 || event.target.localName != "button") return; var ot = event.originalTarget; - var errorDoc = ot.ownerDocument; + var ownerDoc = ot.ownerDocument; // If the event came from an ssl error page, it is probably either the "Add // Exception…" or "Get me out of here!" button - if (/^about:certerror/.test(errorDoc.documentURI)) { - if (ot == errorDoc.getElementById('exceptionDialogButton')) { + if (/^about:certerror/.test(ownerDoc.documentURI)) { + if (ot == ownerDoc.getElementById('exceptionDialogButton')) { var params = { exceptionAdded : false, handlePrivateBrowsing : true }; try { switch (gPrefService.getIntPref("browser.ssl_override_behavior")) { case 2 : // Pre-fetch & pre-populate params.prefetchCert = true; case 1 : // Pre-populate - params.location = errorDoc.location.href; + params.location = ownerDoc.location.href; } } catch (e) { Components.utils.reportError("Couldn't get ssl_override pref: " + e); } window.openDialog('chrome://pippki/content/exceptionDialog.xul', '','chrome,centerscreen,modal', params); // If the user added the exception cert, attempt to reload the page if (params.exceptionAdded) - errorDoc.location.reload(); + ownerDoc.location.reload(); } - else if (ot == errorDoc.getElementById('getMeOutOfHereButton')) { + else if (ot == ownerDoc.getElementById('getMeOutOfHereButton')) { getMeOutOfHere(); } } - else if (/^about:blocked/.test(errorDoc.documentURI)) { + else if (/^about:blocked/.test(ownerDoc.documentURI)) { // The event came from a button on a malware/phishing block page // First check whether it's malware or phishing, so that we can // use the right strings/links - var isMalware = /e=malwareBlocked/.test(errorDoc.documentURI); - - if (ot == errorDoc.getElementById('getMeOutButton')) { + var isMalware = /e=malwareBlocked/.test(ownerDoc.documentURI); + + if (ot == ownerDoc.getElementById('getMeOutButton')) { getMeOutOfHere(); } - else if (ot == errorDoc.getElementById('reportButton')) { + else if (ot == ownerDoc.getElementById('reportButton')) { // This is the "Why is this site blocked" button. For malware, // we can fetch a site-specific report, for phishing, we redirect // to the generic page describing phishing protection. if (isMalware) { // Get the stop badware "why is this blocked" report url, // append the current url, and go there. try { let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true); - reportURL += errorDoc.location.href; + reportURL += ownerDoc.location.href; content.location = reportURL; } catch (e) { Components.utils.reportError("Couldn't get malware report URL: " + e); } } else { // It's a phishing site, not malware try { content.location = formatURL("browser.safebrowsing.warning.infoURL", true); } catch (e) { Components.utils.reportError("Couldn't get phishing info URL: " + e); } } } - else if (ot == errorDoc.getElementById('ignoreWarningButton')) { + else if (ot == ownerDoc.getElementById('ignoreWarningButton')) { // Allow users to override and continue through to the site, // but add a notify bar as a reminder, so that they don't lose // track after, e.g., tab switching. gBrowser.loadURIWithFlags(content.location.href, nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER, null, null, null); Services.perms.add(makeURI(content.location.href), "safe-browsing", @@ -2838,33 +2833,41 @@ function BrowserOnClick(event) { title, value, "chrome://global/skin/icons/blacklist_favicon.png", notificationBox.PRIORITY_CRITICAL_HIGH, buttons ); } } - else if (/^about:home$/i.test(errorDoc.documentURI)) { - if (ot == errorDoc.getElementById("restorePreviousSession")) { + else if (/^about:home$/i.test(ownerDoc.documentURI)) { + if (ot == ownerDoc.getElementById("restorePreviousSession")) { let ss = Cc["@mozilla.org/browser/sessionstore;1"]. getService(Ci.nsISessionStore); if (ss.canRestoreLastSession) ss.restoreLastSession(); - errorDoc.getElementById("sessionRestoreContainer").hidden = true; + ownerDoc.getElementById("launcher").removeAttribute("session"); + } + else if (ot == ownerDoc.getElementById("bookmarks")) { + PlacesCommandHook.showPlacesOrganizer("AllBookmarks"); + } + else if (ot == ownerDoc.getElementById("history")) { + PlacesCommandHook.showPlacesOrganizer("History"); } - else if (ot == errorDoc.getElementById("pairDeviceLink")) { - if (Services.prefs.prefHasUserValue("services.sync.username")) { - gSyncUI.openAddDevice(); - } else { - gSyncUI.openSetup("pair"); - } + else if (ot == ownerDoc.getElementById("settings")) { + openPreferences(); + } + else if (ot == ownerDoc.getElementById("addons")) { + BrowserOpenAddonsMgr(); } - else if (ot == errorDoc.getElementById("setupSyncLink")) { - gSyncUI.openSetup(null); + else if (ot == ownerDoc.getElementById("downloads")) { + BrowserDownloadsUI(); + } + else if (ot == ownerDoc.getElementById("sync")) { + openPreferences("paneSync"); } } } /** * Re-direct the browser to a known-safe page. This function is * used when, for example, the user browses to a known malware page * and is presented with about:blocked. The "Get me out of here!" @@ -5115,16 +5118,17 @@ var TabsProgressListener = { // Attach a listener to watch for "click" events bubbling up from error // pages and other similar page. This lets us fix bugs like 401575 which // require error page UI to do privileged things, without letting error // pages have any privilege themselves. // We can't look for this during onLocationChange since at that point the // document URI is not yet the about:-uri of the error page. if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && + Components.isSuccessCode(aStatus) && /^about:/.test(aWebProgress.DOMWindow.document.documentURI)) { aBrowser.addEventListener("click", BrowserOnClick, false); aBrowser.addEventListener("pagehide", function () { aBrowser.removeEventListener("click", BrowserOnClick, false); aBrowser.removeEventListener("pagehide", arguments.callee, true); }, true); // We also want to make changes to page UI for unprivileged about pages.
--- a/browser/base/content/newtab/drop.js +++ b/browser/base/content/newtab/drop.js @@ -89,17 +89,21 @@ let gDrop = { if (draggedSite) { // Pin the dragged site at its new place. if (aCell != draggedSite.cell) draggedSite.pin(index); } else { // A new link was dragged onto the grid. Create it by pinning its URL. let dt = aEvent.dataTransfer; let [url, title] = dt.getData("text/x-moz-url").split(/[\r\n]+/); - gPinnedLinks.pin({url: url, title: title}, index); + let link = {url: url, title: title}; + gPinnedLinks.pin(link, index); + + // Make sure the newly added link is not blocked. + gBlockedLinks.unblock(link); } }, /** * Time a rearrange with a little delay. * @param aCell The drop target cell. */ _delayedRearrange: function Drop_delayedRearrange(aCell) {
--- a/browser/base/content/nsContextMenu.js +++ b/browser/base/content/nsContextMenu.js @@ -87,16 +87,17 @@ nsContextMenu.prototype = { this.isFrameImage = document.getElementById("isFrameImage"); this.ellipsis = "\u2026"; try { this.ellipsis = gPrefService.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data; } catch (e) { } this.isTextSelected = this.isTextSelection(); this.isContentSelected = this.isContentSelection(); + this.onPlainTextLink = false; // Initialize (disable/remove) menu items. this.initItems(); }, hiding: function CM_hiding() { InlineSpellCheckerUI.clearSuggestionsFromMenu(); InlineSpellCheckerUI.clearDictionaryListFromMenu(); @@ -127,17 +128,16 @@ nsContextMenu.prototype = { getProtocolHandlerInfo("mailto"); isMailtoInternal = (!mailtoHandler.alwaysAskBeforeHandling && mailtoHandler.preferredAction == Ci.nsIHandlerInfo.useHelperApp && (mailtoHandler.preferredApplicationHandler instanceof Ci.nsIWebHandlerApp)); } // Time to do some bad things and see if we've highlighted a URL that // isn't actually linked. - var onPlainTextLink = false; if (this.isTextSelected && !this.onLink) { // Ok, we have some text, let's figure out if it looks like a URL. let selection = document.commandDispatcher.focusedWindow .getSelection(); let linkText = selection.toString().trim(); let uri; if (/^(?:https?|ftp):/i.test(linkText)) { try { @@ -185,24 +185,24 @@ nsContextMenu.prototype = { uri = uriFixup.createFixupURI(linkText, uriFixup.FIXUP_FLAG_NONE); } catch (ex) {} } } if (uri && uri.host) { this.linkURI = uri; this.linkURL = this.linkURI.spec; - onPlainTextLink = true; + this.onPlainTextLink = true; } } - var shouldShow = this.onSaveableLink || isMailtoInternal || onPlainTextLink; + var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink; this.showItem("context-openlink", shouldShow); this.showItem("context-openlinkintab", shouldShow); - this.showItem("context-openlinkincurrent", onPlainTextLink); + this.showItem("context-openlinkincurrent", this.onPlainTextLink); this.showItem("context-sep-open", shouldShow); }, initNavigationItems: function CM_initNavigationItems() { var shouldShow = !(this.isContentSelected || this.onLink || this.onImage || this.onCanvas || this.onVideo || this.onAudio || this.onTextInput); this.showItem("context-back", shouldShow); @@ -217,19 +217,19 @@ nsContextMenu.prototype = { initSaveItems: function CM_initSaveItems() { var shouldShow = !(this.onTextInput || this.onLink || this.isContentSelected || this.onImage || this.onCanvas || this.onVideo || this.onAudio); this.showItem("context-savepage", shouldShow); this.showItem("context-sendpage", shouldShow); - // Save+Send link depends on whether we're in a link. - this.showItem("context-savelink", this.onSaveableLink); - this.showItem("context-sendlink", this.onSaveableLink); + // Save+Send link depends on whether we're in a link, or selected text matches valid URL pattern. + this.showItem("context-savelink", this.onSaveableLink || this.onPlainTextLink); + this.showItem("context-sendlink", this.onSaveableLink || this.onPlainTextLink); // Save image depends on having loaded its content, video and audio don't. this.showItem("context-saveimage", this.onLoadedImage || this.onCanvas); this.showItem("context-savevideo", this.onVideo); this.showItem("context-saveaudio", this.onAudio); this.showItem("context-video-saveimage", this.onVideo); this.setItemAttr("context-savevideo", "disabled", !this.mediaURL); this.setItemAttr("context-saveaudio", "disabled", !this.mediaURL); @@ -305,17 +305,17 @@ nsContextMenu.prototype = { initMiscItems: function CM_initMiscItems() { var isTextSelected = this.isTextSelected; // Use "Bookmark This Link" if on a link. this.showItem("context-bookmarkpage", !(this.isContentSelected || this.onTextInput || this.onLink || this.onImage || this.onVideo || this.onAudio)); - this.showItem("context-bookmarklink", this.onLink && !this.onMailtoLink); + this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink) || this.onPlainTextLink); this.showItem("context-searchselect", isTextSelected); this.showItem("context-keywordfield", this.onTextInput && this.onKeywordField); this.showItem("frame", this.inFrame); this.showItem("frame-sep", this.inFrame && isTextSelected); // Hide menu entries for images, show otherwise if (this.inFrame) { @@ -1068,19 +1068,25 @@ nsContextMenu.prototype = { // kick off the channel with our proxy object as the listener channel.asyncOpen(new saveAsListener(), null); }, // Save URL of clicked-on link. saveLink: function() { var doc = this.target.ownerDocument; + var linkText; + // If selected text is found to match valid URL pattern. + if (this.onPlainTextLink) + linkText = document.commandDispatcher.focusedWindow.getSelection().toString().trim(); + else + linkText = this.linkText(); urlSecurityCheck(this.linkURL, doc.nodePrincipal); - this.saveHelper(this.linkURL, this.linkText(), null, true, doc); + this.saveHelper(this.linkURL, linkText, null, true, doc); }, sendLink: function() { // we don't know the title of the link so pass in an empty string MailIntegration.sendMessage( this.linkURL, "" ); }, // Backwards-compatibility wrapper @@ -1385,18 +1391,24 @@ nsContextMenu.prototype = { openUILinkIn(uri, where); }, bookmarkThisPage: function CM_bookmarkThisPage() { window.top.PlacesCommandHook.bookmarkPage(this.browser, PlacesUtils.bookmarksMenuFolderId, true); }, bookmarkLink: function CM_bookmarkLink() { + var linkText; + // If selected text is found to match valid URL pattern. + if (this.onPlainTextLink) + linkText = document.commandDispatcher.focusedWindow.getSelection().toString().trim(); + else + linkText = this.linkText(); window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId, this.linkURL, - this.linkText()); + linkText); }, addBookmarkForFrame: function CM_addBookmarkForFrame() { var doc = this.target.ownerDocument; var uri = doc.documentURIObject; var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri); if (itemId == -1) {
--- a/browser/base/content/test/browser_aboutHome.js +++ b/browser/base/content/test/browser_aboutHome.js @@ -5,19 +5,16 @@ registerCleanupFunction(function() { // Ensure we don't pollute prefs for next tests. try { Services.prefs.clearUserPref("network.cookies.cookieBehavior"); } catch (ex) {} try { Services.prefs.clearUserPref("network.cookie.lifetimePolicy"); } catch (ex) {} - try { - Services.prefs.clearUserPref("services.sync.username"); - } catch (ex) {} }); let gTests = [ { desc: "Check that rejecting cookies does not prevent page from working", setup: function () { @@ -107,131 +104,21 @@ let gTests = [ run: function () { let doc = gBrowser.selectedTab.linkedBrowser.contentDocument; let snippetsElt = doc.getElementById("snippets"); ok(snippetsElt, "Found snippets element"); is(snippetsElt.getElementsByTagName("span").length, 1, "A default snippet is visible."); - + let storage = getStorage(); + storage.removeItem("snippets"); executeSoon(runNextTest); } }, - -{ - desc: "Check sync links visibility before and after Sync setup", - setup: function () - { - try { - Services.prefs.clearUserPref("services.sync.username"); - } catch (ex) {} - Services.obs.notifyObservers(null, "weave:service:ready", null); - }, - run: function () - { - let doc = gBrowser.selectedTab.linkedBrowser.contentDocument; - let pairLink = doc.getElementById("pairDeviceLink"); - let setupLink = doc.getElementById("setupSyncLink"); - - ok(pairLink, "Found 'Pair Device' link"); - ok(setupLink, "Found 'Set Up Sync' link"); - ok(!pairLink.hidden, "'Pair' link is visible before setup"); - ok(!setupLink.hidden, "'Set Up' link is visible before setup"); - - Services.obs.notifyObservers(null, "weave:service:setup-complete", null); - - executeSoon(function () { - setupLink = doc.getElementById("setupSyncLink"); - ok(setupLink.hidden, "'Set Up' link is hidden after setup"); - ok(!pairLink.hidden, "'Pair' link is visible after setup"); - - executeSoon(runNextTest); - }); - } -}, - -{ - desc: "Check sync links visibility before and after Sync unlink", - setup: function () - { - Services.prefs.setCharPref("services.sync.username", "someuser@domain.com"); - Services.obs.notifyObservers(null, "weave:service:ready", null); - }, - run: function () - { - let doc = gBrowser.selectedTab.linkedBrowser.contentDocument; - let pairLink = doc.getElementById("pairDeviceLink"); - let setupLink = doc.getElementById("setupSyncLink"); - - ok(!pairLink.hidden, "'Pair' link is visible before unlink"); - ok(setupLink.hidden, "'Set Up' link is hidden before unlink"); - - Services.obs.notifyObservers(null, "weave:service:start-over", null); - - executeSoon(function () { - setupLink = doc.getElementById("setupSyncLink"); - ok(!setupLink.hidden, "'Set Up' link is visible after unlink"); - ok(!pairLink.hidden, "'Pair' link is visible after unlink"); - executeSoon(runNextTest); - }); - } -}, - -{ - desc: "Check Pair Device link opens correct dialog with Sync account ", - setup: function () - { - Services.prefs.setCharPref("services.sync.username", "someuser@domain.com"); - Services.obs.notifyObservers(null, "weave:service:ready", null); - }, - run: function () - { - expectDialogWindow("Sync:AddDevice"); - let browser = gBrowser.selectedTab.linkedBrowser; - let button = browser.contentDocument.getElementById("pairDeviceLink"); - EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow); - } -}, - -{ - desc: "Check Pair Device link opens correct dialog without Sync account", - setup: function () - { - try { - Services.prefs.clearUserPref("services.sync.username"); - } catch (ex) {} - Services.obs.notifyObservers(null, "weave:service:ready", null); - }, - run: function () - { - expectDialogWindow("Weave:AccountSetup"); - let browser = gBrowser.selectedTab.linkedBrowser; - let button = browser.contentDocument.getElementById("pairDeviceLink"); - EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow); - } -}, - -{ - desc: "Check Sync Setup link opens correct dialog (without Sync account)", - setup: function () - { - try { - Services.prefs.clearUserPref("services.sync.username"); - } catch (ex) {} - Services.obs.notifyObservers(null, "weave:service:ready", null); - }, - run: function () - { - expectDialogWindow("Weave:AccountSetup"); - let browser = gBrowser.selectedTab.linkedBrowser; - let button = browser.contentDocument.getElementById("setupSyncLink"); - EventUtils.sendMouseEvent({type: "click"}, button, browser.contentWindow); - } -}, ]; function test() { waitForExplicitFinish(); // browser-chrome test harness inits browser specifying an hardcoded page // and this causes nsIBrowserHandler.defaultArgs to not be evaluated since @@ -267,32 +154,16 @@ function runNextTest() executeSoon(test.run); }, true); } else { finish(); } } -function expectDialogWindow(expectedDialog) { - Services.ww.registerNotification(function onWindow(subject, topic) { - let win = subject.QueryInterface(Components.interfaces.nsIDOMWindow); - win.addEventListener("load", function onLoad() { - win.removeEventListener("load", onLoad, false); - let wintype = win.document.documentElement.getAttribute("windowtype"); - if (topic == "domwindowopened" && wintype == expectedDialog) { - Services.ww.unregisterNotification(onWindow); - // Clean up dialog. - win.close(); - executeSoon(runNextTest); - } - }, false); - }); -} - function getStorage() { let aboutHomeURI = Services.io.newURI("moz-safe-about:home", null, null); let principal = Components.classes["@mozilla.org/scriptsecuritymanager;1"]. getService(Components.interfaces.nsIScriptSecurityManager). getCodebasePrincipal(Services.io.newURI("about:home", null, null)); let dsm = Components.classes["@mozilla.org/dom/storagemanager;1"]. getService(Components.interfaces.nsIDOMStorageManager);
--- a/browser/base/content/test/newtab/Makefile.in +++ b/browser/base/content/test/newtab/Makefile.in @@ -20,13 +20,14 @@ include $(topsrcdir)/config/rules.mk browser_newtab_reset.js \ browser_newtab_tabsync.js \ browser_newtab_unpin.js \ browser_newtab_bug722273.js \ browser_newtab_bug723102.js \ browser_newtab_bug723121.js \ browser_newtab_bug725996.js \ browser_newtab_bug734043.js \ + browser_newtab_bug735987.js \ head.js \ $(NULL) libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/base/content/test/newtab/browser_newtab_block.js +++ b/browser/base/content/test/newtab/browser_newtab_block.js @@ -20,41 +20,41 @@ function runTests() { yield blockCell(cells[4]); checkGrid("0,1,2,3,6,7,8,9,"); yield blockCell(cells[4]); checkGrid("0,1,2,3,7,8,9,,"); // we removed a pinned site - reset(); + yield restore(); setLinks("0,1,2,3,4,5,6,7,8"); setPinnedLinks(",1"); yield addNewTabPageTab(); checkGrid("0,1p,2,3,4,5,6,7,8"); yield blockCell(cells[1]); checkGrid("0,2,3,4,5,6,7,8,"); // we remove the last site on the grid (which is pinned) and expect the gap // to be re-filled and the new site to be unpinned - reset(); + yield restore(); setLinks("0,1,2,3,4,5,6,7,8,9"); setPinnedLinks(",,,,,,,,8"); yield addNewTabPageTab(); checkGrid("0,1,2,3,4,5,6,7,8p"); yield blockCell(cells[8]); checkGrid("0,1,2,3,4,5,6,7,9"); // we remove the first site on the grid with the last one pinned. all cells // but the last one should shift to the left and a new site fades in - reset(); + yield restore(); setLinks("0,1,2,3,4,5,6,7,8,9"); setPinnedLinks(",,,,,,,,8"); yield addNewTabPageTab(); checkGrid("0,1,2,3,4,5,6,7,8p"); yield blockCell(cells[0]); checkGrid("1,2,3,4,5,6,7,9,8p");
--- a/browser/base/content/test/newtab/browser_newtab_bug734043.js +++ b/browser/base/content/test/newtab/browser_newtab_bug734043.js @@ -1,30 +1,26 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ function runTests() { - // TODO Bug 735166 - Intermittent timeout in browser_newtab_bug734043.js - return; - setLinks("0,1,2,3,4,5,6,7,8"); setPinnedLinks(""); yield addNewTabPageTab(); + checkGrid("0,1,2,3,4,5,6,7,8"); let receivedError = false; let block = cw.document.querySelector(".newtab-control-block"); function onError() { receivedError = true; } cw.addEventListener("error", onError); - for (let i = 0; i < 3; i++) { + for (let i = 0; i < 3; i++) EventUtils.synthesizeMouseAtCenter(block, {}, cw); - yield executeSoon(TestRunner.next); - } yield whenPagesUpdated(); ok(!receivedError, "we got here without any errors"); cw.removeEventListener("error", onError); }
new file mode 100644 --- /dev/null +++ b/browser/base/content/test/newtab/browser_newtab_bug735987.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +function runTests() { + setLinks("0,1,2,3,4,5,6,7,8"); + setPinnedLinks(""); + + yield addNewTabPageTab(); + checkGrid("0,1,2,3,4,5,6,7,8"); + + yield simulateDrop(cells[1]); + checkGrid("0,99p,1,2,3,4,5,6,7"); + + yield blockCell(cells[1]); + checkGrid("0,1,2,3,4,5,6,7,8"); + + yield simulateDrop(cells[1]); + checkGrid("0,99p,1,2,3,4,5,6,7"); + + yield blockCell(cells[1]); + checkGrid("0,1,2,3,4,5,6,7,8"); +}
--- a/browser/base/content/test/newtab/head.js +++ b/browser/base/content/test/newtab/head.js @@ -5,18 +5,16 @@ const PREF_NEWTAB_ENABLED = "browser.new Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true); let tmp = {}; Cu.import("resource:///modules/NewTabUtils.jsm", tmp); let NewTabUtils = tmp.NewTabUtils; registerCleanupFunction(function () { - reset(); - while (gBrowser.tabs.length > 1) gBrowser.removeTab(gBrowser.tabs[1]); Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED); }); /** * Global variables that are accessed by tests. @@ -52,18 +50,39 @@ let TestRunner = { /** * Runs the next available test or finishes if there's no test left. */ next: function () { try { TestRunner._iter.next(); } catch (e if e instanceof StopIteration) { - finish(); + TestRunner.finish(); } + }, + + /** + * Finishes all tests and cleans up. + */ + finish: function () { + function cleanupAndFinish() { + // Restore the old provider. + NewTabUtils.links._provider = originalProvider; + + whenPagesUpdated(finish); + NewTabUtils.restore(); + } + + let callbacks = NewTabUtils.links._populateCallbacks; + let numCallbacks = callbacks.length; + + if (numCallbacks) + callbacks.splice(0, numCallbacks, cleanupAndFinish); + else + cleanupAndFinish(); } }; /** * Allows to provide a list of links that is used to construct the grid. * @param aLinksPattern the pattern (see below) * * Example: setLinks("1,2,3") @@ -101,23 +120,21 @@ function setPinnedLinks(aLinksPattern) { pinnedLinks[index] = link; }); // Inject the list of pinned links to work with. NewTabUtils.pinnedLinks._links = pinnedLinks; } /** - * Resets the lists of blocked and pinned links and clears the storage. + * Restore the grid state. */ -function reset() { - NewTabUtils.reset(); - - // Restore the old provider to prevent memory leaks. - NewTabUtils.links._provider = originalProvider; +function restore() { + whenPagesUpdated(); + NewTabUtils.restore(); } /** * Creates a new tab containing 'about:newtab'. */ function addNewTabPageTab() { let tab = gBrowser.selectedTab = gBrowser.addTab("about:newtab"); let browser = tab.linkedBrowser; @@ -264,21 +281,21 @@ function simulateDrop(aDropTarget, aDrag if (aDragSource) cw.gDrag.end(aDragSource.site); } /** * Resumes testing when all pages have been updated. */ -function whenPagesUpdated() { +function whenPagesUpdated(aCallback) { let page = { update: function () { NewTabUtils.allPages.unregister(this); - executeSoon(TestRunner.next); + executeSoon(aCallback || TestRunner.next); } }; NewTabUtils.allPages.register(page); registerCleanupFunction(function () { NewTabUtils.allPages.unregister(page); }); }
--- a/browser/base/content/test/subtst_contextmenu.html +++ b/browser/base/content/test/subtst_contextmenu.html @@ -53,11 +53,13 @@ Browser context menu subtest. <menuitem label="Bogus item"></menuitem> </menu> <menu> </menu> <menuitem label="Hidden item" hidden></menuitem> <menuitem></menuitem> </menu> </div> +<div id="test-select-text">Lorem ipsum dolor sit amet, consectetuer adipiscing elit.</div> +<div id="test-select-text-link">http://mozilla.com</div> </body> </html>
--- a/browser/base/content/test/test_contextmenu.html +++ b/browser/base/content/test/test_contextmenu.html @@ -15,16 +15,17 @@ Browser context menu tests. <pre id="test"> <script class="testbody" type="text/javascript"> /** Test for Login Manager: multiple login autocomplete. **/ netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); const Cc = Components.classes; const Ci = Components.interfaces; function openContextMenuFor(element, shiftkey, shouldWaitForFocus) { // Context menu should be closed before we open it again. is(contextMenu.state, "closed", "checking if popup is closed"); @@ -67,16 +68,26 @@ function invokeItemAction(generatedItemI { var item = contextMenu.getElementsByAttribute("generateditemid", generatedItemId)[0]; ok(item, "Got generated XUL menu item"); item.doCommand(); ok(!pagemenu.hasAttribute("hopeless"), "attribute got removed"); } +function selectText(element) { + // Clear any previous selections before selecting new element. + subwindow.getSelection().removeAllRanges(); + + var div = subwindow.document.createRange(); + div.setStartBefore(element); + div.setEndAfter(element); + subwindow.getSelection().addRange(div); +} + function getVisibleMenuItems(aMenu, aData) { var items = []; var accessKeys = {}; for (var i = 0; i < aMenu.childNodes.length; i++) { var item = aMenu.childNodes[i]; if (item.hidden) continue; @@ -662,24 +673,62 @@ function runTest(testNum) { "---", null, "context-viewbgimage", false, "context-selectall", true, "---", null, "context-viewsource", true, "context-viewinfo", true ].concat(inspectItems)); closeContextMenu(); + selectText(selecttext); // Select text prior to opening context menu. + openContextMenuFor(selecttext); // Invoke context menu for next test. + return; + + case 22: + // Context menu for selected text + if (Services.appinfo.OS == "Darwin") { + // This test is only enabled on Mac due to bug 736399. + checkContextMenu(["context-copy", true, + "context-selectall", true, + "---", null, + "context-searchselect", true, + "context-viewpartialsource-selection", true + ].concat(inspectItems)); + } + closeContextMenu(); + selectText(selecttextlink); // Select text prior to opening context menu. + openContextMenuFor(selecttextlink); // Invoke context menu for next test. + return; + + case 23: + // Context menu for selected text which matches valid URL pattern + if (Services.appinfo.OS == "Darwin") { + // This test is only enabled on Mac due to bug 736399. + checkContextMenu(["context-openlinkincurrent", true, + "context-openlinkintab", true, + "context-openlink", true, + "---", null, + "context-bookmarklink", true, + "context-savelink", true, + "context-sendlink", true, + "context-copy", true, + "context-selectall", true, + "---", null, + "context-searchselect", true, + "context-viewpartialsource-selection", true + ].concat(inspectItems)); + } + closeContextMenu(); subwindow.close(); SimpleTest.finish(); return; /* * Other things that would be nice to test: - * - selected text * - spelling / misspelled word (in text input?) * - check state of disabled items * - test execution of menu items (maybe as a separate test?) */ default: ok(false, "Unexpected invocation of test #" + testNum); subwindow.close(); @@ -729,16 +778,18 @@ function startTest() { video_in_iframe = subwindow.document.getElementById("test-video-in-iframe").contentDocument.getElementsByTagName("video")[0]; video_in_iframe.pause(); image_in_iframe = subwindow.document.getElementById("test-image-in-iframe").contentDocument.getElementsByTagName("img")[0]; textarea = subwindow.document.getElementById("test-textarea"); contenteditable = subwindow.document.getElementById("test-contenteditable"); contenteditable.focus(); // content editable needs to be focused to enable spellcheck inputspell = subwindow.document.getElementById("test-input-spellcheck"); pagemenu = subwindow.document.getElementById("test-pagemenu"); + selecttext = subwindow.document.getElementById("test-select-text"); + selecttextlink = subwindow.document.getElementById("test-select-text-link"); contextMenu.addEventListener("popupshown", function() { runTest(++testNum); }, false); runTest(1); } // We open this in a separate window, because the Mochitests run inside a frame. // The frame causes an extra menu item, and prevents running the test // standalone (ie, clicking the test name in the Mochitest window) to see
--- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -105,21 +105,43 @@ function getBoolPref(prefname, def) try { return Services.prefs.getBoolPref(prefname); } catch(er) { return def; } } -// openUILink handles clicks on UI elements that cause URLs to load. -function openUILink( url, e, ignoreButton, ignoreAlt, allowKeywordFixup, postData, referrerUrl ) -{ - var where = whereToOpenLink(e, ignoreButton, ignoreAlt); - openUILinkIn(url, where, allowKeywordFixup, postData, referrerUrl); +/* openUILink handles clicks on UI elements that cause URLs to load. + * + * As the third argument, you may pass an object with the same properties as + * accepted by openUILinkIn, plus "ignoreButton" and "ignoreAlt". + */ +function openUILink(url, event, aIgnoreButton, aIgnoreAlt, aAllowThirdPartyFixup, + aPostData, aReferrerURI) { + let params; + + if (aIgnoreButton && typeof aIgnoreButton == "object") { + params = aIgnoreButton; + + // don't forward "ignoreButton" and "ignoreAlt" to openUILinkIn + aIgnoreButton = params.ignoreButton; + aIgnoreAlt = params.ignoreAlt; + delete params.ignoreButton; + delete params.ignoreAlt; + } else { + params = { + allowThirdPartyFixup: aAllowThirdPartyFixup, + postData: aPostData, + referrerURI: aReferrerURI + }; + } + + let where = whereToOpenLink(event, aIgnoreButton, aIgnoreAlt); + openUILinkIn(url, where, params); } /* whereToOpenLink() looks at an event to decide where to open a link. * * The event may be a mouse event (click, double-click, middle-click) or keypress event (enter). * * On Windows, the modifiers are:
--- a/browser/base/jar.mn +++ b/browser/base/jar.mn @@ -11,23 +11,31 @@ browser.jar: % overlay chrome://global/content/viewSource.xul chrome://browser/content/viewSourceOverlay.xul % overlay chrome://global/content/viewPartialSource.xul chrome://browser/content/viewSourceOverlay.xul % style chrome://global/content/customizeToolbar.xul chrome://browser/content/browser.css % style chrome://global/content/customizeToolbar.xul chrome://browser/skin/ * content/browser/aboutDialog.xul (content/aboutDialog.xul) * content/browser/aboutDialog.js (content/aboutDialog.js) * content/browser/aboutDialog.css (content/aboutDialog.css) * content/browser/aboutRobots.xhtml (content/aboutRobots.xhtml) -* content/browser/aboutHome.xhtml (content/aboutHome.xhtml) -* content/browser/aboutHome.js (content/aboutHome.js) -* content/browser/aboutHome.css (content/aboutHome.css) - content/browser/aboutHome-restore-icon.png (content/aboutHome-restore-icon.png) - content/browser/aboutHome-restore-icon-small.png (content/aboutHome-restore-icon-small.png) - content/browser/aboutHome-snippet1.png (content/aboutHome-snippet1.png) - content/browser/aboutHome-snippet2.png (content/aboutHome-snippet2.png) +* content/browser/abouthome/aboutHome.xhtml (content/abouthome/aboutHome.xhtml) +* content/browser/abouthome/aboutHome.js (content/abouthome/aboutHome.js) +* content/browser/abouthome/aboutHome.css (content/abouthome/aboutHome.css) + content/browser/abouthome/snippet1.png (content/abouthome/snippet1.png) + content/browser/abouthome/snippet2.png (content/abouthome/snippet2.png) + content/browser/abouthome/bookmarks.png (content/abouthome/bookmarks.png) + content/browser/abouthome/history.png (content/abouthome/history.png) + content/browser/abouthome/settings.png (content/abouthome/settings.png) + content/browser/abouthome/addons.png (content/abouthome/addons.png) + content/browser/abouthome/downloads.png (content/abouthome/downloads.png) + content/browser/abouthome/sync.png (content/abouthome/sync.png) + content/browser/abouthome/restore.png (content/abouthome/restore.png) + content/browser/abouthome/restore-large.png (content/abouthome/restore-large.png) + content/browser/abouthome/mozilla.png (content/abouthome/mozilla.png) + content/browser/abouthome/noise.png (content/abouthome/noise.png) content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png) content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png) * content/browser/browser.css (content/browser.css) * content/browser/browser.js (content/browser.js) * content/browser/browser.xul (content/browser.xul) * content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml) * content/browser/content.js (content/content.js) * content/browser/newtab/newTab.xul (content/newtab/newTab.xul)
--- a/browser/components/about/AboutRedirector.cpp +++ b/browser/components/about/AboutRedirector.cpp @@ -97,17 +97,17 @@ static RedirEntry kRedirMap[] = { { "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml", nsIAboutModule::ALLOW_SCRIPT }, #ifdef MOZ_SERVICES_SYNC { "sync-progress", "chrome://browser/content/sync/progress.xhtml", nsIAboutModule::ALLOW_SCRIPT }, { "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul", nsIAboutModule::ALLOW_SCRIPT }, #endif - { "home", "chrome://browser/content/aboutHome.xhtml", + { "home", "chrome://browser/content/abouthome/aboutHome.xhtml", nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT | nsIAboutModule::ALLOW_SCRIPT }, { "newtab", "chrome://browser/content/newtab/newTab.xul", nsIAboutModule::ALLOW_SCRIPT }, { "permissions", "chrome://browser/content/preferences/aboutPermissions.xul", nsIAboutModule::ALLOW_SCRIPT }, }; static const int kRedirTotal = NS_ARRAY_LENGTH(kRedirMap);
--- a/browser/components/nsBrowserContentHandler.js +++ b/browser/components/nsBrowserContentHandler.js @@ -882,17 +882,17 @@ let AboutHomeUtils = { name: defaultEngine.name , searchUrl: submission.uri.spec } this._storage.setItem("search-engine", JSON.stringify(engine)); }, loadSnippetsURL: function AHU_loadSnippetsURL() { - const STARTPAGE_VERSION = 1; + const STARTPAGE_VERSION = 2; let updateURL = Services.prefs .getCharPref(this.SNIPPETS_URL_PREF) .replace("%STARTPAGE_VERSION%", STARTPAGE_VERSION); updateURL = Services.urlFormatter.formatURL(updateURL); this._storage.setItem("snippets-update-url", updateURL); }, };
--- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -63,17 +63,17 @@ XPCOMUtils.defineLazyGetter(this, "Place Cu.import("resource://gre/modules/PlacesUtils.jsm"); return PlacesUtils; }); XPCOMUtils.defineLazyModuleGetter(this, "KeywordURLResetPrompter", "resource:///modules/KeywordURLResetPrompter.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "webappsUI", - "resource://gre/modules/webappsUI.jsm"); + "resource:///modules/webappsUI.jsm"); const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; const PREF_PLUGINS_UPDATEURL = "plugins.update.url"; // We try to backup bookmarks at idle times, to avoid doing that at shutdown. // Number of idle seconds before trying to backup bookmarks. 15 minutes. const BOOKMARKS_BACKUP_IDLE_TIME = 15 * 60; // Minimum interval in milliseconds between backups.
--- a/browser/components/places/tests/unit/test_bookmarksRestoreNotification.js +++ b/browser/components/places/tests/unit/test_bookmarksRestoreNotification.js @@ -226,83 +226,18 @@ var tests = [ this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile); this.file.append("this file doesn't exist because nobody created it"); try { importer.importHTMLFromFile(this.file, true); do_throw(" Restore should have failed"); } catch (e) {} } - }, - - { - desc: "HTML restore into folder: normal restore should succeed", - currTopic: NSIOBSERVER_TOPIC_BEGIN, - finalTopic: NSIOBSERVER_TOPIC_SUCCESS, - data: NSIOBSERVER_DATA_HTML, - run: function () { - this.file = createFile("bookmarks-test_restoreNotification.html"); - addBookmarks(); - importer.exportHTMLToFile(this.file); - remove_all_bookmarks(); - this.folderId = bmsvc.createFolder(bmsvc.unfiledBookmarksFolder, - "test folder", - bmsvc.DEFAULT_INDEX); - print(" Sanity check: createFolder() should have succeeded"); - do_check_true(this.folderId > 0); - try { - importer.importHTMLFromFileToFolder(this.file, this.folderId, false); - } - catch (e) { - do_throw(" Restore should not have failed"); - } - } - }, + } - { - desc: "HTML restore into folder: empty file should succeed", - currTopic: NSIOBSERVER_TOPIC_BEGIN, - finalTopic: NSIOBSERVER_TOPIC_SUCCESS, - data: NSIOBSERVER_DATA_HTML, - run: function () { - this.file = createFile("bookmarks-test_restoreNotification.init.html"); - this.folderId = bmsvc.createFolder(bmsvc.unfiledBookmarksFolder, - "test folder", - bmsvc.DEFAULT_INDEX); - print(" Sanity check: createFolder() should have succeeded"); - do_check_true(this.folderId > 0); - try { - importer.importHTMLFromFileToFolder(this.file, this.folderId, false); - } - catch (e) { - do_throw(" Restore should not have failed"); - } - } - }, - - { - desc: "HTML restore into folder: nonexistent file should fail", - currTopic: NSIOBSERVER_TOPIC_BEGIN, - finalTopic: NSIOBSERVER_TOPIC_FAILED, - data: NSIOBSERVER_DATA_HTML, - run: function () { - this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile); - this.file.append("this file doesn't exist because nobody created it"); - this.folderId = bmsvc.createFolder(bmsvc.unfiledBookmarksFolder, - "test folder", - bmsvc.DEFAULT_INDEX); - print(" Sanity check: createFolder() should have succeeded"); - do_check_true(this.folderId > 0); - try { - importer.importHTMLFromFileToFolder(this.file, this.folderId, false); - do_throw(" Restore should have failed"); - } - catch (e) {} - } - } ]; // nsIObserver that observes bookmarks-restore-begin. var beginObserver = { observe: function _beginObserver(aSubject, aTopic, aData) { var test = tests[currTestIndex]; print(" Observed " + aTopic);
--- a/browser/components/places/tests/unit/test_bookmarks_html.js +++ b/browser/components/places/tests/unit/test_bookmarks_html.js @@ -213,68 +213,16 @@ add_test(function test_emptytitle_export waitForAsyncUpdates(function () { remove_all_bookmarks(); run_next_test(); }); }); }); -add_test(function test_import_preplaces_to_folder() -{ - // Test importing a pre-Places canonical bookmarks file to a specific folder. - // 1. create a new folder - // 2. import bookmarks.preplaces.html to that folder - // 3. run the test-suite - - let testFolder = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarksMenuFolderId, "test-import", - PlacesUtils.bookmarks.DEFAULT_INDEX - ); - try { - importer.importHTMLFromFileToFolder(gBookmarksFileOld, testFolder, false); - } catch(ex) { do_throw("couldn't import the exported file to folder: " + ex); } - - waitForAsyncUpdates(function () { - // Import-to-folder creates subfolders for toolbar and unfiled. - testImportedBookmarksToFolder(testFolder); - - waitForAsyncUpdates(function () { - remove_all_bookmarks(); - run_next_test(); - }); - }); -}); - -add_test(function test_import_to_folder() -{ - // Test importing a Places canonical bookmarks file to a specific folder. - // 1. create a new folder - // 2. import bookmarks.exported.html to that folder - // 3. run the test-suite - - let testFolder = PlacesUtils.bookmarks.createFolder( - PlacesUtils.bookmarksMenuFolderId, "test-import", - PlacesUtils.bookmarks.DEFAULT_INDEX - ); - try { - importer.importHTMLFromFileToFolder(gBookmarksFileNew, testFolder, false); - } catch(ex) { do_throw("couldn't import the exported file to folder: " + ex); } - - waitForAsyncUpdates(function () { - // Import-to-folder creates subfolders for toolbar and unfiled. - testImportedBookmarksToFolder(testFolder); - - waitForAsyncUpdates(function () { - remove_all_bookmarks(); - run_next_test(); - }); - }); -}); - add_test(function test_import_ontop() { // Test importing the exported bookmarks.html file *on top of* the existing // bookmarks. // 1. empty bookmarks db // 2. import the exported bookmarks file // 3. export to file // 3. import the exported bookmarks file
--- a/browser/components/sessionstore/src/nsSessionStartup.js +++ b/browser/components/sessionstore/src/nsSessionStartup.js @@ -67,17 +67,17 @@ /* :::::::: Constants and Helpers ::::::::::::::: */ const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cu = Components.utils; Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource:///modules/TelemetryStopwatch.jsm"); +Cu.import("resource://gre/modules/TelemetryStopwatch.jsm"); const STATE_RUNNING_STR = "running"; const MAX_FILE_SIZE = 100 * 1024 * 1024; // 100 megabytes function debug(aMsg) { aMsg = ("SessionStartup: " + aMsg).replace(/\S{80}/g, "$&\n"); Services.console.logStringMessage(aMsg); }
--- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -126,17 +126,17 @@ const TAB_EVENTS = ["TabOpen", "TabClose #endif Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Services.jsm"); // debug.js adds NS_ASSERT. cf. bug 669196 Cu.import("resource://gre/modules/debug.js"); Cu.import("resource:///modules/TelemetryTimestamps.jsm"); -Cu.import("resource:///modules/TelemetryStopwatch.jsm"); +Cu.import("resource://gre/modules/TelemetryStopwatch.jsm"); XPCOMUtils.defineLazyGetter(this, "NetUtil", function() { Cu.import("resource://gre/modules/NetUtil.jsm"); return NetUtil; }); XPCOMUtils.defineLazyGetter(this, "ScratchpadManager", function() { Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
--- a/browser/components/sessionstore/test/browser_480148.js +++ b/browser/components/sessionstore/test/browser_480148.js @@ -31,132 +31,127 @@ * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ function test() { /** Test for Bug 484108 **/ - requestLongerTimeout(2); - waitForExplicitFinish(); + requestLongerTimeout(3); // builds the tests state based on a few parameters function buildTestState(num, selected, hidden) { let state = { windows: [ { "tabs": [], "selected": selected } ] }; while (num--) { state.windows[0].tabs.push({entries: [{url: "http://example.com/"}]}); let i = state.windows[0].tabs.length - 1; if (hidden.length > 0 && i == hidden[0]) { state.windows[0].tabs[i].hidden = true; hidden.splice(0, 1); } } return state; } - // builds an array of the indexs we expect to see in the order they get loaded - function buildExpectedOrder(num, selected, shown) { - // assume selected is 1-based index - selected--; - let expected = [selected]; - // fill left to selected if space - for (let i = selected - (shown - expected.length); i >= 0 && i < selected; i++) - expected.push(i); - // fill from left to right until right length or end - for (let i = selected + 1; expected.length < shown && i < num; i++) - expected.push(i); - // fill in the remaining - for (let i = 0; i < num; i++) { - if (expected.indexOf(i) == -1) { - expected.push(i); - } + let tests = [ + { testNum: 1, + totalTabs: 13, + selectedTab: 1, + shownTabs: 6, + hiddenTabs: [], + order: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] + }, + { testNum: 2, + totalTabs: 13, + selectedTab: 13, + shownTabs: 6, + hiddenTabs: [], + order: [12, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6] + }, + { testNum: 3, + totalTabs: 13, + selectedTab: 4, + shownTabs: 6, + hiddenTabs: [], + order: [3, 4, 5, 6, 7, 8, 0, 1, 2, 9, 10, 11, 12] + }, + { testNum: 4, + totalTabs: 13, + selectedTab: 11, + shownTabs: 6, + hiddenTabs: [], + order: [10, 7, 8, 9, 11, 12, 0, 1, 2, 3, 4, 5, 6] + }, + { testNum: 5, + totalTabs: 13, + selectedTab: 13, + shownTabs: 6, + hiddenTabs: [0, 4, 9], + order: [12, 6, 7, 8, 10, 11, 1, 2, 3, 5, 0, 4, 9] + }, + { testNum: 6, + totalTabs: 13, + selectedTab: 4, + shownTabs: 6, + hiddenTabs: [1, 7, 12], + order: [3, 4, 5, 6, 8, 9, 0, 2, 10, 11, 1, 7, 12] + }, + { testNum: 7, + totalTabs: 13, + selectedTab: 4, + shownTabs: 6, + hiddenTabs: [0, 1, 2], + order: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2] } - return expected; - } - - // the number of tests we're running - let numTests = 7; - let completedTests = 0; + ]; let tabMinWidth = parseInt(getComputedStyle(gBrowser.selectedTab, null).minWidth); + let testIndex = 0; - function runTest(testNum, totalTabs, selectedTab, shownTabs, hiddenTabs, order) { - let test = { - state: buildTestState(totalTabs, selectedTab, hiddenTabs), - numTabsToShow: shownTabs, - expectedOrder: order, - actualOrder: [], - windowWidth: null, - callback: null, - window: null, - - handleSSTabRestoring: function (aEvent) { - let tab = aEvent.originalTarget; - let tabbrowser = this.window.gBrowser; - let currentIndex = Array.indexOf(tabbrowser.tabs, tab); - this.actualOrder.push(currentIndex); + function runNextTest() { + if (tests.length == 0) { + finish(); + return; + } - if (this.actualOrder.length < this.state.windows[0].tabs.length) - return; - - // all of the tabs should be restoring or restored by now - is(this.actualOrder.length, this.state.windows[0].tabs.length, - "Test #" + testNum + ": Number of restored tabs is as expected"); - - is(this.actualOrder.join(" "), this.expectedOrder.join(" "), - "Test #" + testNum + ": 'visible' tabs restored first"); + info ("Starting test " + (++testIndex)); + let test = tests.shift(); + let state = buildTestState(test.totalTabs, test.selectedTab, test.hiddenTabs); + let tabbarWidth = Math.floor((test.shownTabs - 0.5) * tabMinWidth); + let win = openDialog(location, "_blank", "chrome,all,dialog=no"); + let actualOrder = []; - // cleanup - this.window.close(); - // if we're all done, explicitly finish - if (++completedTests == numTests) { - this.window.removeEventListener("load", this, false); - this.window.removeEventListener("SSTabRestoring", this, false); - finish(); - } - }, + win.addEventListener("SSTabRestoring", function onSSTabRestoring(aEvent) { + let tab = aEvent.originalTarget; + let currentIndex = Array.indexOf(win.gBrowser.tabs, tab); + actualOrder.push(currentIndex); + + if (actualOrder.length < state.windows[0].tabs.length) + return; - handleLoad: function (aEvent) { - let _this = this; - executeSoon(function () { - let extent = _this.window.outerWidth - _this.window.gBrowser.tabContainer.mTabstrip.scrollClientSize; - let windowWidth = _this.tabbarWidth + extent; - _this.window.resizeTo(windowWidth, _this.window.outerHeight); - ss.setWindowState(_this.window, JSON.stringify(_this.state), true); - }); - }, + // all of the tabs should be restoring or restored by now + is(actualOrder.length, state.windows[0].tabs.length, + "Test #" + testIndex + ": Number of restored tabs is as expected"); - // Implement nsIDOMEventListener for handling various window and tab events - handleEvent: function (aEvent) { - switch (aEvent.type) { - case "load": - this.handleLoad(aEvent); - break; - case "SSTabRestoring": - this.handleSSTabRestoring(aEvent); - break; - } - }, + is(actualOrder.join(" "), test.order.join(" "), + "Test #" + testIndex + ": 'visible' tabs restored first"); + + // Cleanup. + win.removeEventListener("SSTabRestoring", onSSTabRestoring, false); + win.close(); + executeSoon(runNextTest); + }, false); - // setup and actually run the test - run: function () { - this.tabbarWidth = Math.floor((this.numTabsToShow - 0.5) * tabMinWidth); - this.window = openDialog(location, "_blank", "chrome,all,dialog=no"); - this.window.addEventListener("SSTabRestoring", this, false); - this.window.addEventListener("load", this, false); - } - }; - test.run(); - } + win.addEventListener("load", function onLoad(aEvent) { + win.removeEventListener("load", onLoad, false); + executeSoon(function () { + let extent = win.outerWidth - win.gBrowser.tabContainer.mTabstrip.scrollClientSize; + let windowWidth = tabbarWidth + extent; + win.resizeTo(windowWidth, win.outerHeight); + ss.setWindowState(win, JSON.stringify(state), true); + }); + }, false); + }; - // actually create & run the tests - runTest(1, 13, 1, 6, [], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); - runTest(2, 13, 13, 6, [], [12, 7, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6]); - runTest(3, 13, 4, 6, [], [3, 4, 5, 6, 7, 8, 0, 1, 2, 9, 10, 11, 12]); - runTest(4, 13, 11, 6, [], [10, 7, 8, 9, 11, 12, 0, 1, 2, 3, 4, 5, 6]); - runTest(5, 13, 13, 6, [0, 4, 9], [12, 6, 7, 8, 10, 11, 1, 2, 3, 5, 0, 4, 9]); - runTest(6, 13, 4, 6, [1, 7, 12], [3, 4, 5, 6, 8, 9, 0, 2, 10, 11, 1, 7, 12]); - runTest(7, 13, 4, 6, [0, 1, 2], [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 1, 2]); - - // finish() is run by the last test to finish, so no cleanup down here + runNextTest(); }
--- a/browser/components/sessionstore/test/browser_624727.js +++ b/browser/components/sessionstore/test/browser_624727.js @@ -1,12 +1,14 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ function test() { + waitForExplicitFinish(); + let assertNumberOfTabs = function (num, msg) { is(gBrowser.tabs.length, num, msg); } let assertNumberOfPinnedTabs = function (num, msg) { is(gBrowser._numPinnedTabs, num, msg); } @@ -20,16 +22,18 @@ function test() { let [tab1, tab2] = gBrowser.tabs; let linkedBrowser = tab1.linkedBrowser; gBrowser.pinTab(tab1); gBrowser.pinTab(tab2); assertNumberOfPinnedTabs(2, "both tabs are now pinned"); // run the test - ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: "about:blank" }] }] })); - assertNumberOfTabs(1, "one tab left after setBrowserState()"); - assertNumberOfPinnedTabs(0, "there are no pinned tabs"); - is(gBrowser.tabs[0].linkedBrowser, linkedBrowser, "first tab's browser got re-used"); - - waitForExplicitFinish(); - waitForSaveState(finish); + waitForBrowserState( + { windows: [{ tabs: [{ url: "about:blank" }] }] }, + function () { + assertNumberOfTabs(1, "one tab left after setBrowserState()"); + assertNumberOfPinnedTabs(0, "there are no pinned tabs"); + is(gBrowser.tabs[0].linkedBrowser, linkedBrowser, "first tab's browser got re-used"); + finish(); + } + ); }
--- a/browser/components/sessionstore/test/browser_625016.js +++ b/browser/components/sessionstore/test/browser_625016.js @@ -9,20 +9,24 @@ function test() { // We'll test this by opening a new window, waiting for the save event, then // closing that window. We'll observe the "sessionstore-state-write" notification // and check that the state contains no _closedWindows. We'll then add a new // tab and make sure that the state following that was reset and the closed // window is now in _closedWindows. waitForExplicitFinish(); + requestLongerTimeout(2); // We speed up the interval between session saves to ensure that the test // runs quickly. - Services.prefs.setIntPref("browser.sessionstore.interval", 2000); + Services.prefs.setIntPref("browser.sessionstore.interval", 4000); + registerCleanupFunction(function () { + Services.prefs.clearUserPref("browser.sessionstore.interval"); + }); // We'll clear all closed windows to make sure our state is clean // forgetClosedWindow doesn't trigger a delayed save while (ss.getClosedWindowCount()) { ss.forgetClosedWindow(0); } is(ss.getClosedWindowCount(), 0, "starting with no closed windows"); @@ -92,17 +96,16 @@ function observe2(aSubject, aTopic, aDat // We'll open a tab, which should trigger another state save which would wipe // the _shouldRestore attribute from the closed window function openTab() { Services.obs.addObserver(observe2, "sessionstore-state-write", false); newTab = gBrowser.addTab("about:mozilla"); } function done() { - Services.prefs.clearUserPref("browser.sessionstore.interval"); gBrowser.removeTab(newTab); // The API still represents the closed window as closed, so we can clear it // with the API, but just to make sure... is(ss.getClosedWindowCount(), 1, "1 closed window according to API"); ss.forgetClosedWindow(0); executeSoon(finish); }
--- a/browser/components/sessionstore/test/browser_705597.js +++ b/browser/components/sessionstore/test/browser_705597.js @@ -2,56 +2,63 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ let tabState = { entries: [{url: "about:home", children: [{url: "about:mozilla"}]}] }; function test() { waitForExplicitFinish(); + requestLongerTimeout(2); + + Services.prefs.setIntPref("browser.sessionstore.interval", 4000); + registerCleanupFunction(function () { + Services.prefs.clearUserPref("browser.sessionstore.interval"); + }); let tab = gBrowser.addTab("about:blank"); - registerCleanupFunction(function () gBrowser.removeTab(tab)); let browser = tab.linkedBrowser; whenBrowserLoaded(browser, function () { ss.setTabState(tab, JSON.stringify(tabState)); let sessionHistory = browser.sessionHistory; let entry = sessionHistory.getEntryAtIndex(0, false); whenChildCount(entry, 1, function () { whenChildCount(entry, 2, function () { whenBrowserLoaded(browser, function () { let {entries} = JSON.parse(ss.getTabState(tab)); is(entries.length, 1, "tab has one history entry"); ok(!entries[0].children, "history entry has no subframes"); - finish(); + // Make sure that we reset the state. + let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank" }] }]}]}; + waitForBrowserState(blankState, finish); }); // reload the browser to deprecate the subframes browser.reload(); }); // create a dynamic subframe let doc = browser.contentDocument; let iframe = doc.createElement("iframe"); + doc.body.appendChild(iframe); iframe.setAttribute("src", "about:mozilla"); - doc.body.appendChild(iframe); }); }); } function whenBrowserLoaded(aBrowser, aCallback) { aBrowser.addEventListener("load", function onLoad() { aBrowser.removeEventListener("load", onLoad, true); executeSoon(aCallback); }, true); } function whenChildCount(aEntry, aChildCount, aCallback) { if (aEntry.childCount == aChildCount) aCallback(); else - executeSoon(function () whenChildCount(aEntry, aChildCount, aCallback)); + setTimeout(function () whenChildCount(aEntry, aChildCount, aCallback), 100); }
--- a/browser/components/sessionstore/test/browser_707862.js +++ b/browser/components/sessionstore/test/browser_707862.js @@ -2,55 +2,64 @@ http://creativecommons.org/publicdomain/zero/1.0/ */ let tabState = { entries: [{url: "about:home", children: [{url: "about:mozilla"}]}] }; function test() { waitForExplicitFinish(); + requestLongerTimeout(2); + + Services.prefs.setIntPref("browser.sessionstore.interval", 4000); + registerCleanupFunction(function () { + Services.prefs.clearUserPref("browser.sessionstore.interval"); + }); let tab = gBrowser.addTab("about:blank"); - registerCleanupFunction(function () gBrowser.removeTab(tab)); let browser = tab.linkedBrowser; whenBrowserLoaded(browser, function () { ss.setTabState(tab, JSON.stringify(tabState)); let sessionHistory = browser.sessionHistory; let entry = sessionHistory.getEntryAtIndex(0, false); whenChildCount(entry, 1, function () { whenChildCount(entry, 2, function () { whenBrowserLoaded(browser, function () { let sessionHistory = browser.sessionHistory; let entry = sessionHistory.getEntryAtIndex(0, false); - whenChildCount(entry, 0, finish); + whenChildCount(entry, 0, function () { + // Make sure that we reset the state. + let blankState = { windows: [{ tabs: [{ entries: [{ url: "about:blank" }] }]}]}; + waitForBrowserState(blankState, finish); + }); }); // reload the browser to deprecate the subframes browser.reload(); }); // create a dynamic subframe let doc = browser.contentDocument; let iframe = doc.createElement("iframe"); + doc.body.appendChild(iframe); iframe.setAttribute("src", "about:mozilla"); - doc.body.appendChild(iframe); }); }); } function whenBrowserLoaded(aBrowser, aCallback) { aBrowser.addEventListener("load", function onLoad() { aBrowser.removeEventListener("load", onLoad, true); executeSoon(aCallback); }, true); } function whenChildCount(aEntry, aChildCount, aCallback) { if (aEntry.childCount == aChildCount) aCallback(); else - executeSoon(function () whenChildCount(aEntry, aChildCount, aCallback)); + setTimeout(function () whenChildCount(aEntry, aChildCount, aCallback), 100); }
--- a/browser/components/tabview/content.js +++ b/browser/components/tabview/content.js @@ -89,21 +89,31 @@ let WindowMessageHandler = { // ---------- // Function: isDocumentLoaded // Checks if the currently active document is loaded. isDocumentLoaded: function WMH_isDocumentLoaded(cx) { let isLoaded = (content.document.readyState == "complete" && !webProgress.isLoadingDocument); sendAsyncMessage(cx.name, {isLoaded: isLoaded}); + }, + + // ---------- + // Function: isImageDocument + // Checks if the currently active document is an image document or not. + isImageDocument: function WMH_isImageDocument(cx) { + let isImageDocument = (content.document instanceof Ci.nsIImageDocument); + + sendAsyncMessage(cx.name, {isImageDocument: isImageDocument}); } }; // add message listeners addMessageListener("Panorama:isDocumentLoaded", WindowMessageHandler.isDocumentLoaded); +addMessageListener("Panorama:isImageDocument", WindowMessageHandler.isImageDocument); // ---------- // WebProgressListener // // Observe the web progress of content pages loaded into this browser. When the // state of a page changes we check if we're still allowed to store page // information permanently. let WebProgressListener = {
--- a/browser/components/tabview/groupitems.js +++ b/browser/components/tabview/groupitems.js @@ -229,20 +229,22 @@ function GroupItem(listOfEls, options) { // ___ app tabs: create app tab tray and populate it let appTabTrayContainer = iQ("<div/>") .addClass("appTabTrayContainer") .appendTo($container); this.$appTabTray = iQ("<div/>") .addClass("appTabTray") .appendTo(appTabTrayContainer); - AllTabs.tabs.forEach(function(xulTab) { + let pinnedTabCount = gBrowser._numPinnedTabs; + AllTabs.tabs.forEach(function (xulTab, index) { + // only adjust tray when it's the last app tab. if (xulTab.pinned) - self.addAppTab(xulTab, {dontAdjustTray: true}); - }); + this.addAppTab(xulTab, {dontAdjustTray: index + 1 < pinnedTabCount}); + }, this); // ___ Undo Close this.$undoContainer = null; this._undoButtonTimeoutId = null; // ___ Superclass initialization this._init($container[0]); @@ -753,17 +755,17 @@ GroupItem.prototype = Utils.extend(new I let self = this; let finalize = function () { self._children.forEach(function(child) { iQ(child.container).show(); }); UI.setActive(self); - self._sendToSubscribers("groupShown", { groupItemId: self.id }); + self._sendToSubscribers("groupShown"); }; let $container = iQ(this.container).show(); if (!options || !options.immediately) { $container.animate({ "-moz-transform": "scale(1)", "opacity": 1 @@ -922,17 +924,17 @@ GroupItem.prototype = Utils.extend(new I setTimeout(function() { self.$undoContainer.animate({ "-moz-transform": "scale(1)", "opacity": 1 }, { easing: "tabviewBounce", duration: 170, complete: function() { - self._sendToSubscribers("groupHidden", { groupItemId: self.id }); + self._sendToSubscribers("groupHidden"); } }); }, 50); // add click handlers this.$undoContainer.click(function(e) { // don't do anything if the close button is clicked. if (e.target == undoClose[0]) @@ -1047,17 +1049,17 @@ GroupItem.prototype = Utils.extend(new I (!GroupItems.getActiveGroupItem() && !item.tab.hidden)) UI.setActive(this); } if (!options.dontArrange) this.arrange({animate: !options.immediately}); this._unfreezeItemSize({dontArrange: true}); - this._sendToSubscribers("childAdded",{ groupItemId: this.id, item: item }); + this._sendToSubscribers("childAdded", { item: item }); UI.setReorderTabsOnHide(this); } catch(e) { Utils.log('GroupItem.add error', e); } }, // ---------- @@ -1150,17 +1152,17 @@ GroupItem.prototype = Utils.extend(new I (this._children.length == 0 && !gBrowser._numPinnedTabs && !item.isDragging)) { this._makeLastActiveGroupItemActive(); } else if (!options.dontArrange) { this.arrange({animate: !options.immediately}); this._unfreezeItemSize({dontArrange: true}); } - this._sendToSubscribers("childRemoved",{ groupItemId: this.id, item: item }); + this._sendToSubscribers("childRemoved", { item: item }); } catch(e) { Utils.log(e); } }, // ---------- // Function: removeAll // Removes all of the groupItem's children. @@ -1176,46 +1178,56 @@ GroupItem.prototype = Utils.extend(new I self.remove(child, newOptions); }); }, // ---------- // Adds the given xul:tab as an app tab in this group's apptab tray // // Parameters: + // xulTab - the xul:tab. // options - change how the app tab is added. // // Options: - // dontAdjustTray - (boolean) if true, the $appTabTray size is not adjusted, - // which means that the adjustAppTabTray() method is not - // called. + // position - the position of the app tab should be added to. + // dontAdjustTray - (boolean) if true, do not adjust the tray. addAppTab: function GroupItem_addAppTab(xulTab, options) { - let self = this; + GroupItems.getAppTabFavIconUrl(xulTab, function(iconUrl) { + let self = this; + let $appTab = iQ("<img>") + .addClass("appTabIcon") + .attr("src", iconUrl) + .data("xulTab", xulTab) + .mousedown(function GroupItem_addAppTab_onAppTabMousedown(event) { + // stop mousedown propagation to disable group dragging on app tabs + event.stopPropagation(); + }) + .click(function GroupItem_addAppTab_onAppTabClick(event) { + if (!Utils.isLeftClick(event)) + return; - let iconUrl = GroupItems.getAppTabFavIconUrl(xulTab); - let $appTab = iQ("<img>") - .addClass("appTabIcon") - .attr("src", iconUrl) - .data("xulTab", xulTab) - .appendTo(this.$appTabTray) - .mousedown(function onAppTabMousedown(event) { - // stop mousedown propagation to disable group dragging on app tabs - event.stopPropagation(); - }) - .click(function(event) { - if (!Utils.isLeftClick(event)) - return; + UI.setActive(self, { dontSetActiveTabInGroup: true }); + UI.goToTab(iQ(this).data("xulTab")); + }); + + if (options && "position" in options) { + let children = this.$appTabTray[0].childNodes; - UI.setActive(self, { dontSetActiveTabInGroup: true }); - UI.goToTab(iQ(this).data("xulTab")); - }); + if (options.position >= children.length) + $appTab.appendTo(this.$appTabTray); + else + this.$appTabTray[0].insertBefore($appTab[0], children[options.position]); + } else { + $appTab.appendTo(this.$appTabTray); + } + if (!options || !options.dontAdjustTray) + this.adjustAppTabTray(true); - // adjust the tray, if needed. - if (!options || !options.dontAdjustTray) - this.adjustAppTabTray(true); + this._sendToSubscribers("appTabIconAdded", { item: $appTab }); + }.bind(this)); }, // ---------- // Removes the given xul:tab as an app tab in this group's apptab tray removeAppTab: function GroupItem_removeAppTab(xulTab) { // remove the icon iQ(".appTabIcon", this.$appTabTray).each(function(icon) { let $icon = iQ(icon); @@ -2073,42 +2085,32 @@ let GroupItems = { // ---------- // Function: _updateAppTabIcons // Update images of any apptab icons that point to passed in xultab _updateAppTabIcons: function GroupItems__updateAppTabIcons(xulTab) { if (!xulTab.pinned) return; - let iconUrl = this.getAppTabFavIconUrl(xulTab); - this.groupItems.forEach(function(groupItem) { - iQ(".appTabIcon", groupItem.$appTabTray).each(function(icon) { - let $icon = iQ(icon); - if ($icon.data("xulTab") != xulTab) - return true; - - if (iconUrl != $icon.attr("src")) - $icon.attr("src", iconUrl); - return false; + this.getAppTabFavIconUrl(xulTab, function(iconUrl) { + iQ(".appTabIcon").each(function GroupItems__updateAppTabIcons_forEach(icon) { + let $icon = iQ(icon); + if ($icon.data("xulTab") == xulTab && iconUrl != $icon.attr("src")) + $icon.attr("src", iconUrl); }); }); }, // ---------- // Function: getAppTabFavIconUrl // Gets the fav icon url for app tab. - getAppTabFavIconUrl: function GroupItems_getAppTabFavIconUrl(xulTab) { - let iconUrl; - - if (UI.shouldLoadFavIcon(xulTab.linkedBrowser)) - iconUrl = UI.getFavIconUrlForTab(xulTab); - else - iconUrl = gFavIconService.defaultFavicon.spec; - - return iconUrl; + getAppTabFavIconUrl: function GroupItems_getAppTabFavIconUrl(xulTab, callback) { + UI.getFavIconUrlForTab(xulTab, function GroupItems_getAppTabFavIconUrl_getFavIconUrlForTab(iconUrl) { + callback(iconUrl || gFavIconService.defaultFavicon.spec); + }); }, // ---------- // Function: addAppTab // Adds the given xul:tab to the app tab tray in all groups addAppTab: function GroupItems_addAppTab(xulTab) { this.groupItems.forEach(function(groupItem) { groupItem.addAppTab(xulTab);
--- a/browser/components/tabview/tabitems.js +++ b/browser/components/tabview/tabitems.js @@ -980,30 +980,31 @@ let TabItems = { Utils.assertThrow(tab, "tab"); // ___ get the TabItem Utils.assertThrow(tab._tabViewTabItem, "must already be linked"); let tabItem = tab._tabViewTabItem; // Even if the page hasn't loaded, display the favicon and title - // ___ icon - if (UI.shouldLoadFavIcon(tab.linkedBrowser)) { - let iconUrl = UI.getFavIconUrlForTab(tab); - - if (tabItem.$favImage[0].src != iconUrl) - tabItem.$favImage[0].src = iconUrl; - - iQ(tabItem.$fav[0]).show(); - } else { - if (tabItem.$favImage[0].hasAttribute("src")) - tabItem.$favImage[0].removeAttribute("src"); - iQ(tabItem.$fav[0]).hide(); - } + UI.getFavIconUrlForTab(tab, function TabItems__update_getFavIconUrlCallback(iconUrl) { + let favImage = tabItem.$favImage[0]; + let fav = tabItem.$fav; + if (iconUrl) { + if (favImage.src != iconUrl) + favImage.src = iconUrl; + fav.show(); + } else { + if (favImage.hasAttribute("src")) + favImage.removeAttribute("src"); + fav.hide(); + } + tabItem._sendToSubscribers("iconUpdated"); + }); // ___ label let label = tab.label; let $name = tabItem.$tabTitle; if ($name.text() != label) $name.text(label); // ___ remove from waiting list now that we have no other
--- a/browser/components/tabview/test/Makefile.in +++ b/browser/components/tabview/test/Makefile.in @@ -153,16 +153,17 @@ include $(topsrcdir)/config/rules.mk browser_tabview_bug656913.js \ browser_tabview_bug662266.js \ browser_tabview_bug663421.js \ browser_tabview_bug665502.js \ browser_tabview_bug669694.js \ browser_tabview_bug673196.js \ browser_tabview_bug673729.js \ browser_tabview_bug677310.js \ + browser_tabview_bug678374.js \ browser_tabview_bug679853.js \ browser_tabview_bug681599.js \ browser_tabview_bug685476.js \ browser_tabview_bug685692.js \ browser_tabview_bug686654.js \ browser_tabview_bug696602.js \ browser_tabview_bug697390.js \ browser_tabview_bug705621.js \ @@ -187,14 +188,16 @@ include $(topsrcdir)/config/rules.mk browser_tabview_thumbnail_storage.js \ browser_tabview_undo_group.js \ dummy_page.html \ head.js \ search1.html \ search2.html \ test_bug600645.html \ test_bug644097.html \ + test_bug678374.html \ + test_bug678374_icon16.png \ $(NULL) # browser_tabview_bug597980.js is disabled for leaking, see bug 711907 libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/tabview/test/browser_tabview_apptabs.js +++ b/browser/components/tabview/test/browser_tabview_apptabs.js @@ -1,23 +1,21 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ function test() { waitForExplicitFinish(); - window.addEventListener("tabviewshown", onTabViewWindowLoaded, false); - TabView.toggle(); + showTabView(onTabViewWindowLoaded); } function onTabViewWindowLoaded() { - window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false); ok(TabView.isVisible(), "Tab View is visible"); - let contentWindow = document.getElementById("tab-view").contentWindow; + let contentWindow = TabView.getContentWindow(); // establish initial state is(contentWindow.GroupItems.groupItems.length, 1, "we start with one group (the default)"); is(gBrowser.tabs.length, 1, "we start with one tab"); let originalTab = gBrowser.tabs[0]; // create a group @@ -31,67 +29,68 @@ function onTabViewWindowLoaded() { let xulTab = gBrowser.loadOneTab("about:blank"); is(gBrowser.tabs.length, 2, "we now have two tabs"); is(groupItemOne._children.length, 1, "the new tab was added to the group"); // make sure the group has no app tabs is(appTabCount(groupItemOne), 0, "there are no app tab icons"); // pin the tab, make sure the TabItem goes away and the icon comes on - gBrowser.pinTab(xulTab); - is(groupItemOne._children.length, 0, - "the app tab's TabItem was removed from the group"); - is(appTabCount(groupItemOne), 1, "there's now one app tab icon"); + whenAppTabIconAdded(function() { + is(groupItemOne._children.length, 0, + "the app tab's TabItem was removed from the group"); + is(appTabCount(groupItemOne), 1, "there's now one app tab icon"); - // create a second group and make sure it gets the icon too - box.offset(box.width + 20, 0); - let groupItemTwo = new contentWindow.GroupItem([], - { bounds: box, title: "test2" }); - is(contentWindow.GroupItems.groupItems.length, 3, "we now have three groups"); - is(appTabCount(groupItemTwo), 1, - "there's an app tab icon in the second group"); + // create a second group and make sure it gets the icon too + box.offset(box.width + 20, 0); + let groupItemTwo = new contentWindow.GroupItem([], + { bounds: box, title: "test2" }); + whenAppTabIconAdded(function() { + is(contentWindow.GroupItems.groupItems.length, 3, "we now have three groups"); + is(appTabCount(groupItemTwo), 1, + "there's an app tab icon in the second group"); - // When the tab was pinned, the last active group with an item got the focus. - // Therefore, switching the focus back to group item one. - contentWindow.UI.setActive(groupItemOne); + // When the tab was pinned, the last active group with an item got the focus. + // Therefore, switching the focus back to group item one. + contentWindow.UI.setActive(groupItemOne); - // unpin the tab, make sure the icon goes away and the TabItem comes on - gBrowser.unpinTab(xulTab); - is(groupItemOne._children.length, 1, "the app tab's TabItem is back"); - is(appTabCount(groupItemOne), 0, "the icon is gone from group one"); - is(appTabCount(groupItemTwo), 0, "the icon is gone from group 2"); + // unpin the tab, make sure the icon goes away and the TabItem comes on + gBrowser.unpinTab(xulTab); + is(groupItemOne._children.length, 1, "the app tab's TabItem is back"); + is(appTabCount(groupItemOne), 0, "the icon is gone from group one"); + is(appTabCount(groupItemTwo), 0, "the icon is gone from group two"); - // pin the tab again - gBrowser.pinTab(xulTab); + whenAppTabIconAdded(function() { + // close the second group + groupItemTwo.close(); - // close the second group - groupItemTwo.close(); + // find app tab in group and hit it + whenTabViewIsHidden(function() { + ok(!TabView.isVisible(), + "Tab View is hidden because we clicked on the app tab"); - // find app tab in group and hit it - let onTabViewHidden = function() { - window.removeEventListener("tabviewhidden", onTabViewHidden, false); - ok(!TabView.isVisible(), - "Tab View is hidden because we clicked on the app tab"); + // delete the app tab and make sure its icon goes away + gBrowser.removeTab(xulTab); + is(appTabCount(groupItemOne), 0, "closing app tab removes its icon"); - // delete the app tab and make sure its icon goes away - gBrowser.removeTab(xulTab); - is(appTabCount(groupItemOne), 0, "closing app tab removes its icon"); + // clean up + groupItemOne.close(); - // clean up - groupItemOne.close(); + is(contentWindow.GroupItems.groupItems.length, 1, + "we finish with one group"); + is(gBrowser.tabs.length, 1, "we finish with one tab"); + ok(!TabView.isVisible(), "we finish with Tab View not visible"); - is(contentWindow.GroupItems.groupItems.length, 1, - "we finish with one group"); - is(gBrowser.tabs.length, 1, "we finish with one tab"); - ok(!TabView.isVisible(), "we finish with Tab View not visible"); + finish(); + }); - finish(); - }; - - window.addEventListener("tabviewhidden", onTabViewHidden, false); - - let appTabIcons = groupItemOne.container.getElementsByClassName("appTabIcon"); - EventUtils.sendMouseEvent({ type: "click" }, appTabIcons[0], contentWindow); + let appTabIcons = groupItemOne.container.getElementsByClassName("appTabIcon"); + EventUtils.sendMouseEvent({ type: "click" }, appTabIcons[0], contentWindow); + }); + gBrowser.pinTab(xulTab); + }); + }); + gBrowser.pinTab(xulTab); } function appTabCount(groupItem) { return groupItem.container.getElementsByClassName("appTabIcon").length; }
--- a/browser/components/tabview/test/browser_tabview_bug587503.js +++ b/browser/components/tabview/test/browser_tabview_bug587503.js @@ -1,13 +1,14 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ function test() { waitForExplicitFinish(); + requestLongerTimeout(2); newWindowWithTabView(onTabViewWindowLoaded); } function onTabViewWindowLoaded(win) { ok(win.TabView.isVisible(), "Tab View is visible"); let contentWindow = win.document.getElementById("tab-view").contentWindow; @@ -100,18 +101,18 @@ function onTabViewWindowLoaded(win) { is(index, 4, "Tab 5 is back and again the fifth tab."); contentWindow.Utils.log('dropSpaceActiveValues',dropSpaceActiveValues); is(dropSpaceActiveValues[0], false, "The group began by not showing a dropSpace"); is(dropSpaceActiveValues[dropSpaceActiveValues.length - 1], true, "In the end, the group was showing a dropSpace"); // Close the window and we're done! win.close(); finish(); - }, 6000, false); - },1000); + }, 10000, false); + }, 2000); }); }); }); }
--- a/browser/components/tabview/test/browser_tabview_bug595965.js +++ b/browser/components/tabview/test/browser_tabview_bug595965.js @@ -48,95 +48,106 @@ function onTabViewShown(win) { let tray = groupItem.$appTabTray; let trayContainer = iQ(tray[0].parentNode); is(parseInt(trayContainer.css("width")), 0, "$appTabTray container is not visible"); // pin the tab, make sure the TabItem goes away and the icon comes on - gBrowser.pinTab(xulTabs[0]); - is(groupItem._children.length, 0, - "the app tab's TabItem was removed from the group"); - is(appTabCount(groupItem), 1, "there's now one app tab icon"); + whenAppTabIconAdded(function() { + is(groupItem._children.length, 0, + "the app tab's TabItem was removed from the group"); + is(appTabCount(groupItem), 1, "there's now one app tab icon"); + + is(tray.css("-moz-column-count"), 1, + "$appTabTray column count is 1"); + isnot(parseInt(trayContainer.css("width")), 0, + "$appTabTray container is visible"); + - is(tray.css("-moz-column-count"), 1, - "$appTabTray column count is 1"); - isnot(parseInt(trayContainer.css("width")), 0, - "$appTabTray container is visible"); + let iconHeight = iQ(iQ(".appTabIcon", tray)[0]).height(); + let trayHeight = parseInt(trayContainer.css("height")); + let rows = Math.floor(trayHeight / iconHeight); + let icons = rows * 2; - let iconHeight = iQ(iQ(".appTabIcon", tray)[0]).height(); - let trayHeight = parseInt(trayContainer.css("height")); - let rows = Math.floor(trayHeight / iconHeight); - let icons = rows * 2; + function pinnedSomeTabs() { + is(appTabCount(groupItem), icons, "number of app tab icons is correct"); + + is(tray.css("-moz-column-count"), 2, + "$appTabTray column count is 2"); - // add enough tabs to have two columns - for (let i = 1; i < icons; i++) { - xulTabs.push(gBrowser.loadOneTab("about:blank")); - gBrowser.pinTab(xulTabs[i]); - } + ok(!trayContainer.hasClass("appTabTrayContainerTruncated"), + "$appTabTray container does not have .appTabTrayContainerTruncated"); - is(appTabCount(groupItem), icons, "number of app tab icons is correct"); + // add one more tab + xulTabs.push(gBrowser.loadOneTab("about:blank")); + whenAppTabIconAdded(function() { + is(tray.css("-moz-column-count"), 3, + "$appTabTray column count is 3"); - is(tray.css("-moz-column-count"), 2, - "$appTabTray column count is 2"); + ok(trayContainer.hasClass("appTabTrayContainerTruncated"), + "$appTabTray container hasClass .appTabTrayContainerTruncated"); - ok(!trayContainer.hasClass("appTabTrayContainerTruncated"), - "$appTabTray container does not have .appTabTrayContainerTruncated"); + // remove all but one app tabs + for (let i = 1; i < xulTabs.length; i++) + gBrowser.removeTab(xulTabs[i]); - // add one more tab - xulTabs.push(gBrowser.loadOneTab("about:blank")); - gBrowser.pinTab(xulTabs[xulTabs.length-1]); + is(tray.css("-moz-column-count"), 1, + "$appTabTray column count is 1"); - is(tray.css("-moz-column-count"), 3, - "$appTabTray column count is 3"); + is(appTabCount(groupItem), 1, "there's now one app tab icon"); - ok(trayContainer.hasClass("appTabTrayContainerTruncated"), - "$appTabTray container hasClass .appTabTrayContainerTruncated"); + ok(!trayContainer.hasClass("appTabTrayContainerTruncated"), + "$appTabTray container does not have .appTabTrayContainerTruncated"); - // remove all but one app tabs - for (let i = 1; i < xulTabs.length; i++) - gBrowser.removeTab(xulTabs[i]); + // unpin the last remaining tab + gBrowser.unpinTab(xulTabs[0]); - is(tray.css("-moz-column-count"), 1, - "$appTabTray column count is 1"); + is(parseInt(trayContainer.css("width")), 0, + "$appTabTray container is not visible"); - is(appTabCount(groupItem), 1, "there's now one app tab icon"); + // When the tab was pinned, the last active group with an item got the focus. + // Therefore, switching the focus back to group item one. + contentWindow.UI.setActive(groupItem); - ok(!trayContainer.hasClass("appTabTrayContainerTruncated"), - "$appTabTray container does not have .appTabTrayContainerTruncated"); + is(appTabCount(groupItem), 0, "there are no app tab icons"); + + is(groupItem._children.length, 1, "the normal tab shows in the group"); + + gBrowser.removeTab(xulTabs[0]); - // When the tab was pinned, the last active group with an item got the focus. - // Therefore, switching the focus back to group item one. - contentWindow.UI.setActive(groupItem); + // close the group + groupItem.close(); - // unpin the last remaining tab - gBrowser.unpinTab(xulTabs[0]); + hideTabView(function() { + ok(!TabView.isVisible(), "Tab View is hidden"); - is(parseInt(trayContainer.css("width")), 0, - "$appTabTray container is not visible"); + is(contentWindow.GroupItems.groupItems.length, 1, + "we finish with one group"); + is(gBrowser.tabs.length, 1, "we finish with one tab"); - is(appTabCount(groupItem), 0, "there are no app tab icons"); + win.close(); - is(groupItem._children.length, 1, "the normal tab shows in the group"); - - gBrowser.removeTab(xulTabs[0]); - - // close the group - groupItem.close(); + executeSoon(finish); + }, win); + }, win); + win.gBrowser.pinTab(xulTabs[xulTabs.length-1]); + }; - hideTabView(function() { - ok(!TabView.isVisible(), "Tab View is hidden"); - - is(contentWindow.GroupItems.groupItems.length, 1, - "we finish with one group"); - is(gBrowser.tabs.length, 1, "we finish with one tab"); - - win.close(); - - executeSoon(finish); + // add enough tabs to have two columns + let returnCount = 0; + for (let i = 1; i < icons; i++) { + xulTabs.push(gBrowser.loadOneTab("about:blank")); + whenAppTabIconAdded(function() { + if (++returnCount == (icons - 1)) + executeSoon(pinnedSomeTabs); + }, win); + win.gBrowser.pinTab(xulTabs[i]); + } }, win); + win.gBrowser.pinTab(xulTabs[0]); } function appTabCount(groupItem) { return groupItem.container.getElementsByClassName("appTabIcon").length; }
--- a/browser/components/tabview/test/browser_tabview_bug600645.js +++ b/browser/components/tabview/test/browser_tabview_bug600645.js @@ -5,25 +5,24 @@ const fi = Cc["@mozilla.org/browser/favi getService(Ci.nsIFaviconService); let newTab; function test() { waitForExplicitFinish(); newTab = gBrowser.addTab(); - gBrowser.pinTab(newTab); - window.addEventListener("tabviewshown", onTabViewWindowLoaded, false); - TabView.toggle(); + showTabView(function() { + whenAppTabIconAdded(onTabPinned); + gBrowser.pinTab(newTab); + }) } -function onTabViewWindowLoaded() { - window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false); - +function onTabPinned() { let contentWindow = document.getElementById("tab-view").contentWindow; is(contentWindow.GroupItems.groupItems.length, 1, "There is one group item on startup"); let groupItem = contentWindow.GroupItems.groupItems[0]; let icon = contentWindow.iQ(".appTabIcon", groupItem.$appTabTray)[0]; let $icon = contentWindow.iQ(icon);
--- a/browser/components/tabview/test/browser_tabview_bug610242.js +++ b/browser/components/tabview/test/browser_tabview_bug610242.js @@ -24,16 +24,17 @@ function onTabViewWindowLoaded(win) { contentWindow.UI.setActive(group); is(contentWindow.GroupItems.getActiveGroupItem(), group, "new group is active"); // Create a bunch of tabs in the group let bg = {inBackground: true}; let datatext = win.gBrowser.loadOneTab("data:text/plain,bug610242", bg); let datahtml = win.gBrowser.loadOneTab("data:text/html,<blink>don't blink!</blink>", bg); let mozilla = win.gBrowser.loadOneTab("about:mozilla", bg); + let robots = win.gBrowser.loadOneTab("about:robots", bg); let html = win.gBrowser.loadOneTab("http://example.com", bg); let png = win.gBrowser.loadOneTab("http://mochi.test:8888/browser/browser/base/content/test/moz.png", bg); let svg = win.gBrowser.loadOneTab("http://mochi.test:8888/browser/browser/base/content/test/title_test.svg", bg); ok(!group.shouldStack(group._children.length), "Group should not stack."); // PREPARE FINISH: group.addSubscriber("close", function onClose() { @@ -41,44 +42,59 @@ function onTabViewWindowLoaded(win) { ok(group.isEmpty(), "The group is empty again"); contentWindow.UI.setActive(currentGroup); isnot(contentWindow.GroupItems.getActiveGroupItem(), null, "There is an active group"); is(win.gBrowser.tabs.length, 1, "There is only one tab left"); is(win.gBrowser.visibleTabs.length, 1, "There is also only one visible tab"); - let onTabViewHidden = function() { - win.removeEventListener("tabviewhidden", onTabViewHidden, false); + whenTabViewIsHidden(function() { win.close(); ok(win.closed, "new window is closed"); finish(); - }; - win.addEventListener("tabviewhidden", onTabViewHidden, false); + }, win); win.gBrowser.selectedTab = originalTab; win.TabView.hide(); }); function check(tab, label, visible) { let display = contentWindow.getComputedStyle(tab._tabViewTabItem.$fav[0], null).getPropertyValue("display"); if (visible) { is(display, "block", label + " has favicon"); } else { is(display, "none", label + " has no favicon"); } } afterAllTabsLoaded(function() { afterAllTabItemsUpdated(function() { - check(datatext, "datatext", false); - check(datahtml, "datahtml", false); - check(mozilla, "about:mozilla", true); - check(html, "html", true); - check(png, "png", false); - check(svg, "svg", true); - - // Get rid of the group and its children - // The group close will trigger a finish(). - closeGroupItem(group); - }, win); + let children = group.getChildren(); + let len = children.length; + let iconUpdateCounter = 0; + + children.forEach(function(tabItem) { + tabItem.addSubscriber("iconUpdated", function onIconUpdated() { + // the tab is not loaded completely so ignore it. + if (tabItem.tab.linkedBrowser.currentURI.spec == "about:blank") + return; + + tabItem.removeSubscriber("iconUpdated", onIconUpdated); + + if (++iconUpdateCounter == len) { + check(datatext, "datatext", false); + check(datahtml, "datahtml", false); + check(mozilla, "about:mozilla", false); + check(robots, "about:robots", true); + check(html, "html", true); + check(png, "png", false); + check(svg, "svg", true); + + // Get rid of the group and its children + // The group close will trigger a finish(). + closeGroupItem(group); + } + }); + }); + }, win); }, win); }
--- a/browser/components/tabview/test/browser_tabview_bug626791.js +++ b/browser/components/tabview/test/browser_tabview_bug626791.js @@ -167,11 +167,12 @@ function test() { test(); }, cw); }; newWindowWithTabView(onShow, onLoad); } waitForExplicitFinish(); + requestLongerTimeout(2); next(); }
--- a/browser/components/tabview/test/browser_tabview_bug640765.js +++ b/browser/components/tabview/test/browser_tabview_bug640765.js @@ -6,63 +6,69 @@ let groupItem; function test() { waitForExplicitFinish(); let newTabOne = gBrowser.addTab(); let newTabTwo = gBrowser.addTab(); let newTabThree = gBrowser.addTab(); - gBrowser.pinTab(newTabOne); - gBrowser.pinTab(newTabTwo); - gBrowser.pinTab(newTabThree); - registerCleanupFunction(function() { TabView.hide(); while (gBrowser.tabs.length > 1) gBrowser.removeTab(gBrowser.tabs[0]); }); showTabView(function() { contentWindow = document.getElementById("tab-view").contentWindow; is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group"); groupItem = contentWindow.GroupItems.groupItems[0]; - is(xulTabForAppTabIcon(0), newTabOne, - "New tab one matches the first app tab icon in tabview"); - is(xulTabForAppTabIcon(1), newTabTwo, - "New tab two matches the second app tab icon in tabview"); - is(xulTabForAppTabIcon(2), newTabThree, - "New tab three matches the third app tab icon in tabview"); + whenAppTabIconAdded(function() { + whenAppTabIconAdded(function() { + whenAppTabIconAdded(function() { - // move the last tab to the first position - gBrowser.moveTabTo(newTabThree, 0); - is(xulTabForAppTabIcon(0), newTabThree, - "New tab three matches the first app tab icon in tabview"); - is(xulTabForAppTabIcon(1), newTabOne, - "New tab one matches the second app tab icon in tabview"); - is(xulTabForAppTabIcon(2), newTabTwo, - "New tab two matches the third app tab icon in tabview"); + is(xulTabForAppTabIcon(0), newTabOne, + "New tab one matches the first app tab icon in tabview"); + is(xulTabForAppTabIcon(1), newTabTwo, + "New tab two matches the second app tab icon in tabview"); + is(xulTabForAppTabIcon(2), newTabThree, + "New tab three matches the third app tab icon in tabview"); + + // move the last tab to the first position + gBrowser.moveTabTo(newTabThree, 0); + is(xulTabForAppTabIcon(0), newTabThree, + "New tab three matches the first app tab icon in tabview"); + is(xulTabForAppTabIcon(1), newTabOne, + "New tab one matches the second app tab icon in tabview"); + is(xulTabForAppTabIcon(2), newTabTwo, + "New tab two matches the third app tab icon in tabview"); - // move the first tab to the second position - gBrowser.moveTabTo(newTabThree, 1); - is(xulTabForAppTabIcon(0), newTabOne, - "New tab one matches the first app tab icon in tabview"); - is(xulTabForAppTabIcon(1), newTabThree, - "New tab three matches the second app tab icon in tabview"); - is(xulTabForAppTabIcon(2), newTabTwo, - "New tab two matches the third app tab icon in tabview"); + // move the first tab to the second position + gBrowser.moveTabTo(newTabThree, 1); + is(xulTabForAppTabIcon(0), newTabOne, + "New tab one matches the first app tab icon in tabview"); + is(xulTabForAppTabIcon(1), newTabThree, + "New tab three matches the second app tab icon in tabview"); + is(xulTabForAppTabIcon(2), newTabTwo, + "New tab two matches the third app tab icon in tabview"); - hideTabView(function() { - gBrowser.removeTab(newTabOne); - gBrowser.removeTab(newTabTwo); - gBrowser.removeTab(newTabThree); - finish(); + hideTabView(function() { + gBrowser.removeTab(newTabOne); + gBrowser.removeTab(newTabTwo); + gBrowser.removeTab(newTabThree); + finish(); + }); + }); + gBrowser.pinTab(newTabThree); + }); + gBrowser.pinTab(newTabTwo); }); + gBrowser.pinTab(newTabOne); }); } function xulTabForAppTabIcon(index) { return contentWindow.iQ( contentWindow.iQ(".appTabIcon", groupItem.$appTabTray)[index]).data("xulTab"); }
new file mode 100644 --- /dev/null +++ b/browser/components/tabview/test/browser_tabview_bug678374.js @@ -0,0 +1,45 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const ICON_URL = "moz-anno:favicon:http://example.com/browser/browser/components/tabview/test/test_bug678374_icon16.png"; +const TEST_URL = "http://example.com/browser/browser/components/tabview/test/test_bug678374.html"; + +function test() { + Services.prefs.setBoolPref("browser.chrome.favicons", false); + + waitForExplicitFinish(); + + newWindowWithTabView(function(win) { + is(win.gBrowser.tabs.length, 3, "There are 3 tabs") + + let newTabOne = win.gBrowser.tabs[1]; + let newTabTwo = win.gBrowser.tabs[2]; + let cw = win.TabView.getContentWindow(); + let groupItem = cw.GroupItems.groupItems[0]; + + // test tab item + let newTabItemOne = newTabOne._tabViewTabItem; + + newTabItemOne.addSubscriber("iconUpdated", function onIconUpdated() { + newTabItemOne.removeSubscriber("iconUpdated", onIconUpdated); + is(newTabItemOne.$favImage[0].src, ICON_URL, "The tab item is showing the right icon."); + + // test pin tab + whenAppTabIconAdded(function() { + let icon = cw.iQ(".appTabIcon", groupItem.$appTabTray)[0]; + is(icon.src, ICON_URL, "The app tab is showing the right icon"); + + finish(); + }, win); + win.gBrowser.pinTab(newTabTwo); + }); + }, function(win) { + registerCleanupFunction(function() { + Services.prefs.clearUserPref("browser.chrome.favicons"); + win.close(); + }); + + win.gBrowser.loadOneTab(TEST_URL); + win.gBrowser.loadOneTab(TEST_URL); + }); +}
--- a/browser/components/tabview/test/browser_tabview_bug685476.js +++ b/browser/components/tabview/test/browser_tabview_bug685476.js @@ -1,24 +1,26 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ function test() { waitForExplicitFinish(); showTabView(function () { let tab = gBrowser.addTab(); - gBrowser.pinTab(tab); registerCleanupFunction(function () gBrowser.removeTab(tab)); - let cw = TabView.getContentWindow(); - let body = cw.document.body; - let [appTabIcon] = cw.iQ(".appTabTray .appTabIcon"); + whenAppTabIconAdded(function() { + let cw = TabView.getContentWindow(); + let body = cw.document.body; + let [appTabIcon] = cw.iQ(".appTabTray .appTabIcon"); - EventUtils.synthesizeMouseAtCenter(appTabIcon, {type: "mousedown"}, cw); - EventUtils.synthesizeMouse(body, 500, 100, {type: "mousemove"}, cw); - EventUtils.synthesizeMouse(body, 500, 100, {type: "mouseup"}, cw); + EventUtils.synthesizeMouseAtCenter(appTabIcon, {type: "mousedown"}, cw); + EventUtils.synthesizeMouse(body, 500, 100, {type: "mousemove"}, cw); + EventUtils.synthesizeMouse(body, 500, 100, {type: "mouseup"}, cw); - ok(TabView.isVisible(), "tabview is still visible"); + ok(TabView.isVisible(), "tabview is still visible"); - hideTabView(finish); + hideTabView(finish); + }); + gBrowser.pinTab(tab); }); }
--- a/browser/components/tabview/test/head.js +++ b/browser/components/tabview/test/head.js @@ -380,8 +380,22 @@ function togglePrivateBrowsing(callback) executeSoon(function () afterAllTabsLoaded(callback)); }, topic, false); let pb = Cc["@mozilla.org/privatebrowsing;1"]. getService(Ci.nsIPrivateBrowsingService); pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled; } + +// ---------- +function whenAppTabIconAdded(callback, win) { + win = win || window; + + let contentWindow = win.TabView.getContentWindow(); + let groupItems = contentWindow.GroupItems.groupItems; + let groupItem = groupItems[(groupItems.length - 1)]; + + groupItem.addSubscriber("appTabIconAdded", function onAppTabIconAdded() { + groupItem.removeSubscriber("appTabIconAdded", onAppTabIconAdded); + callback(); + }); +}
new file mode 100644 --- /dev/null +++ b/browser/components/tabview/test/test_bug678374.html @@ -0,0 +1,7 @@ +<html> + <head> + <title>Bug 678374</title> + <link rel="icon" type="image/png" id="favicon" href="test_bug678374_icon16.png" /> + <body> + </body> +</html>
new file mode 100644 index 0000000000000000000000000000000000000000..bc317a8d5fc3121c939e2bc9a17f5d024b709a12 GIT binary patch literal 924 zc$@*817rM&P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV000AHNkl<Zc-mc( zeMsB|7{{NxvwQL8{qFtMd-Hd{J3a3_@AQ0|Zb~^TaRz2987(%p1>1t&a4q(ah$gW% z8~dwZ5J60-O=1aaVdNh|ZlkDy8`LrwLK_O@pcwJ#7ZFl_e0lzPKHu;6fk%M<E4y&} z-uS66ZY`c!TK)OMmGwLG_a9#Q{O;!H9}xO?Z{qrWF}`<O{sXUU{QlO!<W&-H&a?C4 zb@qPwJx_nOOwXmS2~J!kw*SI|^H*-Y1jx+VpCadOZW%26@XMTK*CdAWG=&2n(J^(I z>SO0|Jo7eH{o{oC$B^hSV(sDJiF4<UEN@6+k$((+THl;76ptc~L89F!P_`dKY21gW zw4Xrfc|wgt#2X(aQE11n77>XdyT@MBCNGKri5)X*Nb_mDp)UMQeK?A{aAjKY*0$iy zwiC#;<Ew4LSJ#X)QAa%Af<4uRsdczoZv4uW41J8$jNp#uN#sk!3p%I+POS+?s)Q|; z$5Nfcmu<lsOyLSA@fUiq^&UPgCl?<qi{~gNF;S@mX=v*}Eq7t9&gx)}%6J~Xo~*>P z_%bE5awie39=BRT5}h|?czkY6#6l>lQE2Z#iKGyZhB=tQ9Livg)Df<4rn&bCwB`<y zdOLThhN^&yWcoH_;Pu(NA~q5!mF|Hj$+qo4suCF8Nh|>kqo;;Pd@0I(1El{pXei@~ z)*%&M47vW_<j|RKz7pv}O(zK_Yp5+X5l-tjdsR%%gg$F9DlxW&G_+z1s@}lvj$pD! zFcbz?<-)@1aglz4@j8;VDuHMmHJ8;>Y7tA23TIrOAwAJYEL*~0^&(<Ilr+*hG9wSs zPimV)#6f+dMoi1-%Zi>5MvPvp&L~yBYCMqy4!?>R6h!P8)4fRltDOSK{EhX4V(TT~ zbK(xf5vkJULJVGPb`QnvWikzU6h*;ev}5*Wsd((jB^f%o^~%pqUps4bw&KzOt2L<0 zhuPxCY<3cfso3@ECNZ-uki|MM`kjnkHp?rYY#F?`v?($&JL<~q!5=Q+^QZ87V|d(A z?3Dprfm)o!-B<_Tn3coto8_Gqk$3NG8H~*RC~|yBtS^6>wmmgA<=A=Xnyc-(d#<)U y>yGXND?0drY454WW#6>O3ui^fmPDq$mA?Qdy0Sx%{P*|(0000<MNUMnLSTZyBg9Go
--- a/browser/components/tabview/ui.js +++ b/browser/components/tabview/ui.js @@ -46,16 +46,22 @@ // Title: ui.js let Keys = { meta: false }; // ########## // Class: UI // Singleton top-level UI manager. let UI = { + // Pref that controls whether to display site icons + PREF_CHROME_SITE_ICONS: "browser.chrome.site_icons", + + // Pref that controls whether to display fav icons + PREF_CHROME_FAVICONS: "browser.chrome.favicons", + // Variable: _frameInitialized // True if the Tab View UI frame has been initialized. _frameInitialized: false, // Variable: _pageBounds // Stores the page bounds. _pageBounds: null, @@ -136,16 +142,22 @@ let UI = { // Variable: _lastOpenedTab // Used to keep track of the last opened tab. _lastOpenedTab: null, // Variable: _originalSmoothScroll // Used to keep track of the tab strip smooth scroll value. _originalSmoothScroll: null, + // Used to keep track of the browser.chrome.site_icons pref value. + _prefSiteIcons: null, + + // Used to keep track of the browser.chrome.favicons pref value. + _prefFavicons: null, + // ---------- // Function: toString // Prints [UI] for debug use toString: function UI_toString() { return "[UI]"; }, // ---------- @@ -236,16 +248,20 @@ let UI = { }); // ___ setup key handlers this._setTabViewFrameKeyHandlers(); // ___ add tab action handlers this._addTabActionHandlers(); + // ___ add preference observers + Services.prefs.addObserver(this.PREF_CHROME_SITE_ICONS, this, false); + Services.prefs.addObserver(this.PREF_CHROME_FAVICONS, this, false); + // ___ groups GroupItems.init(); GroupItems.pauseArrange(); let hasGroupItemsData = GroupItems.load(); // ___ tabs TabItems.init(); TabItems.pausePainting(); @@ -287,16 +303,19 @@ let UI = { this._frameInitialized = true; this._save(); // fire an iframe initialized event so everyone knows tab view is // initialized. let event = document.createEvent("Events"); event.initEvent("tabviewframeinitialized", true, false); dispatchEvent(event); + + // XXX this can be removed when bug 731868 is fixed + event = null; } catch(e) { Utils.log(e); } finally { GroupItems.resumeArrange(); } }, // Function: uninit @@ -309,16 +328,19 @@ let UI = { this._cleanupFunctions = []; // additional clean up TabItems.uninit(); GroupItems.uninit(); Storage.uninit(); StoragePolicy.uninit(); + Services.prefs.removeObserver(this.PREF_CHROME_SITE_ICONS, this); + Services.prefs.removeObserver(this.PREF_CHROME_FAVICONS, this); + this._removeTabActionHandlers(); this._currentTab = null; this._pageBounds = null; this._reorderTabItemsOnShow = null; this._reorderTabsOnHide = null; this._frameInitialized = false; }, @@ -847,16 +869,29 @@ let UI = { // Function: _removeTabActionHandlers // Removes handlers to handle tab actions. _removeTabActionHandlers: function UI__removeTabActionHandlers() { for (let name in this._eventListeners) AllTabs.unregister(name, this._eventListeners[name]); }, // ---------- + // Function: observe + // Observes different preference value changes. + observe: function UI_observe(subject, topic, data) { + if (data == this.PREF_CHROME_SITE_ICONS) { + this._prefSiteIcons = + Services.prefs.getBoolPref(this.PREF_CHROME_SITE_ICONS); + } else if (data == this.PREF_CHROME_FAVICONS) { + this._prefFavicons = + Services.prefs.getBoolPref(this.PREF_CHROME_FAVICONS); + } + }, + + // ---------- // Function: goToTab // Selects the given xul:tab in the browser. goToTab: function UI_goToTab(xulTab) { // If it's not focused, the onFocus listener would handle it. if (gBrowser.selectedTab == xulTab) this.onTabSelect(xulTab); else gBrowser.selectedTab = xulTab; @@ -1599,49 +1634,85 @@ let UI = { if (this._storageSanity(data)) Storage.saveUIData(gWindow, data); }, // ---------- // Function: _saveAll // Saves all data associated with TabView. - // TODO: Save info items _saveAll: function UI__saveAll() { this._save(); GroupItems.saveAll(); TabItems.saveAll(); }, // ---------- - // Function: shouldLoadFavIcon - // Takes a xul:browser and checks whether we should display a favicon for it. - shouldLoadFavIcon: function UI_shouldLoadFavIcon(browser) { - return !(browser.contentDocument instanceof window.ImageDocument) && - (browser.currentURI.schemeIs("about") || - gBrowser.shouldLoadFavIcon(browser.contentDocument.documentURIObject)); + // Function: _isImageDocument + // Checks whether an image is loaded into the given tab. + _isImageDocument: function UI__isImageDocument(tab, callback) { + let mm = tab.linkedBrowser.messageManager; + let message = "Panorama:isImageDocument"; + + mm.addMessageListener(message, function onMessage(cx) { + mm.removeMessageListener(cx.name, onMessage); + callback(cx.json.isImageDocument); + }); + mm.sendAsyncMessage(message); + }, + + // ---------- + // Function: _shouldLoadFavIcon + // Checks whether fav icon should be loaded for a given tab. + _shouldLoadFavIcon: function UI__shouldLoadFavIcon(tab) { + let uri = tab.linkedBrowser.currentURI; + + if (!uri) + return false; + + if (this._prefSiteIcons == null) + this._prefSiteIcons = + Services.prefs.getBoolPref(this.PREF_CHROME_SITE_ICONS); + + if (!this._prefSiteIcons) + return false; + + if (this._prefFavicons == null) + this._prefFavicons = + Services.prefs.getBoolPref(this.PREF_CHROME_FAVICONS); + + return (this._prefFavicons && ("schemeIs" in uri) && + (uri.schemeIs("http") || uri.schemeIs("https"))); }, // ---------- // Function: getFavIconUrlForTab // Gets fav icon url for the given xul:tab. - getFavIconUrlForTab: function UI_getFavIconUrlForTab(tab) { - let url; + getFavIconUrlForTab: function UI_getFavIconUrlForTab(tab, callback) { + this._isImageDocument(tab, function(isImageDoc) { + if (isImageDoc) { + callback(tab.pinned ? tab.image : null); + } else { + let tabImage = tab.image; + if (tabImage) { + // if starts with http/https, fetch icon from favicon service via the moz-anno protocal + if (/^https?:/.test(tabImage)) + tabImage = gFavIconService.getFaviconLinkForIcon(gWindow.makeURI(tab.image)).spec; - if (tab.image) { - // if starts with http/https, fetch icon from favicon service via the moz-anno protocal - if (/^https?:/.test(tab.image)) - url = gFavIconService.getFaviconLinkForIcon(gWindow.makeURI(tab.image)).spec; - else - url = tab.image; - } else { - url = gFavIconService.getFaviconImageForPage(tab.linkedBrowser.currentURI).spec; - } - - return url; + callback(tabImage); + } else { + // determine to load the default/cached icon or not and also ensure we don't show the default icon + // for about:-style error pages + let url = null; + if (this._shouldLoadFavIcon(tab)) + url = gFavIconService.getFaviconImageForPage(tab.linkedBrowser.currentURI).spec; + callback(url); + } + } + }.bind(this)); }, // ---------- // Function: notifySessionRestoreEnabled // Notify the user that session restore has been automatically enabled // by showing a banner that expects no user interaction. It fades out after // some seconds. notifySessionRestoreEnabled: function UI_notifySessionRestoreEnabled() {
--- a/browser/components/thumbnails/PageThumbs.jsm +++ b/browser/components/thumbnails/PageThumbs.jsm @@ -104,31 +104,32 @@ let PageThumbs = { }, /** * Captures a thumbnail for the given browser and stores it to the cache. * @param aBrowser The browser to capture a thumbnail for. * @param aCallback The function to be called when finished (optional). */ captureAndStore: function PageThumbs_captureAndStore(aBrowser, aCallback) { + let url = aBrowser.currentURI.spec; this.capture(aBrowser.contentWindow, function (aInputStream) { let telemetryStoreTime = new Date(); function finish(aSuccessful) { if (aSuccessful) { Services.telemetry.getHistogramById("FX_THUMBNAILS_STORE_TIME_MS") .add(new Date() - telemetryStoreTime); } if (aCallback) aCallback(aSuccessful); } // Get a writeable cache entry. - PageThumbsCache.getWriteEntry(aBrowser.currentURI.spec, function (aEntry) { + PageThumbsCache.getWriteEntry(url, function (aEntry) { if (!aEntry) { finish(false); return; } let outputStream = aEntry.openOutputStream(0); // Write the image data to the cache entry.
--- a/browser/components/thumbnails/test/Makefile.in +++ b/browser/components/thumbnails/test/Makefile.in @@ -8,13 +8,14 @@ srcdir = @srcdir@ VPATH = @srcdir@ relativesrcdir = browser/components/thumbnails/test include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _BROWSER_FILES = \ browser_thumbnails_capture.js \ + browser_thumbnails_bug726727.js \ head.js \ $(NULL) libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/browser/components/thumbnails/test/browser_thumbnails_bug726727.js @@ -0,0 +1,19 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * These tests ensure that capturing a sites's thumbnail, saving it and + * retrieving it from the cache works. + */ +function runTests() { + // Create a tab that shows an error page. + let tab = gBrowser.addTab("http://non-existant.url/"); + let browser = tab.linkedBrowser; + + yield browser.addEventListener("DOMContentLoaded", function onLoad() { + browser.removeEventListener("DOMContentLoaded", onLoad, false); + executeSoon(next); + }, false); + + ok(!gBrowserThumbnails._shouldCapture(browser), "we're not going to capture an error page"); +}
--- a/browser/devtools/debugger/DebuggerUI.jsm +++ b/browser/devtools/debugger/DebuggerUI.jsm @@ -44,17 +44,17 @@ const Cc = Components.classes; const Ci = Components.interfaces; const Cu = Components.utils; Cu.import("resource://gre/modules/NetUtil.jsm"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/devtools/dbg-server.jsm"); Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); -Cu.import("resource:///modules/XPCOMUtils.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/source-editor.jsm"); let EXPORTED_SYMBOLS = ["DebuggerUI"]; /** * Creates a pane that will host the debugger UI. */ function DebuggerPane(aTab) {
--- a/browser/devtools/shared/test/browser_promise_basic.js +++ b/browser/devtools/shared/test/browser_promise_basic.js @@ -192,23 +192,114 @@ function testFailGroup(data) { preResolution = undefined; laterResolution = undefined; member1 = undefined; member2 = undefined; member3 = undefined; laterGroup = undefined; laterRejection = undefined; - finished(); + testTrap(); +} + +function testTrap() { + var p = new Promise(); + var message = "Expected exception"; + p.chainPromise( + function() { + throw new Error(message); + }).trap( + function(aError) { + is(aError instanceof Error, true, "trap received exception"); + is(aError.message, message, "trap received correct exception"); + return 1; + }).chainPromise( + function(aResult) { + is(aResult, 1, "trap restored correct result"); + testAlways(); + }); + p.resolve(); +} + +function testAlways() { + var shouldbeTrue1 = false; + var shouldbeTrue2 = false; + var p = new Promise(); + p.chainPromise( + function() { + throw new Error(); + } + ).chainPromise(// Promise rejected, should not be executed + function() { + ok(false, "This should not be executed"); + } + ).always( + function(x) { + shouldbeTrue1 = true; + return "random value"; + } + ).trap( + function(arg) { + ok((arg instanceof Error), "The random value should be ignored"); + return 1;// We should still have this result later + } + ).trap( + function() { + ok(false, "This should not be executed 2"); + } + ).always( + function() { + shouldbeTrue2 = true; + } + ).then( + function(aResult){ + ok(shouldbeTrue1, "First always must be executed"); + ok(shouldbeTrue2, "Second always must be executed"); + is(aResult, 1, "Result should be unaffected by always"); + + testComplete(); + } + ); + p.resolve(); } function fail() { gBrowser.removeCurrentTab(); info("Failed Promise Tests"); ok(false, "fail called"); finish(); } +/** + * We wish to launch all tests with several configurations (at the moment, + * non-debug and debug mode). + * + * If 0, we have not completed any test yet. + * If 1, we have completed the tests in non-debug mode. + * If 2, we have also completed the tests in debug mode. + */ +var configurationTestComplete = 0; +function testComplete() { + switch (configurationTestComplete) { + case 0: + info("Finished run in non-debug mode"); + configurationTestComplete = 1; + Promise.Debug.setDebug(true); + window.setTimeout(testBasic, 0); + return; + case 1: + info("Finished run in debug mode"); + configurationTestComplete = 2; + Promise.Debug.setDebug(false); + window.setTimeout(finished, 0); + return; + default: + ok(false, "Internal error in testComplete "+configurationTestComplete); + return; + } +} + + function finished() { gBrowser.removeCurrentTab(); info("Finishing Promise Tests"); finish(); }
--- a/browser/installer/Makefile.in +++ b/browser/installer/Makefile.in @@ -43,16 +43,17 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk MOZ_PKG_REMOVALS = $(srcdir)/removed-files.in MOZ_PKG_MANIFEST_P = $(srcdir)/package-manifest.in +MOZ_PKG_FATAL_WARNINGS = 1 MOZ_NONLOCALIZED_PKG_LIST = \ xpcom \ browser \ $(NULL) MOZ_LOCALIZED_PKG_LIST = $(AB_CD)
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -20,28 +20,30 @@ @APPNAME@/Contents/Info.plist @APPNAME@/Contents/PkgInfo @APPNAME@/Contents/Resources/ #endif [@AB_CD@] @BINPATH@/chrome/@AB_CD@@JAREXT@ @BINPATH@/chrome/@AB_CD@.manifest -@BINPATH@/@PREF_DIR@/firefox-l10n.js -@BINPATH@/searchplugins/* @BINPATH@/defaults/profile/bookmarks.html +@BINPATH@/defaults/profile/chrome/* @BINPATH@/defaults/profile/localstore.rdf @BINPATH@/defaults/profile/mimeTypes.rdf -@BINPATH@/defaults/profile/chrome/* +@BINPATH@/dictionaries/* +@BINPATH@/hyphenation/* +@BINPATH@/@PREF_DIR@/firefox-l10n.js +@BINPATH@/searchplugins/* +#ifdef XP_WIN32 +@BINPATH@/uninstall/helper.exe +#endif +#ifdef MOZ_UPDATER @BINPATH@/update.locale @BINPATH@/updater.ini -@BINPATH@/dictionaries/* -@BINPATH@/hyphenation/* -#ifdef XP_WIN32 -@BINPATH@/uninstall/helper.exe #endif [xpcom] @BINPATH@/dependentlibs.list #ifdef XP_WIN32 @BINPATH@/@DLL_PREFIX@gkmedias@DLL_SUFFIX@ #endif @BINPATH@/@DLL_PREFIX@mozalloc@DLL_SUFFIX@ @@ -381,20 +383,20 @@ @BINPATH@/components/contentSecurityPolicy.js @BINPATH@/components/contentAreaDropListener.manifest @BINPATH@/components/contentAreaDropListener.js #ifdef MOZ_B2G_RIL @BINPATH@/components/RadioInterfaceLayer.manifest @BINPATH@/components/RadioInterfaceLayer.js @BINPATH@/components/SmsDatabaseService.manifest @BINPATH@/components/SmsDatabaseService.js -@BINPATH@/components/nsWifiWorker.js -@BINPATH@/components/nsWifiWorker.manifest -@BINPATH@/components/nsDOMWifiManager.js -@BINPATH@/components/nsDOMWifiManager.manifest +@BINPATH@/components/WifiWorker.js +@BINPATH@/components/WifiWorker.manifest +@BINPATH@/components/DOMWifiManager.js +@BINPATH@/components/DOMWifiManager.manifest #endif @BINPATH@/components/BrowserProfileMigrators.manifest @BINPATH@/components/ProfileMigrator.js @BINPATH@/components/ChromeProfileMigrator.js @BINPATH@/components/FirefoxProfileMigrator.js #ifdef XP_MACOSX @BINPATH@/components/libalerts.dylib #endif @@ -551,21 +553,23 @@ #ifdef SOLARIS bin/libfreebl_32fpu_3.so bin/libfreebl_32int_3.so bin/libfreebl_32int64_3.so #endif ; [Updater] ; +#ifdef MOZ_UPDATER #ifdef XP_MACOSX @BINPATH@/updater.app/ #else @BINPATH@/updater@BIN_SUFFIX@ #endif +#endif ; [MaintenanceService] ; #ifdef MOZ_MAINTENANCE_SERVICE @BINPATH@/maintenanceservice.exe @BINPATH@/maintenanceservice_installer.exe #endif
--- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -10,17 +10,16 @@ @DLL_PREFIX@xpistub@DLL_SUFFIX@ @DLL_PREFIX@zlib@DLL_SUFFIX@ @DLL_PREFIX@jemalloc@DLL_SUFFIX@ @DLL_PREFIX@mozutils@DLL_SUFFIX@ #ifdef MOZ_STATIC_JS @DLL_PREFIX@mozjs@DLL_SUFFIX@ #endif LICENSE -update.locale browserconfig.properties chrome/US.jar chrome/app-chrome.manifest chrome/browser.manifest chrome/chrome.rdf chrome/chromelist.txt chrome/classic.jar chrome/classic.manifest @@ -690,16 +689,25 @@ searchplugins/yandex.png searchplugins/yandex.src searchplugins/zoznam-sk.gif searchplugins/zoznam-sk.png searchplugins/zoznam-sk.src uninstall/UninstallDeerPark.exe uninstall/UninstallFirefox.exe uninstall/uninst.exe uninstall/uninstall.exe +update.locale +#ifndef MOZ_UPDATER + #ifdef XP_MACOSX + updater.app/ + #else + updater@BIN_SUFFIX@ + #endif + updater.ini +#endif xpicleanup@BIN_SUFFIX@ #ifdef MOZ_OMNIJAR omni.jar chrome/af.jar chrome/af.manifest chrome/ar.jar chrome/ar.manifest chrome/as.jar @@ -922,20 +930,20 @@ xpicleanup@BIN_SUFFIX@ components/PlacesProtocolHandler.js components/storage-Legacy.js components/storage-mozStorage.js #ifdef MOZ_B2G_RIL components/nsTelephonyWorker.manifest components/nsTelephonyWorker.js components/Telephony.manifest components/Telephony.js - components/nsWifiWorker.js - components/nsWifiWorker.manifest - components/nsDOMWifiManager.js - components/nsDOMWifiManager.manifest + components/WifiWorker.js + components/WifiWorker.manifest + components/DOMWifiManager.js + components/DOMWifiManager.manifest #endif components/txEXSLTRegExFunctions.js components/Weave.js components/Webapps.js components/Webapps.manifest components/WebContentConverter.js defaults/autoconfig/platform.js defaults/autoconfig/prefcalls.js
--- a/browser/locales/en-US/chrome/browser-region/region.properties +++ b/browser/locales/en-US/chrome/browser-region/region.properties @@ -12,17 +12,17 @@ browser.contentHandlers.types.0.title=Go browser.contentHandlers.types.0.uri=http://fusion.google.com/add?feedurl=%s browser.contentHandlers.types.1.title=My Yahoo! browser.contentHandlers.types.1.uri=http://add.my.yahoo.com/rss?url=%s # URL for site-specific search engines # TRANSLATION NOTE: {moz:domain} and {searchTerms} are placeholders for the site # to be searched and the user's search query. Place them in the appropriate location # for your locale's URL but do not translate them. -browser.search.siteSearchURL=http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&q=site%3A{moz:domain}+{searchTerms} +browser.search.siteSearchURL=https://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&q=site%3A{moz:domain}+{searchTerms} # increment this number when anything gets changed in the list below. This will # cause Firefox to re-read these prefs and inject any new handlers into the # profile database. Note that "new" is defined as "has a different URL"; this # means that it's not possible to update the name of existing handler, so # don't make any spelling errors here. gecko.handlerService.defaultHandlersVersion=3
--- a/browser/locales/en-US/chrome/browser/aboutHome.dtd +++ b/browser/locales/en-US/chrome/browser/aboutHome.dtd @@ -1,23 +1,26 @@ -<!ENTITY % brandDTD - SYSTEM "chrome://branding/locale/brand.dtd"> - %brandDTD; +<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> +%brandDTD; +<!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd"> +%syncBrandDTD; <!-- These strings are used in the about:home page --> <!ENTITY abouthome.pageTitle "&brandFullName; Start Page"> <!ENTITY abouthome.searchEngineButton.label "Search"> -<!ENTITY abouthome.aboutMozilla "About Mozilla"> - <!-- LOCALIZATION NOTE (abouthome.defaultSnippet1.v1): text in <a/> will be linked to the Firefox features page on mozilla.com --> <!ENTITY abouthome.defaultSnippet1.v1 "Thanks for choosing Firefox! To get the most out of your browser, learn more about the <a>latest features</a>."> <!-- LOCALIZATION NOTE (abouthome.defaultSnippet2.v1): text in <a/> will be linked to the featured add-ons on addons.mozilla.org --> <!ENTITY abouthome.defaultSnippet2.v1 "It's easy to customize your Firefox exactly the way you want it. <a>Choose from thousands of add-ons</a>."> -<!ENTITY abouthome.syncSetup.label "Set Up Sync"> -<!ENTITY abouthome.pairDevice.label "Pair a Device"> +<!ENTITY abouthome.bookmarksButton.label "Bookmarks"> +<!ENTITY abouthome.historyButton.label "History"> +<!ENTITY abouthome.settingsButton.label "Settings"> +<!ENTITY abouthome.addonsButton.label "Add-ons"> +<!ENTITY abouthome.appsButton.label "Marketplace"> +<!ENTITY abouthome.downloadsButton.label "Downloads">
--- a/browser/locales/en-US/searchplugins/google.xml +++ b/browser/locales/en-US/searchplugins/google.xml @@ -8,27 +8,27 @@ #else #define GOOGLE_CLIENT_PARAM <MozParam name="client" condition="defaultEngine" trueValue="firefox-a" falseValue="firefox"/> #endif <SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/"> <ShortName>Google</ShortName> <Description>Google Search</Description> <InputEncoding>UTF-8</InputEncoding> <Image width="16" height="16">data:image/png;base64,AAABAAEAEBAAAAEAGABoAwAAFgAAACgAAAAQAAAAIAAAAAEAGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADs9Pt8xetPtu9FsfFNtu%2BTzvb2%2B%2Fne4dFJeBw0egA%2FfAJAfAA8ewBBegAAAAD%2B%2FPtft98Mp%2BwWsfAVsvEbs%2FQeqvF8xO7%2F%2F%2F63yqkxdgM7gwE%2FggM%2BfQA%2BegBDeQDe7PIbotgQufcMufEPtfIPsvAbs%2FQvq%2Bfz%2Bf%2F%2B%2B%2FZKhR05hgBBhQI8hgBAgAI9ewD0%2B%2Fg3pswAtO8Cxf4Kw%2FsJvvYAqupKsNv%2B%2Fv7%2F%2FP5VkSU0iQA7jQA9hgBDgQU%2BfQH%2F%2Ff%2FQ6fM4sM4KsN8AteMCruIqqdbZ7PH8%2Fv%2Fg6Nc%2Fhg05kAA8jAM9iQI%2BhQA%2BgQDQu6b97uv%2F%2F%2F7V8Pqw3eiWz97q8%2Ff%2F%2F%2F%2F7%2FPptpkkqjQE4kwA7kAA5iwI8iAA8hQCOSSKdXjiyflbAkG7u2s%2F%2B%2F%2F39%2F%2F7r8utrqEYtjQE8lgA7kwA7kwA9jwA9igA9hACiWSekVRyeSgiYSBHx6N%2F%2B%2Fv7k7OFRmiYtlAA5lwI7lwI4lAA7kgI9jwE9iwI4iQCoVhWcTxCmb0K%2BooT8%2Fv%2F7%2F%2F%2FJ2r8fdwI1mwA3mQA3mgA8lAE8lAE4jwA9iwE%2BhwGfXifWvqz%2B%2Ff%2F58u%2Fev6Dt4tr%2B%2F%2F2ZuIUsggA7mgM6mAM3lgA5lgA6kQE%2FkwBChwHt4dv%2F%2F%2F728ei1bCi7VAC5XQ7kz7n%2F%2F%2F6bsZkgcB03lQA9lgM7kwA2iQktZToPK4r9%2F%2F%2F9%2F%2F%2FSqYK5UwDKZAS9WALIkFn%2B%2F%2F3%2F%2BP8oKccGGcIRJrERILYFEMwAAuEAAdX%2F%2Ff7%2F%2FP%2B%2BfDvGXQLIZgLEWgLOjlf7%2F%2F%2F%2F%2F%2F9QU90EAPQAAf8DAP0AAfMAAOUDAtr%2F%2F%2F%2F7%2B%2Fu2bCTIYwDPZgDBWQDSr4P%2F%2Fv%2F%2F%2FP5GRuABAPkAA%2FwBAfkDAPAAAesAAN%2F%2F%2B%2Fz%2F%2F%2F64g1C5VwDMYwK8Yg7y5tz8%2Fv%2FV1PYKDOcAAP0DAf4AAf0AAfYEAOwAAuAAAAD%2F%2FPvi28ymXyChTATRrIb8%2F%2F3v8fk6P8MAAdUCAvoAAP0CAP0AAfYAAO4AAACAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAQAA</Image> -<Url type="application/x-suggestions+json" method="GET" template="http://suggestqueries.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}&q={searchTerms}"/> -<Url type="text/html" method="GET" template="http://www.google.com/search"> +<Url type="application/x-suggestions+json" method="GET" template="https://www.google.com/complete/search?output=firefox&client=firefox&hl={moz:locale}&q={searchTerms}"/> +<Url type="text/html" method="GET" template="https://www.google.com/search"> #expand __GOOGLE_PARAMS__ #expand __GOOGLE_CLIENT_PARAM__ </Url> <!-- Keyword search URL is the same as the default, but with an additional parameter --> -<Url type="application/x-moz-keywordsearch" method="GET" template="http://www.google.com/search"> +<Url type="application/x-moz-keywordsearch" method="GET" template="https://www.google.com/search"> #expand __GOOGLE_PARAMS__ #expand __GOOGLE_CLIENT_PARAM__ <Param name="channel" value="fflb"/> </Url> <!-- Context/Right-click search URL is the same as the default, but with an additional parameter --> -<Url type="application/x-moz-contextsearch" method="GET" template="http://www.google.com/search"> +<Url type="application/x-moz-contextsearch" method="GET" template="https://www.google.com/search"> #expand __GOOGLE_PARAMS__ #expand __GOOGLE_CLIENT_PARAM__ <Param name="channel" value="rcs"/> </Url> -<SearchForm>http://www.google.com/</SearchForm> +<SearchForm>https://www.google.com/</SearchForm> </SearchPlugin>
--- a/browser/modules/NewTabUtils.jsm +++ b/browser/modules/NewTabUtils.jsm @@ -379,16 +379,25 @@ let BlockedLinks = { // Make sure we unpin blocked links. PinnedLinks.unpin(aLink); Storage.set("blockedLinks", this.links); }, /** + * Unblocks a given link. + * @param aLink The link to unblock. + */ + unblock: function BlockedLinks_unblock(aLink) { + if (this.isBlocked(aLink)) + delete this.links[aLink.url]; + }, + + /** * Returns whether a given link is blocked. * @param aLink The link to check. */ isBlocked: function BlockedLinks_isBlocked(aLink) { return (aLink.url in this.links); }, /** @@ -545,17 +554,17 @@ let Links = { return pinnedLinks; }, /** * Resets the links cache. */ resetCache: function Links_resetCache() { - this._links = []; + this._links = null; }, /** * Implements the nsIObserver interface to get notified about browser history * sanitization. */ observe: function Links_observe(aSubject, aTopic, aData) { // Make sure to update open about:newtab instances. If there are no opened @@ -579,22 +588,26 @@ let Links = { Ci.nsISupportsWeakReference]) }; /** * Singleton that provides the public API of this JSM. */ let NewTabUtils = { /** - * Resets the NewTabUtils module, its links and its storage. + * Restores all sites that have been removed from the grid. */ - reset: function NewTabUtils_reset() { + restore: function NewTabUtils_restore() { Storage.clear(); Links.resetCache(); PinnedLinks.resetCache(); BlockedLinks.resetCache(); + + Links.populateCache(function () { + AllPages.update(); + }, true); }, allPages: AllPages, links: Links, pinnedLinks: PinnedLinks, blockedLinks: BlockedLinks };
index c0396f7abae8a9f498fe87c91e8bf66835d0c172..893a27d76ed0ba02cb06d0bc12b0e8ecd77a7abb GIT binary patch literal 21309 zc${3g19W6v&@~*}wvCzCwmGq_i8Zk`v2EL!*tYGYgO2TQ=6T<>{_p#H^*y!Eu6_ER zzFk$l>O?3hNFu`F!GVB)AWBP#seph$&w_w}#=(I9W%}&k2|+->PL$++h<|>5K0G}G zZ|*N|?;oFD?jN5oZy$iycUQN6>6gdnm%E3jyT_-emzRsbnw$HpyNBz$hriB;r<a@i z$B)m?>$|_Im&d2)zw7TGo*$o|ul_R6FYg~8&o8g9Z*TAK?_XbEf8pQB=jYe${nPtj z-^b_Q>+Rp`{pI!T^5)^@?(wg5_vG^M{QBtpdIxZ^b$qdZdUXuE*#TT0oL%pnT<)D- z?)@9D)(_7Q&aO6(fZP8xf0tYxo?QVhZVu0`SN6|#0hdR>>y0De#vyS3?C&RBuO9+W zE^f8~7aND?Czm%{$H2|wi?xGuz{Ty#-r3Rl)e-RK<l^@D{2K5VT;8r9oFARvEbpBi zoL=pnT&*6QpI+V`oL?{RpDpg3F7BQlU);=W0(Joxo5#T2lZ)Nc%e6z`^6u&G$<^-Z z)z%SkW(zRCeR6nyv$}tNeE#>=mzzfyYlr76`{!p@cL%4JOS@+qM;ALM7fZXRr<XSi z+b00v_0ie&{@L}`(Z#~f$-?gG+4bG>-s#H0`Re}J+QHe<-s#Tq#Ub$e?DF>f`tI!N zc6tANW$%1?6R>rBd46@begND%yV^g!KEJx1-Z<U@T%2Cqo?hMVom{LQob8-k&2Ig@ z$^Gg9aOL1^{Rp_QdosKI_YK%M2F`7t?4Mk19|PA8fTx#trx$;}{`nT*^8EUKW$$$3 z=zJRh+&{TII=eaq-pp+SjxKHhz}t}xz}Win=mub7<9Kl&IR1A$xER|6jBcJx{>6Le z<9`JkfW-sg`1<kE{`u10`Q+yD*v9eF-r3UMhq-?~^%wtLvT-uHaXh(kv~X~-un%0^ z|LeV2JiHj$I9WQpTspcO-8>!HJXt)v9Njn>**sl3x*FL!9oae?-8x%7zFs=M8r?Y` z-9BGAxn4cF8QTG_0ItV&&X@m=yTH}coB3xLVh|7|6lpQxAHVd0Gs3VSOehfbH{YLM zt7Afh5Q@Awkw7d3K|z{wK|t0OK|p*SC+tQXF=6^WzQP!Jr-2{Jd1IwK&Ee7B>}V?V z{j)twOH6m2H3r{wT7IrIm`$XNTzxm5NMZapy?y=={~wZ3hltu>wEUcQCn+aYS4&Y3 zP~7U9D2>8?w>n~K#ICWNZkKGmFl#nl(vQhoX7zJf1ZQ7~AtN`U(&M(gHb!s`y^K}V z;kS7nMPqUqKYpJA&;so4Y@2K<2CkK=lzetDU%wT9id`3T9Sl`rjkg&3k^J{dIjqbE zMroExqQuA?TYG6m50zi&fGQ}wv3DgOD8azXlxp2#pw$wr5|Lga7JdsZ^W@#MbmdS< z)^OVc%gQzme|5w~^N7pgb-OfA)4Bx?Ty7j|o!<Ng_!u1N-j9R3$@LEmQBFrP%G>3> zG*22CnZw#%(t-L@R-`*cdUa`q+LO6j=_Lw8i>SOm7>w15ux9J@UJ{{-UI>h4>{dAw zEp=9}cz>G3i<t$~s7misLLGMm*=LWAh*8gzri{e*_!jBDg3;AWS=$X$%2M}~7E%p} zAPrCbvZM}Rm?XtDUh4bdbxLupr|=s^I3-<_=0_V#IZ+1uUaE)?naT?$y>9pOaeT+! znobMLXSn9ML^TVm4|^x27uB@py3b0Iy%CpTzeGvW7K<loJGrE5y^^d6c2jM6$!3)V zK74}GRpy)V_+RzkL5VfxYiZL1j5l4h7S7T4m#8D{SkBOnR-dJU1dE;F`bDjn&64xW zAW=Zn-M(}U6RxPesQ@j=8_-~e`?+$ID}=0SKAe!$XEcMvFMSsA68Rc7<6(Pst+&C6 zzP0Oh?mlU~3hWt`2(to*!P{p2$z*V0OsTpC{x8xE@Kq_S1X6D?25EfjL|vZaV%W{U z%fM|7aR=kN1PUQ8zx6nW&KSW|$i?Zo@ICWD+OFdbiy)Uoop6Cp;#v0|X3rN&D&^C+ zZ#@~+3=%kN!<)`l2fFty%X3i<F02%3WNdLGqo(^AQoV+Yv|OsDM2QFFe?EZ`Sqi9l zL&Mz?<nX_W3l!>|D1lKSkszINDDkeF^pkY_VithQqh>}Cf(DJs2j2CQr_`OGzV^ld z=(PzHfGYQzc0*g}oA4FSasy%C91!TUgh)phf;|JUsa^3XKnrN1hxsp%p~oHnR7Bik z*o~Z^w!=RdFZqobrM(fqVHeNIfB-`$`z5b|dCEg5+lm0R7G@tYjS>w~ro9Y{J(U)4 zolrdFs(^J#B7!QR^M)RT_kdkP?S$EZE5&0TdvSu-4iGR3glPV<WPcXepv<0&YwA94 zJkKH{o_D9g9tIqi(EDtTK8NwizSK^G{TmBn&slgbWKT?Zz+ebt&GpyHqoYiZ?*C>q zBl79{3-`mol~=y@Vu{bXHcc2zE7x2di`o0AE$cU0TG6M5elc~(02XI<(y>L01y)nl ze!9CK!J@cHEYiz^GHrvk<9~|tl}ZXgrX~+qZ2}PPee~Ki@jLwucP;EbD`C+Qgd)?* z0&}7cE3((ji<11oBPCJiN*ha5gxCi8(b-hU$ToB4+2^Jer%Es#7@T{h$B%%}3|yox z{shxTRI~E56qDI;ny5ZLjqz$!an=e_$c(vz7<2m|VVjzO@_i?Vmg~sRVAlR+8;dZ- zKq|%|FzW3Fia%7F3V?)S4AtBf*`LSq;a_qCOYc(X`uPN{l>={icy~NFVk+IzD3s;2 z{ju$ugf#RZp%0qzp|af|jv23}$x*`<@MfWm!mx*(=DCEGNE@B+2y8CEGU@)boJNUK zgrWU7!bl=sj?1i9o{%Jzf;4)=FhQIKa2Lw$@y-_7->$t<42o7@R|stAlg`~RpbRBJ zD30nd_Y*1kS->G@C(Le25*V20Xd)W31Ppi?N-jk*apxdyBDQ)KK_vK1(VyPl`PXt| zho_9g-oBV(E?BD)Z6x~r;bEsF6vq&h#-0;xrw)&oB%hP*zQ*tvO5x35w!YN|NVgco zD)O5NP2~}}-k~t@S749=Xvc@-3A}^IS{bz;MU&<{T1@a4$u-V~cij{fDR<fyEcB$D z>rH7bZ^GGD3m=1rzLLvo021F=XSc^U(>4t!3&Nxa5x~Skrux%|x7NDP9^#8z`;9|b z-2I2|`IX{`kNwlY%a_EHlV(up#mB~HcNO&$X_rfu<Qq@S%9}=Qx4`1lPGq-%8#Gc( zp<nmn{gUgpuYOs*fs^;-N7Dw;z+C{?8|6pSVz?_x6Njxb4_E&CV}!ZJuxMiNb)wK^ zr5po9Ms1KLczZ?2I{nKwD)=~sf)znA?IBJ+@&sMHFhL^u*1UmXRcK7ONct_j_pJpe zWrcfaOET@9l20F#ivS`R&6D}LhEp<93Zv3NN_g<7Id_;UiD>+w@f^HO`@*wjLUFwo zyg7<<5ZH9m3|;W2lQoG@on=@oP7^5sL!b$UEQ$XRHPE;nk>IBZU1Mgm`Qw!6t(Itd zlJT!o-^9Y?h`h&P6wgX}=Wzi$Xgq1yOh~$ZWd0B<_EFInltg`ui=HUA?0Lx<Mykrg z-3Uh`Xf%;G*sni0TZ;ZECqu=DBI0%QxFNO@(6}Wy$R}QHqZj#tBR7&)2I$R@k_Q^b zrLZ=H^22!cSYXIJ*LkmO>_6!#1tQ8w?S?XF+WK`bb!pNtU%9Yp;<?$|8FNFW3oPnP z_r{@6De+KY58&8WMo5pG@!(8|PaM*`Nw6mO7F>I&omn9gM2h;XE@p0E+v#X!tY}o* zO69ri(Mt*PqeK)akqTFN5ZBSaQH}CX$XgOPcEJTPqsivb+oRPr7ZUInss>^aF#XI{ z_Biz4<r84UB{e<5;X!6D`fioLDLvhW1_8|h4jCV1PZEK7o@PLBoVG9})XRk)+aDF3 zm%R}GgwG?81EW+Y#CT;pXi|@Be9XjXD;&XwMtK*+>F=~IST+93Sp*|)lHKe+qR<b$ zSelAqLVn_2<$fkJf_{IMnnd<XYYs1~*)o9|0^y{{aEix6%mC=UK-Dllk0L)%ucXdz zZ<A9KsRHz)cBI|{)is#aZ+vg==N6|lIw6dhQ__xr?AeJlZyp^n+Gek2lCH6i@eW-G zRJJ-f-ceYf<Fpc|sJ_~ml%Gq2lz|_Ez*2?)R&qNjz-??4yp&2gWjOc>e44cCs!)U) zk(|BlH1RFCr^)A`@x$@m(<xnB5-hy7QE<P!y!dbzC#lG2y}9>fsmRb4S3SWi{WVF= zPk^JpJgr%mf&c%_yD~of`R>BlE#l25rt2IQi<>bKT8ew-5_YnK6w7ZRYDUdNpPTcQ zx=U<BOPyB)t~>W79EOsAB6Yh_GhNP;GX18mFC|QKsoI*=iKDlT%nzRQrgVTLt;Y0f z?iPa|l&+ab+BSO)$R-(#=4&S7vA5S&Ub-?$Vu)8rC`v#Hrhy-F2#eLyhfPc-BK2xA zb`j&YacZ#3rrdg+gIDY{O%<qAQQeSvWWob|vy|Stgze^cVzwpYR^yL3aM0f@eR+N8 zo+4aNZi@JNkt5kb#~PPsJ_8cU!i6QRSkd%29})~NT{>%BYpTKmy1u6c4VQm#9eb4l ztjB$S$;tLP|NZ;ugY}e%)&6Ou$yPZ=z{Ya#p}YwoB++6sH*?Gpu*DHTsq$ZN$YY*M zC51aYi**~4-=8H{lS?6t5s=i7nsGWHpT_N9<`xn)>g{r)8n$S%Y@A_C3W5^-iYA_z zp5+ybQ9uka&vW63r7C2Nr`Qjzm7*XiyRa?*?8OnDiz4259L<jhnX}JQSc1z2Zw8+_ zGh_}#Tv1lE3*bSvDl}pPp=4yYQ(;rPA`t@7qfxS0A)?_G(wdt#P*XL^os2Vkh}Lk8 z57<W`yj!hwm9bIiN@c1Yyw`${%|#W<PzBX6ig0X$zL!VJ8^Oj3p(5Nv@bb*?3+WJN z@uq~k_h61X_t_K71=l>^T<$2TB5~E{7NPMOnBKr{1CmyJMhDB>@IWQU?1BUy4}x(6 zbCnssEtr5!E|Uj!5JE#Bm?A|se{tq)!xkH@W4O^Lf%^iptckNQ^sxJxQN~fJz+ey0 zwI>wq{{<!O`3Knl9fR-n&K!SYf|+oDZ@KeU;+_MHRMYe{eV$kg^VG>Gh%Gn73=N|X zcXxojU=$IUJx^a0(!ZcBhgb~eFR1Y|rTxdhjPd_}W>oR=X#n=GxBuatMC7l`fnPBE zua{K!0rz*U@yn3;LvZy~=NYekfQAhwi#Jg_O$*;}<(vX7?P^vbwQ&Vs4WR2e+wWv+ z`87{d5rxZpfQv?yj!9?113@8uoeZocZAC^eGJe2T788SiVoSAnx@{FK0X#fi4H3;m zYk!?p_0^xyeM`kGcBK)<Pue>1_(m65q^lKWSW4ORd;+GdlKm}i{ZC*T`7Mu7QK^5T zs9|dlueuU(3w%Mdlk$OA#RqT>qM?oN`vSaLOk2CUP}o*~Qu~H07o0msDGoc~gqZjb zrj5zpR~@S>Vb1-u=chEsqq`E$Nn`35kX5xWf--*HPAU2$4zR9o!p!R4vjV>gEC(l5 zzoV;gIR8zBY;HiJP!>=l>Ic`J%=n_ygiV2c&!?sUB-h-@G?Ox?t6egGg#VQDwqtFw zVpls{QnK2!3w|aJ_f>Ph{Dl@|p_0Z3UUTO`Wwak;&5dWz)tHU9N340<Hp>UMKxZ5f z)+Fe)i^{~?z1|Jk&1-sm(YT&^1NGOu<iU(ZU?Mp0IeL06VJlPue?#D;>7O}J>mp^S zlCY@J15Ek_5~L?7G$?vJPHKLmpK>fIODr?b<?(Ztn+aM~xES5IEIL?$ii0p&m<kr+ zT>XYSgR(9ojuQM_D{|}FF7O@_!w65SI<0%;E!zE>N|L@Q$w<ODg3X;Wbuq`AOI93o zR?Mpsm?ljx)!~eT`^%3d>yv2&E3W3iQcajEFgmYR)&?7@8>NZ+M$CM<5}S@i7M*Zz zj{%lV9##(c=OFy(B?^sW0K)i0V2X619_j#briZq~)?sce#c!-^#$Fh^4Q_qw?3)@? zTJtHP#`Wz9I}QKXmlt!pnMm|$y@-C>?@4D~_Q+&BF6jqFFKgQu&EexW0&Ktp?-kpK zJJdE<EauD?@8F1uR%HvQ_QgPu>i8|Y`6R1OpJkg@6U;|Xt;;29bh#Hpg>-muWU7Br zzj7A@^Ix=g?;unE-^ADT2+O~086}?hFL~O(8U&#!K){(%dZ&BFiHnz*384dUM&-Cc zNIqcy=i*gG0vX*(0S9r~Nd^bG?b7%=_0mBCk@5or8T|yN&iy{*LJ+_EGN2qpyPcn5 zMKR2E>o%cXIWla9sVPVc?F|uiSZi>hoVZLsXyYEnF^O8c%nfG#3tA3(CO^0yy%C0W zt(l1p7*8!OGYEF%)}k1-r%Y0BND{9;o4C^glK5rQy^a?o>e;^IT8bu^v-N6{c_sM> zm3X1%QT~K08n%w$J%X8>C>@Lv3L--|P@&(yq!pCG1vhPE^V5=n-POh5$<6kUYG_J) zU^AT5?{|c|Uy=%Qie_MWk<M@)vc|m;>Y&^FCd-|qJ)FeMo>z&hs(D(PD|=^|U{7oH z$Rg7N<s|N|MqQli`dZYBS_w8rIHzer5q4zhnK5PliwfR4<_mmgNqTxje&~n8enSy6 ze!8g;psx6EsJ<cF7zr&tf<xScfjNEWD!$jE98IIyg(@t>=a0(|Ay!k;kkLGTsPXLR z@o?T}AY8E3q$|}2H3sxY7VrAW3$xU=15aG^y)-{>l5-DCg_rgYdPa&#fmP27b>B}w zBnX}AG4z8{1$2@klt;YAhfP5g3Ag;butSFnU(c@+%(3{E+70I6hD~{~<8FRr=U$Q} z43D9Jn<PYyA}u036N45u6s0KSej{?g0?7oOLWM%KO?M*w&udjivstu7gq(=cytMqz z$nuI+g=MkRKkkUD7Eh}5%iYhpGTN$k({HC5Yy4%KZbr?vqc9WtHQE>Swh+}G4~!2O zFx9zwMpvLME5%f32Pxz!KN)g56jg+-#f@NHw!jfqb-I=&G>3LOBmT7c=p}T^Jw~lM zDbMIncAn{oC1B?`&eJK4P$;OvGE^!uvQK=f-;Y(43I=ge+aQcH3Ly`#vM#p@yL9eV zaClr<{A~9nlTn;X&7DG)ZSH#8X@&<~NsY{9mx<Sa#xro&Ltb=CcI&@5yLuSbr4G8O z9DKToNTfkh^f6gS1D@H<w5l25YT(C%0*cLC8do~i3aVLP)Jem2Eb(g@%jtrSgU8MX z1^0j^eC`vy3^KwzeXa5=mbC6Q2-BKF6<Q>jFO65xt^fuI)nZMyL%NY)YYnzhGq0p6 zJRKZI6NXEb%HM_(H2I8I6vgaMcki{V2X&GKzo#q*q^wlW)9Jo(>&Ku)=J8`OY@ao2 z-Fr}UO+<{IjA#0{{W3I&z73`8&So4o&LXX3-9HrJV;j~a<S>C!&@YeUk3~GqIcrl= zGY*b3i_)YG3Mk++gXkc#E0&R6G~`&=)_c?vD`BIjeNn!=B>?}iXRws;Q|$-oegIS= zU2&oGQh?r~Jfv_=6Kz&EY5BON|6M(Weekc|!!-N7X&`k14vpAh49CL&_yw7n8pHA9 zqQ1U*!2&6AIDH8CAmGQ@vg>czup}Ej1<5~BoxzR`tFzSUQ8Q#Zttk~~fdYFOC$f~% zd^WXEo;1G3qAo6UaJE%K7-H;uN+%BY6aJx5WI-ZEGu%%3ZNMh7yMZX4J21o~_QZzL z$YB*s!LUlT1g$@@%(>Ij-+o7mF!L`_x~nL;>Dn?R!)G-9Qs7H+2k*0J%BZ9-v8ZUt zGG~);cGxFC*?Bbx_B?@z_U!X;Vy2;Hc}Gzg{Q?1Xu7*1Lw8pgOv(cshc_`+E?uC{V z_1dY3sZOj=5k7h@Bf+8$aLC8Wqxf^Gd|5JR(kuAw+T-R*z;x9AQiKUP+cSbQnp1t} zVrMebB#L=BjnTR1e~Q_y#x}t}=I53DZ-GDGHT*&~rIY3JDlYWqTSj=+tY5h6UBc5` zv7%?1KXAM{<DWm<cU|E>d<x+8soyvkFDoD4`1S!7_q5Luu1Om9#2hv^H*-O_LB+?0 z1Xp3ZZ=^RtPc-@pwtdiB_(7bP&p9{3JR4U#J$5nw)lfkLzyC$G|Nrv(KK?&jz4(;= zOEa5KyaSH^106n4|Aa5OgwE2g6cPEBmwp<7z06~r9*uz|EAx7EdPvwy@Mtp#fsqkE z&b}H6&tIRq1qp)s44H5cYTkT5gb(}bx!fTDR9*>irGsPpJT^$1Ok&TZ>J~w0#n5nE zUhta)u`oocc)o6MJOhKo=H~3SGh}2bp)`YfLONAVZ5b67;i~O<ViPz_(-xXnOoPfy z+jUOgf_c_xO*c@NrKqRo?mQ+des>$z+|fP7a2E~ofmvOr|9)ciCVCHQy=h~M|Aq1p zBs5A#24Kr!MY(F`KY`?V7#;wPkj!CVV?zyhMBw0=qdMvOK>2Bc;Ushx7f^yeMWW<d z6hyBV9>puDQ9k&S4DHBi+C_yd;zcVl_5SyFv~kcZQcsFvjNgD5<M6s45u<R@wMs<u ziE!~MHW(H}DvFDwqLmJZXz8-Al@2<c{VFjUmX%Awa6#AD=S=eOYB1?393i?g{Tj{h z$SmJ|QxHq&Cv}dK1`d_+#)L3=;-RakMbi^StV_?5NSr}}7E|=*#gh}K)?*peVCO~q zzfavG*c$}bt`>89V8B!T`XTRXsCf{gz&&G19aP1NT2Pts16Pf+TBJwZs!W)EGzAHb z^c&9aFDku3g@MZA(=UutH@V`_K&l^3bbEMm6BK<7)=8BVzjLoKzt=MvGcYepJwqjO zN4qh&5u&Qe>g?fcYd{)D60|0_{zf(Ck%Qi6B9tPrM#jV|s1SwOet%ic)UDTHpy>H= zidiisPDe9oRGcL&XpJiYmk*Q{JCD4O)TT(T6-pA+G!o87A1P87-HV)tQWDq@QAJuC z6Y$9(H(y{K6I*S?si6ySiggrKqzXoelvFAn-&8X-*w}5+$U<lGpZ0d=XLvK8aCHB@ zT2&X_c;|vBQ0|c95|}t@ivYdDgW9S`YQ-kRm#*~v?pZ8l28oIxKw%aqP1Zq%Yf`d& zqlm4!t7MDEc@6(G%X(bKAE4myP7&xznlWP$qW2s3upV)A4yAfEs7roOUbJ<9JL{4( z3Ei_3gx?-9J~i~mea_9#JJ^BquxYWqM%Mt5gTC1T%4OVFPX@lU`{8$j6Z^nz!>Swe z*FReu##RTjH{Kwz+&zjrk|QF$e!7bq?+-O&enJ@h>wb;C2N}Czzs4nc{K7?w>%?4A zBACoS102>WiFfvcsqY5eQUxs)P&ZLZ4NO9(X=5UMt4R<8@-BISIdC)5Rs=OX&FjUW zL@xWKXqK25s3aoET@p@JTabBc>!L6bC~Y__+$V!Njw)?jn=!YX(h3!V7oN*iMXX5( zxwM?=htO<%%a|;HSX%Mtrh0n8;FPf_uy+>>T=O|J-;RZ#evI%@L{xd>FRMRCq^?c4 z()2C8C`T=*{-hCZFUHk0(9d7!zDp5|)y^Glv;~l(Ke6b@etIm7Up7FUJL64;r20=u zs7l9{K#~2K*H-ee8uAv)e|{;-V3<)eylfLmcDmVMI7r^={cUjZ`v*!1@3J=ktX%9I zRJ@p)<hyRU62n<!>lCLC=zeHy(ZT@I5tMWtixt8RimmU{VB1Sd-Wu7ep>LRFwzQHN z*!k`Iy3N(Y#|OU7^QY)qNLM6S3)fKR`O?bMXT^wKQuaL`_Tv}d288MvrwZew6GJl4 zJ2}>@tf<KE+@F;v3G)eb=kV@xovBpg<mTki&9fNe6vzkbZa;B(jG#<J7toOKj&loA zKboH>6{5~nQO62yspRRto}ht4StCCm7xKq+4q*;n(*K)6p=X2|c7^n`M=??1+K&>x z!o<nl0sTu>(Egw40e+PsKj8I^e&pB_$#WAnoUSsUYpES*v7>vCge^|YTOKE<E^QV| z*6+%FC&1m1Q1+8CeOsLmM#1IW3+tnxZW$~Kr_N$p!uQG7nxlVo@oJ<@_~zSB`pvp; zO)wz6M7E0z1vC>BPIf*GpXN9B0-kT8gSR2hU3G=OlN<$`V{Z@bCXI{|JiZF1l}KPq zdJiAU1cE5w+>BNA6H`>&VMhMf!egFiU6$<;2MUSWaj%l@*Zb?k9u5N3-(v2t_4MlS zA7W|~5DfpXx<jo1pqc-3@9sYf{O7}&On&>%74*l6_&@nIaff-_xjKxf@?%epCt9WH z{nG;7(n7qg>0I8<iJArv_%*Ac0Y1x^!<UEizdnN*IAIs_uiqX<Zp<YA%Y-|AQT&g? zic|7mXL9f<{m&S03jS-;MG(Gj|6d)>bn}F1=gY&*Ue<A8Y|Bimq63HB&dARu+Y7*S z9pj2cx(DBv1g@LEB5OpnOI=Y?>-<kM4+EhzV(ZK`j(5||zzxT^*j5e?wyPOo?(PW5 zl{lq`nT3~`oGOoFzV94?RV$Bj+c`#xPo*Dw>~3Be%x+&(6HbVebv;prsY3<=!yv>d z_#005#&Q&H;>N)kZgu<bWP<~A49tZ#(&2-SvN;kZ!F?hzR2;_Pf#fVkb#R>Iyb8kB z3^2{}ztw1;HSJWG7(BT5D6`WWf>hOi4;hlC)`KgN44^}U!9ez-m6V5xe#42V7>*G1 z>8_#uv_e;iKvR~%3MI6L<hJYD*iXS_cq^i@st@CCjU@O}S0qw2ZK2eG2QQ(XFI|T* z2lE|ic?A(30Oi;~^;4}qJH=$96IoWqOEt;il&PVm8|0yjaypGY*C{v1sL8U)`zqs; z!2>cskXlVNV6##T@*ySUa)=l=NLL7}xbN&dBge6%?Z`lpFC9&jfAp?r(-Bm0;Tc~2 zqBqn7L@YQ&rD=C|;1Bz*v*&}1V}-h#RC44gGyAlQ5;^+;)J}^Rpp(eoT{Vx*=gwhn zC$4^B_Pq4YEgE_3cKE@%v96r!-oR&J&8yYN3mrt|e)>~lynAKIYJJEd$#v&f_lA`X zZKt0HQZDb~B)Gi+DW6YAjDB*T)8|1$?S;T+?#G1Qe=&pYWfpg!1T&y?>Bm^xZ>~AX z;FUTD5FDc86l%Etp$sD(g|9}er|?fZ{6Y5b+=uBB8}*;f7yMs3)`k0Dyx}}W{?{Lu z(aJALR5ne|guPVRs4-&l&S)o20bEk3O4%cWeU7+~q^?l8Bi2CCxMiIs-C77sXju}~ zuVPlcAIB#3FzMRFsiD$&Dz8GEi5T<SkLj~M87mBe=#0B|^;SX;JDQ{(9;0Ys3@ovm zM^%F{Z%<Y2>`5E%o~Jm?f|LRpg%U5g&dSWNM-MDW(X84;!}c+ngdVXM{tQu@-|XVZ z1Ny=KR#V_pIn79k=ABGdL4m(id>CYDjmY&x%uUb%iP5v&LELghHBwJI0a!#US-o=< zMSEQ+>8+(kkYvIeARgCrLeO`q^vy0a^a4ARBeLm7k&a?6pF2%4fyQc+zAQPnh-Vys zM0Mpw;t2H1g>0?z9>>XGqpn03ImK!pS#q=`<A-ia%ez|D+`bVY+>RGnn`T8ds`p!w zNewZO&}3=ds{H663CWKzN|Ubuox4|%dSpWFx^m&Q=Bek?($g?j(mbnAG=Bk=O(ify zhmzYZ4?AUn%;S$mx94OB=iI;wd=f)I!E4O~W<r$UsndTyS<%eVzY<`y`JL>aJ<w>+ z`d3P#-o*bq!pP<4<p9O;HzFzf;B>YkP%+JzB;{&@?Eai&Jp%FP>-al(|2w&0;rxs4 z{lGy;K2Sk~UdjJ=(xLJ1C<6i{&TGdBM9BAXf=b*C2de+isgBKsnZcgzR#&F#Jt#Ge z$~ZIm?_IULUGh~tNkPgX(XaP+FRp(ZT+K1~w-M470QSF73ct8}5g_=7e&*^EoGv5= zCTjY3_@ZR|+8wd>s$DL^!m^YO)>c8Qp)t+3xyK>S34EY?Etq+;>u{C@bT3jV7{vSR zsgk8Ll+-6MF$Va`pF0nM-E58qGwjq%BtM3%tf-0O{YZS6E6zWEd^no<vy=T&Z9x8% z@gh_&9-NmGK90;V7@ju>Mot^7#7o*7H&BdWR=<hga?rlH%$OPEG|@>_{~YC`1@k#H z<J%UdfLzO&+G|Stu*Y)i5E5oQdb;a4^vmxBw|69&db0UQIh@}}?+t6%$&22$;V9!^ z^7Y4zA`#w9u-~HV`g<xRW{Se+dQXw3W$W_mz`yJoT;5*RpW3dz==^B_)M*8}alW#) z)OxpX1y1C8;{y<{c&-M}KC_QuSJV9uBn-If(!B2SMi*|2-eMhyPJR)kIb)ohk6v)K zB(9Y>b7m9|t?2hpD*?qp!$Z%Mq$m;t-o3}sBBr{8l<i_S6>OE;z9Z!-ajC$~SP@f3 zA08kyh~$uFWWx;@+p$?9wbocakLf?}>ylTNeH%FdY5!y4!q*fCSzv?@YpOR#J6VnB z#LZ2?ft-94M?y){66E2gQqZFlferqPXuLkkxZE^>vh7PaE_6@YXy!CfMs$LO(lp1q zH^_O42@fLKjmawLd4-0<fE<O?U@Vj@F%iRv4ohKH<|Jxta4S&p=dYV}%f-MwcMV!F zv7pYjS*qqOqC>1JvbzZkvk_9MV1Jf@G9hQ@JR-c*?_>~YcA=H7?Kl1S6hmsbc3Wsc zXZCyj^^RciAT?9U^ub;xnYjCJhzERh+=&Ar;E1(kLd26Z6b;#1ePjpw;9`+`gV;2z zj0;hFb$bp3Hf8SZ^l{g}hS4;)H@dz5-(Y?0;m7|Mr0)#|-MrEG!XQuN9$iotvK(&v z170uSToizCEE`hEe$GIpws{%qW-vmMIWK?Jo~HRz7e5yj!RCs^(%lfAbJt)UXeV7X zsjOsQZFx5fHw%k2E$3prE+1aFko^WnpRL_|A~1Xy6X_|YWjzhU@Eh(ZdZzE(zmp8$ z2PUjKN|k6Z;4T(QE58br?(wtGW$vN~YujAi(W>f9Bw!!#I&<qCXfb}XPvd9lB%OiL zJo3_<t1z-~r)43-*}8cLY)W*7{up7<QvPJ@{S84M2#Z4kNgsH08g0~xx%1@&jRCF9 zeT<_M25=wuwutba0&LVM(Y)S1P!+%`=8-apB7avE@d*05d(fwCs*EKjPU8UF?g)yE zF@r_{hMVG{3B|)C6nzBUBpgYc_6TZmHiir9oPQ?60-(wD)My@|4L$;D-=t&mdcy(@ z*&}HD#y))%HIfRu5tX-coJRDi#C)%I7DPihK18?Oc@S^nv_~73qnF)e*={{6)i{d` z{Yg;n7}KC3h2T`QKm<b~DCRK|IYU(#>**e#kLUg*@Q@r8I9OqO+qmInPaB(kf+ZM} zS2jh@y208Os&qm4BP1DCk^jIheAbF}^-svcPDF!{`Y2M9zQmo7nx4QdC(e#MjHn-L za23;c$*7>g{7@jb!*hv15gUD~PC2;`y2W;?gk02#N0~xMF%4)9Cc!W^BgLOnB-s{L z@&5=YK50vg;o^CbEVFdcN0CuN^*qj-0#k{L{sGr;j<nu93+jXFoQ_93N=1AQ6B2GY za8J`BJ{a0^{W+nlW|_O=lfzYNjzXYL$32{qUsn>%+!FCl`#Y0#xYrpC;+R&KA}A?H z$vwe?c^I~wEtg?5U)=$&<{Qj!_8Hd9<bm;HRXsmC*#@d%ik_{}=6i1ysf|EsYYd8q z1%@Ej*OC*c-JDhjX^#VsFigOyN&$7$8e&xa`e@1}LFo4SmK}YQ`^Ts038cByUDT4U zJ3Tp&Wcb!d%99`c8`n<O`ji9o_bJ8e8--Ob@Ei-2q4Mna!(8lJ1ZMnA4liNujx}b# z0FIp8H3o)8@~)>l#%cPd8;<9~74rNpXk_txl)b<>^XCM7`Sw#aY#bT{QZp6|Y$?U< z3O;S<E2GR{zxBQ>$~C(%?yTS27f~_Qy!{S=K-*9^PWa*ye|@9SJtPx$%SkOmNM=c7 z(Z<wh<QbwQym`Qpmw}|whQgLe{=>${+rm+1Qi{V(`uR617?VhqOaoCib1dFG$gB{{ z+`aK(_D#!Ar6do-xhb@1Peqztv=Xc%j@(v)>|1mxCC6_A`UCeAxDFx9oo&xfc!aq| z-YxB~KmGT0qTD~s3l~D3Ct+-|kH390XIy;VJnOwU289Hq*pqeU@q-#odcT5SO}k~Z z!E)10`}JxtMT%C>e*4xx_lxSm+0&@Y0sA+B0QDj(w-_+~#Pg8Af_wD&J6S((CQY04 zhlb{kwq7y~LX>N6M&GBL**L<*=FS?NE9SYr#!?=~nNOZVjV50u*356K{TFH{%-|;T zX1IPsweh83*tl)J`i(6L4vZfGMsE};IS7IUbW_4olNyPFiQ}i0L&u!zf=AP|H=WI4 z6Q(OgfImMVf@cgA&jKS*wi3Fn=^AN2231(1=9#uBs<TF=5LKN^#-;iSCF<E0h=b0& zh&As{YPmXuzZvf{x=~u+4}_5dy$0PM^W0-1`W;<*Y@Nc-7O%@L8FUTjJUYPK{d6(( zbvU!vCsHk*gaV%A44yh~Ma`{ug5(&*e);W?Z1<-Wxp}-C_+5whFo&e-*4G2xsENWz z6p?)Qd58BOhwSy0QD?#=m2bSaaG!jX773`YIOPSlOw+rwb(&UrK8HoHj(dP9H`e%) zD8mm@3Xc>rRmV+GH7ASWvv8_-Ds_%7O2JCtG_ZRL{Qh7NCV(C#Fwl(D6X%nVc4F4g zRU1VC_%ktEEMJDkM%KpjwGV|$%!vYrA#bfLDi2Sii`|?E-87fx>Dxc{UVF#gU(PtB zcQ5m`WWDdn?YQ0l0OM_2MMgW5_d;P5%m^$bQy4*jv^59IxWV`|7IhL*G>t|udl zNG{2|*567}9?W1>#~xbn_&_8{Fvn-@F3<PImIh1;frWE(<s2iR_EH@#ba}b!77U=L z0D|yb=6MG<ed}ASxQYG!IjLhXT1cJwAd}u8pFKz2r9`FJYX{6ixzoXtrs6gT@CXa7 z9O`#)_B6l#=rG$kBD5RRiM<oNY{{>O3_}_YZ-u&Qs9xGtbFA!TIB#tpai%kfp_hW^ zM+zad)5B#IF}mxQ=Ch$tKoMgxg!P34r1#lsQcl1x5l5|*eW|Yp4QbCLvndVIen7`k zWdI}j6?Th0E?*R%9#Spwsdy6B^=f`wnefAeRK1N|9xoi<C}by<!V!;)O)}Gd0MCFI zMFP&{P^IZNi5yA#;3>b%Z_eQbFLi3wL=5$Nbk=HE__7hoTvfvuzs1Gmqgff=T}G3L zoujId`)g5<h;stGLTK+K>}F;XTt72~u>~pP1JY}5Cy<+UU89+Ur?nAcG``=RZsaML zGG#}9Z8MA8;_-HDUBFuH6le3WUCL~ok~Wu;`%Cyt7-+k*9iBNoY$6mmUE#j>2zJPw z>Ff+&&Olxud))S}FB`~uwr8ceDYL}WRxNASd8MK?nL7Slv!TAS(5SJyn$HdqeF17# zJk5D+72Bw|OE*JJ?QXlzo&YF}O9tL`F*b!;^wXEg;_Y8;U`~j_O|NbR6$=yu83l0~ z$6p4zku}Z@CQ;ADpd@E@@}mtPx<2##aKwCbkVYC0wb*O4rOZzcHWtXuv+^{)@Zt}> z`Ir;?4)0KzO9b6+wN5A;Kk4UGD4Y^%dSIp=eR+6!8OY4p2xcU-_I_Kd{-_z(?`Cm- zlaC~jH(!JDq32NBs~CSl<qsa&VA`$Py=T@Gi3)zf00GZ~DQg1jFKXC?uVjXdH|B+1 z7)MB)D<q0OL=*b`H_%|fTcSXi2idK$jG%vUT$1^=gh09`C0h7Z>3?_%apG&qQJ3SJ z-AWx6V<L?fp1sS6O^!sd)bcH2Q_3-u$t{v@sV6h%5L-M4oatpJ6ti<L$>Y|F_YYi~ z5A8A^QY3Ms6tikPZ1W2E{z?;@v`p*B=k{`JK}Joaa=p-AZMSt^Z{qAbD?z%rnC6)( zUVfFMMp#a}kT+FwTPTz8f*&-*X)&aot*@<uJBcV67}ku<@%?&!R{GBA<h`ZnN8E*! zGk#tw>Rh_hG%C%4ugbEa#B000^SRJiT$NVI6q>AK&64n$oylP~jf|nV?Y|SZz2CQw z62aG$0{6W9XaLOjA1x5{V)bpLj<+~BGWKnH6cjRLJ}Wh#{?@gsh=+Q2^ZrG@w`~B- z61LxHL-T(~q><LuCbOMz!(5qmCZw4vz;D{(x-zFmmJTS8_8coNg~a;R3Vr{$%?mWE z_gedEUDSLG&cj%91WI8AAP+i-#f08ZDyc2;WThgBk|5u)`cH%6-g3cYsWAml9VFlN z?sgFpY-`X+BJ^2VC=&>25(aJgc9cf^L^V!@1t&PZXWso`Kze|9xwO)cz(lZ#Fyht> z)=O}#m24EP0Rr_IK?pSB(eddpfJ1OdtG!8j;n}T?RN0xudtd%MO8tq@<I@HgAzBd> zaJO=6yQL+NJuRs2waqy@*Vk><go)ZPs2%|a39N8JRbB_nK#M-)3Qct~6Vs@`x?O2F z!*k6In2i5JbQ>#teMx)m9vfEOM@aDX?yNK9@aA@%9aqMKLomIg+)gKa9e+ouur5;< zi6{0aco(EPvYP5=6u9YGBy=7^Ues0>JyMF(b6%7P(qrLB$v5mm#kJME><jD9-ToV% zA=TfGWzWKs`LkqeGH~HdkW>a-?o!9;<_=$9J;WA*B{+7Wj+NS_=>dvZV+x?`AN7^Y z0poFzeO>E?`-Xq`pN^P*?&U&L3Rf(Fi`raQx;rR&`I<GhJ(sI`T_kTY`Z6X9=A9rz z55=y&nB20{(qL<<LSw(we%G>Bp6!}Szm(fG-9{-xNXPN?QYEOm92$}}Oml<%x*Z|b z>EGL=66@PV>Z~=encPYSxRoxP8MZm(^l&*yDk27XbzpPb{jsxI!THvnItUwhAxgmF z`t3Vv0tgFeItADXcHsIxr6p(<q|6c*h%@>%n$`*EVxiU|O=_O_he`wzJ*WIi@>%oJ z&<X%B!h2Y<)`^Qy0}YNk0&Aoo^(!ZCu+{0XIcwhAO)+D|sF>&%;w=;MiDWxrpwQd) z^scaD8OO3+EX%-s!4p|+@@Hl-f_gf><t-G1cq@z&tkZQ`JjpEEcoM@=h-Ho!VB;W$ z-Cq1HF?>K?lrl!VgE2$CC12KEvcGy!**s0b?B|ug<Q*Fg9jdUxbd9*bEr`R<hw-?z zr7aLRpJmuV)S}7DUlRh}hEr|{r@6+cvV3db&|4^vD4pcqIB@+^?Pq^``)S}v-OuOx ziv_PXQ)g^_0N&rVa9;{r!Vd-*#T9TOwKj;WuO54Rv8z<LjepG{>ma)E1#Yepk#l53 zXub0j*`ra1IllXFpckU5M(K3#tO`IqD<p52<v$XSh4k!fZhbT}a~AF$)l<7+OXXji ze_FCXnXZ*BE1Q#)uq1)_K@IJB;MT9n{n}RVHwQ*&X=5?;^qFQGA`asTjH>wCkqN$g zsv?3am1y7#X#1-tWXK!A=1C`$RC;2}gO&>uBg)K5XTj4vE*Noxfe6sl1Om=n?uGw! z?8hGKj3GOJNLJC3CPLq$TQ$r9SrC>+HB2x^kz5U8ZGV^_7h-K2G2!zce_2Z8vJ*S= zLEw~_4zJ%VTSuDUY%9#LZ_@Z!C@F=OAJKqzq-~wqgPN-27;H$GrK%v-V9!RTse7m4 z!G46tIjMze-vKBO=Z+~t$dG7<uRE8Vh)vXF38KESs|(kbs7Y17NOB{lG?g<LeT3gV zoUn4&4{a7r<p+pdQU(`$T58n@2ZQEKmiB3;pc1xV;K};UO2s(3aAnV*+=ZtSjPCOE z2Ohstdvp*EM#30r!(jHan8cbvYS!N<Y*L9%jTvov{358goxl1nDg`|brksNz)N7ZY zoB4gv;0bG7R0henhJ9y+Eu``!_1>drIS$EmSrxZ|>+)R-Wemrheh=H=p}BT5Vu15? zxSy(=RutT=f#(`R|NCIxSVkpN+by5+v=ltQ?oe-GhlBsBceJH6y=W*QIIZ7&@le1@ zObRr2Gf|KYva`dJrmW-ouVHtAB7+5pp{YZT+HdThUCXxud>MHI=Lc~qf``L<5A~9z z3(GB}n@EAB{k9EkW+Zjgn1LIe=D`<ginqc#zes&<@*0pv4n$Lb9@!v>kXd!w?V76- zi&zV<K88ttI-C$o$C%Q00Ly$XLNI@~&m{Tg-?Nf#c$!K*%6MbV2&v$neUN=88w{;3 zBN{9Br5u>7fhWR%h^RsU8O?)uIeF$mkVV&C{zxH4hqc<;S%C!0{9~}VZ`dOkY6@wd z12Z54xg9cv?8l`WmvuG+^p_uX(+6KFW;w~!a<x{$^}i`*fX6dEo^#M`Icac10}F2U zZasQqT+xI1?1T@5_v|bx(3Q8?)6GH(Qm)+;0b7{EJGnsvVM)*q(#fL&l`Jr+8n?10 z*((Ojp@2(AXBDYIM47rX;~Xf#Y8+Ld)obhKJBk;&IP$>^9%ru*B2Igq<g`G{R;9(F z0=|#}ZTmb1y<N{8mPkxAWG!Ybi%AG0c5x_75}S=q(A+?q;ho&(g#3hpGXF>}76?2W zb6rEzLcl}Dr59Px=d%#jQjtKBh9ZtzLYp95#YhO%xjlyTarRH^x|8EH9Tf^cb84E% zKezbxw&S6IECpbS6RzDiDl`;^Tl<4A4f}cf6RU9_AvRq^+@z)OBUKX$p5NzVT%dBS z6;dmUhJeMoXDKk_cUm2CXs#l@eUMVUYq}Z{(~J6-|FEl?MA@am9&nW}32EYUAi5YU z-;U+l;d<wWQ%uuI;i-2rXDAh$8$%m;p$%H_%MqY(j`Bf!z;au5qVgmC@Zp|BO?Uiw z_~nomR0m}vcZgfc!&N8Qd*{22g*iYvwUTR@+4#Xdi&Q&5l4+($^<AV{(3z6L-3m&W zP$pX)8ENA(v*xa3BZSqX)urQh(vLj$RsSkDP?rAH;C7S(J#3yG)#f8NhXXSvPg=H| zF<1<4wb(yS2|tP&hPH?tu=SEAmOfb!BUS3Jl3)pfLzTzN$t$c#H*MOjugh!4kPY8! zoCvzA@|!p75`l&iEbq?9_@%J-^E=~!E)EFP0wEM=loIe8b&13xvDqcQn<MF6#C|@& zN`tu#he5hyh|A*s9tdPh$Lu<j_M~o*Kk2@5+o9mP!oKi=W{XLj)ib1S)EAFG6mxQ; zBIGJ=&>I8Yq=`FXh`MWlE`3O7&i!#yJB=6oJ=M@(W<j<ATIHg2VWA=MIh_k3N8>_@ znm}eEG3^?PlwRp~8<--U{Bb)1m{hK`!Gre1{0*m<Me8~j5~QJ^3S}IVGFRMc322x| z017qTa2#0I>CyaKNcx*kQ1^iTWy71bbIcK0teyo+`N3$+t|)<yAiv6VY=XqifRmqQ zEwpiuciBD&m>NwQd8u5V)c`zJ<q-Re>{iIZ3u(9oyJ)sOPCP8CD8^T#&#ImK`2$36 z1Ye#GxI#6@0x$MdENr|_w4Wa)a$PiQ8NiY1&SEJo9Gb>;AjNy?)KbYB1_hiTJ7(Y* zo6B+W<pJsPN^dxesfQoSw5KU-Tfj~uYzgvoThYY!W4@&`9B<=-d>Y%r2ap0uMJ3Bn zc1VK)&%hXYmXN61W3Z%tVNsjyXWX*8)xo_@y0PA!&umc(+l4~DcbS~sf$`q-)_Da9 zuuyX)XQBA*5{zo{M$L0N>cQ~zroH;%3(Fm!2ytnO>`J`d0i)WQG=MDvHyg$87<25s zQI`fwd+evX1(Q4;a)LZ0ploRF6mMj9WR8^IaDa4WH@p$#-el!2+(;3!6`E8<Zy#); z+E}&TJi_X+;!k+NYd}?`3V0l~ZHA={Hf1n>zriAd0%TI;)6SUNtsCvGqKhOs0S@3x zFe=!5utFGiA;)WzflpYmdMKYDA%jdDorD>Q82mvo%S^6SPWZ(*6=x(Elu82|0sZ1x zN^PsvD~g=(!w^W{Lq?NjmvIh7xJDit+l|YHlcXty5PpulTxr#Z-QkePhOPh5XJXQ^ zA&x&qN?Gzrsw+|oh3AodO;`Q(j6|Za@czny%XQv%roCgSocJJ1^U;tS-5h2QQiOut zG#uG;lNk?Y?UGWZ`%5QhXD}tVqx0zL?Sms=G<c!z#oOQFBGWl@4TH2NYWBN^1@Y~5 zC$UMhQPQec%h)aO2E^3FZ2EWw6f=zhWd412K|^nNz#YMqF0^Ha#6Dd}KcSiVX@-n5 zi`V?+uv^yDi6EseLo=UtV*)+XHL-P^;{`Uz1sNtmH_^6e6EVah(I4Z^chV`Z)hL8P z2#Z`n_^@YcsY5d_%^VLJR+-?!-OD3xu!A;wcK@SocCM12WvtXumr}MCyxU!_^kACV zvc~|!v{)1+Y6`?zjP?=pXG<2AIH^%Nx1R|8IsVK-`Sn*SN#xxHEAhM`6=+C^Y{J~I zg>{-{J^r{4AFo2h$9(4fuGgbaa1oj80-t<6gJN=dLzc2MSiyKh`{cx93A|e_hSAGg zs2N@lR}OH{9GB{m3-(La&$o^*IawDrxFOXtSCn+%x_B7EJAgp9NUOPs0|rO?Ccso@ zJ@YqgqnK(GW|S?Lpyd{Q&Q3Tfe$HTi9cX}3^R8iV#j^22R##P-JHl)=b+3+cr!Z-~ zSQUaj<gx;q(^T3Ws2}?h`ON8%CPAni?LjkUV~alwEE7)J0RHG0$hE{DJ=1d7Xt(UC zV#E|;PTDYdpop-Ebv+sF!n5xe1^Y3&DjKm?S$qtVTJ@0IqS{ivb$MnNjNa0jXEouV z!!yp{sGH`QSsd0vReq1vPd4B)!NqKnYjF!lzvRfP!J=4s!V-$r)XIs8c21vfBE@&o zHKN-_AC9ONM$L}qt;!0JyKT#B3LfcunAk|KeVX{8dR7=wyaWgqigfQJAt@Mo5psWo z{LbKnS!4X*DLRmi?z8Q@3Nn!O=G=QQUMH`zl7y1rjO&S|GHqX;krYp*6*%jLeBXtZ zR?4{yNM(cf<-vS`+fukDb#`es6H)CpK-OS408eD?K=ShPt91k%jcm%JQ(rI&4;g^> z+s1FhyFF(=K+N|I>uzc1{2}~$kT2edc$=`zCNqa-Ubfru>;^Jt>FoT(lJ-sJSRmJn zBw?cX*n2bQNXwB>m<mELcNf~rB5{KYImpPMTo<NMf!iND6y!VD;sQygOTMUgB~kup zOQh}|?_M06tmc8VBy$!qH3Sh1g1mA{R;rUVMX~(BvV;2U($gnA0#cP`_r1_t!e*>o z7Us^?%0u>P|7jV^Z0&+vfpM|{F~9>;oj%yi8+>4G62gkJI_LYJ-0#E|As|v{QqmN0 zo{I`%B#SWS_rA950+iys+Gw`1F0gK+39RPSqW1~J1<UUoo7V`8a?%o5g1@X*i-t=} z$>9#}aE(9@8f9d<`v;nwLFl~&^mf&rU+z4)6d9)41+G35CvkYI(W0b197t&!;R8!e z70lwxe}><#Q5O7)SAFpjj(%8hM{~$(mnh>bUYB0Zk>k0PTL{q=xOY*J?vr*_oAZ{R z?>SS$t2PoIQ6kU!fm=~px)OOn0_y6$YwyBaU-Jc#bA7@1{tJHdlO#y{SYG=kPCm_G zPCsy16RI(l77LnWc`u;n;KDRnSUZt})kPg!%|EDWx{!U_kqkt7ch3<-8q7d^%dtIS zT*);*0&!QXGMs#eJJI0XEi2(`P+%bk#J}ed-kH)J9b;IkmY#M<n9H-Cpwy;&r@Ruy zO*F$C@tOx6Nu;s9*_B|816~W<h7)5!)=++u8<GdT_l|yOjm-?rfgr^nR<Mvq|CheL zF(M50|Eb(MqoQb<^}w<cB*z6=K$1!l5G7|2kf`Lm1Q8IBC5WUY3W$VdSCEW>oEIdD zWS8`kk(@zt&e;q2-t*o2<NmmFdg`2~rhBTUpXxKyXR79!PcmG=5;u~x=QPv3XEZyO zMOXtmeoR$Yr)u8QfM%)G!_Dl2pUP&pBi_`$Q{OGXO*Y5OXwJ37^};iyjs7H^%%AH$ z5}G{=hWVvQ6Dp<ovutcY7uR$-gapts9N@1tEQ*1rZ#Ag!?7st$Pi*4;avCI5O%C?* zJq>LH44m5ymIKV}W>lljYD3H@_Q2mCAa>t-zK)1NZ<f@;G@ORUcq{68UA~(h+Epyz z?Q6~JMVO_$z{0CJTaBq(hsOt{6Ils`CS-cKjAJ43?en&r)n=)g9(an$`ED2v<EdWO zkwFS~0(7b8H#Vy#0ej=0<k2?M!D4rF%%0cM0A*6-Yr@gRzbGw2^PR}4u^uw)0aZg& zaBFR|4kul8YJuOjNg-i!dGn8ic=sL|j0HABU<H)C#B0wrHv=S?W|<RkT3L8vVN5tM z=TRH~;Y4;sX!D0je9<oAXy9(Q)Z^K9*LO-FYa-5r7-iRC5JP_ao0de~vFhw<@cumE zFUa!;Ga!&&)Hf*jCO?#ydT|m9z0+E*i+Q&qZjcl(PvN{I{V6<N7ne*nZ>5GOYM0^A z@f5tdqO>PMTXc_ZUu){-FA<h-M;81!e$<!+9osEZ@bb&U(`xz^ChkYW>*d3gRu`>6 zMW90!tbCHr_I~3mzC~O^`7G9<4Pg7)Yi3xEuzk2DUvF1tN!Q0`?&BCk{CJ4==b-#x zZi7;3UMBP*O6GA9b&IhPws3h`Xc!5>=2oYuNHH!BC=W<TRELc4XgPvC9#JTYjhf__ z)vo;0ZFt9$>RZ&Ys(7w(tXo?US@>`mgG923QR8HJ?Htuy?dZ>te#2~6XPPZ;8BG-o zzqd3$Lo#pJ?{9fX!k%aEpj-^rkChocTJ}@xMMyU%|C~e5nCa?CRTRiTvm0Kj>moFW z>9aL*x(m>8Ws((?@r7_KSp>7dM%{A@RoV7xC8rEM5e>zMb3V3`H!TSF+}#SR#JlD@ zO<wO<O2vf?26MQg%NpUikV87qI${03D2w2W#GE-6rgr+*4rBd>8*<C&z9f9ICCW<o zWP2R&D-Axuhb;*Q3ug{v%RVIMRU-K;<1CBD&%T?H1(l|$&5-}9b$k^DSq!4&1Gjko z@ktA!&n)hFHtpU7&pkXmmxFZrCwE|Jwz8AiaTaSRE0f;3B(DgIyE`}W6UbY{%T{4R z?o1nK7_zXr)gIu|DRsx#CiPwPWrgYd#2Z8MnuBF9f;gg~ag!5da`*ZaS~3%N(UDoV zh~2lOqcCK@vuPC7vXsa{(!yqqVwkvi%bl?(s}qMKjPm}p{`T{gKuQq{Cv{HyY%$eP zMY{c1&%D$Pk{@7SJ_SL=d#X9-JcAecp+$?4_jVV1$=uMy7NJXnC`p);Pl=-YaAlO2 zxhai8xb;gzANbEAcv{}+oeN_0NDsv<!#k1ww@f^#5OY0vqx(T-`m=JX(Km&J>wzNu zt8!^AuR1;F*3|GEiVPUmJN<0Ut6IJxmH|NKWG@hKs_+DSZ=IB?vLs9tfna_)r**iM zczq{r007tt8#4lc^6D#G07!S7qQwUQrnOj8`(}RP;7Hs&;X`&w$Bp7eo_kM&Kgx1q zqgDmCFs+uc5f{lH4t?QQDMzS5&A_W4@;MyJq)#-DSCwG?NXnZYX9i?7hs<g`&sbC7 zJcvZtn501yER5xrrpQ^7#ONVrNHr*<aFV^a#^BKgu12xlA%-HuuDMC8;4wKkk7&&T zd7?9L8mojXS>aWS3g5s#eVb}owq>GJu|KqVv^^JqwfKtRjdrf5p&oP4hJ50Jhb79< zZOX)?#w50ofNHY0tbffQO@joe#<w4E<-J{E<6SL|hEjiN)33or=p?O|j55#qKG&g6 zM;CF+b$>O9bSAs2m_PDVxN$QJN@K#1i6hbA&V+}eUTN;S-SoI!Wve$*ik5zAe)PMx z-8nAR<rA~FRS5CR-ouHE$FG`du(0&^f0w&ZNa*Q&Q@I7}-itP4<&~%|Q!p!m;O)VY z{&m7SdFynq2nGE(ya|*=>zqnbu&1j~OhN!fMGIA(5wfPguB?Nv2~3m4*WE64<JD4~ z&HMA=Dz)F+JU>rGr0C4Km;FG^_lD?PVU^$JS=~F~6mK5zyoSzP+_>Euq`azsT@h$^ z&i+4XSq@Dp>4<~`2k=u;wvRbXipp>L)nsDqn~)P5k0Q&JseI+mz|oQFW`eZy;3>z+ zqeLEa$4R3kc>9e5`Zf8ns%~m=yn~_1#i=Uue6ot)O`m|w7LEkM)%Y6RBNW*L<9j7x zD)~wW)C{d7D1;-pfWXEoups_|U>cG}>3dY{Rw9Z*P*5Mwj*(MX!Hy#G#F(Cin!djs zpl1=B?M{-OP!hGrV%b&bDaH0pk#T$wvbDWI{&+^5;&{+&hPS?qil{e*O7Oy6^58f~ zFE(d=2aUKRv-{~+81Ya^f;>*h2`&xKBGb=?)oOi6hBYp$aQv1>b{KBB<JUMmt9+K8 zRHS|03^c92(qqdlqk3U=DI?J3Ymd06Ru}&$eVhW?cF(PW_@kqsb!EYl!x5MM_K|<E z9m3h?VKVPvpElAWqT6M_l&tPcDE!SobDhg^yYpAG%i6hlorUtODz@z^V@RQzx8v`H z3B#O$$*henc6SZ!B9VFM=n6Qo<z=qlyy7|h&#vcAUt8Ax(96O~GoQNsFJ8%?qq=Su z-fz$A>d#!Ax&312pa3VoE>G(OUg&WIC)apkB<cBZ5ik9@Dd%;TsU70F8ZKtRL+%>> zx`Jw&Ck8`*-8iw1CVc|j@P`mAOqq*teZJt(Q-a08i#NUJ0DqBUGGMk|3TT}WCI!Mk zrvMA-8pwa#oe?0%KfsaU0^&as0Sg(L6*z#j_daS&i$Fp4+k+(vhx*Asa$j3N%}&T- zA9J~VKt7tnBA+&~s`Tx*Q!At9rQK9edtm!0xTp$BN9PrX3d(-3GMQZ^7g2ZrjtVN5 z7sYN`WYaxz-kVp#%3VJDU@vz~?yN{_#5ad(@z6?eRFi>!S?N{8N0OzVCgQD@E`8bg zjZfl9BTMOHlt6+1bToFk{T@`liQlZzkW;Wvp032$`~cLc6`>h))aD;S{xVY%%8Ka* zW#DiePzEs)TAbo3gYA};FRJcS-tsH4DN*3B;7_WxmU317&An;0#u{DeYR_u@aFBfB zt3Go~WbJ$$e3m<rS9+(C{(%c56xl5ov|969yMFqXN|$iB=I%<${+X1v&FdLb8_HpZ zeILXehLuT{%abu7+k_B}jm0N?hmYP~jx8dY*!|Uc7~EP&ep{6yqUPsrs&@W~UrV!6 z^DdEQW`}DI;s@W=jGQjIpxAQ|BiunLApwvuoAfH=%lpb_fBaDG=m_61oljs^60cmf zQnRtrQj#6It2*D*njZN}%Ku6vo!1rpsf{JLbk_mVv0iR=om9wU9OfN%i{_Ic0l6`N z+g^XefO|6ytmmKWFW!?nAY9fa>HD5ri)a8`2GP0g5P|q~x$%ij)eGe!iPf^xFQTVe zv&toky&3hKGC*dvfJn9~-fGfb=y|6?bp8#VDBUlyLtiPg7M{WAb#;tHpLPr+B|fdt zgA_+_EKl-!tl<#O3ivwvuYYFpxl5zUU6o-I6EXoA5TUKI%Ltc0#zjh*l5g_ocUUpo z;WFJ@;tMPZ37rWl4JeQoXCWZT>pSPOwJ*zU{^P~$n<NzDo*mAlr{VOdwER{%>zO(| zmh>Yxdq?GI|JW(pXt1yK>Sjk_o={;`$$&FjKhPG(qhi>|^o!k_@4l(vf+QU=9cYEi z1wcB+sMP<w*D}_YZOlPW-rER+_`scC<6UZ%groCg>E7#Q9lHx}f``eQ+ma3ew1-j! z(JpprU(b1Tct#|TOh$NuZQPR@UaUK%d_m0Pd5AdS4@4W6U_Ot`&_8#fd25YPIGHc0 zt&^sgN^L|R?Q79!uI}UuAUxGh`jbhz=yKM%{K)(sMfLmny{<JNGFz0|Nvht)#QNmp znfpYoZKL<=>SNFHaE|k-5m`deKSxeG4Tvt1?jmZV4{jD$tGw7JpZQ;w$EyEjIc+Ug z-0AWituRpnE3n`6>r@yZ#)i<bd`LIIFMUJ4_xWf@?Cb#+V#U-Q>eCY`fQ8JFRbNZ5 z$*?iO$yVEe`Pz{szpN_slr-fJ=}%AlAnW5{Q=(PO3yAOo(6V@XjNJnHWKYlWV7c#{ z=f^SMgg5ox<5J<A39QvNOCu$LD%wj@?mtlHHW%LyClBpk?zOx=oj>#pTbC}Zy;b_K zx3Z$w#u#tf=K&U!hksqhHFqp~RlT*K{O1a_{Pk#;Oyu!A_xrF{gC$r<8RKqtYrGO# z^fSO$J6Jov>eNi#!4dZ|SWV7?w`0$QHb%=p38Z?Lv5Nhp%bSkxHOir)*sw(Tpfmk} z%J)!Z))jx#yJTPjV*y)5(x$r;LHugYYBujwjHA?1UM?9>A~=!7Z}W<!$E=`XrCh`2 z_*N5V>ot^4s~o9zh@H-tE+Sn{dbwN5F6ufDbq|t>IB0i-pq}dQePDB#sRe}$31KGo z9CgWhlLnF_L#;K|9I2LPiD(Ar^Jlf1zR{Im^i?XVn)Y(t(Vuf(`>Ps}aWJwGZ;M%l z=Bbq>8sh38Q2I4UKb-N{n(7M9T$o9JhrT&WcYJGe-zO#Y?kf6mlP5ZFWy#qzbsGYA zT}@e>36Z(T00Kkn9U4Whr{D5CK0ALavXmw3&p_UkBKaZfF4&|aI`F8c2{v^cm)o)L z$R!jn-&8L|snMMg$`JBIQ$F{;)l9b93Ql8qBiXMHy6-=~E{JhOa8fR@eMzm^-=*_* zSHlAUT!FO|{^=<;5a%>sWLPF1u$h4egpdLdF#xs%_<sgnHb6fe3~bVi{YMFKH<>~E z0icN=xcPxevW0odRtTluSi}T4-*y9JmH?S@CIDH04_L3@014axaCx5|oJ$GF;RC^> zfX~57JOKc~3!GjJV7-|5i3k2?tsqR55a_x*iJ<_$8vv6r0-Vi^{@WmE2M<{M>q2qE zi=rsI%MV<Le<u;$1$4*KLgnR`1TUbT3<{8o0W8-Z{FVP&9q|HWqJD_o#RWnvAhhh> zMQ4Oj*gmu-qYoaie`)Ws9mzfhwC=NAnXbPaB##gL)&B?oRsF^P>2iq;xb*?8O^haf zIYlr(IWVDc^6KFozK(Wtne@94PR7qcH$Ywg3<;R;zG2#|=yx;HXgV$RYpIFc6_Mto za_^Doa}IgoY{e<iKZrn_yeNU~sLF><M6wrfBFA@Xexy8D)x1&AWAjm<h|!=l#YSSp zk?EM7^R|boYk&7)*Q9Y<by4sHP154}NvjZsnXqtta6+U1iPn(lGZM*fR@J4g!}H07 zsdC>BxBls+M?Y!d?2pswb~2rm8X1&j;>~Clnz|vS2VTJFulp+;oO&{gqBbO|6XFal z50}}~OiK%*C0#1+B-A8f-MXtX%Vu=j@>);K2R~HHz3#J4?DCrUJ=iH+-pM;&SX;=o zid}!z#!;yvy~81(Uf4~m9c1*zGsDkZ=gzqPpzj`R>~&U`B{Ugb7^lF!vSc)&@WS4+ zkX5=brm}W{?!7Vm#TotaA`Y;t3H(2gtqC(I5&-=90dLxd3mWy?&cja}R&lPG&%BI9 qPdtF&Iywmcj~Mx1@%dN&?dp8X_7`Pk>q-2T85Knhg+e)4;C}%kexi^7
index c0396f7abae8a9f498fe87c91e8bf66835d0c172..893a27d76ed0ba02cb06d0bc12b0e8ecd77a7abb GIT binary patch literal 21309 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
--- a/browser/themes/winstripe/browser-aero.css +++ b/browser/themes/winstripe/browser-aero.css @@ -1,13 +1,13 @@ %define WINSTRIPE_AERO %include browser.css %undef WINSTRIPE_AERO -%define customToolbarColor hsl(214,44%,87%) +%define customToolbarColor hsl(210,75%,92%) %define glassActiveBorderColor rgb(37, 44, 51) %define glassInactiveBorderColor rgb(102, 102, 102) @media not all and (-moz-windows-classic) { #main-window[sizemode="normal"] > #titlebar > #titlebar-content > #appmenu-button-container { margin-top: 1px; }
--- a/browser/themes/winstripe/places/organizer-aero.css +++ b/browser/themes/winstripe/places/organizer-aero.css @@ -1,13 +1,13 @@ %include organizer.css %filter substitution %define toolbarHighlight rgba(255,255,255,.5) -%define customToolbarColor hsl(214,44%,87%) +%define customToolbarColor hsl(210,75%,92%) #placesView { border-top: none; } @media not all and (-moz-windows-classic) { #placesToolbox { -moz-appearance: none;
index c0396f7abae8a9f498fe87c91e8bf66835d0c172..893a27d76ed0ba02cb06d0bc12b0e8ecd77a7abb GIT binary patch literal 21309 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
new file mode 100644 --- /dev/null +++ b/build/autoconf/arch.m4 @@ -0,0 +1,222 @@ +AC_DEFUN([MOZ_ARCH_OPTS], +[ + +dnl ======================================================== +dnl = ARM toolchain tweaks +dnl ======================================================== + +MOZ_THUMB=toolchain-default +MOZ_THUMB_INTERWORK=toolchain-default +MOZ_FPU=toolchain-default +MOZ_FLOAT_ABI=toolchain-default +MOZ_SOFT_FLOAT=toolchain-default + +MOZ_ARG_WITH_STRING(arch, +[ --with-arch=[[type|toolchain-default]] + Use specific CPU features (-march=type). Resets + thumb, fpu, float-abi, etc. defaults when set], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-arch is not supported on non-GNU toolchains]) + fi + MOZ_ARCH=$withval) + +if test -z "$MOZ_ARCH"; then + dnl Defaults + case "${CPU_ARCH}-${OS_TARGET}" in + arm-Android) + MOZ_THUMB=yes + MOZ_ARCH=armv7-a + MOZ_FPU=vfp + MOZ_FLOAT_ABI=softfp + ;; + arm-Darwin) + MOZ_ARCH=toolchain-default + MOZ_THUMB=yes + ;; + arm-*) + if test -n "$MOZ_PLATFORM_MAEMO"; then + MOZ_THUMB=no + MOZ_ARCH=armv7-a + MOZ_FLOAT_ABI=softfp + fi + if test "$MOZ_PLATFORM_MAEMO" = 6; then + MOZ_THUMB=yes + fi + ;; + esac +fi + +MOZ_ARG_WITH_STRING(thumb, +[ --with-thumb[[=yes|no|toolchain-default]]] +[ Use Thumb instruction set (-mthumb)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-thumb is not supported on non-GNU toolchains]) + fi + MOZ_THUMB=$withval) + +MOZ_ARG_WITH_STRING(thumb-interwork, +[ --with-thumb-interwork[[=yes|no|toolchain-default]] + Use Thumb/ARM instuctions interwork (-mthumb-interwork)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-thumb-interwork is not supported on non-GNU toolchains]) + fi + MOZ_THUMB_INTERWORK=$withval) + +MOZ_ARG_WITH_STRING(fpu, +[ --with-fpu=[[type|toolchain-default]] + Use specific FPU type (-mfpu=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-fpu is not supported on non-GNU toolchains]) + fi + MOZ_FPU=$withval) + +MOZ_ARG_WITH_STRING(float-abi, +[ --with-float-abi=[[type|toolchain-default]] + Use specific arm float ABI (-mfloat-abi=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-float-abi is not supported on non-GNU toolchains]) + fi + MOZ_FLOAT_ABI=$withval) + +MOZ_ARG_WITH_STRING(soft-float, +[ --with-soft-float[[=yes|no|toolchain-default]] + Use soft float library (-msoft-float)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-soft-float is not supported on non-GNU toolchains]) + fi + MOZ_SOFT_FLOAT=$withval) + +case "$MOZ_ARCH" in +toolchain-default|"") + arch_flag="" + ;; +*) + arch_flag="-march=$MOZ_ARCH" + ;; +esac + +case "$MOZ_THUMB" in +yes) + MOZ_THUMB2=1 + thumb_flag="-mthumb" + ;; +no) + MOZ_THUMB2= + thumb_flag="-marm" + ;; +*) + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$arch_flag" + AC_TRY_COMPILE([],[return sizeof(__thumb2__);], + MOZ_THUMB2=1, + MOZ_THUMB2=) + CFLAGS="$_SAVE_CFLAGS" + thumb_flag="" + ;; +esac + +if test "$MOZ_THUMB2" = 1; then + AC_DEFINE(MOZ_THUMB2) +fi + +case "$MOZ_THUMB_INTERWORK" in +yes) + thumb_interwork_flag="-mthumb-interwork" + ;; +no) + thumb_interwork_flag="-mno-thumb-interwork" + ;; +*) # toolchain-default + thumb_interwork_flag="" + ;; +esac + +case "$MOZ_FPU" in +toolchain-default|"") + fpu_flag="" + ;; +*) + fpu_flag="-mfpu=$MOZ_FPU" + ;; +esac + +case "$MOZ_FLOAT_ABI" in +toolchain-default|"") + float_abi_flag="" + ;; +*) + float_abi_flag="-mfloat-abi=$MOZ_FLOAT_ABI" + ;; +esac + +case "$MOZ_SOFT_FLOAT" in +yes) + soft_float_flag="-msoft-float" + ;; +no) + soft_float_flag="-mno-soft-float" + ;; +*) # toolchain-default + soft_float_flag="" + ;; +esac + +dnl Use echo to avoid accumulating space characters +all_flags=`echo $arch_flag $thumb_flag $thumb_interwork_flag $fpu_flag $float_abi_flag $soft_float_flag` +if test -n "$all_flags"; then + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$all_flags" + AC_MSG_CHECKING(whether the chosen combination of compiler flags ($all_flags) works) + AC_TRY_COMPILE([],[return 0;], + AC_MSG_RESULT([yes]), + AC_MSG_ERROR([no])) + + CFLAGS="$_SAVE_CFLAGS $all_flags" + CXXFLAGS="$CXXFLAGS $all_flags" + ASFLAGS="$ASFLAGS $all_flags" + if test -n "$thumb_flag"; then + LDFLAGS="$LDFLAGS $thumb_flag" + fi +fi + +AC_SUBST(MOZ_THUMB2) + +if test "$CPU_ARCH" = "arm"; then + AC_MSG_CHECKING(for ARM SIMD support in compiler) + # We try to link so that this also fails when + # building with LTO. + AC_TRY_LINK([], + [asm("uqadd8 r1, r1, r2");], + result="yes", result="no") + AC_MSG_RESULT("$result") + if test "$result" = "yes"; then + AC_DEFINE(HAVE_ARM_SIMD) + HAVE_ARM_SIMD=1 + fi + + AC_MSG_CHECKING(for ARM NEON support in compiler) + # We try to link so that this also fails when + # building with LTO. + AC_TRY_LINK([], + [asm(".fpu neon\n vadd.i8 d0, d0, d0");], + result="yes", result="no") + AC_MSG_RESULT("$result") + if test "$result" = "yes"; then + AC_DEFINE(HAVE_ARM_NEON) + HAVE_ARM_NEON=1 + fi +fi # CPU_ARCH = arm + +AC_SUBST(HAVE_ARM_SIMD) +AC_SUBST(HAVE_ARM_NEON) + +if test -n "$MOZ_ARCH"; then + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-arch=$MOZ_ARCH" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb=$MOZ_THUMB" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-thumb-interwork=$MOZ_THUMB_INTERWORK" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-fpu=$MOZ_FPU" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-float-abi=$MOZ_FLOAT_ABI" + NSPR_CONFIGURE_ARGS="$NSPR_CONFIGURE_ARGS --with-soft-float=$MOZ_SOFT_FLOAT" +fi + +])