From c0f208e1023c7dc432716172eca72b562998c20f Mon Sep 17 00:00:00 2001 From: Mihai Sucan Date: Thu, 5 Apr 2012 13:47:26 +0300 Subject: [PATCH 1/8] Bug 728830 - Intermittent browser_dbg_update-editor-mode.js | Found the expected editor mode. - Got js, expected html | The correct script was loaded initially. | The first script is displayed. | Test timed out; r=rcampbell --- .../test/browser_dbg_update-editor-mode.js | 61 ++++++++++++------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js index 717d2cb2323..93b1b54c869 100644 --- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js +++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js @@ -20,44 +20,63 @@ var gScripts = null; function test() { + let scriptShown = false; + let framesAdded = false; + debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { gTab = aTab; gDebuggee = aDebuggee; gPane = aPane; gDebugger = gPane.debuggerWindow; - testScriptsDisplay(); + gPane.activeThread.addOneTimeListener("framesadded", function() { + framesAdded = true; + runTest(); + }); + gDebuggee.firstCall(); }); + + window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) { + let url = aEvent.detail.url; + if (url.indexOf("editor-mode") != -1) { + scriptShown = true; + window.removeEventListener(aEvent.type, _onEvent); + runTest(); + } + }); + + function runTest() + { + if (scriptShown && framesAdded) { + Services.tm.currentThread.dispatch({ run: testScriptsDisplay }, 0); + } + } } function testScriptsDisplay() { - gPane.activeThread.addOneTimeListener("framesadded", function() { - Services.tm.currentThread.dispatch({ run: function() { - gScripts = gDebugger.DebuggerView.Scripts._scripts; + gScripts = gDebugger.DebuggerView.Scripts._scripts; - is(gDebugger.StackFrames.activeThread.state, "paused", - "Should only be getting stack frames while paused."); + is(gDebugger.StackFrames.activeThread.state, "paused", + "Should only be getting stack frames while paused."); - is(gScripts.itemCount, 2, "Found the expected number of scripts."); + is(gScripts.itemCount, 2, "Found the expected number of scripts."); - is(gDebugger.editor.getMode(), SourceEditor.MODES.HTML, - "Found the expected editor mode."); + is(gDebugger.editor.getMode(), SourceEditor.MODES.HTML, + "Found the expected editor mode."); - ok(gDebugger.editor.getText().search(/debugger/) != -1, - "The correct script was loaded initially."); + ok(gDebugger.editor.getText().search(/debugger/) != -1, + "The correct script was loaded initially."); - gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, - function onChange() { - gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, - onChange); - testSwitchPaused(); - }); - gScripts.selectedIndex = 0; - gDebugger.SourceScripts.onChange({ target: gScripts }); - }}, 0); + window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) { + let url = aEvent.detail.url; + if (url.indexOf("switching-01.js") != -1) { + window.removeEventListener(aEvent.type, _onEvent); + testSwitchPaused(); + } }); - gDebuggee.firstCall(); + let url = gDebuggee.document.querySelector("script").src; + gDebugger.DebuggerView.Scripts.selectScript(url); } function testSwitchPaused() From c1c4d9cef2a1332130d93f2d65aec49c39610605 Mon Sep 17 00:00:00 2001 From: Mihai Sucan Date: Thu, 5 Apr 2012 16:00:05 +0300 Subject: [PATCH 2/8] Bug 728926 - Intermittent failure in browser_dbg_script-switching.js | The correct script was loaded initially. | The first script is displayed. r=rcampbell --- .../test/browser_dbg_script-switching.js | 104 +++++++++++------- 1 file changed, 62 insertions(+), 42 deletions(-) diff --git a/browser/devtools/debugger/test/browser_dbg_script-switching.js b/browser/devtools/debugger/test/browser_dbg_script-switching.js index 9bdb87fded5..aa8ef71183e 100644 --- a/browser/devtools/debugger/test/browser_dbg_script-switching.js +++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js @@ -20,62 +20,80 @@ var gScripts = null; function test() { + let scriptShown = false; + let framesAdded = false; + debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) { gTab = aTab; gDebuggee = aDebuggee; gPane = aPane; gDebugger = gPane.debuggerWindow; - testScriptsDisplay(); + gPane.activeThread.addOneTimeListener("framesadded", function() { + framesAdded = true; + runTest(); + }); + + gDebuggee.firstCall(); }); + + window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) { + let url = aEvent.detail.url; + if (url.indexOf("-02.js") != -1) { + scriptShown = true; + window.removeEventListener(aEvent.type, _onEvent); + runTest(); + } + }); + + function runTest() + { + if (scriptShown && framesAdded) { + Services.tm.currentThread.dispatch({ run: testScriptsDisplay }, 0); + } + } } function testScriptsDisplay() { - gPane.activeThread.addOneTimeListener("framesadded", function() { - Services.tm.currentThread.dispatch({ run: function() { - gScripts = gDebugger.DebuggerView.Scripts._scripts; + gScripts = gDebugger.DebuggerView.Scripts._scripts; - is(gDebugger.StackFrames.activeThread.state, "paused", - "Should only be getting stack frames while paused."); + is(gDebugger.StackFrames.activeThread.state, "paused", + "Should only be getting stack frames while paused."); - is(gScripts.itemCount, 2, "Found the expected number of scripts."); + is(gScripts.itemCount, 2, "Found the expected number of scripts."); - for (let i = 0; i < gScripts.itemCount; i++) { - info("label: " + i + " " + gScripts.getItemAtIndex(i).getAttribute("label")); - } + for (let i = 0; i < gScripts.itemCount; i++) { + info("label: " + i + " " + gScripts.getItemAtIndex(i).getAttribute("label")); + } - let label1 = "test-script-switching-01.js"; - let label2 = "test-script-switching-02.js"; + let label1 = "test-script-switching-01.js"; + let label2 = "test-script-switching-02.js"; - ok(gDebugger.DebuggerView.Scripts.contains(EXAMPLE_URL + - label1), "First script url is incorrect."); - ok(gDebugger.DebuggerView.Scripts.contains(EXAMPLE_URL + - label2), "Second script url is incorrect."); + ok(gDebugger.DebuggerView.Scripts.contains(EXAMPLE_URL + + label1), "First script url is incorrect."); + ok(gDebugger.DebuggerView.Scripts.contains(EXAMPLE_URL + + label2), "Second script url is incorrect."); - ok(gDebugger.DebuggerView.Scripts.containsLabel( - label1), "First script label is incorrect."); - ok(gDebugger.DebuggerView.Scripts.containsLabel( - label2), "Second script label is incorrect."); + ok(gDebugger.DebuggerView.Scripts.containsLabel( + label1), "First script label is incorrect."); + ok(gDebugger.DebuggerView.Scripts.containsLabel( + label2), "Second script label is incorrect."); + ok(gDebugger.editor.getText().search(/debugger/) != -1, + "The correct script was loaded initially."); - ok(gDebugger.editor.getText().search(/debugger/) != -1, - "The correct script was loaded initially."); + is(gDebugger.editor.getDebugLocation(), 5, + "editor debugger location is correct."); - is(gDebugger.editor.getDebugLocation(), 5, - "editor debugger location is correct."); - - gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, - function onChange() { - gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, - onChange); - testSwitchPaused(); - }); - gScripts.selectedIndex = 0; - gDebugger.SourceScripts.onChange({ target: gScripts }); - }}, 0); + window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) { + let url = aEvent.detail.url; + if (url.indexOf("-01.js") != -1) { + window.removeEventListener(aEvent.type, _onEvent); + testSwitchPaused(); + } }); - gDebuggee.firstCall(); + gDebugger.DebuggerView.Scripts.selectScript(EXAMPLE_URL + label1); } function testSwitchPaused() @@ -90,14 +108,16 @@ function testSwitchPaused() "editor debugger location has been cleared."); gDebugger.StackFrames.activeThread.resume(function() { - gDebugger.editor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, - function onSecondChange() { - gDebugger.editor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, - onSecondChange); - testSwitchRunning(); + window.addEventListener("Debugger:ScriptShown", function _onEvent(aEvent) { + let url = aEvent.detail.url; + if (url.indexOf("-02.js") != -1) { + window.removeEventListener(aEvent.type, _onEvent); + testSwitchRunning(); + } }); - gScripts.selectedIndex = 1; - gDebugger.SourceScripts.onChange({ target: gScripts }); + + gDebugger.DebuggerView.Scripts.selectScript(EXAMPLE_URL + + "test-script-switching-02.js"); }); } From 18bec8c91fd13fbb7e71a6f0492b1e8c4a86d44c Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Fri, 6 Apr 2012 00:33:47 +0200 Subject: [PATCH 3/8] Bug 742782 - [New Tab Page] Error: aCallback is not defined; r=dietrich --- browser/base/content/newtab/sites.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/browser/base/content/newtab/sites.js b/browser/base/content/newtab/sites.js index 899dc709259..7a3bff50325 100644 --- a/browser/base/content/newtab/sites.js +++ b/browser/base/content/newtab/sites.js @@ -83,10 +83,7 @@ Site.prototype = { * when done. */ block: function Site_block() { - if (gBlockedLinks.isBlocked(this._link)) { - if (aCallback) - aCallback(); - } else { + if (!gBlockedLinks.isBlocked(this._link)) { gBlockedLinks.block(this._link); gUpdater.updateGrid(); } From a9d2fb3edee0e7d9cb7f0f25fa850e7abce4457d Mon Sep 17 00:00:00 2001 From: Girish Sharma Date: Mon, 9 Apr 2012 13:31:45 +0300 Subject: [PATCH 4/8] Bug 739192 - Source Editor should toggle comment on line or selection with one key binding; r=msucan --- .../sourceeditor/source-editor-orion.jsm | 72 ++++++++++++++++--- .../browser_bug725430_comment_uncomment.js | 30 ++++---- 2 files changed, 77 insertions(+), 25 deletions(-) diff --git a/browser/devtools/sourceeditor/source-editor-orion.jsm b/browser/devtools/sourceeditor/source-editor-orion.jsm index f6d379618c5..1aa2181e055 100644 --- a/browser/devtools/sourceeditor/source-editor-orion.jsm +++ b/browser/devtools/sourceeditor/source-editor-orion.jsm @@ -153,16 +153,10 @@ const DEFAULT_KEYBINDINGS = [ alt: true, }, { - action: "Comment", + action: "Comment/Uncomment", code: Ci.nsIDOMKeyEvent.DOM_VK_SLASH, accel: true, }, - { - action: "Uncomment", - code: Ci.nsIDOMKeyEvent.DOM_VK_SLASH, - accel: true, - shift: true, - }, ]; var EXPORTED_SYMBOLS = ["SourceEditor"]; @@ -403,8 +397,7 @@ SourceEditor.prototype = { "Find Previous Occurrence": [this.ui.findPrevious, this.ui], "Goto Line...": [this.ui.gotoLine, this.ui], "Move Lines Down": [this._moveLines, this], - "Comment": [this._doComment, this], - "Uncomment": [this._doUncomment, this], + "Comment/Uncomment": [this._doCommentUncomment, this], }; for (let name in actions) { @@ -1050,6 +1043,63 @@ SourceEditor.prototype = { return {line: line, blockStart: blockCommentStart, blockEnd: blockCommentEnd}; }, + /** + * Decide whether to comment the selection/current line or to uncomment it. + * + * @private + */ + _doCommentUncomment: function SE__doCommentUncomment() + { + if (this.readOnly) { + return false; + } + + let commentObject = this._getCommentStrings(); + if (!commentObject) { + return false; + } + + let selection = this.getSelection(); + let model = this._model; + let firstLine = model.getLineAtOffset(selection.start); + let lastLine = model.getLineAtOffset(selection.end); + + // Checks for block comment. + let firstLineText = model.getLine(firstLine); + let lastLineText = model.getLine(lastLine); + let openIndex = firstLineText.indexOf(commentObject.blockStart); + let closeIndex = lastLineText.lastIndexOf(commentObject.blockEnd); + if (openIndex != -1 && closeIndex != -1 && + (firstLine != lastLine || + (closeIndex - openIndex) >= commentObject.blockStart.length)) { + return this._doUncomment(); + } + + if (!commentObject.line) { + return this._doComment(); + } + + // If the selection is not a block comment, check for the first and the last + // lines to be line commented. + let firstLastCommented = [firstLineText, + lastLineText].every(function(aLineText) { + let openIndex = aLineText.indexOf(commentObject.line); + if (openIndex != -1) { + let textUntilComment = aLineText.slice(0, openIndex); + if (!textUntilComment || /^\s+$/.test(textUntilComment)) { + return true; + } + } + return false; + }); + if (firstLastCommented) { + return this._doUncomment(); + } + + // If we reach here, then we have to comment the selection/line. + return this._doComment(); + }, + /** * Wrap the selected text in comments. If nothing is selected the current * caret line is commented out. Single line and block comments depend on the @@ -1123,7 +1173,9 @@ SourceEditor.prototype = { let lastLineText = this._model.getLine(lastLine); let openIndex = firstLineText.indexOf(commentObject.blockStart); let closeIndex = lastLineText.lastIndexOf(commentObject.blockEnd); - if (openIndex != -1 && closeIndex != -1) { + if (openIndex != -1 && closeIndex != -1 && + (firstLine != lastLine || + (closeIndex - openIndex) >= commentObject.blockStart.length)) { let firstLineStartOffset = this.getLineStart(firstLine); let lastLineStartOffset = this.getLineStart(lastLine); let openOffset = firstLineStartOffset + openIndex; diff --git a/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js b/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js index fca682a692b..5fbe1d89bd8 100644 --- a/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js +++ b/browser/devtools/sourceeditor/test/browser_bug725430_comment_uncomment.js @@ -48,7 +48,7 @@ function test() { editor.redo(); is(editor.getText(), "//" + text, "Redo works"); editor.setCaretPosition(0); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), text, "JS Single Line Uncommenting works"); editor.setText(text); @@ -61,18 +61,18 @@ function test() { editor.redo(); is(editor.getText(), "/*" + text + "*/", "Redo works"); EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), text, "JS Block Uncommenting works"); editor.undo(); is(editor.getText(), "/*" + text + "*/", "Undo Block Uncommenting works"); editor.redo(); is(editor.getText(), text, "Redo works"); - let regText = "//firstline\n // secondline\nthird//line\nfourthline//"; - let expText = "firstline\n secondline\nthird//line\nfourthline//"; + let regText = "//firstline\n // secondline\nthird//line\n//fourthline"; + let expText = "firstline\n secondline\nthird//line\nfourthline"; editor.setText(regText); EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), expText, "JS Multiple Line Uncommenting works"); editor.undo(); is(editor.getText(), regText, "Undo Multiple Line Uncommenting works"); @@ -87,7 +87,7 @@ function test() { EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), expText, "CSS Commenting without selection works"); editor.setCaretPosition(0); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), text, "CSS Uncommenting without selection works"); editor.setText(text); @@ -96,7 +96,7 @@ function test() { EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), "/*" + text + "*/", "CSS Multiple Line Commenting works"); EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), text, "CSS Multiple Line Uncommenting works"); editor.setMode(SourceEditor.MODES.HTML); @@ -107,7 +107,7 @@ function test() { EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), expText, "HTML Commenting without selection works"); editor.setCaretPosition(0); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), text, "HTML Uncommenting without selection works"); editor.setText(text); @@ -116,7 +116,7 @@ function test() { EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), "", "HTML Multiple Line Commenting works"); EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), text, "HTML Multiple Line Uncommenting works"); editor.setMode(SourceEditor.MODES.TEXT); @@ -125,10 +125,10 @@ function test() { editor.setCaretPosition(0); EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), text, "Commenting disabled in Text mode"); - editor.setText("//" + text); + editor.setText(regText); EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); - is(editor.getText(), "//" + text, "Uncommenting disabled in Text mode"); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), regText, "Uncommenting disabled in Text mode"); editor.setText(text); editor.readOnly = true; @@ -136,10 +136,10 @@ function test() { editor.setCaretPosition(0); EventUtils.synthesizeKey("/", {accelKey: true}, testWin); is(editor.getText(), text, "Commenting disabled in ReadOnly mode"); - editor.setText("//" + text); + editor.setText(regText); EventUtils.synthesizeKey("VK_A", {accelKey: true}, testWin); - EventUtils.synthesizeKey("/", {accelKey: true, shiftKey: true}, testWin); - is(editor.getText(), "//" + text, "Uncommenting disabled in ReadOnly mode"); + EventUtils.synthesizeKey("/", {accelKey: true}, testWin); + is(editor.getText(), regText, "Uncommenting disabled in ReadOnly mode"); editor.destroy(); From e37e9fa965670e69bdd2bac3814af454c007c016 Mon Sep 17 00:00:00 2001 From: Girish Sharma Date: Mon, 9 Apr 2012 13:31:45 +0300 Subject: [PATCH 5/8] Bug 729960 - Source Editor: add shortcuts to quickly jump to the code block start and end; r=msucan --- .../sourceeditor/source-editor-orion.jsm | 105 +++++++++++ .../devtools/sourceeditor/test/Makefile.in | 1 + .../browser_bug729960_block_bracket_jump.js | 164 ++++++++++++++++++ browser/devtools/styleeditor/StyleEditor.jsm | 3 +- .../test/browser_styleeditor_new.js | 8 + 5 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js diff --git a/browser/devtools/sourceeditor/source-editor-orion.jsm b/browser/devtools/sourceeditor/source-editor-orion.jsm index 1aa2181e055..20a60562d0b 100644 --- a/browser/devtools/sourceeditor/source-editor-orion.jsm +++ b/browser/devtools/sourceeditor/source-editor-orion.jsm @@ -157,6 +157,16 @@ const DEFAULT_KEYBINDINGS = [ code: Ci.nsIDOMKeyEvent.DOM_VK_SLASH, accel: true, }, + { + action: "Move to Bracket Opening", + code: Ci.nsIDOMKeyEvent.DOM_VK_OPEN_BRACKET, + accel: true, + }, + { + action: "Move to Bracket Closing", + code: Ci.nsIDOMKeyEvent.DOM_VK_CLOSE_BRACKET, + accel: true, + }, ]; var EXPORTED_SYMBOLS = ["SourceEditor"]; @@ -398,6 +408,8 @@ SourceEditor.prototype = { "Goto Line...": [this.ui.gotoLine, this.ui], "Move Lines Down": [this._moveLines, this], "Comment/Uncomment": [this._doCommentUncomment, this], + "Move to Bracket Opening": [this._moveToBracketOpening, this], + "Move to Bracket Closing": [this._moveToBracketClosing, this], }; for (let name in actions) { @@ -1213,6 +1225,99 @@ SourceEditor.prototype = { return true; }, + /** + * Helper function for _moveToBracket{Opening/Closing} to find the offset of + * matching bracket. + * + * @param number aOffset + * The offset of the bracket for which you want to find the bracket. + * @private + */ + _getMatchingBracketIndex: function SE__getMatchingBracketIndex(aOffset) + { + return this._styler._findMatchingBracket(this._model, aOffset); + }, + + /** + * Move the cursor to the matching opening bracket if at corresponding closing + * bracket, otherwise move to the opening bracket for the current block of code. + * + * @private + */ + _moveToBracketOpening: function SE__moveToBracketOpening() + { + let mode = this.getMode(); + // Returning early if not in JavaScipt or CSS mode. + if (mode != SourceEditor.MODES.JAVASCRIPT && + mode != SourceEditor.MODES.CSS) { + return false; + } + + let caretOffset = this.getCaretOffset() - 1; + let matchingIndex = this._getMatchingBracketIndex(caretOffset); + + // If the caret is not at the closing bracket "}", find the index of the + // opening bracket "{" for the current code block. + if (matchingIndex == -1 || matchingIndex > caretOffset) { + let text = this.getText(); + let closingOffset = text.indexOf("}", caretOffset); + while (closingOffset > -1) { + let closingMatchingIndex = this._getMatchingBracketIndex(closingOffset); + if (closingMatchingIndex < caretOffset && closingMatchingIndex != -1) { + matchingIndex = closingMatchingIndex; + break; + } + closingOffset = text.indexOf("}", closingOffset + 1); + } + } + + if (matchingIndex > -1) { + this.setCaretOffset(matchingIndex); + } + + return true; + }, + + /** + * Moves the cursor to the matching closing bracket if at corresponding opening + * bracket, otherwise move to the closing bracket for the current block of code. + * + * @private + */ + _moveToBracketClosing: function SE__moveToBracketClosing() + { + let mode = this.getMode(); + // Returning early if not in JavaScipt or CSS mode. + if (mode != SourceEditor.MODES.JAVASCRIPT && + mode != SourceEditor.MODES.CSS) { + return false; + } + + let caretOffset = this.getCaretOffset(); + let matchingIndex = this._getMatchingBracketIndex(caretOffset - 1); + + // If the caret is not at the opening bracket "{", find the index of the + // closing bracket "}" for the current code block. + if (matchingIndex == -1 || matchingIndex < caretOffset) { + let text = this.getText(); + let openingOffset = text.lastIndexOf("{", caretOffset); + while (openingOffset > -1) { + let openingMatchingIndex = this._getMatchingBracketIndex(openingOffset); + if (openingMatchingIndex > caretOffset) { + matchingIndex = openingMatchingIndex; + break; + } + openingOffset = text.lastIndexOf("{", openingOffset - 1); + } + } + + if (matchingIndex > -1) { + this.setCaretOffset(matchingIndex); + } + + return true; + }, + /** * Add an event listener to the editor. You can use one of the known events. * diff --git a/browser/devtools/sourceeditor/test/Makefile.in b/browser/devtools/sourceeditor/test/Makefile.in index 5ff930550d8..d7a6c977cb0 100644 --- a/browser/devtools/sourceeditor/test/Makefile.in +++ b/browser/devtools/sourceeditor/test/Makefile.in @@ -63,6 +63,7 @@ _BROWSER_TEST_FILES = \ browser_bug729480_line_vertical_align.js \ browser_bug725430_comment_uncomment.js \ browser_bug731721_debugger_stepping.js \ + browser_bug729960_block_bracket_jump.js \ head.js \ libs:: $(_BROWSER_TEST_FILES) diff --git a/browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js b/browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js new file mode 100644 index 00000000000..fdafca08b1f --- /dev/null +++ b/browser/devtools/sourceeditor/test/browser_bug729960_block_bracket_jump.js @@ -0,0 +1,164 @@ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +function test() { + + let temp = {}; + Cu.import("resource:///modules/source-editor.jsm", temp); + let SourceEditor = temp.SourceEditor; + + waitForExplicitFinish(); + + let editor; + + const windowUrl = "data:text/xml," + + ""; + const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no"; + + let testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null); + testWin.addEventListener("load", function onWindowLoad() { + testWin.removeEventListener("load", onWindowLoad, false); + waitForFocus(initEditor, testWin); + }, false); + + function initEditor() + { + let hbox = testWin.document.querySelector("hbox"); + editor = new SourceEditor(); + editor.init(hbox, {showLineNumbers: true}, editorLoaded); + } + + function editorLoaded() + { + editor.focus(); + let JSText = "function foo(aVar) {\n" + + " // Block Level 1\n\n" + + " function level2() {\n" + + " let baz = aVar;\n" + + " // Block Level 2\n" + + " function level3() {\n" + + " // Block Level 3\n" + + " }\n" + + " }\n" + + " // Block Level 1" + + " function bar() { /* Block Level 2 */ }\n" + + "}"; + + editor.setMode(SourceEditor.MODES.JAVASCRIPT); + editor.setText(JSText); + + // Setting caret at Line 1 bracket start. + editor.setCaretOffset(19); + EventUtils.synthesizeKey("]", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 220, + "JS : Jump to closing bracket of the code block when caret at block start"); + + EventUtils.synthesizeKey("[", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 19, + "JS : Jump to opening bracket of the code block when caret at block end"); + + // Setting caret at Line 10 start. + editor.setCaretOffset(161); + EventUtils.synthesizeKey("[", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 19, + "JS : Jump to opening bracket of code block when inside the function"); + + editor.setCaretOffset(161); + EventUtils.synthesizeKey("]", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 220, + "JS : Jump to closing bracket of code block when inside the function"); + + // Setting caret at Line 6 start. + editor.setCaretOffset(67); + EventUtils.synthesizeKey("]", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 159, + "JS : Jump to closing bracket in a nested function with caret inside"); + + editor.setCaretOffset(67); + EventUtils.synthesizeKey("[", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 61, + "JS : Jump to opening bracket in a nested function with caret inside"); + + let CSSText = "#object {\n" + + " property: value;\n" + + " /* comment */\n" + + "}"; + + editor.setMode(SourceEditor.MODES.CSS); + editor.setText(CSSText); + + // Setting caret at Line 1 bracket start. + editor.setCaretOffset(8); + EventUtils.synthesizeKey("]", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 45, + "CSS : Jump to closing bracket of the code block when caret at block start"); + + EventUtils.synthesizeKey("[", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 8, + "CSS : Jump to opening bracket of the code block when caret at block end"); + + // Setting caret at Line 3 start. + editor.setCaretOffset(28); + EventUtils.synthesizeKey("[", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 8, + "CSS : Jump to opening bracket of code block when inside the function"); + + editor.setCaretOffset(28); + EventUtils.synthesizeKey("]", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 45, + "CSS : Jump to closing bracket of code block when inside the function"); + + let HTMLText = "\n" + + " \n" + + " Testing Block Jump\n" + + " \n" + + " \n" + + ""; + + editor.setMode(SourceEditor.MODES.HTML); + editor.setText(HTMLText); + + // Setting caret at Line 1 end. + editor.setCaretOffset(6); + EventUtils.synthesizeKey("]", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 6, + "HTML : Jump to block end : Nothing happens in html mode"); + + // Setting caret at Line 4 end. + editor.setCaretOffset(64); + EventUtils.synthesizeKey("[", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 64, + "HTML : Jump to block start : Nothing happens in html mode"); + + let text = "line 1\n" + + "line 2\n" + + "line 3\n" + + "line 4\n"; + + editor.setMode(SourceEditor.MODES.TEXT); + editor.setText(text); + + // Setting caret at Line 1 start. + editor.setCaretOffset(0); + EventUtils.synthesizeKey("]", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 0, + "Text : Jump to block end : Nothing happens in text mode"); + + // Setting caret at Line 4 end. + editor.setCaretOffset(28); + EventUtils.synthesizeKey("[", {accelKey: true}, testWin); + is(editor.getCaretOffset(), 28, + "Text : Jump to block start : Nothing happens in text mode"); + + editor.destroy(); + + testWin.close(); + testWin = editor = null; + + waitForFocus(finish, window); + } +} diff --git a/browser/devtools/styleeditor/StyleEditor.jsm b/browser/devtools/styleeditor/StyleEditor.jsm index 6b1a45a7ebc..dbf34901c7c 100644 --- a/browser/devtools/styleeditor/StyleEditor.jsm +++ b/browser/devtools/styleeditor/StyleEditor.jsm @@ -1177,7 +1177,8 @@ function setupBracketCompletion(aSourceEditor) editorElement.addEventListener("keypress", function onKeyPress(aEvent) { let pair = pairs[aEvent.charCode]; - if (!pair) { + if (!pair || aEvent.ctrlKey || aEvent.metaKey || + aEvent.accelKey || aEvent.altKey) { return true; } diff --git a/browser/devtools/styleeditor/test/browser_styleeditor_new.js b/browser/devtools/styleeditor/test/browser_styleeditor_new.js index 994c098fce6..4eb482e1ba8 100644 --- a/browser/devtools/styleeditor/test/browser_styleeditor_new.js +++ b/browser/devtools/styleeditor/test/browser_styleeditor_new.js @@ -90,6 +90,14 @@ function testEditorAdded(aChrome, aEditor) is(computedStyle.backgroundColor, "rgb(255, 255, 255)", "content's background color is initially white"); + EventUtils.synthesizeKey("[", {accelKey: true}, gChromeWindow); + is(aEditor.sourceEditor.getText(), "", + "Nothing happened as it is a known shortcut in source editor"); + + EventUtils.synthesizeKey("]", {accelKey: true}, gChromeWindow); + is(aEditor.sourceEditor.getText(), "", + "Nothing happened as it is a known shortcut in source editor"); + for each (let c in TESTCASE_CSS_SOURCE) { EventUtils.synthesizeKey(c, {}, gChromeWindow); } From 8c677c07212861c4561dd69e6d8fa9dc40843d7b Mon Sep 17 00:00:00 2001 From: Steven Michaud Date: Mon, 9 Apr 2012 13:58:34 -0500 Subject: [PATCH 6/8] Bug 737509 - [10.5] Crash in _cairo_quartz_surface_mask @ _cairo_user_data_array_fini. r=jmuizelaar --- gfx/cairo/cairo/src/cairo-quartz-surface.c | 14 +--- gfx/cairo/quartz-surface-mask-patch | 79 ---------------------- 2 files changed, 2 insertions(+), 91 deletions(-) delete mode 100644 gfx/cairo/quartz-surface-mask-patch diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c index 2576cca3be8..05ef97f3033 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -135,8 +135,6 @@ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; -static SInt32 _cairo_quartz_osx_version = 0x0; - static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; /* @@ -172,11 +170,6 @@ static void quartz_ensure_symbols(void) CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); - if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) { - // assume 10.5 - _cairo_quartz_osx_version = 0x1050; - } - _cairo_quartz_symbol_lookup_done = TRUE; } @@ -1154,7 +1147,7 @@ _cairo_surface_to_cgimage (cairo_surface_t *source, if (source_img == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - source_img->surface = cairo_surface_reference(source); + source_img->surface = _cairo_surface_snapshot(source); status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra); if (status) { @@ -3043,11 +3036,8 @@ _cairo_quartz_surface_mask_cg (void *abstract_surface, /* If we have CGContextClipToMask, we can do more complex masks */ if (CGContextClipToMaskPtr) { /* For these, we can skip creating a temporary surface, since we already have one */ - /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */ - if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE && - mask->extend == CAIRO_EXTEND_NONE) { + if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE) return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); - } return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); } diff --git a/gfx/cairo/quartz-surface-mask-patch b/gfx/cairo/quartz-surface-mask-patch deleted file mode 100644 index d5ee7d8be3c..00000000000 --- a/gfx/cairo/quartz-surface-mask-patch +++ /dev/null @@ -1,79 +0,0 @@ -diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c ---- a/gfx/cairo/cairo/src/cairo-quartz-surface.c -+++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c -@@ -128,20 +128,22 @@ CG_EXTERN CGImageRef CGBitmapContextCrea - */ - static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL; - static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL; - static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; - static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL; - static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; - static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; - static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; - static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; - -+static SInt32 _cairo_quartz_osx_version = 0x0; -+ - static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; - - /* - * Utility functions - */ - - #ifdef QUARTZ_DEBUG - static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest); - static void quartz_image_to_png (CGImageRef, char *dest); - #endif -@@ -163,20 +165,25 @@ static void quartz_ensure_symbols(void) - - CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask"); - CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage"); - CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType"); - CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts"); - CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath"); - CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); - CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); - CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); - -+ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) { -+ // assume 10.5 -+ _cairo_quartz_osx_version = 0x1050; -+ } -+ - _cairo_quartz_symbol_lookup_done = TRUE; - } - - CGImageRef - _cairo_quartz_create_cgimage (cairo_format_t format, - unsigned int width, - unsigned int height, - unsigned int stride, - void *data, - cairo_bool_t interpolate, -@@ -3028,22 +3035,25 @@ static cairo_int_status_t - CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); - rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip); - CGContextSetAlpha (surface->cgContext, 1.0); - - return rv; - } - - /* If we have CGContextClipToMask, we can do more complex masks */ - if (CGContextClipToMaskPtr) { - /* For these, we can skip creating a temporary surface, since we already have one */ -- if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE) -+ /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */ -+ if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE && -+ mask->extend == CAIRO_EXTEND_NONE) { - return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); -+ } - - return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); - } - - /* So, CGContextClipToMask is not present in 10.3.9, so we're - * doomed; if we have imageData, we can do fallback, otherwise - * just pretend success. - */ - if (surface->imageData) - return CAIRO_INT_STATUS_UNSUPPORTED; From 18205f3197476180b1b85d7dc5e428b5a2dfbaf3 Mon Sep 17 00:00:00 2001 From: Kyle Huey Date: Mon, 9 Apr 2012 13:48:44 -0700 Subject: [PATCH 7/8] Refix bug 741248. r=me --- dom/bindings/Makefile.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dom/bindings/Makefile.in b/dom/bindings/Makefile.in index 9754c097ef9..3bf63711d3f 100644 --- a/dom/bindings/Makefile.in +++ b/dom/bindings/Makefile.in @@ -75,7 +75,7 @@ bindinggen_dependencies := \ $(binding_header_files): %Binding.h: $(bindinggen_dependencies) \ $(webidl_base)/%.webidl \ $(NULL) - $(PYTHON_PATH) \ + $(PYTHON) $(topsrcdir)/config/pythonpath.py \ $(PLY_INCLUDE) -I$(srcdir)/parser \ $(srcdir)/BindingGen.py $(ACCESSOR_OPT) header \ $(srcdir)/Bindings.conf $*Binding \ @@ -84,7 +84,7 @@ $(binding_header_files): %Binding.h: $(bindinggen_dependencies) \ $(binding_cpp_files): %Binding.cpp: $(bindinggen_dependencies) \ $(webidl_base)/%.webidl \ $(NULL) - $(PYTHON_PATH) \ + $(PYTHON) $(topsrcdir)/config/pythonpath.py \ $(PLY_INCLUDE) -I$(srcdir)/parser \ $(srcdir)/BindingGen.py $(ACCESSOR_OPT) cpp \ $(srcdir)/Bindings.conf $*Binding \ From 16c0bf68725e9cb63465b30ffe04a3a9bc5ecd39 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Mon, 9 Apr 2012 14:45:13 -0700 Subject: [PATCH 8/8] Back out 17e4143dd6f0 (bug 737509) on suspicion of causing a crash in browser_tabview_bug597248.js --- gfx/cairo/cairo/src/cairo-quartz-surface.c | 14 +++- gfx/cairo/quartz-surface-mask-patch | 79 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) create mode 100644 gfx/cairo/quartz-surface-mask-patch diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c index 05ef97f3033..2576cca3be8 100644 --- a/gfx/cairo/cairo/src/cairo-quartz-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c @@ -135,6 +135,8 @@ static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; +static SInt32 _cairo_quartz_osx_version = 0x0; + static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; /* @@ -170,6 +172,11 @@ static void quartz_ensure_symbols(void) CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); + if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) { + // assume 10.5 + _cairo_quartz_osx_version = 0x1050; + } + _cairo_quartz_symbol_lookup_done = TRUE; } @@ -1147,7 +1154,7 @@ _cairo_surface_to_cgimage (cairo_surface_t *source, if (source_img == NULL) return _cairo_error (CAIRO_STATUS_NO_MEMORY); - source_img->surface = _cairo_surface_snapshot(source); + source_img->surface = cairo_surface_reference(source); status = _cairo_surface_acquire_source_image (source_img->surface, &source_img->image_out, &source_img->image_extra); if (status) { @@ -3036,8 +3043,11 @@ _cairo_quartz_surface_mask_cg (void *abstract_surface, /* If we have CGContextClipToMask, we can do more complex masks */ if (CGContextClipToMaskPtr) { /* For these, we can skip creating a temporary surface, since we already have one */ - if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE) + /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */ + if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE && + mask->extend == CAIRO_EXTEND_NONE) { return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); + } return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); } diff --git a/gfx/cairo/quartz-surface-mask-patch b/gfx/cairo/quartz-surface-mask-patch new file mode 100644 index 00000000000..d5ee7d8be3c --- /dev/null +++ b/gfx/cairo/quartz-surface-mask-patch @@ -0,0 +1,79 @@ +diff --git a/gfx/cairo/cairo/src/cairo-quartz-surface.c b/gfx/cairo/cairo/src/cairo-quartz-surface.c +--- a/gfx/cairo/cairo/src/cairo-quartz-surface.c ++++ b/gfx/cairo/cairo/src/cairo-quartz-surface.c +@@ -128,20 +128,22 @@ CG_EXTERN CGImageRef CGBitmapContextCrea + */ + static void (*CGContextClipToMaskPtr) (CGContextRef, CGRect, CGImageRef) = NULL; + static void (*CGContextDrawTiledImagePtr) (CGContextRef, CGRect, CGImageRef) = NULL; + static unsigned int (*CGContextGetTypePtr) (CGContextRef) = NULL; + static void (*CGContextSetShouldAntialiasFontsPtr) (CGContextRef, bool) = NULL; + static void (*CGContextSetAllowsFontSmoothingPtr) (CGContextRef, bool) = NULL; + static bool (*CGContextGetAllowsFontSmoothingPtr) (CGContextRef) = NULL; + static CGPathRef (*CGContextCopyPathPtr) (CGContextRef) = NULL; + static CGFloat (*CGContextGetAlphaPtr) (CGContextRef) = NULL; + ++static SInt32 _cairo_quartz_osx_version = 0x0; ++ + static cairo_bool_t _cairo_quartz_symbol_lookup_done = FALSE; + + /* + * Utility functions + */ + + #ifdef QUARTZ_DEBUG + static void quartz_surface_to_png (cairo_quartz_surface_t *nq, char *dest); + static void quartz_image_to_png (CGImageRef, char *dest); + #endif +@@ -163,20 +165,25 @@ static void quartz_ensure_symbols(void) + + CGContextClipToMaskPtr = dlsym(RTLD_DEFAULT, "CGContextClipToMask"); + CGContextDrawTiledImagePtr = dlsym(RTLD_DEFAULT, "CGContextDrawTiledImage"); + CGContextGetTypePtr = dlsym(RTLD_DEFAULT, "CGContextGetType"); + CGContextSetShouldAntialiasFontsPtr = dlsym(RTLD_DEFAULT, "CGContextSetShouldAntialiasFonts"); + CGContextCopyPathPtr = dlsym(RTLD_DEFAULT, "CGContextCopyPath"); + CGContextGetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextGetAllowsFontSmoothing"); + CGContextSetAllowsFontSmoothingPtr = dlsym(RTLD_DEFAULT, "CGContextSetAllowsFontSmoothing"); + CGContextGetAlphaPtr = dlsym(RTLD_DEFAULT, "CGContextGetAlpha"); + ++ if (Gestalt(gestaltSystemVersion, &_cairo_quartz_osx_version) != noErr) { ++ // assume 10.5 ++ _cairo_quartz_osx_version = 0x1050; ++ } ++ + _cairo_quartz_symbol_lookup_done = TRUE; + } + + CGImageRef + _cairo_quartz_create_cgimage (cairo_format_t format, + unsigned int width, + unsigned int height, + unsigned int stride, + void *data, + cairo_bool_t interpolate, +@@ -3028,22 +3035,25 @@ static cairo_int_status_t + CGContextSetAlpha (surface->cgContext, solid_mask->color.alpha); + rv = _cairo_quartz_surface_paint_cg (surface, op, source, clip); + CGContextSetAlpha (surface->cgContext, 1.0); + + return rv; + } + + /* If we have CGContextClipToMask, we can do more complex masks */ + if (CGContextClipToMaskPtr) { + /* For these, we can skip creating a temporary surface, since we already have one */ +- if (mask->type == CAIRO_PATTERN_TYPE_SURFACE && mask->extend == CAIRO_EXTEND_NONE) ++ /* For some reason this doesn't work reliably on OS X 10.5. See bug 721663. */ ++ if (_cairo_quartz_osx_version >= 0x1060 && mask->type == CAIRO_PATTERN_TYPE_SURFACE && ++ mask->extend == CAIRO_EXTEND_NONE) { + return _cairo_quartz_surface_mask_with_surface (surface, op, source, (cairo_surface_pattern_t *) mask, clip); ++ } + + return _cairo_quartz_surface_mask_with_generic (surface, op, source, mask, clip); + } + + /* So, CGContextClipToMask is not present in 10.3.9, so we're + * doomed; if we have imageData, we can do fallback, otherwise + * just pretend success. + */ + if (surface->imageData) + return CAIRO_INT_STATUS_UNSUPPORTED;