Index: src/__phutil_library_map__.php =================================================================== --- src/__phutil_library_map__.php +++ src/__phutil_library_map__.php @@ -81,6 +81,7 @@ 'AphrontRequestTestCase' => 'aphront/__tests__/AphrontRequestTestCase.php', 'AphrontResponse' => 'aphront/response/AphrontResponse.php', 'AphrontSideNavFilterView' => 'view/layout/AphrontSideNavFilterView.php', + 'AphrontStackTraceView' => 'view/widget/AphrontStackTraceView.php', 'AphrontTableView' => 'view/control/AphrontTableView.php', 'AphrontTagView' => 'view/AphrontTagView.php', 'AphrontTokenizerTemplateView' => 'view/control/AphrontTokenizerTemplateView.php', @@ -2401,6 +2402,7 @@ 'AphrontRequestFailureView' => 'AphrontView', 'AphrontRequestTestCase' => 'PhabricatorTestCase', 'AphrontSideNavFilterView' => 'AphrontView', + 'AphrontStackTraceView' => 'AphrontView', 'AphrontTableView' => 'AphrontView', 'AphrontTagView' => 'AphrontView', 'AphrontTokenizerTemplateView' => 'AphrontView', Index: src/aphront/configuration/AphrontDefaultApplicationConfiguration.php =================================================================== --- src/aphront/configuration/AphrontDefaultApplicationConfiguration.php +++ src/aphront/configuration/AphrontDefaultApplicationConfiguration.php @@ -243,7 +243,9 @@ } if (PhabricatorEnv::getEnvConfig('phabricator.developer-mode')) { - $trace = $this->renderStackTrace($ex->getTrace(), $user); + $trace = id(new AphrontStackTraceView()) + ->setUser($user) + ->setTrace($ex->getTrace()); } else { $trace = null; } @@ -290,110 +292,4 @@ )); } - private function renderStackTrace($trace, PhabricatorUser $user) { - - $libraries = PhutilBootloader::getInstance()->getAllLibraries(); - - // TODO: Make this configurable? - $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/'; - - $callsigns = array( - 'arcanist' => 'ARC', - 'phutil' => 'PHU', - 'phabricator' => 'P', - ); - - $rows = array(); - $depth = count($trace); - foreach ($trace as $part) { - $lib = null; - $file = idx($part, 'file'); - $relative = $file; - foreach ($libraries as $library) { - $root = phutil_get_library_root($library); - if (Filesystem::isDescendant($file, $root)) { - $lib = $library; - $relative = Filesystem::readablePath($file, $root); - break; - } - } - - $where = ''; - if (isset($part['class'])) { - $where .= $part['class'].'::'; - } - if (isset($part['function'])) { - $where .= $part['function'].'()'; - } - - if ($file) { - if (isset($callsigns[$lib])) { - $attrs = array('title' => $file); - try { - $attrs['href'] = $user->loadEditorLink( - '/src/'.$relative, - $part['line'], - $callsigns[$lib]); - } catch (Exception $ex) { - // The database can be inaccessible. - } - if (empty($attrs['href'])) { - $attrs['href'] = sprintf($path, $callsigns[$lib]). - str_replace(DIRECTORY_SEPARATOR, '/', $relative). - '$'.$part['line']; - $attrs['target'] = '_blank'; - } - $file_name = phutil_tag( - 'a', - $attrs, - $relative); - } else { - $file_name = phutil_tag( - 'span', - array( - 'title' => $file, - ), - $relative); - } - $file_name = hsprintf('%s : %d', $file_name, $part['line']); - } else { - $file_name = phutil_tag('em', array(), '(Internal)'); - } - - - $rows[] = array( - $depth--, - $lib, - $file_name, - $where, - ); - } - $table = new AphrontTableView($rows); - $table->setHeaders( - array( - 'Depth', - 'Library', - 'File', - 'Where', - )); - $table->setColumnClasses( - array( - 'n', - '', - '', - 'wide', - )); - - return phutil_tag( - 'div', - array('class' => 'exception-trace'), - array( - phutil_tag( - 'div', - array('class' => 'exception-trace-header'), - pht('Stack Trace')), - $table->render(), - )); - } - } Index: src/aphront/response/AphrontRedirectResponse.php =================================================================== --- src/aphront/response/AphrontRedirectResponse.php +++ src/aphront/response/AphrontRedirectResponse.php @@ -8,6 +8,14 @@ class AphrontRedirectResponse extends AphrontResponse { private $uri; + private $stackWhenCreated; + + public function __construct() { + if ($this->shouldStopForDebugging()) { + // If we're going to stop, capture the stack so we can print it out. + $this->stackWhenCreated = id(new Exception())->getTrace(); + } + } public function setURI($uri) { $this->uri = $uri; @@ -33,31 +41,44 @@ public function buildResponseString() { if ($this->shouldStopForDebugging()) { + $user = new PhabricatorUser(); + $view = new PhabricatorStandardPageView(); $view->setRequest($this->getRequest()); $view->setApplicationName('Debug'); $view->setTitle('Stopped on Redirect'); - $error = new AphrontErrorView(); - $error->setSeverity(AphrontErrorView::SEVERITY_NOTICE); - $error->setTitle('Stopped on Redirect'); + $dialog = new AphrontDialogView(); + $dialog->setUser($user); + $dialog->setTitle('Stopped on Redirect'); + + $dialog->appendParagraph( + pht( + 'You were stopped here because %s is set in your configuration.', + phutil_tag('tt', array(), 'debug.stop-on-redirect'))); + + $dialog->appendParagraph( + pht( + 'You are being redirected to: %s', + phutil_tag('tt', array(), $this->getURI()))); + + $dialog->addCancelButton($this->getURI(), pht('Continue')); + + $dialog->appendChild(phutil_tag('br')); - $error->appendChild(phutil_tag('p', array(), pht( - 'You were stopped here because %s is set in your configuration.', - phutil_tag('tt', array(), 'debug.stop-on-redirect')))); + $dialog->appendChild( + id(new AphrontStackTraceView()) + ->setUser($user) + ->setTrace($this->stackWhenCreated)); - $link = phutil_tag( - 'a', - array( - 'href' => $this->getURI(), - ), - $this->getURI()); + $dialog->setIsStandalone(true); + $dialog->setWidth(AphrontDialogView::WIDTH_FULL); - $error->appendChild(phutil_tag('p', array(), pht( - 'Continue to: %s', - $link))); + $box = id(new PHUIBoxView()) + ->addMargin(PHUI::MARGIN_LARGE) + ->appendChild($dialog); - $view->appendChild($error); + $view->appendChild($box); return $view->render(); } Index: src/view/widget/AphrontStackTraceView.php =================================================================== --- /dev/null +++ src/view/widget/AphrontStackTraceView.php @@ -0,0 +1,120 @@ +trace = $trace; + return $this; + } + + public function render() { + $user = $this->getUser(); + $trace = $this->trace; + + $libraries = PhutilBootloader::getInstance()->getAllLibraries(); + + // TODO: Make this configurable? + $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/'; + + $callsigns = array( + 'arcanist' => 'ARC', + 'phutil' => 'PHU', + 'phabricator' => 'P', + ); + + $rows = array(); + $depth = count($trace); + foreach ($trace as $part) { + $lib = null; + $file = idx($part, 'file'); + $relative = $file; + foreach ($libraries as $library) { + $root = phutil_get_library_root($library); + if (Filesystem::isDescendant($file, $root)) { + $lib = $library; + $relative = Filesystem::readablePath($file, $root); + break; + } + } + + $where = ''; + if (isset($part['class'])) { + $where .= $part['class'].'::'; + } + if (isset($part['function'])) { + $where .= $part['function'].'()'; + } + + if ($file) { + if (isset($callsigns[$lib])) { + $attrs = array('title' => $file); + try { + $attrs['href'] = $user->loadEditorLink( + '/src/'.$relative, + $part['line'], + $callsigns[$lib]); + } catch (Exception $ex) { + // The database can be inaccessible. + } + if (empty($attrs['href'])) { + $attrs['href'] = sprintf($path, $callsigns[$lib]). + str_replace(DIRECTORY_SEPARATOR, '/', $relative). + '$'.$part['line']; + $attrs['target'] = '_blank'; + } + $file_name = phutil_tag( + 'a', + $attrs, + $relative); + } else { + $file_name = phutil_tag( + 'span', + array( + 'title' => $file, + ), + $relative); + } + $file_name = hsprintf('%s : %d', $file_name, $part['line']); + } else { + $file_name = phutil_tag('em', array(), '(Internal)'); + } + + + $rows[] = array( + $depth--, + $lib, + $file_name, + $where, + ); + } + $table = new AphrontTableView($rows); + $table->setHeaders( + array( + 'Depth', + 'Library', + 'File', + 'Where', + )); + $table->setColumnClasses( + array( + 'n', + '', + '', + 'wide', + )); + + return phutil_tag( + 'div', + array('class' => 'exception-trace'), + array( + phutil_tag( + 'div', + array('class' => 'exception-trace-header'), + pht('Stack Trace')), + $table->render(), + )); + } + +}