diff --git a/accessible/src/base/nsARIAMap.cpp b/accessible/src/base/nsARIAMap.cpp index f308c40298eb..d6bf7bcf897b 100644 --- a/accessible/src/base/nsARIAMap.cpp +++ b/accessible/src/base/nsARIAMap.cpp @@ -235,8 +235,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = eNoValue, eNoAction, eNoLiveAttr, - states::READONLY, - eARIAMultiSelectable + states::READONLY }, { "listbox", @@ -256,9 +255,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = eNoValue, eNoAction, // XXX: should depend on state, parent accessible eNoLiveAttr, - states::READONLY, - eARIASelectable, - eARIACheckedMixed + states::READONLY }, { "log", diff --git a/accessible/src/base/nsAccessNode.h b/accessible/src/base/nsAccessNode.h index 839b96f430e3..6153953f75e9 100644 --- a/accessible/src/base/nsAccessNode.h +++ b/accessible/src/base/nsAccessNode.h @@ -161,12 +161,6 @@ public: */ virtual bool IsPrimaryForNode() const; - /** - * Return the string bundle - */ - static nsIStringBundle* GetStringBundle() - { return gStringBundle; } - /** * Interface methods on nsIAccessible shared with ISimpleDOM. */ diff --git a/accessible/tests/mochitest/focus/test_takeFocus.html b/accessible/tests/mochitest/focus/test_takeFocus.html index 343968842024..157fc2434678 100644 --- a/accessible/tests/mochitest/focus/test_takeFocus.html +++ b/accessible/tests/mochitest/focus/test_takeFocus.html @@ -96,9 +96,9 @@ link
-
item1
-
item2
-
item3
+
item1
+
item2
+
item3
diff --git a/accessible/tests/mochitest/selectable/test_aria.html b/accessible/tests/mochitest/selectable/test_aria.html index 53dea07d9f1c..2b4db6f3ff41 100644 --- a/accessible/tests/mochitest/selectable/test_aria.html +++ b/accessible/tests/mochitest/selectable/test_aria.html @@ -36,25 +36,6 @@ function doTest() { - ////////////////////////////////////////////////////////////////////////// - // role="list" - - var id = "list1"; - ok(isAccessible(id, [nsIAccessibleSelectable]), - "No selectable accessible for " + id); - - testSelectableSelection(id, [ ]); - - var select = getAccessible(id, [nsIAccessibleSelectable]); - select.addChildToSelection(0); - testSelectableSelection(id, [ ]); - select.removeChildFromSelection(0); - testSelectableSelection(id, [ ]); - select.selectAllSelection(); - testSelectableSelection(id, [ ]); - select.clearSelection(); - testSelectableSelection(id, [ ]); - ////////////////////////////////////////////////////////////////////////// // role="listbox" @@ -82,7 +63,7 @@ testSelectableSelection(id, [ "listbox2_item1", "listbox2_item2" ]); select.clearSelection(); testSelectableSelection(id, [ ]); - + ////////////////////////////////////////////////////////////////////////// // role="grid" @@ -152,19 +133,14 @@
   
-
-
item1
-
item2
-
-
-
item1
-
item2
+
item1
+
item2
-
item1
-
item2
+
item1
+
item2
diff --git a/accessible/tests/mochitest/test_aria_token_attrs.html b/accessible/tests/mochitest/test_aria_token_attrs.html index 4dcf12ede616..7967b9c660fc 100644 --- a/accessible/tests/mochitest/test_aria_token_attrs.html +++ b/accessible/tests/mochitest/test_aria_token_attrs.html @@ -89,12 +89,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452388 testStates("listbox_multiselectable_undefined", 0, 0, STATE_MULTISELECTABLE | STATE_EXTSELECTABLE); testStates("listbox_multiselectable_absent", 0, 0, STATE_MULTISELECTABLE | STATE_EXTSELECTABLE); - // test (listitem) checkable and checked states - testStates("listitem_checked_true", (STATE_CHECKABLE | STATE_CHECKED)); - testStates("listitem_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED); - testStates("listitem_checked_empty", 0 , 0, STATE_CHECKABLE | STATE_CHECKED); - testStates("listitem_checked_undefined", 0, 0, STATE_CHECKABLE | STATE_CHECKED); - testStates("listitem_checked_absent", 0, 0, STATE_CHECKABLE | STATE_CHECKED); + // test (option) checkable and checked states + testStates("option_checked_true", (STATE_CHECKABLE | STATE_CHECKED)); + testStates("option_checked_false", STATE_CHECKABLE, 0, STATE_CHECKED); + testStates("option_checked_empty", 0 , 0, STATE_CHECKABLE | STATE_CHECKED); + testStates("option_checked_undefined", 0, 0, STATE_CHECKABLE | STATE_CHECKED); + testStates("option_checked_absent", 0, 0, STATE_CHECKABLE | STATE_CHECKED); // test (menuitem) checkable and checked states testStates("menuitem_checked_true", (STATE_CHECKABLE | STATE_CHECKED)); @@ -239,19 +239,19 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=452388
-
item
+
item
-
item
+
item
-
item
+
item
-
item
+
item
-
item
+
item
diff --git a/allmakefiles.sh b/allmakefiles.sh index 628c72ef8718..d0325ff973d1 100755 --- a/allmakefiles.sh +++ b/allmakefiles.sh @@ -68,6 +68,7 @@ config/nspr/Makefile config/doxygen.cfg config/expandlibs_config.py config/tests/src-simple/Makefile +mfbt/Makefile probes/Makefile extensions/Makefile " @@ -125,6 +126,11 @@ if [ "$ENABLE_TESTS" ]; then add_makefiles " build/autoconf/test/Makefile " + if [ ! "$LIBXUL_SDK" ]; then + add_makefiles " + mozglue/tests/Makefile + " + fi if [ "$_MSC_VER" -a "$OS_TEST" != "x86_64" ]; then add_makefiles " build/win32/vmwarerecordinghelper/Makefile @@ -137,6 +143,7 @@ if [ "$ENABLE_TESTS" ]; then fi if [ "$MOZ_WIDGET_TOOLKIT" = "android" ]; then add_makefiles " + build/mobile/robocop/Makefile build/mobile/sutagent/android/Makefile build/mobile/sutagent/android/fencp/Makefile build/mobile/sutagent/android/ffxcp/Makefile diff --git a/b2g/makefiles.sh b/b2g/makefiles.sh index 8406b1ddf89d..ad14c5d4c398 100644 --- a/b2g/makefiles.sh +++ b/b2g/makefiles.sh @@ -35,21 +35,13 @@ # ***** END LICENSE BLOCK ***** add_makefiles " -netwerk/locales/Makefile -dom/locales/Makefile -toolkit/locales/Makefile -security/manager/locales/Makefile b2g/app/Makefile $MOZ_BRANDING_DIRECTORY/Makefile +$MOZ_BRANDING_DIRECTORY/content/Makefile +$MOZ_BRANDING_DIRECTORY/locales/Makefile b2g/chrome/Makefile b2g/components/Makefile b2g/installer/Makefile b2g/locales/Makefile b2g/Makefile " - -if test -n "$MOZ_UPDATE_PACKAGING"; then - add_makefiles " - tools/update-packaging/Makefile - " -fi diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 9561276c6c18..f3148e85ab2f 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -5865,7 +5865,7 @@ function hrefAndLinkNodeForClickEvent(event) function contentAreaClick(event, isPanelClick) { if (!event.isTrusted || event.defaultPrevented || event.button == 2) - return true; + return; let [href, linkNode] = hrefAndLinkNodeForClickEvent(event); if (!href) { @@ -5876,7 +5876,7 @@ function contentAreaClick(event, isPanelClick) middleMousePaste(event); event.preventDefault(); } - return true; + return; } // This code only applies if we have a linkNode (i.e. clicks on real anchor @@ -5893,7 +5893,7 @@ function contentAreaClick(event, isPanelClick) if (linkNode.getAttribute("onclick") || href.substr(0, 11) === "javascript:" || href.substr(0, 5) === "data:") - return true; + return; try { urlSecurityCheck(href, linkNode.ownerDocument.nodePrincipal); @@ -5901,16 +5901,16 @@ function contentAreaClick(event, isPanelClick) catch(ex) { // Prevent loading unsecure destinations. event.preventDefault(); - return true; + return; } let postData = {}; let url = getShortcutOrURI(href, postData); if (!url) - return true; + return; loadURI(url, null, postData.value, false); event.preventDefault(); - return true; + return; } if (linkNode.getAttribute("rel") == "sidebar") { @@ -5927,7 +5927,7 @@ function contentAreaClick(event, isPanelClick) , "keyword" ] }, window); event.preventDefault(); - return true; + return; } } @@ -5940,8 +5940,6 @@ function contentAreaClick(event, isPanelClick) try { PlacesUIUtils.markPageAsFollowedLink(href); } catch (ex) { /* Skip invalid URIs. */ } - - return true; } /** diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 13ccc2833085..64c99679b3f4 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -966,7 +966,7 @@ tabcontainer="tabbrowser-tabs" contentcontextmenu="contentAreaContextMenu" autocompletepopup="PopupAutoComplete" - onclick="return contentAreaClick(event, false);"/> + onclick="contentAreaClick(event, false);"/> +Mozilla Bug 725426 +
+
+
diff --git a/layout/build/nsContentDLF.cpp b/layout/build/nsContentDLF.cpp index b88818ee8538..1e25531a130d 100644 --- a/layout/build/nsContentDLF.cpp +++ b/layout/build/nsContentDLF.cpp @@ -310,7 +310,7 @@ nsContentDLF::CreateInstance(const char* aCommand, nsCOMPtr pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID)); nsPluginHost *pluginHost = static_cast(pluginHostCOM.get()); if(pluginHost && - NS_SUCCEEDED(pluginHost->IsPluginEnabledForType(aContentType, true))) { + NS_SUCCEEDED(pluginHost->IsPluginEnabledForType(aContentType))) { return CreateDocument(aCommand, aChannel, aLoadGroup, aContainer, kPluginDocumentCID, diff --git a/layout/generic/nsColumnSetFrame.cpp b/layout/generic/nsColumnSetFrame.cpp index 7e29e1026269..a42caa865c35 100644 --- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -1083,7 +1083,11 @@ nsColumnSetFrame::Reflow(nsPresContext* aPresContext, CheckInvalidateSizeChange(aDesiredSize); - FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); + // XXXjwir3: This call should be replaced with FinishWithAbsoluteFrames + // when bug 724978 is fixed and nsColumnSetFrame is a full absolute + // container. + FinishAndStoreOverflow(&aDesiredSize); + aDesiredSize.mCarriedOutBottomMargin = carriedOutBottomMargin; NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); diff --git a/layout/reftests/abs-pos/reftest.list b/layout/reftests/abs-pos/reftest.list index 4795848faf94..c5285d44a92f 100644 --- a/layout/reftests/abs-pos/reftest.list +++ b/layout/reftests/abs-pos/reftest.list @@ -13,6 +13,8 @@ fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)&&!layersGPUAccelerated) == au == select-1-dynamic.html select-1-ref.html == select-2.html select-2-ref.html == select-3.html select-3-ref.html -== multi-column-1.html multi-column-1-ref.html + +# Fails due to bug 724978. Should be re-enabled once this is fixed. +fails == multi-column-1.html multi-column-1-ref.html == button-1.html button-1-ref.html == button-2.html button-2-ref.html diff --git a/layout/reftests/backgrounds/vector/empty/reftest.list b/layout/reftests/backgrounds/vector/empty/reftest.list index 8cacdc1e469b..a7be83c99053 100644 --- a/layout/reftests/backgrounds/vector/empty/reftest.list +++ b/layout/reftests/backgrounds/vector/empty/reftest.list @@ -3,15 +3,16 @@ == wide--contain--height.html ref-wide-empty.html == wide--contain--width.html ref-wide-empty.html -# Either OS X 32-bit or 10.5, judging from imprecise Tinderbox results, renders -# these tests as empty boxes, not filled boxes. We don't really care about this +# We don't really care about the failures for this # extreme edge case (the test exists more to test for safety against division by # zero), so there is no bug has been filed to fix it, although a patch would # probably be accepted. -random-if(cocoaWidget) == tall--cover--height.html ref-tall-lime.html -random-if(cocoaWidget) == tall--cover--width.html ref-tall-lime.html -random-if(cocoaWidget) == wide--cover--height.html ref-wide-lime.html -random-if(cocoaWidget) == wide--cover--width.html ref-wide-lime.html +# They're still marked as failing though, rather than 'load', since +# we want to know if they start working when we upgrade to Azure. +fails == tall--cover--height.html ref-tall-lime.html +fails == tall--cover--width.html ref-tall-lime.html +fails == wide--cover--height.html ref-wide-lime.html +fails == wide--cover--width.html ref-wide-lime.html == zero-height-ratio-contain.html ref-tall-empty.html == zero-height-ratio-cover.html ref-tall-empty.html diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 5868dcee87a0..92886094b786 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -683,11 +683,13 @@ fails-if(Android) != 376532-3.html 376532-3-ref.html == 379316-1.html 379316-1-ref.html fails-if(Android) random-if(cocoaWidget) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 379316-2.html 379316-2-ref.html # bug 379786 == 379328-1.html 379328-1-ref.html -== 379349-1a.xhtml 379349-1-ref.xhtml -== 379349-1b.xhtml 379349-1-ref.xhtml -== 379349-1c.xhtml 379349-1-ref.xhtml -== 379349-2a.xhtml 379349-2-ref.xhtml -== 379349-2b.xhtml 379349-2-ref.xhtml +# The next set of reftests all fail until bug 724978 has been fixed, and the +# overflow container ability of nsColumnSetFrame is restored. +fails == 379349-1a.xhtml 379349-1-ref.xhtml +fails == 379349-1b.xhtml 379349-1-ref.xhtml +fails == 379349-1c.xhtml 379349-1-ref.xhtml +fails == 379349-2a.xhtml 379349-2-ref.xhtml +fails == 379349-2b.xhtml 379349-2-ref.xhtml == 379349-3a.xhtml 379349-3-ref.xhtml == 379349-3b.xhtml 379349-3-ref.xhtml == 379361-1.html 379361-1-ref.html diff --git a/layout/reftests/transform/abspos-1f.html b/layout/reftests/transform/abspos-1f.html new file mode 100644 index 000000000000..2b9d954e60fa --- /dev/null +++ b/layout/reftests/transform/abspos-1f.html @@ -0,0 +1,12 @@ + + + + +
+ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +
+ 0 1 2 3 4 5 6 7 8 9 +
+
+ + diff --git a/layout/reftests/transform/abspos-1g.html b/layout/reftests/transform/abspos-1g.html new file mode 100644 index 000000000000..32cf19e0cba6 --- /dev/null +++ b/layout/reftests/transform/abspos-1g.html @@ -0,0 +1,12 @@ + + + + +
+ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +
+ 0 1 2 3 4 5 6 7 8 9 +
+
+ + diff --git a/layout/reftests/transform/reftest.list b/layout/reftests/transform/reftest.list index 5d5da5a804e0..b57672abf0ae 100644 --- a/layout/reftests/transform/reftest.list +++ b/layout/reftests/transform/reftest.list @@ -66,6 +66,8 @@ random == rotate-1f.html rotate-1-ref.html == abspos-1c.html abspos-1-ref.html == abspos-1d.html abspos-1-ref.html != abspos-1e.html abspos-1-ref.html +== abspos-1f.html abspos-1-ref.html +== abspos-1g.html abspos-1-ref.html # Origin can use "top" "right" etc. == origin-name-1a.html origin-name-1-ref.html == origin-name-1b.html origin-name-1-ref.html @@ -110,3 +112,9 @@ fails-if(Android) == stresstest-1.html stresstest-1-ref.html # Bugs == 601894-1.html 601894-ref.html == 601894-2.html 601894-ref.html +# Bug 722777 +== table-1a.html table-1-ref.html +== table-1b.html table-1-ref.html +== table-1c.html table-1-ref.html +== table-2a.html table-2-ref.html +== table-2b.html table-2-ref.html diff --git a/layout/reftests/transform/table-1-ref.html b/layout/reftests/transform/table-1-ref.html new file mode 100644 index 000000000000..ca213123b528 --- /dev/null +++ b/layout/reftests/transform/table-1-ref.html @@ -0,0 +1,7 @@ + +
+ + +
Hello
there! +
+
diff --git a/layout/reftests/transform/table-1a.html b/layout/reftests/transform/table-1a.html new file mode 100644 index 000000000000..ce7458c8db38 --- /dev/null +++ b/layout/reftests/transform/table-1a.html @@ -0,0 +1,5 @@ + + + +
Hello
there! +
diff --git a/layout/reftests/transform/table-1b.html b/layout/reftests/transform/table-1b.html new file mode 100644 index 000000000000..ad045a3af4ea --- /dev/null +++ b/layout/reftests/transform/table-1b.html @@ -0,0 +1,5 @@ + + + +
Hello
there! +
diff --git a/layout/reftests/transform/table-1c.html b/layout/reftests/transform/table-1c.html new file mode 100644 index 000000000000..50f4d1add59f --- /dev/null +++ b/layout/reftests/transform/table-1c.html @@ -0,0 +1,7 @@ + +
+ + +
Hello
there! +
+
diff --git a/layout/reftests/transform/table-2-ref.html b/layout/reftests/transform/table-2-ref.html new file mode 100644 index 000000000000..60e7bbb294a6 --- /dev/null +++ b/layout/reftests/transform/table-2-ref.html @@ -0,0 +1,6 @@ + +
+ + +
there!
Hello +
diff --git a/layout/reftests/transform/table-2a.html b/layout/reftests/transform/table-2a.html new file mode 100644 index 000000000000..6e7f91ee5781 --- /dev/null +++ b/layout/reftests/transform/table-2a.html @@ -0,0 +1,5 @@ + + + +
there!
Hello +
diff --git a/layout/reftests/transform/table-2b.html b/layout/reftests/transform/table-2b.html new file mode 100644 index 000000000000..3d94dcf97b0c --- /dev/null +++ b/layout/reftests/transform/table-2b.html @@ -0,0 +1,5 @@ + + + +
there!
Hello +
diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index f043d51d898e..5a0f3988447d 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1108,13 +1108,12 @@ nsComputedDOMStyle::DoGetMozTransform() resultString.Append(NS_LITERAL_STRING(", ")); } resultString.AppendFloat(matrix._41); - resultString.Append(NS_LITERAL_STRING("px, ")); + resultString.Append(NS_LITERAL_STRING(", ")); resultString.AppendFloat(matrix._42); - resultString.Append(NS_LITERAL_STRING("px")); if (is3D) { resultString.Append(NS_LITERAL_STRING(", ")); resultString.AppendFloat(matrix._43); - resultString.Append(NS_LITERAL_STRING("px, ")); + resultString.Append(NS_LITERAL_STRING(", ")); resultString.AppendFloat(matrix._44); } resultString.Append(NS_LITERAL_STRING(")")); diff --git a/layout/style/test/test_transitions_per_property.html b/layout/style/test/test_transitions_per_property.html index 7cc54e99ef92..1b763dc3ebe7 100644 --- a/layout/style/test/test_transitions_per_property.html +++ b/layout/style/test/test_transitions_per_property.html @@ -1315,41 +1315,41 @@ function test_transform_transition(prop) { // translate { start: 'translate(20px)', end: 'none', expected_uncomputed: 'translate(15px)', - expected: 'matrix(1, 0, 0, 1, 15px, 0px)' }, + expected: 'matrix(1, 0, 0, 1, 15, 0)' }, { start: 'translate(20px, 12px)', end: 'none', expected_uncomputed: 'translate(15px, 9px)', - expected: 'matrix(1, 0, 0, 1, 15px, 9px)' }, + expected: 'matrix(1, 0, 0, 1, 15, 9)' }, { start: 'translateX(-20px)', end: 'none', expected_uncomputed: 'translateX(-15px)', - expected: 'matrix(1, 0, 0, 1, -15px, 0px)' }, + expected: 'matrix(1, 0, 0, 1, -15, 0)' }, { start: 'translateY(-40px)', end: 'none', expected_uncomputed: 'translateY(-30px)', - expected: 'matrix(1, 0, 0, 1, 0px, -30px)' }, + expected: 'matrix(1, 0, 0, 1, 0, -30)' }, { start: 'translateZ(40px)', end: 'none', expected_uncomputed: 'translateZ(30px)', - expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0px, 0px, 30px, 1)', + expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 30, 1)', requires_3d: true }, { start: 'none', end: 'translate3D(40px, 60px, -40px)', expected_uncomputed: 'translate3D(10px, 15px, -10px)', - expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10px, 15px, -10px, 1)', + expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 10, 15, -10, 1)', requires_3d: true }, // percentages are relative to 300px (width) and 50px (height) // per the prerequisites in property_database.js { start: 'translate(20%)', end: 'none', expected_uncomputed: 'translate(15%)', - expected: 'matrix(1, 0, 0, 1, 45px, 0px)', + expected: 'matrix(1, 0, 0, 1, 45, 0)', round_error_ok: true }, { start: 'translate(20%, 12%)', end: 'none', expected_uncomputed: 'translate(15%, 9%)', - expected: 'matrix(1, 0, 0, 1, 45px, 4.5px)', + expected: 'matrix(1, 0, 0, 1, 45, 4.5)', round_error_ok: true }, { start: 'translateX(-20%)', end: 'none', expected_uncomputed: 'translateX(-15%)', - expected: 'matrix(1, 0, 0, 1, -45px, 0px)', + expected: 'matrix(1, 0, 0, 1, -45, 0)', round_error_ok: true }, { start: 'translateY(-40%)', end: 'none', expected_uncomputed: 'translateY(-30%)', - expected: 'matrix(1, 0, 0, 1, 0px, -15px)', + expected: 'matrix(1, 0, 0, 1, 0, -15)', round_error_ok: true }, { start: 'none', end: 'rotate(90deg) translate(20%, 20%) rotate(-90deg)', expected_uncomputed: 'rotate(22.5deg) translate(5%, 5%) rotate(-22.5deg)', @@ -1360,11 +1360,11 @@ function test_transform_transition(prop) { // test percent translation using matrix decomposition { start: 'rotate(45deg) rotate(-45deg)', end: 'rotate(90deg) translate(20%, 20%) rotate(-90deg)', - expected: 'matrix(1, 0, 0, 1, -2.5px, 15px)', + expected: 'matrix(1, 0, 0, 1, -2.5, 15)', round_error_ok: true }, { start: 'rotate(45deg) rotate(-45deg)', end: 'rotate(-90deg) translate(20%, 20%) rotate(90deg)', - expected: 'matrix(1, 0, 0, 1, 2.5px, -15px)', + expected: 'matrix(1, 0, 0, 1, 2.5, -15)', round_error_ok: true }, // test calc() in translate // Note that font-size: is 20px, and that percentages are relative @@ -1373,42 +1373,42 @@ function test_transform_transition(prop) { { start: 'translateX(20%)', /* 60px */ end: 'translateX(-moz-calc(10% + 1em))', /* 30px + 20px = 50px */ expected_uncomputed: 'translateX(-moz-calc(17.5% + 0.25em))', - expected: 'matrix(1, 0, 0, 1, 57.5px, 0px)' }, + expected: 'matrix(1, 0, 0, 1, 57.5, 0)' }, { start: 'translate(-moz-calc(0.75 * 3em + 1.5 * 10%), -moz-calc(0.5 * 5em + 0.5 * 8%))', /* 90px, 52px */ end: 'rotate(90deg) translateY(20%) rotate(90deg) translateY(-moz-calc(10% + 0.5em)) rotate(180deg)', /* -10px, -15px */ - expected: 'matrix(1, 0, 0, 1, 65px, 35.25px)' }, + expected: 'matrix(1, 0, 0, 1, 65, 35.25)' }, // scale { start: 'scale(2)', end: 'none', expected_uncomputed: 'scale(1.75)', - expected: 'matrix(1.75, 0, 0, 1.75, 0px, 0px)' }, + expected: 'matrix(1.75, 0, 0, 1.75, 0, 0)' }, { start: 'none', end: 'scale(0.4)', expected_uncomputed: 'scale(0.85)', - expected: 'matrix(0.85, 0, 0, 0.85, 0px, 0px)', + expected: 'matrix(0.85, 0, 0, 0.85, 0, 0)', round_error_ok: true }, { start: 'scale(2)', end: 'scale(-2)', expected_uncomputed: 'scale(1)', - expected: 'matrix(1, 0, 0, 1, 0px, 0px)' }, + expected: 'matrix(1, 0, 0, 1, 0, 0)' }, { start: 'scale(2)', end: 'scale(-6)', expected_uncomputed: 'scale(0)', - expected: 'matrix(0, 0, 0, 0, 0px, 0px)' }, + expected: 'matrix(0, 0, 0, 0, 0, 0)' }, { start: 'scale(2, 0.4)', end: 'none', expected_uncomputed: 'scale(1.75, 0.55)', - expected: 'matrix(1.75, 0, 0, 0.55, 0px, 0px)', + expected: 'matrix(1.75, 0, 0, 0.55, 0, 0)', round_error_ok: true }, { start: 'scaleX(3)', end: 'none', expected_uncomputed: 'scaleX(2.5)', - expected: 'matrix(2.5, 0, 0, 1, 0px, 0px)' }, + expected: 'matrix(2.5, 0, 0, 1, 0, 0)' }, { start: 'scaleY(5)', end: 'none', expected_uncomputed: 'scaleY(4)', - expected: 'matrix(1, 0, 0, 4, 0px, 0px)' }, + expected: 'matrix(1, 0, 0, 4, 0, 0)' }, { start: 'scaleZ(5)', end: 'none', expected_uncomputed: 'scaleZ(4)', - expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0px, 0px, 0px, 1)', + expected: 'matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 4, 0, 0, 0, 0, 1)', requires_3d: true }, { start: 'none', end: 'scale3D(5, 5, 5)', expected_uncomputed: 'scale3D(2, 2, 2)', - expected: 'matrix3d(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0px, 0px, 0px, 1)', + expected: 'matrix3d(2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1)', requires_3d: true }, // skew @@ -1428,15 +1428,15 @@ function test_transform_transition(prop) { expected_uncomputed: 'skewY(22.5deg)' }, // matrix : skewX - { start: 'matrix(1, 0, 3, 1, 0px, 0px)', end: 'none', - expected: 'matrix(1, 0, ' + 3 * 0.75 + ', 1, 0px, 0px)', + { start: 'matrix(1, 0, 3, 1, 0, 0)', end: 'none', + expected: 'matrix(1, 0, ' + 3 * 0.75 + ', 1, 0, 0)', round_error_ok: true }, { start: 'skewX(0)', end: 'skewX(-45deg) translate(0)', - expected: 'matrix(1, 0, -0.25, 1, 0px, 0px)', + expected: 'matrix(1, 0, -0.25, 1, 0, 0)', round_error_ok: true }, // matrix : rotate - { start: 'rotate(-30deg)', end: 'matrix(0, 1, -1, 0, 0px, 0px)', - expected: 'matrix(1, 0, 0, 1, 0px, 0px)', + { start: 'rotate(-30deg)', end: 'matrix(0, 1, -1, 0, 0, 0)', + expected: 'matrix(1, 0, 0, 1, 0, 0)', round_error_ok: true }, { start: 'rotate(-30deg) translateX(0)', end: 'translateX(0) rotate(-90deg)', @@ -1456,7 +1456,7 @@ function test_transform_transition(prop) { expected: c('rotate(135deg)') }, { start: 'scale(-1)', end: 'none', expected_uncomputed: 'scale(-0.5)', - expected: 'matrix(-0.5, 0, 0, -0.5, 0px, 0px)' }, + expected: 'matrix(-0.5, 0, 0, -0.5, 0, 0)' }, { start: 'rotate(180deg)', end: 'none', expected_uncomputed: 'rotate(135deg)' }, { start: 'rotate(-180deg)', end: 'none', @@ -1466,7 +1466,7 @@ function test_transform_transition(prop) { // matrix followed by scale { start: 'matrix(2, 0, 0, 2, 10px, 20px) scale(2)', end: 'none', - expected: 'matrix(3.0625, 0, 0, 3.0625, 7.5px, 15px)' }, + expected: 'matrix(3.0625, 0, 0, 3.0625, 7.5, 15)' }, // ... and a bunch of similar possibilities. The spec isn't settled // here; there are multiple options. See: @@ -1505,42 +1505,42 @@ function test_transform_transition(prop) { { start: 'none', end: 'matrix(1, 0, 1.5, 1, 0pt, 0pt)', /* skewX(atan(1.5)) */ - expected: 'matrix(1, 0, ' + 1.5 * 0.25 + ', 1, 0px, 0px)', + expected: 'matrix(1, 0, ' + 1.5 * 0.25 + ', 1, 0, 0)', round_error_ok: true }, { start: 'none', end: 'matrix(-1, 0, 2, -1, 0pt, 0pt)', /* rotate(180deg) skewX(atan(-2)) */ - expected: c('rotate(45deg) matrix(1, 0, ' + -2 * 0.25 + ', 1, 0px, 0px)'), + expected: c('rotate(45deg) matrix(1, 0, ' + -2 * 0.25 + ', 1, 0, 0)'), round_error_ok: true }, { start: 'none', end: 'matrix(0, -1, 1, -3, 0pt, 0pt)', /* rotate(-90deg) skewX(atan(3)) */ - expected: c('rotate(-22.5deg) matrix(1, 0, ' + 3 * 0.25 + ', 1, 0px, 0px)'), + expected: c('rotate(-22.5deg) matrix(1, 0, ' + 3 * 0.25 + ', 1, 0, 0)'), round_error_ok: true }, { start: 'none', end: 'matrix(0, 1, -1, 4, 0pt, 0pt)', /* rotate(90deg) skewX(atan(4)) */ - expected: c('rotate(22.5deg) matrix(1, 0, ' + 4 * 0.25 + ', 1, 0px, 0px)'), + expected: c('rotate(22.5deg) matrix(1, 0, ' + 4 * 0.25 + ', 1, 0, 0)'), round_error_ok: true }, // and then four with negative determinants { start: 'none', end: 'matrix(1, 0, 1, -1, 0pt, 0pt)', /* rotate(-180deg) skewX(atan(-1)) scaleX(-1) */ - expected: c('rotate(-45deg) matrix(1, 0, ' + -1 * 0.25 + ', 1, 0px, 0px) scaleX(0.5)'), + expected: c('rotate(-45deg) matrix(1, 0, ' + -1 * 0.25 + ', 1, 0, 0) scaleX(0.5)'), round_error_ok: true }, { start: 'none', end: 'matrix(-1, 0, -1, 1, 0pt, 0pt)', /* skewX(atan(-1)) scaleX(-1) */ - expected: c('matrix(1, 0, ' + -1 * 0.25 + ', 1, 0px, 0px) scaleX(0.5)') }, + expected: c('matrix(1, 0, ' + -1 * 0.25 + ', 1, 0, 0) scaleX(0.5)') }, { start: 'none', end: 'matrix(0, 1, 1, -2, 0pt, 0pt)', /* rotate(-90deg) skewX(atan(2)) scaleX(-1) */ - expected: c('rotate(-22.5deg) matrix(1, 0, ' + 2 * 0.25 + ', 1, 0px, 0px) scaleX(0.5)'), + expected: c('rotate(-22.5deg) matrix(1, 0, ' + 2 * 0.25 + ', 1, 0, 0) scaleX(0.5)'), round_error_ok: true }, { start: 'none', end: 'matrix(0, -1, -1, 0.5, 0pt, 0pt)', /* rotate(90deg) skewX(atan(0.5)) scaleX(-1) */ - expected: c('rotate(22.5deg) matrix(1, 0, ' + 0.5 * 0.25 + ', 1, 0px, 0px) scaleX(0.5)'), + expected: c('rotate(22.5deg) matrix(1, 0, ' + 0.5 * 0.25 + ', 1, 0, 0) scaleX(0.5)'), round_error_ok: true }, // lists vs. matrix decomposition @@ -1552,7 +1552,7 @@ function test_transform_transition(prop) { expected_uncomputed: 'skewY(22.5deg) rotate(90deg)' }, { start: 'skewY(45deg) rotate(90deg) translate(0)', end: 'skewY(-45deg) rotate(90deg)', - expected: 'matrix(0, 1, -1, -0.5, 0px, 0px)', + expected: 'matrix(0, 1, -1, -0.5, 0, 0)', round_error_ok: true }, { start: 'skewX(45deg) rotate(90deg)', end: 'skewX(-45deg) rotate(90deg)', @@ -1563,7 +1563,7 @@ function test_transform_transition(prop) { round_error_ok: true }, ]; - var matrix_re = /^matrix\(([^,]*), ([^,]*), ([^,]*), ([^,]*), ([^,]*)px, ([^,]*)px\)$/; + var matrix_re = /^matrix\(([^,]*), ([^,]*), ([^,]*), ([^,]*), ([^,]*), ([^,]*)\)$/; for (var i in tests) { var test = tests[i]; if (!("expected" in test)) { diff --git a/layout/style/ua.css b/layout/style/ua.css index c0edf782480e..edfb8582d35a 100644 --- a/layout/style/ua.css +++ b/layout/style/ua.css @@ -74,6 +74,9 @@ page-break-after: inherit; vertical-align: inherit; /* needed for inline-table */ line-height: inherit; /* needed for vertical-align on inline-table */ + /* Bug 722777 */ + -moz-transform: inherit; + -moz-transform-origin: inherit; } *|*::-moz-table-row { diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index 44d1e8d7fd6f..67de74f79dba 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -229,10 +229,29 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGRenderState *aContext, return NS_ERROR_FAILURE; } + nsRect kidDirtyRect = kid->GetVisualOverflowRect(); + /* Check if we need to draw anything. */ if (aDirtyRect) { - PRInt32 appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); - if (!mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect)) + // Transform the dirty rect into app units in our userspace. + gfxMatrix invmatrix = matrix; + invmatrix.Invert(); + NS_ASSERTION(!invmatrix.IsSingular(), + "inverse of non-singular matrix should be non-singular"); + + gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y, + aDirtyRect->width, aDirtyRect->height); + transDirtyRect = invmatrix.TransformBounds(transDirtyRect); + + kidDirtyRect.IntersectRect(kidDirtyRect, + nsLayoutUtils::RoundGfxRectToAppRect(transDirtyRect, + PresContext()->AppUnitsPerCSSPixel())); + + // XXX after bug 614732 is fixed, we will compare mRect with aDirtyRect, + // not with kidDirtyRect. I.e. + // PRInt32 appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); + // mRect.ToOutsidePixels(appUnitsPerDevPx).Intersects(*aDirtyRect) + if (kidDirtyRect.IsEmpty()) return NS_OK; } @@ -258,21 +277,6 @@ nsSVGForeignObjectFrame::PaintSVG(nsSVGRenderState *aContext, NS_ASSERTION(!invmatrix.IsSingular(), "inverse of non-singular matrix should be non-singular"); - nsRect kidDirtyRect = kid->GetVisualOverflowRect(); - if (aDirtyRect) { - gfxRect transDirtyRect = gfxRect(aDirtyRect->x, aDirtyRect->y, - aDirtyRect->width, aDirtyRect->height); - transDirtyRect = invmatrix.TransformBounds(transDirtyRect); - - transDirtyRect.Scale(nsPresContext::AppUnitsPerCSSPixel()); - nsPoint tl(NSToCoordFloor(transDirtyRect.X()), - NSToCoordFloor(transDirtyRect.Y())); - nsPoint br(NSToCoordCeil(transDirtyRect.XMost()), - NSToCoordCeil(transDirtyRect.YMost())); - kidDirtyRect.IntersectRect(kidDirtyRect, - nsRect(tl.x, tl.y, br.x - tl.x, br.y - tl.y)); - } - PRUint32 flags = nsLayoutUtils::PAINT_IN_TRANSFORM; if (aContext->IsPaintingToWindow()) { flags |= nsLayoutUtils::PAINT_TO_WINDOW; @@ -344,7 +348,9 @@ nsSVGForeignObjectFrame::GetFrameForPoint(const nsPoint &aPoint) NS_IMETHODIMP_(nsRect) nsSVGForeignObjectFrame::GetCoveredRegion() { - return mRect; + // See bug 614732 comment 32: + //return nsSVGUtils::TransformFrameRectToOuterSVG(mRect, GetCanvasTM(), PresContext()); + return mCoveredRegion; } NS_IMETHODIMP @@ -362,8 +368,11 @@ nsSVGForeignObjectFrame::UpdateCoveredRegion() if (h < 0.0f) h = 0.0f; // GetCanvasTM includes the x,y translation - mRect = ToCanvasBounds(gfxRect(0.0, 0.0, w, h), GetCanvasTM(), PresContext()); - + mRect = nsLayoutUtils::RoundGfxRectToAppRect( + gfxRect(0.0, 0.0, w, h), + PresContext()->AppUnitsPerDevPixel()); + mCoveredRegion = ToCanvasBounds(gfxRect(0.0, 0.0, w, h), GetCanvasTM(), PresContext()); + return NS_OK; } @@ -645,19 +654,18 @@ nsSVGForeignObjectFrame::InvalidateDirtyRect(nsSVGOuterSVGFrame* aOuter, if (aRect.IsEmpty()) return; - // The areas dirtied by children are in app units, relative to this frame. - // We need to convert the rect to userspace to use IntersectRect. - - gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height); - r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel()); - - nsRect rect = ToCanvasBounds(r, GetCanvasTM(), PresContext()); - // Don't invalidate areas outside our bounds: - rect.IntersectRect(rect, mRect); + nsRect rect = aRect.Intersect(mRect); if (rect.IsEmpty()) return; + // The areas dirtied by children are in app units, relative to this frame. + // We need to convert the rect from app units in our userspace to app units + // relative to our nsSVGOuterSVGFrame's content rect. + + gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height); + r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel()); + rect = ToCanvasBounds(r, GetCanvasTM(), PresContext()); rect = nsSVGUtils::FindFilterInvalidation(this, rect); aOuter->InvalidateWithFlags(rect, aFlags); } diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.h b/layout/svg/base/src/nsSVGForeignObjectFrame.h index 2bc685b95faf..f2d016c587fc 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.h +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.h @@ -166,6 +166,8 @@ protected: // Areas dirtied by changes to sub-documents embedded by our decendents nsRegion mSubDocDirtyRegion; + nsRect mCoveredRegion; + bool mInReflow; }; diff --git a/layout/svg/base/src/nsSVGGeometryFrame.h b/layout/svg/base/src/nsSVGGeometryFrame.h index 15065c75b6b3..07e0d6581288 100644 --- a/layout/svg/base/src/nsSVGGeometryFrame.h +++ b/layout/svg/base/src/nsSVGGeometryFrame.h @@ -124,6 +124,8 @@ protected: */ float MaybeOptimizeOpacity(float aFillOrStrokeOpacity); + nsRect mCoveredRegion; + private: bool GetStrokeDashData(FallibleTArray& dashes, gfxFloat *dashOffset); }; diff --git a/layout/svg/base/src/nsSVGGlyphFrame.cpp b/layout/svg/base/src/nsSVGGlyphFrame.cpp index 307a8e361f91..3a372b960d7d 100644 --- a/layout/svg/base/src/nsSVGGlyphFrame.cpp +++ b/layout/svg/base/src/nsSVGGlyphFrame.cpp @@ -440,7 +440,9 @@ nsSVGGlyphFrame::GetFrameForPoint(const nsPoint &aPoint) NS_IMETHODIMP_(nsRect) nsSVGGlyphFrame::GetCoveredRegion() { - return mRect; + // See bug 614732 comment 32: + //return nsSVGUtils::TransformFrameRectToOuterSVG(mRect, GetCanvasTM(), PresContext()); + return mCoveredRegion; } NS_IMETHODIMP @@ -448,13 +450,9 @@ nsSVGGlyphFrame::UpdateCoveredRegion() { mRect.SetEmpty(); - gfxMatrix matrix = GetCanvasTM(); - if (matrix.IsSingular()) { - return NS_ERROR_FAILURE; - } - + // XXX here we have tmpCtx use its default identity matrix, but does this + // function call anything that will call GetCanvasTM and break things? nsRefPtr tmpCtx = MakeTmpCtx(); - tmpCtx->Multiply(matrix); bool hasStroke = HasStroke(); if (hasStroke) { @@ -487,13 +485,19 @@ nsSVGGlyphFrame::UpdateCoveredRegion() // calls to record and then reset the stroke width. gfxRect extent = tmpCtx->GetUserPathExtent(); if (hasStroke) { - extent = nsSVGUtils::PathExtentsToMaxStrokeExtents(extent, this); + extent = + nsSVGUtils::PathExtentsToMaxStrokeExtents(extent, this, gfxMatrix()); } if (!extent.IsEmpty()) { - mRect = nsSVGUtils::ToAppPixelRect(PresContext(), extent); + mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, + PresContext()->AppUnitsPerDevPixel()); } + // See bug 614732 comment 32. + mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG( + mRect, GetCanvasTM(), PresContext()); + return NS_OK; } @@ -620,7 +624,9 @@ nsSVGGlyphFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, if ((aFlags & nsSVGUtils::eBBoxIncludeStroke) != 0 && ((aFlags & nsSVGUtils::eBBoxIgnoreStrokeIfNone) == 0 || HasStroke())) { bbox = - bbox.Union(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents, this)); + bbox.Union(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents, + this, + aToBBoxUserspace)); } return bbox; diff --git a/layout/svg/base/src/nsSVGImageFrame.cpp b/layout/svg/base/src/nsSVGImageFrame.cpp index e42b5b836c09..9a2cd3205d6e 100644 --- a/layout/svg/base/src/nsSVGImageFrame.cpp +++ b/layout/svg/base/src/nsSVGImageFrame.cpp @@ -365,7 +365,9 @@ nsSVGImageFrame::PaintSVG(nsSVGRenderState *aContext, if (aDirtyRect) { dirtyRect = aDirtyRect->ToAppUnits(appUnitsPerDevPx); // Adjust dirtyRect to match our local coordinate system. - dirtyRect.MoveBy(-mRect.TopLeft()); + nsRect rootRect = + nsSVGUtils::TransformFrameRectToOuterSVG(mRect, GetCanvasTM(), PresContext()); + dirtyRect.MoveBy(-rootRect.TopLeft()); } // XXXbholley - I don't think huge images in SVGs are common enough to @@ -483,15 +485,20 @@ nsSVGImageFrame::UpdateCoveredRegion() gfxContext context(gfxPlatform::GetPlatform()->ScreenReferenceSurface()); - GeneratePath(&context); - context.IdentityMatrix(); + gfxMatrix identity; + GeneratePath(&context, &identity); gfxRect extent = context.GetUserPathExtent(); if (!extent.IsEmpty()) { - mRect = nsSVGUtils::ToAppPixelRect(PresContext(), extent); + mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, + PresContext()->AppUnitsPerDevPixel()); } + // See bug 614732 comment 32. + mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG( + mRect, GetCanvasTM(), PresContext()); + return NS_OK; } diff --git a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp index c7f2d5ae3e59..e989c287eb58 100644 --- a/layout/svg/base/src/nsSVGPathGeometryFrame.cpp +++ b/layout/svg/base/src/nsSVGPathGeometryFrame.cpp @@ -159,8 +159,16 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint) fillRule = GetClipRule(); } else { hitTestFlags = GetHitTestFlags(); + // XXX once bug 614732 is fixed, aPoint won't need any conversion in order + // to compare it with mRect. + gfxMatrix canvasTM = GetCanvasTM(); + if (canvasTM.IsSingular()) { + return nsnull; + } + nsPoint point = + nsSVGUtils::TransformOuterSVGPointToChildFrame(aPoint, canvasTM, PresContext()); if (!hitTestFlags || ((hitTestFlags & SVG_HIT_TEST_CHECK_MRECT) && - !mRect.Contains(aPoint))) + !mRect.Contains(point))) return nsnull; fillRule = GetStyleSVG()->mFillRule; } @@ -196,17 +204,25 @@ nsSVGPathGeometryFrame::GetFrameForPoint(const nsPoint &aPoint) NS_IMETHODIMP_(nsRect) nsSVGPathGeometryFrame::GetCoveredRegion() { - return mRect; + // See bug 614732 comment 32: + //return nsSVGUtils::TransformFrameRectToOuterSVG(mRect, GetCanvasTM(), PresContext()); + return mCoveredRegion; } NS_IMETHODIMP nsSVGPathGeometryFrame::UpdateCoveredRegion() { - gfxRect extent = GetBBoxContribution(GetCanvasTM(), + gfxRect extent = GetBBoxContribution(gfxMatrix(), nsSVGUtils::eBBoxIncludeFill | nsSVGUtils::eBBoxIgnoreFillIfNone | nsSVGUtils::eBBoxIncludeStroke | nsSVGUtils::eBBoxIgnoreStrokeIfNone | nsSVGUtils::eBBoxIncludeMarkers); - mRect = nsSVGUtils::ToAppPixelRect(PresContext(), extent); + mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, + PresContext()->AppUnitsPerDevPixel()); + + // See bug 614732 comment 32. + mCoveredRegion = nsSVGUtils::TransformFrameRectToOuterSVG( + mRect, GetCanvasTM(), PresContext()); + return NS_OK; } @@ -307,7 +323,9 @@ nsSVGPathGeometryFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace, pathExtents.SizeTo(0, 0); } bbox = - bbox.Union(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents, this)); + bbox.Union(nsSVGUtils::PathExtentsToMaxStrokeExtents(pathExtents, + this, + aToBBoxUserspace)); } // Account for markers: diff --git a/layout/svg/base/src/nsSVGUtils.cpp b/layout/svg/base/src/nsSVGUtils.cpp index 982446b3ead7..e6ced730902a 100644 --- a/layout/svg/base/src/nsSVGUtils.cpp +++ b/layout/svg/base/src/nsSVGUtils.cpp @@ -1010,8 +1010,10 @@ nsSVGUtils::PaintFrameWithEffects(nsSVGRenderState *aContext, if (!aDirtyRect->Intersects(filterFrame->GetFilterBBox(aFrame, nsnull))) return; } else { + nsRect leafBounds = nsSVGUtils::TransformFrameRectToOuterSVG( + aFrame->GetRect(), GetCanvasTM(aFrame), aFrame->PresContext()); nsRect rect = aDirtyRect->ToAppUnits(aFrame->PresContext()->AppUnitsPerDevPixel()); - if (!rect.Intersects(aFrame->GetRect())) + if (!rect.Intersects(leafBounds)) return; } } @@ -1178,22 +1180,33 @@ nsSVGUtils::GetCoveredRegion(const nsFrameList &aFrames) return rect; } -nsRect -nsSVGUtils::ToAppPixelRect(nsPresContext *aPresContext, - double xmin, double ymin, - double xmax, double ymax) +nsPoint +nsSVGUtils::TransformOuterSVGPointToChildFrame(nsPoint aPoint, + const gfxMatrix& aFrameToCanvasTM, + nsPresContext* aPresContext) { - return ToAppPixelRect(aPresContext, - gfxRect(xmin, ymin, xmax - xmin, ymax - ymin)); + gfxMatrix devToUser = aFrameToCanvasTM; + devToUser.Invert(); + NS_ABORT_IF_FALSE(!devToUser.IsSingular(), "should not get here"); + gfxPoint devPt = gfxPoint(aPoint.x, aPoint.y) / + aPresContext->AppUnitsPerDevPixel(); + gfxPoint userPt = devToUser.Transform(devPt).Round(); + gfxPoint appPt = userPt * aPresContext->AppUnitsPerCSSPixel(); + userPt.x = clamped(appPt.x, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX)); + userPt.y = clamped(appPt.y, gfxFloat(nscoord_MIN), gfxFloat(nscoord_MAX)); + // now guaranteed to be safe: + return nsPoint(nscoord(userPt.x), nscoord(userPt.y)); } nsRect -nsSVGUtils::ToAppPixelRect(nsPresContext *aPresContext, const gfxRect& rect) +nsSVGUtils::TransformFrameRectToOuterSVG(const nsRect& aRect, + const gfxMatrix& aMatrix, + nsPresContext* aPresContext) { - return nsRect(aPresContext->DevPixelsToAppUnits(NSToIntFloor(rect.X())), - aPresContext->DevPixelsToAppUnits(NSToIntFloor(rect.Y())), - aPresContext->DevPixelsToAppUnits(NSToIntCeil(rect.XMost()) - NSToIntFloor(rect.X())), - aPresContext->DevPixelsToAppUnits(NSToIntCeil(rect.YMost()) - NSToIntFloor(rect.Y()))); + gfxRect r(aRect.x, aRect.y, aRect.width, aRect.height); + r.Scale(1.0 / nsPresContext::AppUnitsPerCSSPixel()); + return nsLayoutUtils::RoundGfxRectToAppRect( + aMatrix.TransformBounds(r), aPresContext->AppUnitsPerDevPixel()); } gfxIntSize @@ -1495,15 +1508,14 @@ nsSVGUtils::WritePPM(const char *fname, gfxImageSurface *aSurface) static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, nsSVGGeometryFrame* aFrame, - double styleExpansionFactor) + double styleExpansionFactor, + const gfxMatrix& aMatrix) { double style_expansion = styleExpansionFactor * aFrame->GetStrokeWidth(); - gfxMatrix ctm = aFrame->GetCanvasTM(); - - double dx = style_expansion * (fabs(ctm.xx) + fabs(ctm.xy)); - double dy = style_expansion * (fabs(ctm.yy) + fabs(ctm.yx)); + double dx = style_expansion * (fabs(aMatrix.xx) + fabs(aMatrix.xy)); + double dy = style_expansion * (fabs(aMatrix.yy) + fabs(aMatrix.yx)); gfxRect strokeExtents = aPathExtents; strokeExtents.Inflate(dx, dy); @@ -1512,14 +1524,16 @@ PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, /*static*/ gfxRect nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, - nsSVGGeometryFrame* aFrame) + nsSVGGeometryFrame* aFrame, + const gfxMatrix& aMatrix) { - return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5); + return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, 0.5, aMatrix); } /*static*/ gfxRect nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, - nsSVGPathGeometryFrame* aFrame) + nsSVGPathGeometryFrame* aFrame, + const gfxMatrix& aMatrix) { double styleExpansionFactor = 0.5; @@ -1539,7 +1553,8 @@ nsSVGUtils::PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, return ::PathExtentsToMaxStrokeExtents(aPathExtents, aFrame, - styleExpansionFactor); + styleExpansionFactor, + aMatrix); } // ---------------------------------------------------------------------- diff --git a/layout/svg/base/src/nsSVGUtils.h b/layout/svg/base/src/nsSVGUtils.h index 41673f314da6..e7fcb2ed3c6d 100644 --- a/layout/svg/base/src/nsSVGUtils.h +++ b/layout/svg/base/src/nsSVGUtils.h @@ -447,14 +447,19 @@ public: static nsRect GetCoveredRegion(const nsFrameList &aFrames); - /* - * Convert a rect from device pixel units to app pixel units by inflation. - */ + // Converts aPoint from an app unit point in outer- content rect space + // to an app unit point in a frame's SVG userspace. + // This is a temporary helper we should no longer need after bug 614732 is + // fixed. + static nsPoint + TransformOuterSVGPointToChildFrame(nsPoint aPoint, + const gfxMatrix& aFrameToCanvasTM, + nsPresContext* aPresContext); + static nsRect - ToAppPixelRect(nsPresContext *aPresContext, - double xmin, double ymin, double xmax, double ymax); - static nsRect - ToAppPixelRect(nsPresContext *aPresContext, const gfxRect& rect); + TransformFrameRectToOuterSVG(const nsRect& aRect, + const gfxMatrix& aMatrix, + nsPresContext* aPresContext); /* * Convert a surface size to an integer for use by thebes @@ -581,9 +586,11 @@ public: * This should die once bug 478152 is fixed. */ static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, - nsSVGGeometryFrame* aFrame); + nsSVGGeometryFrame* aFrame, + const gfxMatrix& aMatrix); static gfxRect PathExtentsToMaxStrokeExtents(const gfxRect& aPathExtents, - nsSVGPathGeometryFrame* aFrame); + nsSVGPathGeometryFrame* aFrame, + const gfxMatrix& aMatrix); /** * Convert a floating-point value to a 32-bit integer value, clamping to diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp index c4dc86bd85a8..0893fda82eec 100644 --- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -213,6 +213,10 @@ nsTableFrame::Init(nsIContent* aContent, const nsStyleTableBorder* tableStyle = GetStyleTableBorder(); bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse); SetBorderCollapse(borderCollapse); + + // Transforms need to affect the outer frame, not the inner frame (bug 722777) + mState &= ~NS_FRAME_MAY_BE_TRANSFORMED; + // Create the cell map if this frame is the first-in-flow. if (!aPrevInFlow) { mCellMap = new nsTableCellMap(*this, borderCollapse); diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index ca8ad2775fb5..875f554aee5a 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -457,8 +457,8 @@ abstract public class GeckoApp // Disable share menuitem for about:, chrome: and file: URIs String scheme = Uri.parse(tab.getURL()).getScheme(); - boolean enabled = !(scheme.equals("about") || scheme.equals("chrome") || - scheme.equals("file")); + boolean enabled = scheme != null && !(scheme.equals("about") || scheme.equals("chrome") || + scheme.equals("file")); share.setEnabled(enabled); // Disable save as PDF for about:home and xul pages @@ -622,8 +622,8 @@ abstract public class GeckoApp message.put("source", source); JSONObject destination = new JSONObject(); - source.put("width", dw); - source.put("height", dh); + destination.put("width", dw); + destination.put("height", dh); message.put("destination", destination); String json = message.toString(); @@ -635,8 +635,14 @@ abstract public class GeckoApp } void processThumbnail(Tab thumbnailTab, Bitmap bitmap, byte[] compressed) { - if (Tabs.getInstance().isSelectedTab(thumbnailTab)) + if (Tabs.getInstance().isSelectedTab(thumbnailTab)) { + if (compressed == null) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos); + compressed = bos.toByteArray(); + } mLastScreen = compressed; + } if (thumbnailTab.getURL().equals("about:home")) { thumbnailTab.updateThumbnail(null); return; @@ -2322,21 +2328,21 @@ abstract public class GeckoApp private void checkMigrateProfile() { File profileDir = getProfileDir(); + long currentTime = SystemClock.uptimeMillis(); + if (profileDir != null) { - long currentTime = SystemClock.uptimeMillis(); Log.i(LOGTAG, "checking profile migration in: " + profileDir.getAbsolutePath()); final GeckoApp app = GeckoApp.mAppContext; final SetupScreen setupScreen = new SetupScreen(app); // don't show unless we take a while setupScreen.showDelayed(mMainHandler); - GeckoAppShell.ensureSQLiteLibsLoaded(app.getApplication().getPackageResourcePath()); ProfileMigrator profileMigrator = new ProfileMigrator(app.getContentResolver(), profileDir); profileMigrator.launch(); setupScreen.dismiss(); - long timeDiff = SystemClock.uptimeMillis() - currentTime; - Log.i(LOGTAG, "Profile migration took " + timeDiff + " ms"); } + long timeDiff = SystemClock.uptimeMillis() - currentTime; + Log.i(LOGTAG, "Profile migration took " + timeDiff + " ms"); } private SynchronousQueue mFilePickerResult = new SynchronousQueue(); diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 1ac7748ad7f2..1d0fa011f179 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -558,6 +558,17 @@ public class GeckoAppShell mInputConnection.notifyIMEChange(text, start, end, newEnd); } + public static void notifyScreenShot(ByteBuffer data, int tabId, int width, int height) { + final Bitmap b = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); + b.copyPixelsFromBuffer(data); + final Tab tab = Tabs.getInstance().getTab(tabId); + getHandler().post(new Runnable() { + public void run() { + GeckoApp.mAppContext.processThumbnail(tab, b, null); + } + }); + } + private static CountDownLatch sGeckoPendingAcks = null; // Block the current thread until the Gecko event loop is caught up diff --git a/mobile/android/base/ProfileMigrator.java b/mobile/android/base/ProfileMigrator.java index b3b769de2cd6..1e444a53034a 100644 --- a/mobile/android/base/ProfileMigrator.java +++ b/mobile/android/base/ProfileMigrator.java @@ -280,6 +280,7 @@ public class ProfileMigrator { File dbFileShm = new File(dbPathShm); SQLiteBridge db = null; + GeckoAppShell.ensureSQLiteLibsLoaded(GeckoApp.mAppContext.getApplication().getPackageResourcePath()); try { db = new SQLiteBridge(dbPath); migrateBookmarks(db); diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 407fef725be7..0aff6405f7f0 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -68,7 +68,6 @@ public class Tabs implements GeckoEventListener { GeckoAppShell.registerGeckoEventListener("Tab:Added", this); GeckoAppShell.registerGeckoEventListener("Tab:Close", this); GeckoAppShell.registerGeckoEventListener("Tab:Select", this); - GeckoAppShell.registerGeckoEventListener("Tab:ScreenshotData", this); GeckoAppShell.registerGeckoEventListener("Session:RestoreBegin", this); GeckoAppShell.registerGeckoEventListener("Session:RestoreEnd", this); } @@ -288,13 +287,6 @@ public class Tabs implements GeckoEventListener { closeTab(tab); } else if (event.equals("Tab:Select")) { selectTab(message.getInt("tabID")); - } else if (event.equals("Tab:ScreenshotData")) { - Tab tab = getTab(message.getInt("tabID")); - String data = message.getString("data"); - if (data.length() < 22) - return; - byte[] compressed = GeckoAppShell.decodeBase64(data.substring(22), GeckoAppShell.BASE64_DEFAULT); - GeckoApp.mAppContext.processThumbnail(tab, null, compressed); } else if (event.equals("Session:RestoreBegin")) { mRestoringSession = true; } else if (event.equals("Session:RestoreEnd")) { diff --git a/mobile/android/base/tests/BaseTest.java.in b/mobile/android/base/tests/BaseTest.java.in index 483c0cc07fec..bf6976bc939c 100644 --- a/mobile/android/base/tests/BaseTest.java.in +++ b/mobile/android/base/tests/BaseTest.java.in @@ -104,8 +104,6 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { } protected final void enterUrl(String url) { - mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); - Activity awesomeBarActivity = clickOnAwesomeBar(); Element urlbar = mDriver.findElement(awesomeBarActivity, "awesomebar_text"); mActions.sendKeys(url); diff --git a/mobile/android/base/tests/testAwesomebar.java.in b/mobile/android/base/tests/testAwesomebar.java.in index 6754194d84c0..26726cd4694b 100644 --- a/mobile/android/base/tests/testAwesomebar.java.in +++ b/mobile/android/base/tests/testAwesomebar.java.in @@ -7,6 +7,8 @@ public class testAwesomebar extends BaseTest { public void testAwesomebar() { setTestType("mochitest"); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + String url = getAbsoluteUrl("/robocop/robocop_blank_01.html"); loadUrl(url); diff --git a/mobile/android/base/tests/testAxisLocking.java.in b/mobile/android/base/tests/testAxisLocking.java.in index 9b37528aed52..cf1ddc61453a 100644 --- a/mobile/android/base/tests/testAxisLocking.java.in +++ b/mobile/android/base/tests/testAxisLocking.java.in @@ -19,6 +19,8 @@ public class testAxisLocking extends PixelTest { MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + // load page and check we're at 0,0 loadAndVerifyBoxes(url); diff --git a/mobile/android/base/tests/testBookmark.java.in b/mobile/android/base/tests/testBookmark.java.in index fa6459f41271..f2539b4c38b7 100644 --- a/mobile/android/base/tests/testBookmark.java.in +++ b/mobile/android/base/tests/testBookmark.java.in @@ -12,6 +12,9 @@ public class testBookmark extends BaseTest { public void testBookmark() { setTestType("mochitest"); + + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + String url = getAbsoluteUrl("/robocop/robocop_blank_02.html"); enterUrl(url); diff --git a/mobile/android/base/tests/testCheck.java.in b/mobile/android/base/tests/testCheck.java.in index 157955980a1d..60494c03ab91 100644 --- a/mobile/android/base/tests/testCheck.java.in +++ b/mobile/android/base/tests/testCheck.java.in @@ -15,6 +15,9 @@ public class testCheck extends PixelTest { public void testCheck() { setTestType("talos"); String url = getAbsoluteUrl("/startup_test/fennecmark/wikipedia.html"); + + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + loadAndPaint(url); mDriver.setupScrollHandling(); diff --git a/mobile/android/base/tests/testFlingCorrectness.java.in b/mobile/android/base/tests/testFlingCorrectness.java.in index 3dbe8ab49fd4..5d2ff67eb65d 100644 --- a/mobile/android/base/tests/testFlingCorrectness.java.in +++ b/mobile/android/base/tests/testFlingCorrectness.java.in @@ -17,6 +17,8 @@ public class testFlingCorrectness extends PixelTest { MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + // load page and check we're at 0,0 loadAndVerifyBoxes(url); diff --git a/mobile/android/base/tests/testLoad.java.in b/mobile/android/base/tests/testLoad.java.in index 6c77e010c3fe..e4f34edc7d32 100644 --- a/mobile/android/base/tests/testLoad.java.in +++ b/mobile/android/base/tests/testLoad.java.in @@ -14,6 +14,8 @@ public class testLoad extends PixelTest { setTestType("mochitest"); String url = getAbsoluteUrl("/robocop/robocop_boxes.html"); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + loadAndVerifyBoxes(url); verifyUrl(url); diff --git a/mobile/android/base/tests/testOverscroll.java.in b/mobile/android/base/tests/testOverscroll.java.in index 885a229df175..205d06531b88 100644 --- a/mobile/android/base/tests/testOverscroll.java.in +++ b/mobile/android/base/tests/testOverscroll.java.in @@ -17,6 +17,8 @@ public class testOverscroll extends PixelTest { MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + // load page and check we're at 0,0 loadAndVerifyBoxes(url); diff --git a/mobile/android/base/tests/testPan.java.in b/mobile/android/base/tests/testPan.java.in index e2b2a8586bd8..79e6a9098af8 100644 --- a/mobile/android/base/tests/testPan.java.in +++ b/mobile/android/base/tests/testPan.java.in @@ -14,6 +14,8 @@ public class testPan extends PixelTest { setTestType("talos"); String url = getAbsoluteUrl("/startup_test/fennecmark/wikipedia.html"); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + loadAndPaint(url); mDriver.setupScrollHandling(); diff --git a/mobile/android/base/tests/testPanCorrectness.java.in b/mobile/android/base/tests/testPanCorrectness.java.in index 42505f0778dd..131c82c45084 100644 --- a/mobile/android/base/tests/testPanCorrectness.java.in +++ b/mobile/android/base/tests/testPanCorrectness.java.in @@ -17,6 +17,8 @@ public class testPanCorrectness extends PixelTest { MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + // load page and check we're at 0,0 loadAndVerifyBoxes(url); diff --git a/mobile/android/base/tests/test_bug720538.java.in b/mobile/android/base/tests/test_bug720538.java.in index 5b970aba1f66..59019a03d7be 100644 --- a/mobile/android/base/tests/test_bug720538.java.in +++ b/mobile/android/base/tests/test_bug720538.java.in @@ -9,6 +9,8 @@ public class test_bug720538 extends PixelTest { setTestType("mochitest"); String url = getAbsoluteUrl("/robocop/test_bug720538.html"); + mActions.expectGeckoEvent("Gecko:Ready").blockForEvent(); + /* * for this test, we load the associated test_bug720538.html file. this file has two * iframes (painted completely blue - #0000FF) and the rest of the page is the background diff --git a/mobile/android/chrome/content/aboutAddons.xhtml b/mobile/android/chrome/content/aboutAddons.xhtml index 5ad6e6dac29b..613dffb223b6 100644 --- a/mobile/android/chrome/content/aboutAddons.xhtml +++ b/mobile/android/chrome/content/aboutAddons.xhtml @@ -140,12 +140,14 @@ function init() { window.addEventListener("popstate", onPopState, false); + Services.obs.addObserver(Addons, "browser-search-engine-modified", false); AddonManager.addInstallListener(Addons); Addons.getAddons(); } function uninit() { + Services.obs.removeObserver(Addons, "browser-search-engine-modified"); AddonManager.removeInstallListener(Addons); } @@ -490,8 +492,8 @@ // visible if the user later re-adds it (works around bug 341833) detailItem.addon.engine.hidden = false; Services.search.removeEngine(detailItem.addon.engine); - // the search-engine-modified observer in browser.js will take care of - // updating the list + // the search-engine-modified observer will take care of updating the list + history.back(); } else { detailItem.addon.uninstall(); let opType = this._getOpTypeForOperations(detailItem.addon.pendingOperations); @@ -554,6 +556,18 @@ element.setAttribute("opType", "needs-restart"); }, + observe: function observe(aSubject, aTopic, aData) { + if (aTopic == "browser-search-engine-modified") { + switch (aData) { + case "engine-added": + case "engine-removed": + case "engine-changed": + this.getAddons(); + break; + } + } + }, + onInstallFailed: function(aInstall) { }, diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 574ca985a996..d819ab0d6726 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -353,29 +353,43 @@ var BrowserApp = { }, _showTelemetryPrompt: function _showTelemetryPrompt() { - let telemetryPrompted = false; + const PREF_TELEMETRY_PROMPTED = "toolkit.telemetry.prompted"; + const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled"; + const PREF_TELEMETRY_REJECTED = "toolkit.telemetry.rejected"; + + // This is used to reprompt users when privacy message changes + const TELEMETRY_PROMPT_REV = 2; + + let telemetryPrompted = null; try { - telemetryPrompted = Services.prefs.getBoolPref("toolkit.telemetry.prompted"); + telemetryPrompted = Services.prefs.getIntPref(PREF_TELEMETRY_PROMPTED); } catch (e) { /* Optional */ } - if (telemetryPrompted) + + // If the user has seen the latest telemetry prompt, do not prompt again + // else clear old prefs and reprompt + if (telemetryPrompted === TELEMETRY_PROMPT_REV) return; + Services.prefs.clearUserPref(PREF_TELEMETRY_PROMPTED); + Services.prefs.clearUserPref(PREF_TELEMETRY_ENABLED); + let buttons = [ { label: Strings.browser.GetStringFromName("telemetry.optin.yes"), callback: function () { - Services.prefs.setBoolPref("toolkit.telemetry.prompted", true); - Services.prefs.setBoolPref("toolkit.telemetry.enabled", true); + Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV); + Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); } }, { label: Strings.browser.GetStringFromName("telemetry.optin.no"), callback: function () { - Services.prefs.setBoolPref("toolkit.telemetry.prompted", true); - Services.prefs.setBoolPref("toolkit.telemetry.enabled", false); + Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV); + Services.prefs.setBoolPref(PREF_TELEMETRY_REJECTED, true); } } ]; + let brandShortName = Strings.brand.GetStringFromName("brandShortName"); let message = Strings.browser.formatStringFromName("telemetry.optin.message", [brandShortName], 1); NativeWindow.doorhanger.show(message, "telemetry-optin", buttons); @@ -1365,7 +1379,7 @@ nsBrowserAccess.prototype = { let parentId = -1; if (newTab && !isExternal) { - let parent = BrowserApp.getTabForBrowser(BrowserApp.getBrowserForWindow(aOpener)); + let parent = BrowserApp.getTabForBrowser(BrowserApp.getBrowserForWindow(aOpener.top)); if (parent) parentId = parent.id; } @@ -1601,24 +1615,7 @@ Tab.prototype = { if (!this.browser || !this.browser.contentWindow) return; - let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas"); - canvas.setAttribute("width", aDst.width); - canvas.setAttribute("height", aDst.height); - canvas.setAttribute("moz-opaque", "true"); - - let ctx = canvas.getContext("2d"); - let flags = ctx.DRAWWINDOW_DO_NOT_FLUSH; - ctx.drawWindow(this.browser.contentWindow, 0, 0, aSrc.width, aSrc.height, "#fff", flags); - let message = { - gecko: { - type: "Tab:ScreenshotData", - tabID: this.id, - width: aDst.width, - height: aDst.height, - data: canvas.toDataURL() - } - }; - sendMessageToJava(message); + getBridge().takeScreenshot(this.browser.contentWindow, 0, 0, aSrc.width, aSrc.height, aDst.width, aDst.height, this.id); Services.tm.mainThread.dispatch(function() { BrowserApp.doNextScreenshot() }, Ci.nsIThread.DISPATCH_NORMAL); @@ -2907,14 +2904,6 @@ var FormAssistant = { return (aElement instanceof HTMLSelectElement); }, - _isOptionElement: function(aElement) { - return aElement instanceof HTMLOptionElement; - }, - - _isOptionGroupElement: function(aElement) { - return aElement instanceof HTMLOptGroupElement; - }, - getListForElement: function(aElement) { let result = { type: "Prompt:Show", @@ -2929,15 +2918,15 @@ var FormAssistant = { ]; } - this.forOptions(aElement, function(aNode, aIndex) { + this.forOptions(aElement, function(aNode, aIndex, aIsGroup, aInGroup) { let item = { label: aNode.text || aNode.label, - isGroup: this._isOptionGroupElement(aNode), - inGroup: this._isOptionGroupElement(aNode.parentNode), + isGroup: aIsGroup, + inGroup: aInGroup, disabled: aNode.disabled, id: aIndex } - if (item.inGroup) + if (aInGroup) item.disabled = item.disabled || aNode.parentNode.disabled; result.listitems[aIndex] = item; @@ -2949,26 +2938,27 @@ var FormAssistant = { forOptions: function(aElement, aFunction) { let optionIndex = 0; let children = aElement.children; + let numChildren = children.length; // if there are no children in this select, we add a dummy row so that at least something appears - if (children.length == 0) + if (numChildren == 0) aFunction.call(this, {label:""}, optionIndex); - for (let i = 0; i < children.length; i++) { + for (let i = 0; i < numChildren; i++) { let child = children[i]; - if (this._isOptionGroupElement(child)) { - aFunction.call(this, child, optionIndex); + if (child instanceof HTMLOptionElement) { + // This is a regular choice under no group. + aFunction.call(this, child, optionIndex, false, false); + optionIndex++; + } else if (child instanceof HTMLOptGroupElement) { + aFunction.call(this, child, optionIndex, true, false); optionIndex++; let subchildren = child.children; - for (let j = 0; j < subchildren.length; j++) { + let numSubchildren = subchildren.length; + for (let j = 0; j < numSubchildren; j++) { let subchild = subchildren[j]; - aFunction.call(this, subchild, optionIndex); + aFunction.call(this, subchild, optionIndex, false, true); optionIndex++; } - - } else if (this._isOptionElement(child)) { - // This is a regular choice under no group. - aFunction.call(this, child, optionIndex); - optionIndex++; } } } @@ -4165,7 +4155,6 @@ var CharacterEncoding = { let docCharset = browser.docShell.QueryInterface(Ci.nsIDocCharset); docCharset.charset = aEncoding; browser.reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE); - }, - + } }; diff --git a/mobile/android/makefiles.sh b/mobile/android/makefiles.sh index 4d259ef1e304..1e27c17a1960 100644 --- a/mobile/android/makefiles.sh +++ b/mobile/android/makefiles.sh @@ -36,30 +36,31 @@ # ***** END LICENSE BLOCK ***** add_makefiles " -netwerk/locales/Makefile -dom/locales/Makefile -toolkit/locales/Makefile -security/manager/locales/Makefile +mobile/locales/Makefile +mobile/android/Makefile mobile/android/app/Makefile mobile/android/app/profile/extensions/Makefile mobile/android/base/Makefile mobile/android/base/locales/Makefile -mobile/locales/Makefile $MOZ_BRANDING_DIRECTORY/Makefile +$MOZ_BRANDING_DIRECTORY/content/Makefile $MOZ_BRANDING_DIRECTORY/locales/Makefile mobile/android/chrome/Makefile -mobile/android/chrome/tests/Makefile mobile/android/components/Makefile -mobile/android/components/build/Makefile mobile/android/modules/Makefile mobile/android/installer/Makefile mobile/android/locales/Makefile -mobile/android/Makefile mobile/android/themes/core/Makefile " -if test -n "$MOZ_UPDATE_PACKAGING"; then - add_makefiles " - tools/update-packaging/Makefile - " +if [ ! "$LIBXUL_SDK" ]; then + add_makefiles " + mobile/android/components/build/Makefile + " +fi + +if [ "$ENABLE_TESTS" ]; then + add_makefiles " + mobile/android/chrome/tests/Makefile + " fi diff --git a/mobile/xul/makefiles.sh b/mobile/xul/makefiles.sh index d0eb64c54966..3d713f1a00a2 100644 --- a/mobile/xul/makefiles.sh +++ b/mobile/xul/makefiles.sh @@ -36,6 +36,8 @@ # ***** END LICENSE BLOCK ***** add_makefiles " +mobile/locales/Makefile +mobile/xul/Makefile mobile/xul/app/Makefile mobile/xul/app/profile/extensions/Makefile $MOZ_BRANDING_DIRECTORY/Makefile @@ -43,15 +45,18 @@ $MOZ_BRANDING_DIRECTORY/content/Makefile $MOZ_BRANDING_DIRECTORY/locales/Makefile mobile/xul/chrome/Makefile mobile/xul/components/Makefile -mobile/xul/components/build/Makefile mobile/xul/modules/Makefile mobile/xul/installer/Makefile mobile/xul/locales/Makefile -mobile/locales/Makefile -mobile/xul/Makefile mobile/xul/themes/core/Makefile " +if [ ! "$LIBXUL_SDK" ]; then + add_makefiles " + mobile/xul/components/build/Makefile + " +fi + if [ "$ENABLE_TESTS" ]; then add_makefiles " mobile/xul/chrome/tests/Makefile diff --git a/netwerk/protocol/http/HttpBaseChannel.cpp b/netwerk/protocol/http/HttpBaseChannel.cpp index d5bcfd603e99..a1007e182c26 100644 --- a/netwerk/protocol/http/HttpBaseChannel.cpp +++ b/netwerk/protocol/http/HttpBaseChannel.cpp @@ -1552,14 +1552,13 @@ bool HttpBaseChannel::ShouldRewriteRedirectToGET(PRUint32 httpStatus, nsHttpAtom method) { - // always rewrite for 301 and 302, but see bug 598304 - // and RFC 2616, Section 8.3. + // for 301 and 302, only rewrite POST if (httpStatus == 301 || httpStatus == 302) - return true; + return method == nsHttp::Post; - // always rewrite for 303 + // rewrite for 303 unless it was HEAD if (httpStatus == 303) - return true; + return method != nsHttp::Head; // otherwise, such as for 307, do not rewrite return false; diff --git a/netwerk/test/unit/test_XHR_redirects.js b/netwerk/test/unit/test_XHR_redirects.js index 697a650e60de..6908c80f8929 100644 --- a/netwerk/test/unit/test_XHR_redirects.js +++ b/netwerk/test/unit/test_XHR_redirects.js @@ -75,23 +75,23 @@ function run_test() { // same-origin variant var tests = [ // 301: rewrite just POST - [301, "DELETE", "GET", 200], // but see bug 598304 + [301, "DELETE", null, 301], [301, "GET", "GET", 200], - [301, "HEAD", "GET", 200], // but see bug 598304 + [301, "HEAD", "HEAD", 200], [301, "POST", "GET", 200], - [301, "PUT", "GET", 200], // but see bug 598304 - [301, "PROPFIND", "GET", 200], // but see bug 598304 + [301, "PUT", null, 301], + [301, "PROPFIND", "PROPFIND", 200], // 302: see 301 - [302, "DELETE", "GET", 200], // but see bug 598304 + [302, "DELETE", null, 302], [302, "GET", "GET", 200], - [302, "HEAD", "GET", 200], // but see bug 598304 + [302, "HEAD", "HEAD", 200], [302, "POST", "GET", 200], - [302, "PUT", "GET", 200], // but see bug 598304 - [302, "PROPFIND", "GET", 200], // but see bug 598304 + [302, "PUT", null, 302], + [302, "PROPFIND", "PROPFIND", 200], // 303: rewrite to GET except HEAD [303, "DELETE", "GET", 200], [303, "GET", "GET", 200], - [303, "HEAD", "GET", 200], + [303, "HEAD", "HEAD", 200], [303, "POST", "GET", 200], [303, "PUT", "GET", 200], [303, "PROPFIND", "GET", 200], diff --git a/toolkit/components/downloads/nsDownloadManager.cpp b/toolkit/components/downloads/nsDownloadManager.cpp index 17c9dcb39993..cb1f1f6099f3 100644 --- a/toolkit/components/downloads/nsDownloadManager.cpp +++ b/toolkit/components/downloads/nsDownloadManager.cpp @@ -111,11 +111,12 @@ static const PRInt64 gUpdateInterval = 400 * PR_USEC_PER_MSEC; //////////////////////////////////////////////////////////////////////////////// //// nsDownloadManager -NS_IMPL_ISUPPORTS3( +NS_IMPL_ISUPPORTS4( nsDownloadManager , nsIDownloadManager , nsINavHistoryObserver , nsIObserver +, nsISupportsWeakReference ) nsDownloadManager *nsDownloadManager::gDownloadManagerService = nsnull; @@ -886,23 +887,19 @@ nsDownloadManager::Init() // completely initialized), but the observerservice would still keep a reference // to us and notify us about shutdown, which may cause crashes. // failure to add an observer is not critical - // - // These observers will be cleaned up automatically at app shutdown. We do - // not bother explicitly breaking the observers because we are a singleton - // that lives for the duration of the app. - (void)mObserverService->AddObserver(this, "quit-application", false); - (void)mObserverService->AddObserver(this, "quit-application-requested", false); - (void)mObserverService->AddObserver(this, "offline-requested", false); - (void)mObserverService->AddObserver(this, "sleep_notification", false); - (void)mObserverService->AddObserver(this, "wake_notification", false); - (void)mObserverService->AddObserver(this, "profile-before-change", false); - (void)mObserverService->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, false); - (void)mObserverService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, false); - (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_REQUEST_TOPIC, false); - (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, false); + (void)mObserverService->AddObserver(this, "quit-application", true); + (void)mObserverService->AddObserver(this, "quit-application-requested", true); + (void)mObserverService->AddObserver(this, "offline-requested", true); + (void)mObserverService->AddObserver(this, "sleep_notification", true); + (void)mObserverService->AddObserver(this, "wake_notification", true); + (void)mObserverService->AddObserver(this, "profile-before-change", true); + (void)mObserverService->AddObserver(this, NS_IOSERVICE_GOING_OFFLINE_TOPIC, true); + (void)mObserverService->AddObserver(this, NS_IOSERVICE_OFFLINE_STATUS_TOPIC, true); + (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_REQUEST_TOPIC, true); + (void)mObserverService->AddObserver(this, NS_PRIVATE_BROWSING_SWITCH_TOPIC, true); if (history) - (void)history->AddObserver(this, false); + (void)history->AddObserver(this, true); return NS_OK; } diff --git a/toolkit/components/downloads/nsDownloadManager.h b/toolkit/components/downloads/nsDownloadManager.h index 54312e400494..2750db9fd341 100644 --- a/toolkit/components/downloads/nsDownloadManager.h +++ b/toolkit/components/downloads/nsDownloadManager.h @@ -58,6 +58,7 @@ #include "nsIObserverService.h" #include "nsIStringBundle.h" #include "nsISupportsPrimitives.h" +#include "nsWeakReference.h" #include "nsITimer.h" #include "nsString.h" @@ -76,7 +77,8 @@ class nsDownload; class nsDownloadManager : public nsIDownloadManager, public nsINavHistoryObserver, - public nsIObserver + public nsIObserver, + public nsSupportsWeakReference { public: NS_DECL_ISUPPORTS diff --git a/toolkit/components/maintenanceservice/Makefile.in b/toolkit/components/maintenanceservice/Makefile.in index d9d418724892..7b7c5d30dc9e 100644 --- a/toolkit/components/maintenanceservice/Makefile.in +++ b/toolkit/components/maintenanceservice/Makefile.in @@ -57,9 +57,10 @@ CPPSRCS = \ PROGRAM = maintenanceservice$(BIN_SUFFIX) DIST_PROGRAM = maintenanceservice$(BIN_SUFFIX) -# Don't link the maintenanceservice against libmozutils. See bug 687139 -MOZ_UTILS_LDFLAGS = -MOZ_UTILS_PROGRAM_LDFLAGS = +# Don't link the maintenanceservice against mozglue.dll. See bug 687139 and +# bug 725876 +MOZ_GLUE_LDFLAGS = +MOZ_GLUE_PROGRAM_LDFLAGS = LIBS += \ ../../mozapps/update/common/$(LIB_PREFIX)updatecommon.$(LIB_SUFFIX) \ diff --git a/toolkit/components/startup/Makefile.in b/toolkit/components/startup/Makefile.in index 861d8790a4e5..1069463178b4 100644 --- a/toolkit/components/startup/Makefile.in +++ b/toolkit/components/startup/Makefile.in @@ -73,10 +73,8 @@ endif XPCSHELL_TESTS = tests/unit -ifdef ENABLE_TESTS ifneq (mobile,$(MOZ_BUILD_APP)) -DIRS += tests/browser -endif +TEST_DIRS += tests/browser endif include $(topsrcdir)/config/rules.mk diff --git a/toolkit/mozapps/shared/Makefile.in b/toolkit/mozapps/shared/Makefile.in index 244085f1b9f2..e9cbb8fb6941 100644 --- a/toolkit/mozapps/shared/Makefile.in +++ b/toolkit/mozapps/shared/Makefile.in @@ -50,8 +50,9 @@ EXTRA_PP_JS_MODULES = \ FileUtils.jsm \ $(NULL) +TEST_DIRS += test/chrome + ifdef ENABLE_TESTS -DIRS += test/chrome XPCSHELL_TESTS = test/unit endif diff --git a/toolkit/mozapps/update/Makefile.in b/toolkit/mozapps/update/Makefile.in index a143f15334bb..752647bbad05 100644 --- a/toolkit/mozapps/update/Makefile.in +++ b/toolkit/mozapps/update/Makefile.in @@ -77,14 +77,12 @@ endif endif endif -ifdef ENABLE_TESTS -DIRS += test_timermanager +TEST_DIRS += test_timermanager # Update tests require the updater binary ifdef MOZ_UPDATER -DIRS += test +TEST_DIRS += test ifdef MOZ_MAINTENANCE_SERVICE -DIRS += test_svc -endif +TEST_DIRS += test_svc endif endif diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh index afb7c132133b..b02f33eba4e7 100644 --- a/toolkit/toolkit-makefiles.sh +++ b/toolkit/toolkit-makefiles.sh @@ -67,10 +67,17 @@ MAKEFILES_dom=" dom/interfaces/xbl/Makefile dom/interfaces/xpath/Makefile dom/interfaces/xul/Makefile - dom/ipc/Makefile dom/base/Makefile dom/battery/Makefile dom/indexedDB/Makefile + dom/ipc/Makefile + dom/locales/Makefile + dom/network/Makefile + dom/network/interfaces/Makefile + dom/network/src/Makefile + dom/plugins/base/Makefile + dom/plugins/ipc/Makefile + dom/power/Makefile dom/sms/Makefile dom/sms/interfaces/Makefile dom/sms/src/Makefile @@ -84,9 +91,6 @@ MAKEFILES_dom=" dom/src/storage/Makefile dom/system/Makefile dom/workers/Makefile - dom/locales/Makefile - dom/plugins/base/Makefile - dom/plugins/ipc/Makefile " MAKEFILES_editor=" @@ -255,6 +259,7 @@ MAKEFILES_layout=" layout/ipc/Makefile layout/inspector/public/Makefile layout/inspector/src/Makefile + layout/media/Makefile layout/style/Makefile layout/style/xbl-marquee/Makefile layout/tables/Makefile @@ -468,7 +473,6 @@ MAKEFILES_xulapp=" toolkit/components/filepicker/Makefile toolkit/components/find/Makefile toolkit/components/intl/Makefile - toolkit/components/maintenanceservice/Makefile toolkit/components/microformats/Makefile toolkit/components/parentalcontrols/Makefile toolkit/components/passwordmgr/Makefile @@ -714,6 +718,7 @@ if [ "$ENABLE_TESTS" ]; then chrome/test/Makefile content/base/test/Makefile content/base/test/chrome/Makefile + content/base/test/websocket_hybi/Makefile content/canvas/test/Makefile content/canvas/test/crossorigin/Makefile content/canvas/test/webgl/Makefile @@ -736,8 +741,11 @@ if [ "$ENABLE_TESTS" ]; then docshell/test/navigation/Makefile dom/battery/test/Makefile dom/indexedDB/test/Makefile + dom/indexedDB/test/unit/Makefile + dom/network/tests/Makefile dom/plugins/test/Makefile dom/plugins/test/testplugin/Makefile + dom/power/test/Makefile dom/sms/tests/Makefile dom/src/foo/Makefile dom/src/json/test/Makefile @@ -894,6 +902,7 @@ if [ "$ENABLE_TESTS" ]; then toolkit/content/tests/chrome/rtlchrome/Makefile toolkit/content/tests/chrome/rtltest/Makefile toolkit/content/tests/widgets/Makefile + toolkit/devtools/debugger/tests/Makefile toolkit/mozapps/downloads/tests/Makefile toolkit/mozapps/downloads/tests/chrome/Makefile toolkit/mozapps/extensions/test/Makefile @@ -926,11 +935,13 @@ if [ "$ENABLE_TESTS" ]; then accessible/tests/mochitest/hyperlink/Makefile accessible/tests/mochitest/hypertext/Makefile accessible/tests/mochitest/name/Makefile + accessible/tests/mochitest/pivot/Makefile accessible/tests/mochitest/relations/Makefile accessible/tests/mochitest/selectable/Makefile accessible/tests/mochitest/states/Makefile accessible/tests/mochitest/table/Makefile accessible/tests/mochitest/text/Makefile + accessible/tests/mochitest/textcaret/Makefile accessible/tests/mochitest/textselection/Makefile accessible/tests/mochitest/tree/Makefile accessible/tests/mochitest/treeupdate/Makefile @@ -1007,6 +1018,11 @@ if [ "$ENABLE_TESTS" ]; then toolkit/mozapps/update/test/chrome/Makefile " fi + if [ "$MOZ_MAINTENANCE_SERVICE" ]; then + add_makefiles " + toolkit/mozapps/update/test_svc/Makefile + " + fi fi if [ "$MOZ_URL_CLASSIFIER" ]; then add_makefiles " @@ -1038,7 +1054,7 @@ if [ "$ENABLE_TESTS" ]; then toolkit/components/downloads/test/browser/Makefile toolkit/components/passwordmgr/test/browser/Makefile toolkit/components/places/tests/browser/Makefile - toolkit/components/startup/tests/Makefile + toolkit/components/startup/tests/browser/Makefile toolkit/content/tests/browser/Makefile toolkit/content/tests/browser/common/Makefile toolkit/content/tests/browser/data/Makefile @@ -1071,6 +1087,11 @@ if [ "$ENABLE_TESTS" ]; then xpcom/tests/windows/Makefile " fi + if [ "$MOZ_BUILD_APP" = "mobile/android" ]; then + add_makefiles " + testing/mochitest/roboextender/Makefile + " + fi fi @@ -1142,6 +1163,7 @@ if [ "$MOZ_B2G_RIL" ]; then add_makefiles " dom/system/b2g/Makefile dom/telephony/Makefile + dom/wifi/Makefile ipc/ril/Makefile " fi @@ -1250,6 +1272,12 @@ if [ "$MOZ_FEEDS" ]; then " fi +if [ "$MOZ_GRAPHITE" ]; then + add_makefiles " + gfx/graphite2/src/Makefile + " +fi + if [ "$MOZ_HELP_VIEWER" ]; then add_makefiles " toolkit/components/help/Makefile @@ -1286,6 +1314,12 @@ if [ "$MOZ_JSDEBUGGER" ]; then " fi +if [ "$MOZ_MAINTENANCE_SERVICE" ]; then + add_makefiles " + toolkit/components/maintenanceservice/Makefile + " +fi + if [ ! "$MOZ_NATIVE_SQLITE" ]; then add_makefiles " db/sqlite3/src/Makefile @@ -1330,7 +1364,6 @@ if [ "$MOZ_UPDATER" ]; then modules/libmar/Makefile modules/libmar/src/Makefile modules/libmar/tool/Makefile - toolkit/mozapps/readstrings/Makefile " if [ ! "$SYSTEM_BZ2" ]; then add_makefiles " @@ -1345,6 +1378,17 @@ if [ "$MOZ_UPDATER" ]; then fi fi +if [ "$MOZ_UPDATER" -o "$MOZ_MAINTENANCE_SERVICE" ]; then + add_makefiles " + toolkit/mozapps/readstrings/Makefile + " + if [ "$OS_TARGET" != "Android" ]; then + add_makefiles " + toolkit/mozapps/update/common/Makefile + " + fi +fi + if [ "$MOZ_UPDATER" -o "$MOZ_UPDATE_PACKAGING" ]; then add_makefiles " other-licenses/bsdiff/Makefile diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index c23928bb73d2..af51a6f648a0 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -51,6 +51,11 @@ #include "nsThreadUtils.h" #include "nsIThreadManager.h" #include "mozilla/dom/sms/PSms.h" +#include "gfxImageSurface.h" +#include "gfxContext.h" +#include "nsPresContext.h" +#include "nsIDocShell.h" +#include "nsPIDOMWindow.h" #ifdef DEBUG #define ALOG_BRIDGE(args...) ALOG(args) @@ -103,6 +108,7 @@ AndroidBridge::Init(JNIEnv *jEnv, jNotifyIME = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIME", "(II)V"); jNotifyIMEEnabled = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEEnabled", "(ILjava/lang/String;Ljava/lang/String;Z)V"); jNotifyIMEChange = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyIMEChange", "(Ljava/lang/String;III)V"); + jNotifyScreenShot = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "notifyScreenShot", "(Ljava/nio/ByteBuffer;III)V"); jAcknowledgeEventSync = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "acknowledgeEventSync", "()V"); jEnableDeviceMotion = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableDeviceMotion", "(Z)V"); @@ -1959,3 +1965,46 @@ AndroidBridge::HideSurface(jobject surface) env->CallStaticVoidMethod(cls, method, surface); #endif } + + +/* void takeScreenshot (in nsIDOMWindow win, in PRInt32 srcX, in PRInt32 srcY, in PRInt32 srcW, in PRInt32 srcH, in PRInt32 dstX, in PRInt32 dstY, in PRInt32 dstW, in PRInt32 dstH, in AString color); */ +NS_IMETHODIMP nsAndroidBridge::TakeScreenshot(nsIDOMWindow *window, PRInt32 srcX, PRInt32 srcY, PRInt32 srcW, PRInt32 srcH, PRInt32 dstW, PRInt32 dstH, PRInt32 tabId) +{ + nsCOMPtr win = do_QueryInterface(window); + if (!win) + return NS_ERROR_FAILURE; + nsRefPtr presContext; + nsIDocShell* docshell = win->GetDocShell(); + if (docshell) { + docshell->GetPresContext(getter_AddRefs(presContext)); + } + if (!presContext) + return NS_ERROR_FAILURE; + nscolor bgColor = NS_RGB(255, 255, 255); + nsIPresShell* presShell = presContext->PresShell(); + PRUint32 renderDocFlags = (nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING | + nsIPresShell::RENDER_DOCUMENT_RELATIVE); + nsRect r(nsPresContext::CSSPixelsToAppUnits(srcX), + nsPresContext::CSSPixelsToAppUnits(srcY), + nsPresContext::CSSPixelsToAppUnits(srcW), + nsPresContext::CSSPixelsToAppUnits(srcH)); + + nsRefPtr surf = new gfxImageSurface(nsIntSize(dstW, dstH), gfxASurface::ImageFormatRGB16_565); + nsRefPtr context = new gfxContext(surf); + nsresult rv = presShell->RenderDocument(r, renderDocFlags, bgColor, context); + NS_ENSURE_SUCCESS(rv, rv); + AndroidBridge::Bridge()->NotifyScreenshot(surf->Data(), surf->GetDataSize(), tabId, dstW, dstH); + return NS_OK; +} + +void AndroidBridge::NotifyScreenshot(unsigned char* data, int size, int tabId, int width, int height) +{ + JNIEnv* jenv = GetJNIEnv(); + if (!jenv) + return; + AutoLocalJNIFrame jniFrame(jenv, 1); + jobject buffer = jenv->NewDirectByteBuffer(data, size); + if (!buffer) + return; + jenv->CallStaticVoidMethod(mGeckoAppShellClass, jNotifyScreenShot, buffer, tabId, width, height); +} diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index faafc7184192..4cd206a98582 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -156,6 +156,8 @@ public: static void NotifyIMEChange(const PRUnichar *aText, PRUint32 aTextLen, int aStart, int aEnd, int aNewEnd); + void NotifyScreenshot(unsigned char* data, int size, int tabId, int width, int height); + void AcknowledgeEventSync(); void EnableDeviceMotion(bool aEnable); @@ -420,6 +422,7 @@ protected: jmethodID jNotifyIME; jmethodID jNotifyIMEEnabled; jmethodID jNotifyIMEChange; + jmethodID jNotifyScreenShot; jmethodID jAcknowledgeEventSync; jmethodID jEnableDeviceMotion; jmethodID jEnableLocation; diff --git a/widget/android/nsIAndroidBridge.idl b/widget/android/nsIAndroidBridge.idl index e3c860e18f19..c1636f863f67 100644 --- a/widget/android/nsIAndroidBridge.idl +++ b/widget/android/nsIAndroidBridge.idl @@ -1,4 +1,5 @@ #include "nsISupports.idl" +#include "nsIDOMWindow.idl" [scriptable, uuid(38b5c83a-3e8d-45c2-8311-6e36bd5116c0)] interface nsIAndroidDrawMetadataProvider : nsISupports { @@ -16,4 +17,6 @@ interface nsIAndroidBridge : nsISupports { AString handleGeckoMessage(in AString message); void setDrawMetadataProvider(in nsIAndroidDrawMetadataProvider provider); + void takeScreenshot(in nsIDOMWindow win, in PRInt32 srcX, in PRInt32 srcY, in PRInt32 srcW, in PRInt32 srcH, + in PRInt32 dstW, in PRInt32 dstH, in PRInt32 tabId); }; diff --git a/widget/gtk2/gtk3drawing.c b/widget/gtk2/gtk3drawing.c new file mode 100644 index 000000000000..49dfb4d9afa6 --- /dev/null +++ b/widget/gtk2/gtk3drawing.c @@ -0,0 +1,3338 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Ryner (Original Author) + * Pierre Chanial + * Michael Ventnor + * Martin Stransky + * Jan Horak + * + * 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 + * decision by deleting the provisions above and replace them with the notice + * 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 ***** */ + +/* + * This file contains painting functions for each of the gtk2 widgets. + * Adapted from the gtkdrawing.c, and gtk+2.0 source. + */ + +#include +#include +#include +#include "gtkdrawing.h" +#include "nsDebug.h" +#include "prinrval.h" + +#include + +static GtkWidget* gProtoWindow; +static GtkWidget* gProtoLayout; +static GtkWidget* gButtonWidget; +static GtkWidget* gToggleButtonWidget; +static GtkWidget* gButtonArrowWidget; +static GtkWidget* gCheckboxWidget; +static GtkWidget* gRadiobuttonWidget; +static GtkWidget* gHorizScrollbarWidget; +static GtkWidget* gVertScrollbarWidget; +static GtkWidget* gSpinWidget; +static GtkWidget* gHScaleWidget; +static GtkWidget* gVScaleWidget; +static GtkWidget* gEntryWidget; +static GtkWidget* gComboBoxWidget; +static GtkWidget* gComboBoxButtonWidget; +static GtkWidget* gComboBoxArrowWidget; +static GtkWidget* gComboBoxSeparatorWidget; +static GtkWidget* gComboBoxEntryWidget; +static GtkWidget* gComboBoxEntryTextareaWidget; +static GtkWidget* gComboBoxEntryButtonWidget; +static GtkWidget* gComboBoxEntryArrowWidget; +static GtkWidget* gHandleBoxWidget; +static GtkWidget* gToolbarWidget; +static GtkWidget* gFrameWidget; +static GtkWidget* gStatusbarWidget; +static GtkWidget* gProgressWidget; +static GtkWidget* gTabWidget; +static GtkWidget* gTooltipWidget; +static GtkWidget* gMenuBarWidget; +static GtkWidget* gMenuBarItemWidget; +static GtkWidget* gMenuPopupWidget; +static GtkWidget* gMenuItemWidget; +static GtkWidget* gImageMenuItemWidget; +static GtkWidget* gCheckMenuItemWidget; +static GtkWidget* gTreeViewWidget; +static GtkTreeViewColumn* gMiddleTreeViewColumn; +static GtkWidget* gTreeHeaderCellWidget; +static GtkWidget* gTreeHeaderSortArrowWidget; +static GtkWidget* gExpanderWidget; +static GtkWidget* gToolbarSeparatorWidget; +static GtkWidget* gMenuSeparatorWidget; +static GtkWidget* gHPanedWidget; +static GtkWidget* gVPanedWidget; +static GtkWidget* gScrolledWindowWidget; + +static style_prop_t style_prop_func; +static gboolean have_arrow_scaling; +static gboolean is_initialized; + +#define ARROW_UP 0 +#define ARROW_DOWN G_PI +#define ARROW_RIGHT G_PI_2 +#define ARROW_LEFT (G_PI+G_PI_2) + +static GtkStateFlags +GetStateFlagsFromGtkWidgetState(GtkWidgetState* state) +{ + GtkStateFlags stateFlags = GTK_STATE_FLAG_NORMAL; + + if (state->disabled) + stateFlags = GTK_STATE_FLAG_INSENSITIVE; + else { + if (state->depressed || state->active) + stateFlags |= GTK_STATE_FLAG_ACTIVE; + if (state->inHover) + stateFlags |= GTK_STATE_FLAG_PRELIGHT; + if (state->focused) + stateFlags |= GTK_STATE_FLAG_FOCUSED; + } + + return stateFlags; +} + +/* Because we have such an unconventional way of drawing widgets, signal to the GTK theme engine + that they are drawing for Mozilla instead of a conventional GTK app so they can do any specific + things they may want to do. */ +static void +moz_gtk_set_widget_name(GtkWidget* widget) +{ + gtk_widget_set_name(widget, "MozillaGtkWidget"); +} + +gint +moz_gtk_enable_style_props(style_prop_t styleGetProp) +{ + style_prop_func = styleGetProp; + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_window_widget() +{ + if (!gProtoWindow) { + gProtoWindow = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_realize(gProtoWindow); + moz_gtk_set_widget_name(gProtoWindow); + } + return MOZ_GTK_SUCCESS; +} + +static gint +setup_widget_prototype(GtkWidget* widget) +{ + ensure_window_widget(); + if (!gProtoLayout) { + gProtoLayout = gtk_fixed_new(); + gtk_container_add(GTK_CONTAINER(gProtoWindow), gProtoLayout); + } + + gtk_container_add(GTK_CONTAINER(gProtoLayout), widget); + gtk_widget_realize(widget); + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_button_widget() +{ + if (!gButtonWidget) { + gButtonWidget = gtk_button_new_with_label("M"); + setup_widget_prototype(gButtonWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_hpaned_widget() +{ + if (!gHPanedWidget) { + gHPanedWidget = gtk_hpaned_new(); + setup_widget_prototype(gHPanedWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_vpaned_widget() +{ + if (!gVPanedWidget) { + gVPanedWidget = gtk_vpaned_new(); + setup_widget_prototype(gVPanedWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_toggle_button_widget() +{ + if (!gToggleButtonWidget) { + gToggleButtonWidget = gtk_toggle_button_new(); + setup_widget_prototype(gToggleButtonWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_button_arrow_widget() +{ + if (!gButtonArrowWidget) { + ensure_toggle_button_widget(); + + gButtonArrowWidget = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT); + gtk_container_add(GTK_CONTAINER(gToggleButtonWidget), gButtonArrowWidget); + gtk_widget_realize(gButtonArrowWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_checkbox_widget() +{ + if (!gCheckboxWidget) { + gCheckboxWidget = gtk_check_button_new_with_label("M"); + setup_widget_prototype(gCheckboxWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_radiobutton_widget() +{ + if (!gRadiobuttonWidget) { + gRadiobuttonWidget = gtk_radio_button_new_with_label(NULL, "M"); + setup_widget_prototype(gRadiobuttonWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_scrollbar_widget() +{ + if (!gVertScrollbarWidget) { + gVertScrollbarWidget = gtk_vscrollbar_new(NULL); + setup_widget_prototype(gVertScrollbarWidget); + } + if (!gHorizScrollbarWidget) { + gHorizScrollbarWidget = gtk_hscrollbar_new(NULL); + setup_widget_prototype(gHorizScrollbarWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_spin_widget() +{ + if (!gSpinWidget) { + gSpinWidget = gtk_spin_button_new(NULL, 1, 0); + setup_widget_prototype(gSpinWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_scale_widget() +{ + if (!gHScaleWidget) { + gHScaleWidget = gtk_hscale_new(NULL); + setup_widget_prototype(gHScaleWidget); + } + if (!gVScaleWidget) { + gVScaleWidget = gtk_vscale_new(NULL); + setup_widget_prototype(gVScaleWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_entry_widget() +{ + if (!gEntryWidget) { + gEntryWidget = gtk_entry_new(); + setup_widget_prototype(gEntryWidget); + } + return MOZ_GTK_SUCCESS; +} + +/* We need to have pointers to the inner widgets (button, separator, arrow) + * of the ComboBox to get the correct rendering from theme engines which + * special cases their look. Since the inner layout can change, we ask GTK + * to NULL our pointers when they are about to become invalid because the + * corresponding widgets don't exist anymore. It's the role of + * g_object_add_weak_pointer(). + * Note that if we don't find the inner widgets (which shouldn't happen), we + * fallback to use generic "non-inner" widgets, and they don't need that kind + * of weak pointer since they are explicit children of gProtoWindow and as + * such GTK holds a strong reference to them. */ +static void +moz_gtk_get_combo_box_inner_button(GtkWidget *widget, gpointer client_data) +{ + if (GTK_IS_TOGGLE_BUTTON(widget)) { + gComboBoxButtonWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxButtonWidget); + gtk_widget_realize(widget); + } +} + +static void +moz_gtk_get_combo_box_button_inner_widgets(GtkWidget *widget, + gpointer client_data) +{ + if (GTK_IS_SEPARATOR(widget)) { + gComboBoxSeparatorWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxSeparatorWidget); + } else if (GTK_IS_ARROW(widget)) { + gComboBoxArrowWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxArrowWidget); + } else + return; + gtk_widget_realize(widget); +} + +static gint +ensure_combo_box_widgets() +{ + GtkWidget* buttonChild; + + if (gComboBoxButtonWidget && gComboBoxArrowWidget) + return MOZ_GTK_SUCCESS; + + /* Create a ComboBox if needed */ + if (!gComboBoxWidget) { + gComboBoxWidget = gtk_combo_box_new(); + setup_widget_prototype(gComboBoxWidget); + } + + /* Get its inner Button */ + gtk_container_forall(GTK_CONTAINER(gComboBoxWidget), + moz_gtk_get_combo_box_inner_button, + NULL); + + if (gComboBoxButtonWidget) { + /* Get the widgets inside the Button */ + buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxButtonWidget)); + if (GTK_IS_HBOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because the ComboBox + * needs to place a cell renderer, a separator, and an arrow in + * the button when appears-as-list is FALSE. */ + gtk_container_forall(GTK_CONTAINER(buttonChild), + moz_gtk_get_combo_box_button_inner_widgets, + NULL); + } else if(GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + gComboBoxArrowWidget = buttonChild; + g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) + &gComboBoxArrowWidget); + gtk_widget_realize(gComboBoxArrowWidget); + } + } else { + /* Shouldn't be reached with current internal gtk implementation; we + * use a generic toggle button as last resort fallback to avoid + * crashing. */ + ensure_toggle_button_widget(); + gComboBoxButtonWidget = gToggleButtonWidget; + } + + if (!gComboBoxArrowWidget) { + /* Shouldn't be reached with current internal gtk implementation; + * we gButtonArrowWidget as last resort fallback to avoid + * crashing. */ + ensure_button_arrow_widget(); + gComboBoxArrowWidget = gButtonArrowWidget; + } + + /* We don't test the validity of gComboBoxSeparatorWidget since there + * is none when "appears-as-list" = TRUE or "cell-view" = FALSE; if it + * is invalid we just won't paint it. */ + + return MOZ_GTK_SUCCESS; +} + +/* We need to have pointers to the inner widgets (entry, button, arrow) of + * the ComboBoxEntry to get the correct rendering from theme engines which + * special cases their look. Since the inner layout can change, we ask GTK + * to NULL our pointers when they are about to become invalid because the + * corresponding widgets don't exist anymore. It's the role of + * g_object_add_weak_pointer(). + * Note that if we don't find the inner widgets (which shouldn't happen), we + * fallback to use generic "non-inner" widgets, and they don't need that kind + * of weak pointer since they are explicit children of gProtoWindow and as + * such GTK holds a strong reference to them. */ +static void +moz_gtk_get_combo_box_entry_inner_widgets(GtkWidget *widget, + gpointer client_data) +{ + if (GTK_IS_TOGGLE_BUTTON(widget)) { + gComboBoxEntryButtonWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxEntryButtonWidget); + } else if (GTK_IS_ENTRY(widget)) { + gComboBoxEntryTextareaWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxEntryTextareaWidget); + } else + return; + gtk_widget_realize(widget); +} + +static void +moz_gtk_get_combo_box_entry_arrow(GtkWidget *widget, gpointer client_data) +{ + if (GTK_IS_ARROW(widget)) { + gComboBoxEntryArrowWidget = widget; + g_object_add_weak_pointer(G_OBJECT(widget), + (gpointer) &gComboBoxEntryArrowWidget); + gtk_widget_realize(widget); + } +} + +static gint +ensure_combo_box_entry_widgets() +{ + GtkWidget* buttonChild; + + if (gComboBoxEntryTextareaWidget && + gComboBoxEntryButtonWidget && + gComboBoxEntryArrowWidget) + return MOZ_GTK_SUCCESS; + + /* Create a ComboBoxEntry if needed */ + if (!gComboBoxEntryWidget) { + gComboBoxEntryWidget = NULL; /* TODO - gtk_combo_box_entry_new();*/ + setup_widget_prototype(gComboBoxEntryWidget); + } + + /* Get its inner Entry and Button */ + gtk_container_forall(GTK_CONTAINER(gComboBoxEntryWidget), + moz_gtk_get_combo_box_entry_inner_widgets, + NULL); + + if (!gComboBoxEntryTextareaWidget) { + ensure_entry_widget(); + gComboBoxEntryTextareaWidget = gEntryWidget; + } + + if (gComboBoxEntryButtonWidget) { + /* Get the Arrow inside the Button */ + buttonChild = gtk_bin_get_child(GTK_BIN(gComboBoxEntryButtonWidget)); + if (GTK_IS_HBOX(buttonChild)) { + /* appears-as-list = FALSE, cell-view = TRUE; the button + * contains an hbox. This hbox is there because ComboBoxEntry + * inherits from ComboBox which needs to place a cell renderer, + * a separator, and an arrow in the button when appears-as-list + * is FALSE. Here the hbox should only contain an arrow, since + * a ComboBoxEntry doesn't need all those widgets in the + * button. */ + gtk_container_forall(GTK_CONTAINER(buttonChild), + moz_gtk_get_combo_box_entry_arrow, + NULL); + } else if(GTK_IS_ARROW(buttonChild)) { + /* appears-as-list = TRUE, or cell-view = FALSE; + * the button only contains an arrow */ + gComboBoxEntryArrowWidget = buttonChild; + g_object_add_weak_pointer(G_OBJECT(buttonChild), (gpointer) + &gComboBoxEntryArrowWidget); + gtk_widget_realize(gComboBoxEntryArrowWidget); + } + } else { + /* Shouldn't be reached with current internal gtk implementation; + * we use a generic toggle button as last resort fallback to avoid + * crashing. */ + ensure_toggle_button_widget(); + gComboBoxEntryButtonWidget = gToggleButtonWidget; + } + + if (!gComboBoxEntryArrowWidget) { + /* Shouldn't be reached with current internal gtk implementation; + * we gButtonArrowWidget as last resort fallback to avoid + * crashing. */ + ensure_button_arrow_widget(); + gComboBoxEntryArrowWidget = gButtonArrowWidget; + } + + return MOZ_GTK_SUCCESS; +} + + +static gint +ensure_handlebox_widget() +{ + if (!gHandleBoxWidget) { + gHandleBoxWidget = gtk_handle_box_new(); + setup_widget_prototype(gHandleBoxWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_toolbar_widget() +{ + if (!gToolbarWidget) { + ensure_handlebox_widget(); + gToolbarWidget = gtk_toolbar_new(); + gtk_container_add(GTK_CONTAINER(gHandleBoxWidget), gToolbarWidget); + gtk_widget_realize(gToolbarWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_toolbar_separator_widget() +{ + if (!gToolbarSeparatorWidget) { + ensure_toolbar_widget(); + gToolbarSeparatorWidget = GTK_WIDGET(gtk_separator_tool_item_new()); + setup_widget_prototype(gToolbarSeparatorWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_tooltip_widget() +{ + if (!gTooltipWidget) { + gTooltipWidget = gtk_window_new(GTK_WINDOW_POPUP); + gtk_widget_realize(gTooltipWidget); + moz_gtk_set_widget_name(gTooltipWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_tab_widget() +{ + if (!gTabWidget) { + gTabWidget = gtk_notebook_new(); + setup_widget_prototype(gTabWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_progress_widget() +{ + if (!gProgressWidget) { + gProgressWidget = gtk_progress_bar_new(); + setup_widget_prototype(gProgressWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_statusbar_widget() +{ + if (!gStatusbarWidget) { + gStatusbarWidget = gtk_statusbar_new(); + setup_widget_prototype(gStatusbarWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_frame_widget() +{ + if (!gFrameWidget) { + ensure_statusbar_widget(); + gFrameWidget = gtk_frame_new(NULL); + gtk_container_add(GTK_CONTAINER(gStatusbarWidget), gFrameWidget); + gtk_widget_realize(gFrameWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_menu_bar_widget() +{ + if (!gMenuBarWidget) { + gMenuBarWidget = gtk_menu_bar_new(); + setup_widget_prototype(gMenuBarWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_menu_bar_item_widget() +{ + if (!gMenuBarItemWidget) { + ensure_menu_bar_widget(); + gMenuBarItemWidget = gtk_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(gMenuBarWidget), + gMenuBarItemWidget); + gtk_widget_realize(gMenuBarItemWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_menu_popup_widget() +{ + if (!gMenuPopupWidget) { + ensure_menu_bar_item_widget(); + gMenuPopupWidget = gtk_menu_new(); + gtk_menu_item_set_submenu(GTK_MENU_ITEM(gMenuBarItemWidget), + gMenuPopupWidget); + gtk_widget_realize(gMenuPopupWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_menu_item_widget() +{ + if (!gMenuItemWidget) { + ensure_menu_popup_widget(); + gMenuItemWidget = gtk_menu_item_new_with_label("M"); + gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), + gMenuItemWidget); + gtk_widget_realize(gMenuItemWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_image_menu_item_widget() +{ + if (!gImageMenuItemWidget) { + ensure_menu_popup_widget(); + gImageMenuItemWidget = gtk_image_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), + gImageMenuItemWidget); + gtk_widget_realize(gImageMenuItemWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_menu_separator_widget() +{ + if (!gMenuSeparatorWidget) { + ensure_menu_popup_widget(); + gMenuSeparatorWidget = gtk_separator_menu_item_new(); + gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), + gMenuSeparatorWidget); + gtk_widget_realize(gMenuSeparatorWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_check_menu_item_widget() +{ + if (!gCheckMenuItemWidget) { + ensure_menu_popup_widget(); + gCheckMenuItemWidget = gtk_check_menu_item_new_with_label("M"); + gtk_menu_shell_append(GTK_MENU_SHELL(gMenuPopupWidget), + gCheckMenuItemWidget); + gtk_widget_realize(gCheckMenuItemWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_tree_view_widget() +{ + if (!gTreeViewWidget) { + gTreeViewWidget = gtk_tree_view_new(); + setup_widget_prototype(gTreeViewWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_tree_header_cell_widget() +{ + if(!gTreeHeaderCellWidget) { + /* + * Some GTK engines paint the first and last cell + * of a TreeView header with a highlight. + * Since we do not know where our widget will be relative + * to the other buttons in the TreeView header, we must + * paint it as a button that is between two others, + * thus ensuring it is neither the first or last button + * in the header. + * GTK doesn't give us a way to do this explicitly, + * so we must paint with a button that is between two + * others. + */ + + GtkTreeViewColumn* firstTreeViewColumn; + GtkTreeViewColumn* lastTreeViewColumn; + + ensure_tree_view_widget(); + + /* Create and append our three columns */ + firstTreeViewColumn = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(firstTreeViewColumn, "M"); + gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), firstTreeViewColumn); + + gMiddleTreeViewColumn = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(gMiddleTreeViewColumn, "M"); + gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), + gMiddleTreeViewColumn); + + lastTreeViewColumn = gtk_tree_view_column_new(); + gtk_tree_view_column_set_title(lastTreeViewColumn, "M"); + gtk_tree_view_append_column(GTK_TREE_VIEW(gTreeViewWidget), lastTreeViewColumn); + + /* Use the middle column's header for our button */ + /* TODO */ + gTreeHeaderCellWidget = NULL; + gTreeHeaderSortArrowWidget = NULL; + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_expander_widget() +{ + if (!gExpanderWidget) { + gExpanderWidget = gtk_expander_new("M"); + setup_widget_prototype(gExpanderWidget); + } + return MOZ_GTK_SUCCESS; +} + +static gint +ensure_scrolled_window_widget() +{ + if (!gScrolledWindowWidget) { + gScrolledWindowWidget = gtk_scrolled_window_new(NULL, NULL); + setup_widget_prototype(gScrolledWindowWidget); + } + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_init() +{ + GtkWidgetClass *entry_class; + + if (is_initialized) + return MOZ_GTK_SUCCESS; + + is_initialized = TRUE; + have_arrow_scaling = (gtk_major_version > 2 || + (gtk_major_version == 2 && gtk_minor_version >= 12)); + + /* Add style property to GtkEntry. + * Adding the style property to the normal GtkEntry class means that it + * will work without issues inside GtkComboBox and for Spinbuttons. */ + entry_class = g_type_class_ref(GTK_TYPE_ENTRY); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_checkbox_get_metrics(gint* indicator_size, gint* indicator_spacing) +{ + ensure_checkbox_widget(); + + gtk_widget_style_get (gCheckboxWidget, + "indicator_size", indicator_size, + "indicator_spacing", indicator_spacing, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_radio_get_metrics(gint* indicator_size, gint* indicator_spacing) +{ + ensure_radiobutton_widget(); + + gtk_widget_style_get (gRadiobuttonWidget, + "indicator_size", indicator_size, + "indicator_spacing", indicator_spacing, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_widget_get_focus(GtkWidget* widget, gboolean* interior_focus, + gint* focus_width, gint* focus_pad) +{ + gtk_widget_style_get (widget, + "interior-focus", interior_focus, + "focus-line-width", focus_width, + "focus-padding", focus_pad, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_menuitem_get_horizontal_padding(gint* horizontal_padding) +{ + ensure_menu_item_widget(); + + gtk_widget_style_get (gMenuItemWidget, + "horizontal-padding", horizontal_padding, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_checkmenuitem_get_horizontal_padding(gint* horizontal_padding) +{ + ensure_check_menu_item_widget(); + + gtk_widget_style_get (gCheckMenuItemWidget, + "horizontal-padding", horizontal_padding, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_button_get_default_overflow(gint* border_top, gint* border_left, + gint* border_bottom, gint* border_right) +{ + GtkBorder* default_outside_border; + + ensure_button_widget(); + gtk_widget_style_get(gButtonWidget, + "default-outside-border", &default_outside_border, + NULL); + + if (default_outside_border) { + *border_top = default_outside_border->top; + *border_left = default_outside_border->left; + *border_bottom = default_outside_border->bottom; + *border_right = default_outside_border->right; + gtk_border_free(default_outside_border); + } else { + *border_top = *border_left = *border_bottom = *border_right = 0; + } + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_button_get_default_border(gint* border_top, gint* border_left, + gint* border_bottom, gint* border_right) +{ + GtkBorder* default_border; + + ensure_button_widget(); + gtk_widget_style_get(gButtonWidget, + "default-border", &default_border, + NULL); + + if (default_border) { + *border_top = default_border->top; + *border_left = default_border->left; + *border_bottom = default_border->bottom; + *border_right = default_border->right; + gtk_border_free(default_border); + } else { + /* see gtkbutton.c */ + *border_top = *border_left = *border_bottom = *border_right = 1; + } + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_splitter_get_metrics(gint orientation, gint* size) +{ + if (orientation == GTK_ORIENTATION_HORIZONTAL) { + ensure_hpaned_widget(); + gtk_widget_style_get(gHPanedWidget, "handle_size", size, NULL); + } else { + ensure_vpaned_widget(); + gtk_widget_style_get(gVPanedWidget, "handle_size", size, NULL); + } + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_button_get_inner_border(GtkWidget* widget, GtkBorder* inner_border) +{ + static const GtkBorder default_inner_border = { 1, 1, 1, 1 }; + GtkBorder *tmp_border; + + gtk_widget_style_get (widget, "inner-border", &tmp_border, NULL); + + if (tmp_border) { + *inner_border = *tmp_border; + gtk_border_free(tmp_border); + } + else + *inner_border = default_inner_border; + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_button_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkReliefStyle relief, GtkWidget* widget, + GtkTextDirection direction) +{ + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style = gtk_widget_get_style_context(widget); + gint x = rect->x, y=rect->y, width=rect->width, height=rect->height; + + gboolean interior_focus; + gint focus_width, focus_pad; + + moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); + gtk_widget_set_direction(widget, direction); + + if (!interior_focus && state->focused) { + x += focus_width + focus_pad; + y += focus_width + focus_pad; + width -= 2 * (focus_width + focus_pad); + height -= 2 * (focus_width + focus_pad); + } + + gtk_style_context_save(style); + gtk_style_context_set_state(style, state_flags); + + if (state->isDefault && relief == GTK_RELIEF_NORMAL) { + /* handle default borders both outside and inside the button */ + gint default_top, default_left, default_bottom, default_right; + moz_gtk_button_get_default_overflow(&default_top, &default_left, + &default_bottom, &default_right); + x -= default_left; + y -= default_top; + width += default_left + default_right; + height += default_top + default_bottom; + gtk_render_background(style, cr, x, y, width, height); + gtk_render_frame(style, cr, x, y, width, height); + moz_gtk_button_get_default_border(&default_top, &default_left, + &default_bottom, &default_right); + x += default_left; + y += default_top; + width -= (default_left + default_right); + height -= (default_top + default_bottom); + } + + if (relief != GTK_RELIEF_NONE || state->depressed || + (state_flags & GTK_STATE_FLAG_PRELIGHT)) { + /* the following line can trigger an assertion (Crux theme) + file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area): + assertion `GDK_IS_WINDOW (window)' failed */ + gtk_render_background(style, cr, x, y, width, height); + gtk_render_frame(style, cr, x, y, width, height); + } + + if (state->focused) { + if (interior_focus) { + GtkBorder border; + gtk_style_context_get_border(style, state_flags, &border); + x += border.left + focus_pad; + y += border.top + focus_pad; + width -= 2 * (border.left + focus_pad); + height -= 2 * (border.top + focus_pad); + } else { + x -= focus_width + focus_pad; + y -= focus_width + focus_pad; + width += 2 * (focus_width + focus_pad); + height += 2 * (focus_width + focus_pad); + } + + gtk_render_focus(style, cr, x, y, width, height); + } + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_toggle_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean selected, gboolean inconsistent, + gboolean isradio, GtkTextDirection direction) +{ + gint indicator_size, indicator_spacing; + gint x, y, width, height; + gint focus_x, focus_y, focus_width, focus_height; + GtkWidget *w; + GtkStyleContext *style; + + if (isradio) { + moz_gtk_radio_get_metrics(&indicator_size, &indicator_spacing); + w = gRadiobuttonWidget; + } else { + moz_gtk_checkbox_get_metrics(&indicator_size, &indicator_spacing); + w = gCheckboxWidget; + } + + NS_ASSERTION(rect->width == indicator_size, + "GetMinimumWidgetSize was ignored"); + /* + * vertically center in the box, since XUL sometimes ignores our + * GetMinimumWidgetSize in the vertical dimension + */ + x = rect->x; + y = rect->y + (rect->height - indicator_size) / 2; + width = indicator_size; + height = indicator_size; + + focus_x = x - indicator_spacing; + focus_y = y - indicator_spacing; + focus_width = width + 2 * indicator_spacing; + focus_height = height + 2 * indicator_spacing; + + style = gtk_widget_get_style_context(w); + + gtk_widget_set_sensitive(w, !state->disabled); + gtk_widget_set_direction(w, direction); + gtk_style_context_save(style); + + if (isradio) { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); + if (selected) { + gtk_style_context_set_state(style, GTK_STATE_FLAG_ACTIVE); + } + gtk_render_option(style, cr, x, y, width, height); + if (state->focused) { + gtk_render_focus(style, cr, focus_x, focus_y, + focus_width, focus_height); + } + } + else { + /* + * 'indeterminate' type on checkboxes. In GTK, the shadow type + * must also be changed for the state to be drawn. + */ + gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); + if (inconsistent) { + gtk_style_context_set_state(style, GTK_STATE_FLAG_INCONSISTENT); + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), TRUE); + } else if (selected) { + gtk_style_context_set_state(style, GTK_STATE_FLAG_ACTIVE); + } else { + gtk_toggle_button_set_inconsistent(GTK_TOGGLE_BUTTON(gCheckboxWidget), FALSE); + } + gtk_render_check(style, cr, x, y, width, height); + if (state->focused) { + gtk_render_focus(style, cr, + focus_x, focus_y, focus_width, focus_height); + } + } + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +calculate_button_inner_rect(GtkWidget* button, GdkRectangle* rect, + GdkRectangle* inner_rect, + GtkTextDirection direction, + gboolean ignore_focus) +{ + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + GtkStyleContext* style; + GtkBorder border; + + style = gtk_widget_get_style_context(button); + + /* This mirrors gtkbutton's child positioning */ + moz_gtk_button_get_inner_border(button, &inner_border); + moz_gtk_widget_get_focus(button, &interior_focus, + &focus_width, &focus_pad); + + if (ignore_focus) + focus_width = focus_pad = 0; + + gtk_style_context_get_border(style, 0, &border); + + inner_rect->x = rect->x + border.left + focus_width + focus_pad; + inner_rect->x += direction == GTK_TEXT_DIR_LTR ? + inner_border.left : inner_border.right; + inner_rect->y = rect->y + inner_border.top + border.top + + focus_width + focus_pad; + inner_rect->width = MAX(1, rect->width - inner_border.left - + inner_border.right - (border.left + focus_pad + focus_width) * 2); + inner_rect->height = MAX(1, rect->height - inner_border.top - + inner_border.bottom - (border.top + focus_pad + focus_width) * 2); + + return MOZ_GTK_SUCCESS; +} + + +static gint +calculate_arrow_rect(GtkWidget* arrow, GdkRectangle* rect, + GdkRectangle* arrow_rect, GtkTextDirection direction) +{ + /* defined in gtkarrow.c */ + gfloat arrow_scaling = 0.7; + gfloat xalign, xpad; + gint extent; + gint mxpad, mypad; + gfloat mxalign, myalign; + GtkMisc* misc = GTK_MISC(arrow); + + if (have_arrow_scaling) + gtk_widget_style_get(arrow, "arrow_scaling", &arrow_scaling, NULL); + + gtk_misc_get_padding(misc, &mxpad, &mypad); + extent = MIN((rect->width - mxpad * 2), + (rect->height - mypad * 2)) * arrow_scaling; + + gtk_misc_get_alignment(misc, &mxalign, &myalign); + + xalign = direction == GTK_TEXT_DIR_LTR ? mxalign : 1.0 - mxalign; + xpad = mxpad + (rect->width - extent) * xalign; + + arrow_rect->x = direction == GTK_TEXT_DIR_LTR ? + floor(rect->x + xpad) : ceil(rect->x + xpad); + arrow_rect->y = floor(rect->y + mypad + + ((rect->height - extent) * myalign)); + + arrow_rect->width = arrow_rect->height = extent; + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_button_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkScrollbarButtonFlags flags, + GtkTextDirection direction) +{ + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStateType saved_state; + GdkRectangle arrow_rect; + gdouble arrow_angle; + GtkStyleContext* style; + GtkWidget *scrollbar; + gint arrow_displacement_x, arrow_displacement_y; + const char* detail = (flags & MOZ_GTK_STEPPER_VERTICAL) ? + "vscrollbar" : "hscrollbar"; + + ensure_scrollbar_widget(); + + if (flags & MOZ_GTK_STEPPER_VERTICAL) + scrollbar = gVertScrollbarWidget; + else + scrollbar = gHorizScrollbarWidget; + + gtk_widget_set_direction(scrollbar, direction); + + if (flags & MOZ_GTK_STEPPER_VERTICAL) { + arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_DOWN : ARROW_UP; + } else { + arrow_angle = (flags & MOZ_GTK_STEPPER_DOWN) ? ARROW_RIGHT : ARROW_LEFT; + } + + style = gtk_widget_get_style_context(scrollbar); + + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCROLLBAR); + gtk_style_context_set_state(style, state_flags); + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + + arrow_rect.width = rect->width / 2; + arrow_rect.height = rect->height / 2; + arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; + arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; + + if (state_flags & GTK_STATE_FLAG_ACTIVE) { + gtk_widget_style_get(scrollbar, + "arrow-displacement-x", &arrow_displacement_x, + "arrow-displacement-y", &arrow_displacement_y, + NULL); + + arrow_rect.x += arrow_displacement_x; + arrow_rect.y += arrow_displacement_y; + } + + gtk_render_arrow(style, cr, arrow_angle, + arrow_rect.x, + arrow_rect.y, + arrow_rect.width); + + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_trough_paint(GtkThemeWidgetType widget, + cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStyleContext* style; + GtkScrollbar *scrollbar; + + ensure_scrollbar_widget(); + + if (widget == MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL) + scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget); + else + scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget); + + gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); + + style = gtk_widget_get_style_context(GTK_WIDGET(scrollbar)); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH); + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + + if (state->focused) { + gtk_render_focus(style, cr, + rect->x, rect->y, rect->width, rect->height); + } + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scrollbar_thumb_paint(GtkThemeWidgetType widget, + cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; + GtkScrollbar *scrollbar; + GtkAdjustment *adj; + + ensure_scrollbar_widget(); + + if (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) + scrollbar = GTK_SCROLLBAR(gHorizScrollbarWidget); + else + scrollbar = GTK_SCROLLBAR(gVertScrollbarWidget); + + gtk_widget_set_direction(GTK_WIDGET(scrollbar), direction); + + style = gtk_widget_get_style_context(GTK_WIDGET(scrollbar)); + gtk_style_context_save(style); + + gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER); + gtk_style_context_set_state(style, state_flags); + + gtk_render_slider(style, cr, rect->x, rect->y, + rect->width, rect->height, + (widget == MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL) ? + GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL); + + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_spin_paint(cairo_t *cr, GdkRectangle* rect, + GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_spin_widget(); + gtk_widget_set_direction(gSpinWidget, direction); + style = gtk_widget_get_style_context(gSpinWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_SPINBUTTON); + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_spin_updown_paint(cairo_t *cr, GdkRectangle* rect, + gboolean isDown, GtkWidgetState* state, + GtkTextDirection direction) +{ + GdkRectangle arrow_rect; + GtkStyleContext* style; + + ensure_spin_widget(); + style = gtk_widget_get_style_context(gSpinWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_SPINBUTTON); + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + gtk_widget_set_direction(gSpinWidget, direction); + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + + + /* hard code these values */ + arrow_rect.width = 6; + arrow_rect.height = 6; + arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; + arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; + arrow_rect.y += isDown ? -1 : 1; + + gtk_render_arrow(style, cr, + isDown ? ARROW_DOWN : ARROW_UP, + arrow_rect.x, arrow_rect.y, + arrow_rect.width); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scale_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkOrientation flags, GtkTextDirection direction) +{ + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + gint x = 0, y = 0; + GtkStyleContext* style; + GtkWidget* widget; + GtkBorder border; + + ensure_scale_widget(); + widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style_context(widget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_SCALE); + gtk_style_context_get_border(style, state_flags, &border); + + if (flags == GTK_ORIENTATION_HORIZONTAL) { + x = border.left; + y++; + } + else { + x++; + y = border.top; + } + + gtk_render_background(style, cr, rect->x + x, rect->y + y, + rect->width - 2*x, rect->height - 2*y); + gtk_render_frame(style, cr, rect->x + x, rect->y + y, + rect->width - 2*x, rect->height - 2*y); + + if (state->focused) + gtk_render_focus(style, cr, + rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_scale_thumb_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkOrientation flags, GtkTextDirection direction) +{ + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; + GtkWidget* widget; + gint thumb_width, thumb_height, x, y; + + ensure_scale_widget(); + widget = ((flags == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style_context(widget); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_SLIDER); + gtk_style_context_save(style); + gtk_style_context_set_state(style, state_flags); + /* determine the thumb size, and position the thumb in the center in the opposite axis + */ + if (flags == GTK_ORIENTATION_HORIZONTAL) { + moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_HORIZONTAL, &thumb_width, &thumb_height); + x = rect->x; + y = rect->y + (rect->height - thumb_height) / 2; + } + else { + moz_gtk_get_scalethumb_metrics(GTK_ORIENTATION_VERTICAL, &thumb_height, &thumb_width); + x = rect->x + (rect->width - thumb_width) / 2; + y = rect->y; + } + + gtk_render_slider(style, cr, x, y, thumb_width, thumb_height, flags); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_gripper_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_handlebox_widget(); + gtk_widget_set_direction(gHandleBoxWidget, direction); + + style = gtk_widget_get_style_context(gHandleBoxWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP); + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_hpaned_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state) +{ + GtkStyleContext* style; + + ensure_hpaned_widget(); + style = gtk_widget_get_style_context(gHPanedWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR); + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + gtk_render_handle(style, cr, + rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_vpaned_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state) +{ + GtkStyleContext* style; + + ensure_vpaned_widget(); + style = gtk_widget_get_style_context(gVPanedWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_PANE_SEPARATOR); + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + gtk_render_handle(style, cr, + rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_caret_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GdkRectangle location = *rect; + + if (direction == GTK_TEXT_DIR_RTL) { + /* gtk_draw_insertion_cursor ignores location.width */ + location.x = rect->x + rect->width; + } + + ensure_entry_widget(); + gtk_draw_insertion_cursor(gEntryWidget, cr, + &location, TRUE, direction, FALSE); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_entry_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkWidget* widget, GtkTextDirection direction) +{ + gint x = rect->x, y = rect->y, width = rect->width, height = rect->height; + GtkStyleContext* style; + gboolean interior_focus; + gint focus_width; + + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style_context(widget); + + gtk_widget_style_get(widget, + "interior-focus", &interior_focus, + "focus-line-width", &focus_width, + NULL); + + /* gtkentry.c uses two windows, one for the entire widget and one for the + * text area inside it. The background of both windows is set to the "base" + * color of the new state in gtk_entry_state_changed, but only the inner + * textarea window uses gtk_paint_flat_box when exposed */ + + /* This gets us a lovely greyish disabledish look */ + gtk_widget_set_sensitive(widget, !state->disabled); + + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_ENTRY); + + /* Now paint the shadow and focus border. + * We do like in gtk_entry_draw_frame, we first draw the shadow, a tad + * smaller when focused if the focus is not interior, then the focus. */ + + if (state->focused && !state->disabled) { + /* This will get us the lit borders that focused textboxes enjoy on + * some themes. */ + gtk_style_context_set_state(style, GTK_STATE_FLAG_FOCUSED); + if (!interior_focus) { + /* Indent the border a little bit if we have exterior focus + (this is what GTK does to draw native entries) */ + x += focus_width; + y += focus_width; + width -= 2 * focus_width; + height -= 2 * focus_width; + } + } + + if (state->disabled) { + gtk_style_context_set_state(style, GTK_STATE_FLAG_INSENSITIVE); + } + + gtk_render_background(style, cr, x, y, width, height); + gtk_render_frame(style, cr, x, y, width, height); + + if (state->focused && !state->disabled) { + if (!interior_focus) { + gtk_render_focus(style, cr, rect->x, rect->y, rect->width, rect->height); + } + } + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_treeview_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTextDirection direction) +{ + gint xthickness, ythickness; + GtkStyleContext *style; + GtkStyleContext *style_tree; + GtkStateFlags state_flags; + GtkBorder border; + + ensure_tree_view_widget(); + ensure_scrolled_window_widget(); + + gtk_widget_set_direction(gTreeViewWidget, direction); + gtk_widget_set_direction(gScrolledWindowWidget, direction); + + /* only handle disabled and normal states, otherwise the whole background + * area will be painted differently with other states */ + state_flags = state->disabled ? GTK_STATE_FLAG_INSENSITIVE : GTK_STATE_FLAG_NORMAL; + + style = gtk_widget_get_style_context(gScrolledWindowWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_FRAME); + gtk_style_context_get_border(style, state_flags, &border); + xthickness = border.left; + ythickness = border.top; + + style_tree = gtk_widget_get_style_context(gTreeViewWidget); + gtk_style_context_save(style_tree); + gtk_style_context_add_class(style_tree, GTK_STYLE_CLASS_VIEW); + + gtk_render_background(style_tree, cr, + rect->x + xthickness, rect->y + ythickness, + rect->width - 2 * xthickness, + rect->height - 2 * ythickness); + gtk_render_frame(style, cr, + rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + gtk_style_context_restore(style_tree); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_tree_header_cell_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean isSorted, GtkTextDirection direction) +{ + moz_gtk_button_paint(cr, rect, cliprect, state, GTK_RELIEF_NORMAL, + gTreeHeaderCellWidget, direction); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_tree_header_sort_arrow_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, + GtkWidgetState* state, GtkArrowType arrow_type, + GtkTextDirection direction) +{ + GdkRectangle arrow_rect; + gdouble arrow_angle; + GtkStyleContext* style; + + ensure_tree_header_cell_widget(); + gtk_widget_set_direction(gTreeHeaderSortArrowWidget, direction); + + /* hard code these values */ + arrow_rect.width = 11; + arrow_rect.height = 11; + arrow_rect.x = rect->x + (rect->width - arrow_rect.width) / 2; + arrow_rect.y = rect->y + (rect->height - arrow_rect.height) / 2; + style = gtk_widget_get_style_context(gTreeHeaderSortArrowWidget); + gtk_style_context_save(style); + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + + switch (arrow_type) { + case GTK_ARROW_LEFT: + arrow_angle = ARROW_LEFT; + break; + case GTK_ARROW_RIGHT: + arrow_angle = ARROW_RIGHT; + break; + case GTK_ARROW_DOWN: + arrow_angle = ARROW_DOWN; + break; + default: + arrow_angle = ARROW_UP; + break; + } + if (arrow_type != GTK_ARROW_NONE) + gtk_render_arrow(style, cr, arrow_angle, + arrow_rect.x, arrow_rect.y, + arrow_rect.width); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_treeview_expander_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkExpanderStyle expander_state, + GtkTextDirection direction) +{ + GtkStyleContext *style; + + ensure_tree_view_widget(); + gtk_widget_set_direction(gTreeViewWidget, direction); + + style = gtk_widget_get_style_context(gTreeViewWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_EXPANDER); + /* Because the frame we get is of the entire treeview, we can't get the precise + * event state of one expander, thus rendering hover and active feedback useless. */ + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + gtk_render_expander(style, cr, + rect->x + rect->width / 2, rect->y + rect->height / 2, + rect->width, rect->height); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_expander_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkExpanderStyle expander_state, + GtkTextDirection direction) +{ + GtkStyleContext *style; + + ensure_expander_widget(); + gtk_widget_set_direction(gExpanderWidget, direction); + + style = gtk_widget_get_style_context(gExpanderWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_EXPANDER); + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + gtk_render_expander(style, cr, + rect->x + rect->width / 2, rect->y + rect->height / 2, + rect->width, rect->height); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_combo_box_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean ishtml, GtkTextDirection direction) +{ + GdkRectangle arrow_rect, real_arrow_rect; + gint arrow_size, separator_width; + gboolean wide_separators; + GtkStyleContext* style; + GtkRequisition arrow_req; + + ensure_combo_box_widgets(); + + /* Also sets the direction on gComboBoxButtonWidget, which is then + * inherited by the separator and arrow */ + moz_gtk_button_paint(cr, rect, cliprect, state, GTK_RELIEF_NORMAL, + gComboBoxButtonWidget, direction); + + calculate_button_inner_rect(gComboBoxButtonWidget, + rect, &arrow_rect, direction, ishtml); + /* Now arrow_rect contains the inner rect ; we want to correct the width + * to what the arrow needs (see gtk_combo_box_size_allocate) */ + gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req); + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x += arrow_rect.width - arrow_req.width; + arrow_rect.width = arrow_req.width; + + calculate_arrow_rect(gComboBoxArrowWidget, + &arrow_rect, &real_arrow_rect, direction); + + style = gtk_widget_get_style_context(gComboBoxArrowWidget); + gtk_render_arrow(style, cr, ARROW_DOWN, + real_arrow_rect.x, real_arrow_rect.y, + real_arrow_rect.width); + + /* If there is no separator in the theme, there's nothing left to do. */ + if (!gComboBoxSeparatorWidget) + return MOZ_GTK_SUCCESS; + style = gtk_widget_get_style_context(gComboBoxSeparatorWidget); + gtk_widget_style_get(gComboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (wide_separators) { + if (direction == GTK_TEXT_DIR_LTR) + arrow_rect.x -= separator_width; + else + arrow_rect.x += arrow_rect.width; + + gtk_render_frame(style, cr, arrow_rect.x, arrow_rect.y, separator_width, arrow_rect.height); + } else { + if (direction == GTK_TEXT_DIR_LTR) { + GtkBorder border; + gtk_style_context_get_border(style, GetStateFlagsFromGtkWidgetState(state), &border); + arrow_rect.x -= border.left; + } + else + arrow_rect.x += arrow_rect.width; + + gtk_render_line(style, cr, + arrow_rect.x, arrow_rect.y, + arrow_rect.x, arrow_rect.y + arrow_rect.height); + } + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_arrow_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkArrowType arrow_type, GtkTextDirection direction) +{ + GtkStyleContext* style; + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GdkRectangle arrow_rect; + gdouble arrow_angle = ARROW_UP; + + ensure_button_arrow_widget(); + style = gtk_widget_get_style_context(gButtonArrowWidget); + gtk_style_context_save(style); + gtk_style_context_set_state(style, state_flags); + gtk_widget_set_direction(gButtonArrowWidget, direction); + + calculate_arrow_rect(gButtonArrowWidget, rect, &arrow_rect, + direction); + + if (direction == GTK_TEXT_DIR_RTL) { + if (arrow_type == GTK_ARROW_LEFT) + arrow_angle = ARROW_RIGHT; + else if (arrow_type == GTK_ARROW_RIGHT) + arrow_angle = ARROW_LEFT; + } else if (arrow_type == GTK_ARROW_DOWN) { + arrow_angle = ARROW_DOWN; + } + if (arrow_type != GTK_ARROW_NONE) + gtk_render_arrow(style, cr, arrow_angle, + arrow_rect.x, arrow_rect.y, arrow_rect.width); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_combo_box_entry_button_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, + GtkWidgetState* state, + gboolean input_focus, + GtkTextDirection direction) +{ + gint x_displacement, y_displacement; + GdkRectangle arrow_rect, real_arrow_rect; + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; + + ensure_combo_box_entry_widgets(); + + moz_gtk_button_paint(cr, rect, cliprect, state, GTK_RELIEF_NORMAL, + gComboBoxEntryButtonWidget, direction); + + calculate_button_inner_rect(gComboBoxEntryButtonWidget, + rect, &arrow_rect, direction, FALSE); + if (state_flags & GTK_STATE_FLAG_ACTIVE) { + gtk_widget_style_get(gComboBoxEntryButtonWidget, + "child-displacement-x", &x_displacement, + "child-displacement-y", &y_displacement, + NULL); + arrow_rect.x += x_displacement; + arrow_rect.y += y_displacement; + } + + calculate_arrow_rect(gComboBoxEntryArrowWidget, + &arrow_rect, &real_arrow_rect, direction); + + style = gtk_widget_get_style_context(gComboBoxEntryArrowWidget); + + gtk_render_arrow(style, cr, ARROW_DOWN, + real_arrow_rect.x, real_arrow_rect.y, + real_arrow_rect.width); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_container_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean isradio, GtkTextDirection direction) +{ + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; + GtkWidget *widget; + gboolean interior_focus; + gint focus_width, focus_pad; + + if (isradio) { + ensure_radiobutton_widget(); + widget = gRadiobuttonWidget; + } else { + ensure_checkbox_widget(); + widget = gCheckboxWidget; + } + gtk_widget_set_direction(widget, direction); + + style = gtk_widget_get_style_context(widget); + gtk_style_context_save(style); + moz_gtk_widget_get_focus(widget, &interior_focus, &focus_width, &focus_pad); + gtk_style_context_set_state(style, state_flags); + + /* this is for drawing a prelight box */ + if (state_flags & GTK_STATE_FLAG_PRELIGHT) { + gtk_render_background(style, cr, + rect->x, rect->y, rect->width, rect->height); + } + + if (state->focused && !interior_focus) { + gtk_render_focus(style, cr, + rect->x, rect->y, rect->width, rect->height); + } + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_toggle_label_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean isradio, GtkTextDirection direction) +{ + GtkStyleContext *style; + GtkWidget *widget; + gboolean interior_focus; + + if (!state->focused) + return MOZ_GTK_SUCCESS; + + if (isradio) { + ensure_radiobutton_widget(); + widget = gRadiobuttonWidget; + } else { + ensure_checkbox_widget(); + widget = gCheckboxWidget; + } + style = gtk_widget_get_style_context(widget); + gtk_style_context_save(style); + if (isradio) { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); + } else { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); + } + gtk_widget_set_direction(widget, direction); + + gtk_widget_style_get(widget, "interior-focus", &interior_focus, NULL); + if (!interior_focus) + return MOZ_GTK_SUCCESS; + + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + gtk_render_focus(style, cr, + rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_toolbar_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_toolbar_widget(); + gtk_widget_set_direction(gToolbarWidget, direction); + + style = gtk_widget_get_style_context(gToolbarWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLBAR); + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_toolbar_separator_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, + GtkTextDirection direction) +{ + GtkStyleContext* style; + gint separator_width; + gint paint_width; + gboolean wide_separators; + + /* Defined as constants in GTK+ 2.10.14 */ + const double start_fraction = 0.2; + const double end_fraction = 0.8; + + ensure_toolbar_separator_widget(); + gtk_widget_set_direction(gToolbarSeparatorWidget, direction); + + style = gtk_widget_get_style_context(gToolbarSeparatorWidget); + + gtk_widget_style_get(gToolbarWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (wide_separators) { + if (separator_width > rect->width) + separator_width = rect->width; + + gtk_render_frame(style, cr, + rect->x + (rect->width - separator_width) / 2, + rect->y + rect->height * start_fraction, + separator_width, + rect->height * (end_fraction - start_fraction)); + } else { + GtkBorder border; + gtk_style_context_get_border(style, 0, &border); + + paint_width = border.left; + + if (paint_width > rect->width) + paint_width = rect->width; + + gtk_render_line(style, cr, + rect->x + (rect->width - paint_width) / 2, + rect->y + rect->height * start_fraction, + rect->x + (rect->width - paint_width) / 2, + rect->y + rect->height * end_fraction); + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_tooltip_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_tooltip_widget(); + gtk_widget_set_direction(gTooltipWidget, direction); + + style = gtk_widget_get_style_context(gTooltipWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_TOOLTIP); + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_resizer_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_frame_widget(); + gtk_widget_set_direction(gStatusbarWidget, direction); + + style = gtk_widget_get_style_context(gStatusbarWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_GRIP); + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + + gtk_render_handle(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_frame_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_frame_widget(); + gtk_widget_set_direction(gFrameWidget, direction); + style = gtk_widget_get_style_context(gFrameWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_FRAME); + + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_progressbar_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_progress_widget(); + gtk_widget_set_direction(gProgressWidget, direction); + + style = gtk_widget_get_style_context(gProgressWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_TROUGH); + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_progress_chunk_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction, + GtkThemeWidgetType widget) +{ + GtkStyleContext* style; + + ensure_progress_widget(); + gtk_widget_set_direction(gProgressWidget, direction); + + style = gtk_widget_get_style_context(gProgressWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_PROGRESSBAR); + + if (widget == MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE || + widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE) { + /** + * The bar's size and the bar speed are set depending of the progress' + * size. These could also be constant for all progress bars easily. + */ + gboolean vertical = (widget == MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE); + + /* The size of the dimension we are going to use for the animation. */ + const gint progressSize = vertical ? rect->height : rect->width; + + /* The bar is using a fifth of the element size, based on GtkProgressBar + * activity-blocks property. */ + const gint barSize = MAX(1, progressSize / 5); + + /* Represents the travel that has to be done for a complete cycle. */ + const gint travel = 2 * (progressSize - barSize); + + /* period equals to travel / pixelsPerMillisecond + * where pixelsPerMillisecond equals progressSize / 1000.0. + * This is equivalent to 1600. */ + static const guint period = 1600; + const gint t = PR_IntervalToMilliseconds(PR_IntervalNow()) % period; + const gint dx = travel * t / period; + + if (vertical) { + rect->y += (dx < travel / 2) ? dx : travel - dx; + rect->height = barSize; + } else { + rect->x += (dx < travel / 2) ? dx : travel - dx; + rect->width = barSize; + } + } + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_activity(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_tab_thickness(void) +{ + GtkBorder border; + + ensure_tab_widget(); + GtkStyleContext * style = gtk_widget_get_style_context(gTabWidget); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); + gtk_style_context_get_border(style, 0, &border); + + if (border.top < 2) + return 2; /* some themes don't set ythickness correctly */ + + return border.top; +} + +/* actual small tabs */ +static gint +moz_gtk_tab_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTabFlags flags, GtkTextDirection direction) +{ + /* When the tab isn't selected, we just draw a notebook extension. + * When it is selected, we overwrite the adjacent border of the tabpanel + * touching the tab with a pierced border (called "the gap") to make the + * tab appear physically attached to the tabpanel; see details below. */ + + GtkStyleContext* style; + GdkRectangle focusRect; + + ensure_tab_widget(); + gtk_widget_set_direction(gTabWidget, direction); + + style = gtk_widget_get_style_context(gTabWidget); + focusRect = *rect; + + gtk_style_context_save(style); + + if ((flags & MOZ_GTK_TAB_SELECTED) == 0) { + /* Only draw the tab */ + gtk_style_context_set_state(style, GTK_STATE_FLAG_NORMAL); + gtk_render_extension(style, cr, + rect->x, rect->y, rect->width, rect->height, + (flags & MOZ_GTK_TAB_BOTTOM) ? + GTK_POS_TOP : GTK_POS_BOTTOM ); + } else { + /* Draw the tab and the gap + * We want the gap to be positioned exactly on the tabpanel top + * border; since tabbox.css may set a negative margin so that the tab + * frame rect already overlaps the tabpanel frame rect, we need to take + * that into account when drawing. To that effect, nsNativeThemeGTK + * passes us this negative margin (bmargin in the graphic below) in the + * lowest bits of |flags|. We use it to set gap_voffset, the distance + * between the top of the gap and the bottom of the tab (resp. the + * bottom of the gap and the top of the tab when we draw a bottom tab), + * while ensuring that the gap always touches the border of the tab, + * i.e. 0 <= gap_voffset <= gap_height, to avoid surprinsing results + * with big negative or positive margins. + * Here is a graphical explanation in the case of top tabs: + * ___________________________ + * / \ + * | T A B | + * ----------|. . . . . . . . . . . . . . .|----- top of tabpanel + * : ^ bmargin : ^ + * : | (-negative margin, : | + * bottom : v passed in flags) : | gap_height + * of -> :.............................: | (the size of the + * the tab . part of the gap . | tabpanel top border) + * . outside of the tab . v + * ---------------------------------------------- + * + * To draw the gap, we use gtk_paint_box_gap(), see comment in + * moz_gtk_tabpanels_paint(). This box_gap is made 3 * gap_height tall, + * which should suffice to ensure that the only visible border is the + * pierced one. If the tab is in the middle, we make the box_gap begin + * a bit to the left of the tab and end a bit to the right, adjusting + * the gap position so it still is under the tab, because we want the + * rendering of a gap in the middle of a tabpanel. This is the role of + * the gints gap_{l,r}_offset. On the contrary, if the tab is the + * first, we align the start border of the box_gap with the start + * border of the tab (left if LTR, right if RTL), by setting the + * appropriate offset to 0.*/ + gint gap_loffset, gap_roffset, gap_voffset, gap_height; + + /* Get height needed by the gap */ + gap_height = moz_gtk_get_tab_thickness(); + + /* Extract gap_voffset from the first bits of flags */ + gap_voffset = flags & MOZ_GTK_TAB_MARGIN_MASK; + if (gap_voffset > gap_height) + gap_voffset = gap_height; + + /* Set gap_{l,r}_offset to appropriate values */ + gap_loffset = gap_roffset = 20; /* should be enough */ + if (flags & MOZ_GTK_TAB_FIRST) { + if (direction == GTK_TEXT_DIR_RTL) + gap_roffset = 0; + else + gap_loffset = 0; + } + + gtk_style_context_set_state(style, GTK_STATE_FLAG_ACTIVE); + + /* Adwaita theme engine crashes without it (rhbz#713764) */ + gtk_style_context_add_region(style, GTK_STYLE_REGION_TAB, 0); + + if (flags & MOZ_GTK_TAB_BOTTOM) { + /* Draw the tab on bottom */ + focusRect.y += gap_voffset; + focusRect.height -= gap_voffset; + + gtk_render_extension(style, cr, + rect->x, rect->y + gap_voffset, rect->width, + rect->height - gap_voffset, GTK_POS_TOP); + + /* Draw the gap; erase with background color before painting in + * case theme does not */ + gtk_render_background(style, cr, + rect->x, + rect->y + gap_voffset + - gap_height, + rect->width, gap_height); + gtk_render_frame_gap(style, cr, + rect->x - gap_loffset, + rect->y + gap_voffset - 3 * gap_height, + rect->width + gap_loffset + gap_roffset, + 3 * gap_height, GTK_POS_BOTTOM, + gap_loffset, gap_loffset + rect->width); + + } else { + /* Draw the tab on top */ + focusRect.height -= gap_voffset; + gtk_render_extension(style, cr, + rect->x, rect->y, rect->width, + rect->height - gap_voffset, GTK_POS_BOTTOM); + + /* Draw the gap; erase with background color before painting in + * case theme does not */ + gtk_render_background(style, cr, + rect->x, + rect->y + rect->height + - gap_voffset, + rect->width, gap_height); + gtk_render_frame_gap(style, cr, + rect->x - gap_loffset, + rect->y + rect->height - gap_voffset, + rect->width + gap_loffset + gap_roffset, + 3 * gap_height, GTK_POS_TOP, + gap_loffset, gap_loffset + rect->width); + } + } + + if (state->focused) { + /* Paint the focus ring */ + GtkBorder border; + gtk_style_context_get_border(style, GetStateFlagsFromGtkWidgetState(state), &border); + + focusRect.x += border.left; + focusRect.width -= (border.left + border.right); + focusRect.y += border.top; + focusRect.height -= (border.top + border.bottom); + + gtk_render_focus(style, cr, + focusRect.x, focusRect.y, focusRect.width, focusRect.height); + } + + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +/* tab area*/ +static gint +moz_gtk_tabpanels_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_tab_widget(); + gtk_widget_set_direction(gTabWidget, direction); + + style = gtk_widget_get_style_context(gTabWidget); + gtk_style_context_save(style); + + gtk_render_background(style, cr, rect->x, rect->y, + rect->width, rect->height); + /* + * The gap size is not needed in moz_gtk_tabpanels_paint because + * the gap will be painted with the foreground tab in moz_gtk_tab_paint. + * + * However, if moz_gtk_tabpanels_paint just uses gtk_render_frame(), + * the theme will think that there are no tabs and may draw something + * different.Hence the trick of using two clip regions, and drawing the + * gap outside each clip region, to get the correct frame for + * a tabpanel with tabs. + */ + /* left side */ + cairo_save(cr); + cairo_rectangle(rect->x, rect->y, + rect->x + rect->width / 2, + rect->y + rect->height) + cairo_clip(cr); + gtk_render_frame_gap(style, cr, + rect->x, rect->y, + rect->width, rect->height, + GTK_POS_TOP, rect->width - 1, rect->width); + cairo_restore(cr); + + /* right side */ + cairo_save(cr); + cairo_rectangle(rect->x + rect->width / 2, rect->y, + rect->x + rect->width, + rect->y + rect->height) + cairo_clip(cr); + gtk_render_frame_gap(style, cr, + rect->x, rect->y, + rect->width, rect->height, + GTK_POS_TOP, 0, 1); + cairo_restore(cr); + + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_tab_scroll_arrow_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkArrowType arrow_type, + GtkTextDirection direction) +{ + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; + gdouble arrow_angle; + gint arrow_size = MIN(rect->width, rect->height); + gint x = rect->x + (rect->width - arrow_size) / 2; + gint y = rect->y + (rect->height - arrow_size) / 2; + + ensure_tab_widget(); + + style = gtk_widget_get_style_context(gTabWidget); + gtk_style_context_save(style); + if (direction == GTK_TEXT_DIR_RTL) { + arrow_type = (arrow_type == GTK_ARROW_LEFT) ? + GTK_ARROW_RIGHT : GTK_ARROW_LEFT; + } + switch (arrow_type) { + case GTK_ARROW_LEFT: + arrow_angle = ARROW_LEFT; + break; + case GTK_ARROW_RIGHT: + arrow_angle = ARROW_RIGHT; + break; + case GTK_ARROW_DOWN: + arrow_angle = ARROW_DOWN; + break; + default: + arrow_angle = ARROW_UP; + break; + } + if (arrow_type != GTK_ARROW_NONE) { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_NOTEBOOK); /* TODO TEST */ + gtk_style_context_set_state(style, state_flags); + gtk_render_arrow(style, cr, arrow_angle, + x, y, arrow_size); + } + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_menu_bar_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_menu_bar_widget(); + gtk_widget_set_direction(gMenuBarWidget, direction); + + style = gtk_widget_get_style_context(gMenuBarWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR); + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_menu_popup_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_menu_popup_widget(); + gtk_widget_set_direction(gMenuPopupWidget, direction); + + style = gtk_widget_get_style_context(gMenuPopupWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENU); + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_menu_separator_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + gboolean wide_separators; + gint separator_height; + guint horizontal_padding; + gint paint_height; + GtkBorder border; + + ensure_menu_separator_widget(); + gtk_widget_set_direction(gMenuSeparatorWidget, direction); + + style = gtk_widget_get_style_context(gMenuSeparatorWidget); + + gtk_style_context_save(style); + + gtk_widget_style_get(gMenuSeparatorWidget, + "wide-separators", &wide_separators, + "separator-height", &separator_height, + "horizontal-padding", &horizontal_padding, + NULL); + + gtk_style_context_get_border(style, 0, &border); + + if (wide_separators) { + if (separator_height > rect->height) + separator_height = rect->height; + + gtk_render_frame(style, cr, + rect->x + horizontal_padding + border.left, + rect->y + (rect->height - separator_height - border.top) / 2, + rect->width - 2 * (horizontal_padding + border.left), + separator_height); + } else { + paint_height = border.top; + if (paint_height > rect->height) + paint_height = rect->height; + + gtk_render_line(style, cr, + rect->x + horizontal_padding + border.left, + rect->y + (rect->height - border.top) / 2, + rect->x + rect->width - horizontal_padding - border.left - 1, + rect->y + (rect->height - border.top) / 2); + } + + gtk_style_context_restore(style); + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_menu_item_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gint flags, GtkTextDirection direction) +{ + GtkStyleContext* style; + GtkWidget* item_widget; + + if (state->inHover && !state->disabled) { + gtk_style_context_save(style); + style = gtk_widget_get_style_context(item_widget); + + if (flags & MOZ_TOPLEVEL_MENU_ITEM) { + ensure_menu_bar_item_widget(); + item_widget = gMenuBarItemWidget; + gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUBAR); + } else { + ensure_menu_item_widget(); + item_widget = gMenuItemWidget; + } + + gtk_widget_set_direction(item_widget, direction); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUITEM); + gtk_style_context_set_state(style, GetStateFlagsFromGtkWidgetState(state)); + + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_render_frame(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + } + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_menu_arrow_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + GtkTextDirection direction) +{ + GtkStyleContext* style; + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + + ensure_menu_item_widget(); + gtk_widget_set_direction(gMenuItemWidget, direction); + + style = gtk_widget_get_style_context(gMenuItemWidget); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_MENUITEM); + gtk_style_context_set_state(style, state_flags); + gtk_render_arrow(style, cr, + (direction == GTK_TEXT_DIR_LTR) ? ARROW_RIGHT : ARROW_LEFT, + rect->x, rect->y, rect->width); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_check_menu_item_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkWidgetState* state, + gboolean checked, gboolean isradio, + GtkTextDirection direction) +{ + GtkStateFlags state_flags = GetStateFlagsFromGtkWidgetState(state); + GtkStyleContext* style; + GtkBorder border; + gint offset; + gint indicator_size, horizontal_padding; + gint x, y; + + moz_gtk_menu_item_paint(cr, rect, cliprect, state, FALSE, direction); + + ensure_check_menu_item_widget(); + gtk_widget_set_direction(gCheckMenuItemWidget, direction); + + gtk_widget_style_get (gCheckMenuItemWidget, + "indicator-size", &indicator_size, + "horizontal-padding", &horizontal_padding, + NULL); + + style = gtk_widget_get_style_context(gCheckMenuItemWidget); + gtk_style_context_save(style); + if (isradio) { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_RADIO); + } else { + gtk_style_context_add_class(style, GTK_STYLE_CLASS_CHECK); + } + + if (checked) + state_flags |= GTK_STATE_FLAG_ACTIVE; + + gtk_style_context_set_state(style, state_flags); + gtk_style_context_get_border(style, state_flags, &border); + + offset = gtk_container_get_border_width(GTK_CONTAINER(gCheckMenuItemWidget)) + + border.left + 2; + + x = (direction == GTK_TEXT_DIR_RTL) ? + rect->width - indicator_size - offset - horizontal_padding: rect->x + offset + horizontal_padding; + y = rect->y + (rect->height - indicator_size) / 2; + + if (isradio) { + gtk_render_option(style, cr, x, y, indicator_size, indicator_size); + } else { + gtk_render_check(style, cr, x, y, indicator_size, indicator_size); + } + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +static gint +moz_gtk_window_paint(cairo_t *cr, GdkRectangle* rect, + GdkRectangle* cliprect, GtkTextDirection direction) +{ + GtkStyleContext* style; + + ensure_window_widget(); + gtk_widget_set_direction(gProtoWindow, direction); + + style = gtk_widget_get_style_context(gProtoWindow); + gtk_style_context_save(style); + gtk_style_context_add_class(style, GTK_STYLE_CLASS_BACKGROUND); + gtk_render_background(style, cr, rect->x, rect->y, rect->width, rect->height); + gtk_style_context_restore(style); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_widget_border(GtkThemeWidgetType widget, gint* left, gint* top, + gint* right, gint* bottom, GtkTextDirection direction, + gboolean inhtml) +{ + GtkWidget* w; + GtkStyleContext* style; + GtkBorder border; + + switch (widget) { + case MOZ_GTK_BUTTON: + { + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + + ensure_button_widget(); + *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gButtonWidget)); + + /* Don't add this padding in HTML, otherwise the buttons will + become too big and stuff the layout. */ + if (!inhtml) { + moz_gtk_widget_get_focus(gButtonWidget, &interior_focus, &focus_width, &focus_pad); + moz_gtk_button_get_inner_border(gButtonWidget, &inner_border); + *left += focus_width + focus_pad + inner_border.left; + *right += focus_width + focus_pad + inner_border.right; + *top += focus_width + focus_pad + inner_border.top; + *bottom += focus_width + focus_pad + inner_border.bottom; + } + + style = gtk_widget_get_style_context(gButtonWidget); + gtk_style_context_get_border(style, 0, &border); + + *left += border.left; + *right += border.right; + *top += border.top; + *bottom += border.bottom; + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_ENTRY: + ensure_entry_widget(); + w = gEntryWidget; + break; + case MOZ_GTK_TREEVIEW: + ensure_tree_view_widget(); + w = gTreeViewWidget; + break; + case MOZ_GTK_TREE_HEADER_CELL: + { + /* A Tree Header in GTK is just a different styled button + * It must be placed in a TreeView for getting the correct style + * assigned. + * That is why the following code is the same as for MOZ_GTK_BUTTON. + * */ + + GtkBorder inner_border; + gboolean interior_focus; + gint focus_width, focus_pad; + + ensure_tree_header_cell_widget(); + *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(gTreeHeaderCellWidget)); + + moz_gtk_widget_get_focus(gTreeHeaderCellWidget, &interior_focus, &focus_width, &focus_pad); + moz_gtk_button_get_inner_border(gTreeHeaderCellWidget, &inner_border); + *left += focus_width + focus_pad + inner_border.left; + *right += focus_width + focus_pad + inner_border.right; + *top += focus_width + focus_pad + inner_border.top; + *bottom += focus_width + focus_pad + inner_border.bottom; + + style = gtk_widget_get_style_context(gTreeHeaderCellWidget); + gtk_style_context_get_border(style, 0, &border); + + *left += border.left; + *right += border.right; + *top += border.top; + *bottom += border.bottom; + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_TREE_HEADER_SORTARROW: + ensure_tree_header_cell_widget(); + w = gTreeHeaderSortArrowWidget; + break; + case MOZ_GTK_DROPDOWN_ENTRY: + ensure_combo_box_entry_widgets(); + w = gComboBoxEntryTextareaWidget; + break; + case MOZ_GTK_DROPDOWN_ARROW: + ensure_combo_box_entry_widgets(); + w = gComboBoxEntryButtonWidget; + break; + case MOZ_GTK_DROPDOWN: + { + /* We need to account for the arrow on the dropdown, so text + * doesn't come too close to the arrow, or in some cases spill + * into the arrow. */ + gboolean ignored_interior_focus, wide_separators; + gint focus_width, focus_pad, separator_width; + GtkRequisition arrow_req; + + ensure_combo_box_widgets(); + + *left = gtk_container_get_border_width(GTK_CONTAINER(gComboBoxButtonWidget)); + + if (!inhtml) { + moz_gtk_widget_get_focus(gComboBoxButtonWidget, + &ignored_interior_focus, + &focus_width, &focus_pad); + *left += focus_width + focus_pad; + } + + style = gtk_widget_get_style_context(gComboBoxButtonWidget); + gtk_style_context_get_border(style, 0, &border); + + *top = *left + border.top; + *left += border.left; + + *right = *left; *bottom = *top; + + /* If there is no separator, don't try to count its width. */ + separator_width = 0; + if (gComboBoxSeparatorWidget) { + gtk_widget_style_get(gComboBoxSeparatorWidget, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + + if (!wide_separators) { + style = gtk_widget_get_style_context(gComboBoxSeparatorWidget); + gtk_style_context_get_border(style, 0, &border); + separator_width = border.left; + } + } + + gtk_widget_size_request(gComboBoxArrowWidget, &arrow_req); + + if (direction == GTK_TEXT_DIR_RTL) + *left += separator_width + arrow_req.width; + else + *right += separator_width + arrow_req.width; + + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_TABPANELS: + ensure_tab_widget(); + w = gTabWidget; + break; + case MOZ_GTK_PROGRESSBAR: + ensure_progress_widget(); + w = gProgressWidget; + break; + case MOZ_GTK_SPINBUTTON_ENTRY: + case MOZ_GTK_SPINBUTTON_UP: + case MOZ_GTK_SPINBUTTON_DOWN: + ensure_spin_widget(); + w = gSpinWidget; + break; + case MOZ_GTK_SCALE_HORIZONTAL: + ensure_scale_widget(); + w = gHScaleWidget; + break; + case MOZ_GTK_SCALE_VERTICAL: + ensure_scale_widget(); + w = gVScaleWidget; + break; + case MOZ_GTK_FRAME: + ensure_frame_widget(); + w = gFrameWidget; + break; + case MOZ_GTK_CHECKBUTTON_LABEL: + case MOZ_GTK_RADIOBUTTON_LABEL: + { + gboolean interior_focus; + gint focus_width, focus_pad; + + /* If the focus is interior, then the label has a border of + (focus_width + focus_pad). */ + if (widget == MOZ_GTK_CHECKBUTTON_LABEL) { + ensure_checkbox_widget(); + moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus, + &focus_width, &focus_pad); + } + else { + ensure_radiobutton_widget(); + moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus, + &focus_width, &focus_pad); + } + + if (interior_focus) + *left = *top = *right = *bottom = (focus_width + focus_pad); + else + *left = *top = *right = *bottom = 0; + + return MOZ_GTK_SUCCESS; + } + + case MOZ_GTK_CHECKBUTTON_CONTAINER: + case MOZ_GTK_RADIOBUTTON_CONTAINER: + { + gboolean interior_focus; + gint focus_width, focus_pad; + + /* If the focus is _not_ interior, then the container has a border + of (focus_width + focus_pad). */ + if (widget == MOZ_GTK_CHECKBUTTON_CONTAINER) { + ensure_checkbox_widget(); + moz_gtk_widget_get_focus(gCheckboxWidget, &interior_focus, + &focus_width, &focus_pad); + w = gCheckboxWidget; + } else { + ensure_radiobutton_widget(); + moz_gtk_widget_get_focus(gRadiobuttonWidget, &interior_focus, + &focus_width, &focus_pad); + w = gRadiobuttonWidget; + } + + *left = *top = *right = *bottom = gtk_container_get_border_width(GTK_CONTAINER(w)); + + if (!interior_focus) { + *left += (focus_width + focus_pad); + *right += (focus_width + focus_pad); + *top += (focus_width + focus_pad); + *bottom += (focus_width + focus_pad); + } + + return MOZ_GTK_SUCCESS; + } + case MOZ_GTK_MENUPOPUP: + ensure_menu_popup_widget(); + w = gMenuPopupWidget; + break; + case MOZ_GTK_MENUITEM: + ensure_menu_item_widget(); + ensure_menu_bar_item_widget(); + w = gMenuItemWidget; + break; + case MOZ_GTK_CHECKMENUITEM: + case MOZ_GTK_RADIOMENUITEM: + ensure_check_menu_item_widget(); + w = gCheckMenuItemWidget; + break; + case MOZ_GTK_TAB: + ensure_tab_widget(); + w = gTabWidget; + break; + /* These widgets have no borders, since they are not containers. */ + case MOZ_GTK_SPLITTER_HORIZONTAL: + case MOZ_GTK_SPLITTER_VERTICAL: + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + case MOZ_GTK_SCROLLBAR_BUTTON: + case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + case MOZ_GTK_SCALE_THUMB_HORIZONTAL: + case MOZ_GTK_SCALE_THUMB_VERTICAL: + case MOZ_GTK_GRIPPER: + case MOZ_GTK_PROGRESS_CHUNK: + case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE: + case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE: + case MOZ_GTK_EXPANDER: + case MOZ_GTK_TREEVIEW_EXPANDER: + case MOZ_GTK_TOOLBAR_SEPARATOR: + case MOZ_GTK_MENUSEPARATOR: + /* These widgets have no borders.*/ + case MOZ_GTK_SPINBUTTON: + case MOZ_GTK_TOOLTIP: + case MOZ_GTK_WINDOW: + case MOZ_GTK_RESIZER: + case MOZ_GTK_MENUARROW: + case MOZ_GTK_TOOLBARBUTTON_ARROW: + case MOZ_GTK_TOOLBAR: + case MOZ_GTK_MENUBAR: + case MOZ_GTK_TAB_SCROLLARROW: + case MOZ_GTK_ENTRY_CARET: + *left = *top = *right = *bottom = 0; + return MOZ_GTK_SUCCESS; + default: + g_warning("Unsupported widget type: %d", widget); + return MOZ_GTK_UNKNOWN_WIDGET; + } + /* TODO - we're still missing some widget implementations */ + if (!w) { + *right = *left = 0; + *bottom = *top = 0; + } else { + style = gtk_widget_get_style_context(w); + gtk_style_context_get_border(style, 0, &border); + *left = border.left; + *right = border.right; + *top = border.top; + *bottom = border.bottom; + } + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_combo_box_entry_button_size(gint* width, gint* height) +{ + /* + * We get the requisition of the drop down button, which includes + * all padding, border and focus line widths the button uses, + * as well as the minimum arrow size and its padding + * */ + GtkRequisition requisition; + ensure_combo_box_entry_widgets(); + + gtk_widget_size_request(gComboBoxEntryButtonWidget, &requisition); + *width = requisition.width; + *height = requisition.height; + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_tab_scroll_arrow_size(gint* width, gint* height) +{ + gint arrow_size; + + ensure_tab_widget(); + gtk_widget_style_get(gTabWidget, + "scroll-arrow-hlength", &arrow_size, + NULL); + + *height = *width = arrow_size; + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_arrow_size(gint* width, gint* height) +{ + GtkRequisition requisition; + ensure_button_arrow_widget(); + + gtk_widget_size_request(gButtonArrowWidget, &requisition); + *width = requisition.width; + *height = requisition.height; + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_toolbar_separator_width(gint* size) +{ + gboolean wide_separators; + gint separator_width; + GtkStyleContext* style; + GtkBorder border; + + ensure_toolbar_widget(); + style = gtk_widget_get_style_context(gToolbarWidget); + + gtk_widget_style_get(gToolbarWidget, + "space-size", size, + "wide-separators", &wide_separators, + "separator-width", &separator_width, + NULL); + /* Just in case... */ + gtk_style_context_get_border(style, 0, &border); + *size = MAX(*size, (wide_separators ? separator_width : border.left)); + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_expander_size(gint* size) +{ + ensure_expander_widget(); + gtk_widget_style_get(gExpanderWidget, + "expander-size", size, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_treeview_expander_size(gint* size) +{ + ensure_tree_view_widget(); + gtk_widget_style_get(gTreeViewWidget, + "expander-size", size, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_menu_separator_height(gint *size) +{ + gboolean wide_separators; + gint separator_height; + GtkBorder border; + GtkStyleContext* style; + + ensure_menu_separator_widget(); + + gtk_widget_style_get(gMenuSeparatorWidget, + "wide-separators", &wide_separators, + "separator-height", &separator_height, + NULL); + + style = gtk_widget_get_style_context(gMenuSeparatorWidget); + gtk_style_context_get_border(style, 0, &border); + + if (wide_separators) + *size = separator_height + border.top; + else + *size = border.top + border.bottom; + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_scalethumb_metrics(GtkOrientation orient, gint* thumb_length, gint* thumb_height) +{ + GtkWidget* widget; + + ensure_scale_widget(); + widget = ((orient == GTK_ORIENTATION_HORIZONTAL) ? gHScaleWidget : gVScaleWidget); + + gtk_widget_style_get (widget, + "slider_length", thumb_length, + "slider_width", thumb_height, + NULL); + + return MOZ_GTK_SUCCESS; +} + +gint +moz_gtk_get_scrollbar_metrics(MozGtkScrollbarMetrics *metrics) +{ + ensure_scrollbar_widget(); + + gtk_widget_style_get (gHorizScrollbarWidget, + "slider_width", &metrics->slider_width, + "trough_border", &metrics->trough_border, + "stepper_size", &metrics->stepper_size, + "stepper_spacing", &metrics->stepper_spacing, + NULL); + + metrics->min_slider_size = + gtk_range_get_min_slider_size(GTK_RANGE(gHorizScrollbarWidget)); + + return MOZ_GTK_SUCCESS; +} + +gboolean +moz_gtk_images_in_menus() +{ + gboolean result; + GtkSettings* settings; + + ensure_image_menu_item_widget(); + settings = gtk_widget_get_settings(gImageMenuItemWidget); + + g_object_get(settings, "gtk-menu-images", &result, NULL); + return result; +} + +gboolean +moz_gtk_images_in_buttons() +{ + gboolean result; + GtkSettings* settings; + + ensure_button_widget(); + settings = gtk_widget_get_settings(gButtonWidget); + + g_object_get(settings, "gtk-button-images", &result, NULL); + return result; +} + +/* cairo_t *cr argument has to be a system-cairo. */ +/* TODO: GdkRectangle* cliprect is unused, shall we remove it? */ +gint +moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t *cr, + GdkRectangle* rect, GdkRectangle* cliprect, + GtkWidgetState* state, gint flags, + GtkTextDirection direction) +{ + switch (widget) { + case MOZ_GTK_BUTTON: + if (state->depressed) { + ensure_toggle_button_widget(); + return moz_gtk_button_paint(cr, rect, cliprect, state, + (GtkReliefStyle) flags, + gToggleButtonWidget, direction); + } + ensure_button_widget(); + return moz_gtk_button_paint(cr, rect, cliprect, state, + (GtkReliefStyle) flags, gButtonWidget, + direction); + break; + case MOZ_GTK_CHECKBUTTON: + case MOZ_GTK_RADIOBUTTON: + return moz_gtk_toggle_paint(cr, rect, cliprect, state, + !!(flags & MOZ_GTK_WIDGET_CHECKED), + !!(flags & MOZ_GTK_WIDGET_INCONSISTENT), + (widget == MOZ_GTK_RADIOBUTTON), + direction); + break; + case MOZ_GTK_SCROLLBAR_BUTTON: + return moz_gtk_scrollbar_button_paint(cr, rect, cliprect, state, + (GtkScrollbarButtonFlags) flags, + direction); + break; + case MOZ_GTK_SCROLLBAR_TRACK_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_TRACK_VERTICAL: + return moz_gtk_scrollbar_trough_paint(widget, cr, rect, + cliprect, state, direction); + break; + case MOZ_GTK_SCROLLBAR_THUMB_HORIZONTAL: + case MOZ_GTK_SCROLLBAR_THUMB_VERTICAL: + return moz_gtk_scrollbar_thumb_paint(widget, cr, rect, + cliprect, state, direction); + break; + case MOZ_GTK_SCALE_HORIZONTAL: + case MOZ_GTK_SCALE_VERTICAL: + return moz_gtk_scale_paint(cr, rect, cliprect, state, + (GtkOrientation) flags, direction); + break; + case MOZ_GTK_SCALE_THUMB_HORIZONTAL: + case MOZ_GTK_SCALE_THUMB_VERTICAL: + return moz_gtk_scale_thumb_paint(cr, rect, cliprect, state, + (GtkOrientation) flags, direction); + break; + case MOZ_GTK_SPINBUTTON: + return moz_gtk_spin_paint(cr, rect, direction); + break; + case MOZ_GTK_SPINBUTTON_UP: + case MOZ_GTK_SPINBUTTON_DOWN: + return moz_gtk_spin_updown_paint(cr, rect, + (widget == MOZ_GTK_SPINBUTTON_DOWN), + state, direction); + break; + case MOZ_GTK_SPINBUTTON_ENTRY: + ensure_spin_widget(); + return moz_gtk_entry_paint(cr, rect, cliprect, state, + gSpinWidget, direction); + break; + case MOZ_GTK_GRIPPER: + return moz_gtk_gripper_paint(cr, rect, cliprect, state, + direction); + break; + case MOZ_GTK_TREEVIEW: + return moz_gtk_treeview_paint(cr, rect, cliprect, state, + direction); + break; + case MOZ_GTK_TREE_HEADER_CELL: + return moz_gtk_tree_header_cell_paint(cr, rect, cliprect, state, + flags, direction); + break; + case MOZ_GTK_TREE_HEADER_SORTARROW: + return moz_gtk_tree_header_sort_arrow_paint(cr, rect, cliprect, + state, + (GtkArrowType) flags, + direction); + break; + case MOZ_GTK_TREEVIEW_EXPANDER: + return moz_gtk_treeview_expander_paint(cr, rect, cliprect, state, + (GtkExpanderStyle) flags, direction); + break; + case MOZ_GTK_EXPANDER: + return moz_gtk_expander_paint(cr, rect, cliprect, state, + (GtkExpanderStyle) flags, direction); + break; + case MOZ_GTK_ENTRY: + ensure_entry_widget(); + return moz_gtk_entry_paint(cr, rect, cliprect, state, + gEntryWidget, direction); + break; + case MOZ_GTK_ENTRY_CARET: + return moz_gtk_caret_paint(cr, rect, cliprect, direction); + break; + case MOZ_GTK_DROPDOWN: + return moz_gtk_combo_box_paint(cr, rect, cliprect, state, + (gboolean) flags, direction); + break; + case MOZ_GTK_DROPDOWN_ARROW: + return moz_gtk_combo_box_entry_button_paint(cr, rect, cliprect, + state, flags, direction); + break; + case MOZ_GTK_DROPDOWN_ENTRY: + ensure_combo_box_entry_widgets(); + return moz_gtk_entry_paint(cr, rect, cliprect, state, + gComboBoxEntryTextareaWidget, direction); + break; + case MOZ_GTK_CHECKBUTTON_CONTAINER: + case MOZ_GTK_RADIOBUTTON_CONTAINER: + return moz_gtk_container_paint(cr, rect, cliprect, state, + (widget == MOZ_GTK_RADIOBUTTON_CONTAINER), + direction); + break; + case MOZ_GTK_CHECKBUTTON_LABEL: + case MOZ_GTK_RADIOBUTTON_LABEL: + return moz_gtk_toggle_label_paint(cr, rect, cliprect, state, + (widget == MOZ_GTK_RADIOBUTTON_LABEL), + direction); + break; + case MOZ_GTK_TOOLBAR: + return moz_gtk_toolbar_paint(cr, rect, cliprect, direction); + break; + case MOZ_GTK_TOOLBAR_SEPARATOR: + return moz_gtk_toolbar_separator_paint(cr, rect, cliprect, + direction); + break; + case MOZ_GTK_TOOLTIP: + return moz_gtk_tooltip_paint(cr, rect, cliprect, direction); + break; + case MOZ_GTK_FRAME: + return moz_gtk_frame_paint(cr, rect, cliprect, direction); + break; + case MOZ_GTK_RESIZER: + return moz_gtk_resizer_paint(cr, rect, cliprect, state, + direction); + break; + case MOZ_GTK_PROGRESSBAR: + return moz_gtk_progressbar_paint(cr, rect, cliprect, direction); + break; + case MOZ_GTK_PROGRESS_CHUNK: + case MOZ_GTK_PROGRESS_CHUNK_INDETERMINATE: + case MOZ_GTK_PROGRESS_CHUNK_VERTICAL_INDETERMINATE: + return moz_gtk_progress_chunk_paint(cr, rect, cliprect, + direction, widget); + break; + case MOZ_GTK_TAB: + return moz_gtk_tab_paint(cr, rect, cliprect, state, + (GtkTabFlags) flags, direction); + break; + case MOZ_GTK_TABPANELS: + return moz_gtk_tabpanels_paint(cr, rect, cliprect, direction); + break; + case MOZ_GTK_TAB_SCROLLARROW: + return moz_gtk_tab_scroll_arrow_paint(cr, rect, cliprect, state, + (GtkArrowType) flags, direction); + break; + case MOZ_GTK_MENUBAR: + return moz_gtk_menu_bar_paint(cr, rect, cliprect, direction); + break; + case MOZ_GTK_MENUPOPUP: + return moz_gtk_menu_popup_paint(cr, rect, cliprect, direction); + break; + case MOZ_GTK_MENUSEPARATOR: + return moz_gtk_menu_separator_paint(cr, rect, cliprect, + direction); + break; + case MOZ_GTK_MENUITEM: + return moz_gtk_menu_item_paint(cr, rect, cliprect, state, flags, + direction); + break; + case MOZ_GTK_MENUARROW: + return moz_gtk_menu_arrow_paint(cr, rect, cliprect, state, + direction); + break; + case MOZ_GTK_TOOLBARBUTTON_ARROW: + return moz_gtk_arrow_paint(cr, rect, cliprect, state, + (GtkArrowType) flags, direction); + break; + case MOZ_GTK_CHECKMENUITEM: + case MOZ_GTK_RADIOMENUITEM: + return moz_gtk_check_menu_item_paint(cr, rect, cliprect, state, + (gboolean) flags, + (widget == MOZ_GTK_RADIOMENUITEM), + direction); + break; + case MOZ_GTK_SPLITTER_HORIZONTAL: + return moz_gtk_vpaned_paint(cr, rect, cliprect, state); + break; + case MOZ_GTK_SPLITTER_VERTICAL: + return moz_gtk_hpaned_paint(cr, rect, cliprect, state); + break; + case MOZ_GTK_WINDOW: + return moz_gtk_window_paint(cr, rect, cliprect, direction); + break; + default: + g_warning("Unknown widget type: %d", widget); + } + + return MOZ_GTK_UNKNOWN_WIDGET; +} + +GtkWidget* moz_gtk_get_scrollbar_widget(void) +{ + NS_ASSERTION(is_initialized, "Forgot to call moz_gtk_init()"); + ensure_scrollbar_widget(); + return gHorizScrollbarWidget; +} + +gint +moz_gtk_shutdown() +{ + GtkWidgetClass *entry_class; + + if (gTooltipWidget) + gtk_widget_destroy(gTooltipWidget); + /* This will destroy all of our widgets */ + if (gProtoWindow) + gtk_widget_destroy(gProtoWindow); + + gProtoWindow = NULL; + gProtoLayout = NULL; + gButtonWidget = NULL; + gToggleButtonWidget = NULL; + gButtonArrowWidget = NULL; + gCheckboxWidget = NULL; + gRadiobuttonWidget = NULL; + gHorizScrollbarWidget = NULL; + gVertScrollbarWidget = NULL; + gSpinWidget = NULL; + gHScaleWidget = NULL; + gVScaleWidget = NULL; + gEntryWidget = NULL; + gComboBoxWidget = NULL; + gComboBoxButtonWidget = NULL; + gComboBoxSeparatorWidget = NULL; + gComboBoxArrowWidget = NULL; + gComboBoxEntryWidget = NULL; + gComboBoxEntryButtonWidget = NULL; + gComboBoxEntryArrowWidget = NULL; + gComboBoxEntryTextareaWidget = NULL; + gHandleBoxWidget = NULL; + gToolbarWidget = NULL; + gStatusbarWidget = NULL; + gFrameWidget = NULL; + gProgressWidget = NULL; + gTabWidget = NULL; + gTooltipWidget = NULL; + gMenuBarWidget = NULL; + gMenuBarItemWidget = NULL; + gMenuPopupWidget = NULL; + gMenuItemWidget = NULL; + gImageMenuItemWidget = NULL; + gCheckMenuItemWidget = NULL; + gTreeViewWidget = NULL; + gMiddleTreeViewColumn = NULL; + gTreeHeaderCellWidget = NULL; + gTreeHeaderSortArrowWidget = NULL; + gExpanderWidget = NULL; + gToolbarSeparatorWidget = NULL; + gMenuSeparatorWidget = NULL; + gHPanedWidget = NULL; + gVPanedWidget = NULL; + gScrolledWindowWidget = NULL; + + entry_class = g_type_class_peek(GTK_TYPE_ENTRY); + g_type_class_unref(entry_class); + + is_initialized = FALSE; + + return MOZ_GTK_SUCCESS; +} diff --git a/widget/gtk2/gtkdrawing.h b/widget/gtk2/gtkdrawing.h index f81752a0c443..346ab5875fb3 100644 --- a/widget/gtk2/gtkdrawing.h +++ b/widget/gtk2/gtkdrawing.h @@ -246,12 +246,15 @@ gint moz_gtk_enable_style_props(style_prop_t styleGetProp); */ gint moz_gtk_shutdown(); +#if defined(MOZ_WIDGET_GTK2) /** * Retrieves the colormap to use for drawables passed to moz_gtk_widget_paint. */ GdkColormap* moz_gtk_widget_get_colormap(); +#endif /*** Widget drawing ***/ +#if defined(MOZ_WIDGET_GTK2) /** * Paint a widget in the current theme. * widget: a constant giving the widget to paint @@ -268,6 +271,13 @@ moz_gtk_widget_paint(GtkThemeWidgetType widget, GdkDrawable* drawable, GdkRectangle* rect, GdkRectangle* cliprect, GtkWidgetState* state, gint flags, GtkTextDirection direction); +#else +gint +moz_gtk_widget_paint(GtkThemeWidgetType widget, cairo_t *cr, + GdkRectangle* rect, GdkRectangle* cliprect, + GtkWidgetState* state, gint flags, + GtkTextDirection direction); +#endif /*** Widget metrics ***/